音视频开发学习

本文介绍了音视频开发的基础知识,包括视频的RGB和YUV格式、压缩编码原理,以及音频的采样率和播放原理。随后详细阐述了如何使用ffmpeg的C++接口将H264视频转换为YUV格式,涉及解码、重采样等步骤,并提到了音频和视频的复用实现。
摘要由CSDN通过智能技术生成

一、音视频开发基础知识

在这里插入图片描述
在这里插入图片描述

1. 视频基础理论

  1. 视频的表示
    RGB格式:数字图像用RGB表示,每个RGB分量用8bit表示 = 256,采用的数值越大,表现出来的现象越精细。
    YUV格式:数字图像也可以用YUV,Y表示明亮度(灰阶值)、U、V表示的色度,作用是描述影像色彩及饱和度。主要用于视频信号的压缩、传输和存储。YUV格式下又有两大类:planar、packed
    ·packed:packed格式下每个像素点下的Y、U、V三个分量的数值是相邻连续的
    在这里插入图片描述
    ·planar:planar格式下每个像素点下的Y、U、V三个分量的数值是分开存储的
    在这里插入图片描述
    ·YUV444:单个像素点下Y、U、V分量之间一一对应
    ·YUV422:两个相邻Y对应一组UV分量
    在这里插入图片描述
    ·YUV420:4个相邻的Y对应一组UV分量
    在这里插入图片描述
    采用YUV420P是可以将原先采用RGB编码格式的图像/视频内存减少一半

  2. 视频的压缩/编码:压缩分为帧内压缩与帧间压缩,压缩的目的是使电影、图像的容量减小
    ①帧内压缩:相邻的像素点做同一个表示
    ②帧间压缩:记录两个变化不大的帧偏移位置

  3. YUV->RGB转换公式:因为YUV最终还是要转到RGB显示,容易出现解码异常的问题:YUV各分量的值都为0
    在这里插入图片描述
    在这里插入图片描述
    从低分辨率->高分辨率画质不会得到提升

  4. I B P 帧的概念
    ①I 帧:不需要参考其他画面即可生成自己的图像
    在这里插入图片描述
    ②P帧:
    在这里插入图片描述
    在这里插入图片描述

    ③B帧:
    在这里插入图片描述
    在这里插入图片描述

  5. 常见视频压缩算法:H264、H265

2. 音频基础理论

  1. 音频采样率/采样精度:一秒钟采样44.1khz或者48khz
    在这里插入图片描述
    如左图音频是连续的一个波值,右图则是离散采样后的效果
    在这里插入图片描述
    在这里插入图片描述
    通道:音源越多越真实。

    1. 声卡播放原理:声卡必须固定参数,声音/视频参数传到系统里面中就会先做MIX操作(转成统一声卡需要的格式,然后混音)
      在这里插入图片描述
  2. 交错模式/非交错模式
    在这里插入图片描述
    ①交错模式:左右左右
    在这里插入图片描述
    ②非交错模式:左、右
    在这里插入图片描述

封装、解码、重采样、像素格式
封装:从文件中把音频/视频读取出来
解码:将读取出来的音频/视频进行解压,视频需要转换成显卡适配的格式。音频则需要转换成声卡所需的格式。才能播放

  1. 封装格式:
    MPEG-4 封装标准:
    MPEG-4是一套用于音频、视频信息的压缩编码标准
    MPEG-4 Part 14 MPEG-4文件格式Part 15AVC文件格式,常用的mp4文件就属于MPEG-4 Part 14
    采用 H264 做视频压缩,采用AVC Part10做音频压缩
    AVI 封装标准:压缩标准可任意选择
    FLV ts 流媒体格式
  2. 编码格式:
    视频 H264 ( AVC Part10 ) , wmv ,XviD ( Part2 ) , mjpeg
    音频 aac MP3 ape flac
  3. 封装格式/编码格式 解码过程:
    在这里插入图片描述
    封装格式头:头部装入视频/音频 流的信息
    在这里插入图片描述
    时评编码帧:

二、ffmpeg编程示例

1. 用C++ 编程将H264 视频转为YUV格式

1)、具体代码步骤:

(一)、 初始化全局AVFormatContext对象
  1. avformat_alloc_context() 创建AVFormatContext,它是存储音视频封装格式中包含的信息的结构体,也是FFmpeg中统领全局的结构体,对文件的封装、编码操作从这里开始。

    //创建上下文对象
    AVFormatContext* formatContext = avformat_alloc_context();
    
(二)、 打开流媒体获取流媒体具体信息并设置到AVFormatContext对象中
  1. avformat_open_input() 打开流媒体文件:

    //打开流媒体文件
    char* inputFile = "C:\\Users\\Administrator\\Desktop\\Titanic.h264";
    avformat_open_input(&formatContext,inputFile,NULL,NULL);
    
  2. avformat_find_stream_info() 获取媒体文件中的具体信息

    //获取媒体文件中的具体流信息
    avformat_find_stream_info(formatContext,NULL);
    
(三)、 查找/配置/开启 处理视频流的解码器
  1. av_find_best_stream查找适配视频流的流索引并配置该流索引下的解码器,返回该流的索引值

     const AVCodec * codec;  // 解码器
    //查找属于AVMEDIA_TYPE_VIDEO流索引,并配置该流索性下的解码器
     int videoStreamIndex = av_find_best_stream(formatContext,AVMEDIA_TYPE_VIDEO,-1,-1,&codec,-1);
    
  2. avcodec_alloc_context3根据查到的解码器创建分配空间以及初始化解码器上的下文信息

      AVCodecContext * codecContext = avcodec_alloc_context3(codec);
    
  3. videoStreamIndex 查到 formatContext中的属于AVMEDIA_TYPE_VIDEO类型解码器的信息

    //获取该视频类型流下解码器的具体参数
    AVCodecParameters* codecParameters = formatContext->streams[videoStreamIndex]->codecpar;
    
  4. avcodec_parameters_to_context()将参数信息设置到解码器上下文信息对象中

     //将这些参数设置到解码器上下文中
    avcodec_parameters_to_context(codecContext,codecParameters);
    
  5. avcodec_open2()打开解码器:

    //打开解码器
    avcodec_open2(codecContext,codec,NULL);
    
(四)、解码前/后帧数据的缓存位置创建
  1. av_packet_alloc() 创建packet,用于存储解码前的数据

    AVPacket *packet = av_packet_alloc();
    
  2. av_frame_alloc()创建Frame 并设置参数用于存储解码后的数据

    //创建Frame并设置参数,用于存储解码后的数据
    AVFrame * frame = av_frame_alloc();
    frame->width = codecParameters->width;
    frame->height = codecParameters->height;
    frame->format = codecParameters->format;
    av_frame_get_buffer(frame,32);
    
    AVFrame *yuvFrame = av_frame_alloc();
    yuvFrame->width = codecParameters->width;
    yuvFrame->height = codecParameters->height;
    yuvFrame->format = AV_PIX_FMT_YUV420P;
    av_frame_get_buffer(yuvFrame,32);
    
(五)、设置重采样上下文
  1. 利用sws_getContext()设置重采样上下文

     SwsContext* imgSwsContext = sws_getContext(codecParameters->width,
                       codecParameters->height,
                       static_cast<AVPixelFormat>(codecParameters->format),
                       codecParameters->width,
                       codecParameters->height,
                       AV_PIX_FMT_YUV420P,
                       SWS_BICUBIC,
                       NULL, NULL, NULL);
    
(六)、读取媒体流中每一帧数据并进行编解码
  1. av_read_frame()+while 读取每一帧数据

    	while(av_read_frame(formatContext,packet)>=0){
    		//如果该帧不是视频流则丢弃
            if (packet->stream_index!=videoStreamIndex){
                av_packet_unref(packet);  //当不用这个packet了,你需要释放av_packet_unref(&packet), 此时包头就会恢复成默认值.
            }
    	}
    
  2. avcodec_send_packet() 发送编码数据包。该函数只是将一个packet放入到队列中等待解码。并不是一个packet,就代表一个frame,解码操作是在该函数中进行的。完成此操作后,解码后的数据放在avctx->internal->buff_frame中。

    avcodec_send_packet(codecContext,packet);
    
  3. avcodec_receive_frame() +while 接收解码后数据,并写入到目标文件中

     while (avcodec_receive_frame(cod_ctx, frame) >= 0)
        {
    
           printf("decode frame count = %d\n" , count++);
           sws_scale(imgSwsContext,frame->data,frame->linesize,0,codecParameters->height,yuvFrame->data,yuvFrame->linesize);
        
            int y_size = yuv_frame->width * yuv_frame->height;
            fwrite(yuv_frame->data[0], 1, y_size, out_fb);
            fwrite(yuv_frame->data[1], 1, y_size/4, out_fb);
            fwrite(yuv_frame->data[2], 1, y_size/4, out_fb);
        }
    
        av_packet_unref(packet);
    
(七)、释放对象:
    fclose(outputFile);

    av_frame_free(&destFrame);
    av_frame_free(&srcFrame);
    avcodec_close(codecContext);
    avformat_close_input(&formatContext);

2)、具体成员及方法

1. AVFormatContext:

AVFormatContext是存储音视频封装格式中包含的信息的结构体,也是FFmpeg中统领全局的结构体,对文件的封装、编码操作从这里开始。

2. avformat_alloc_context(void);

该方法主要完成
AVFormatContext的空间分配,注意分配在堆上
②给AVFormatContext的成员赋默认值
③完成AVFormatContext内部使用对象AVFormatInternal结构体的空间分配及其部分成员字段的赋值

3. avformat_open_input():

打开输入流,读取媒体文件的头信息,但不打开编码器,需要使用编码相关的函数打开编码器;

4.avformat_find_stream_info():

从读取到的媒体文件的音视频中获取流的具体信息。

5. AVCodec:

存储编解码器信息的结构体

6. av_find_best_stream():

用于找到用户想要寻找的流的信息,比如查找视频流,音频流,字幕流,如果没有指定解码器则使用默认的解码器,如果对应的流没有相应的解码器支持则会被忽略。

7. avcodec_alloc_context3():

AVCodecContext分配内存空间,并设置默认的值

8. AVCodecContext:

AVCodecContext结构表示程序运行的当前Codec使用的上下文,记录了所有Codec共有的属性(并且是在程序运行时才能确定其值)和关联其他结构的字段。不管是编码,还是解码都会用到

9. AVCodecParameters:

解码结构体的结构体参数,解码之前都需要通过它来初始化AVCodecContext

10. avcodec_parameters_to_context():

将音频流信息拷贝到新的AVCodecContext结构体中

11.avcodec_open2():

打开解码器

12.AVPacket:

是存储压缩编码数据相关信息的结构体。保存了压缩后的数据以及这些数据的信息,比如显示时间戳pts,解码时间戳dts,每一帧持续的时间duration以及媒体流的索引等

13. av_packet_alloc():

简单的创建一个AVPacket,将其字段设为默认值

14. AVFrame:

AVFrame结构体一般用于存储原始数据(即非压缩数据,例如对视频来说是YUV,RGB,对音频来说是PCM),此外还包含了一些相关的信息。

15. av_frame_alloc():

分配并默认初始化一个AVFrame对象

16. av_frame_get_buffer:

对于视频frame,只用在调用函数之前设置好图像的宽高和图像格式等信息,音频设置好nb_samples,channel_layout 和采样格式等信息,此函数将填充AVFrame.data和AVFrame.buf数组,并在必要时分配和填充AVFrame.extended_data和AVFrame.extended_buf。而且采用这种方式填充AVFrame的data后,在最后释放时调用av_frame_free可以直接释放掉AVFrame的所有内存,不会出现内存泄漏的问题。

17. SwsContext:

视频格式转换上下文,编解码中可能需要对视频格式重新设置,就需要用到SwsContext。

18. sws_getContext():

设置视频转换SwsContext 对象的上下文。

19.av_read_frame():

用是读取码流中的音频若干帧或者视频一帧。例如,解码视频的时候,每解码一个视频帧,需要先调用 av_read_frame()获得一帧视频的压缩数据,然后才能对该数据进行解码

20.avcodec_send_packet():

负责把AVpacket数据包发送给解码器,avcodec_receive_frame()则从解码器取出一帧AVFrame数据。返回0,代表解码成功;返回EAGAIN,代表当前状态无可输出的数据;返回EOF,代表到达文件流结尾;返回INVAL,代表解码器未打开或者当前打开的是编码器;返回INPUT_CHANGED,代表输入参数发生变化,比如width和height改变。

21.avcodec_receive_frame():

函数的主要功能是从解码队列中取出一帧

22.sws_scale():

主要用于处理图片像素数据的类库。可以完成图片像素格式的转换,图片的拉伸等工作

2. 用C++ 调用 ffmpeg接口实现音频+视频复用

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值