FFmpeg音视频开发知识点(一)

系列文章目录



前言

最近在做音视频编解码实时流项目【h323+ffmpeg+rtp】,特意记录一下开发中遇到的部分问题,以及现学现用的部分知识点,前期急急忙忙没认真看,直接拿过来用,用的不对,各种问题,真的一言难尽,最终还是要静下心来认真研究才行;

温馨提示:我使用的ffmpeg版本是4.4,不同版本可能有所差异


一、AVCodecContext详解

分为音频编码、音频解码、视频编码、视频解码四个部分,虽说同用的是同一个结构体、但是含义却不一样,我分开记录一下

1.视频编码器

编码器上下文AVCodecContext是FFmpeg中用于描述编码器状态的结构体,包含了许多参数和配置选项,用于控制编码器的行为和性能。下面是一些常用的视频编码器上下文参数及其意义的详细讲解

1、codec_id:指定编码器的ID,如AV_CODEC_ID_H264表示H.264编码器。

pCodecCtx->codec_id = AV_CODEC_ID_H264;

2、bit_rate:指定编码的比特率,即视频数据每秒使用的比特数,通常以bit/s为单位。该参数的大小直接影响到视频的质量和大小,过高的比特率可能会导致视频过大,而过低的比特率可能会导致视频质量下降。

pCodecCtx->bit_rate = 3000000; // 比特率3000Kbps

2、width/height:指定视频的宽度和高度,单位为像素。

pCodecCtx->width = 1920;        // 1080P
pCodecCtx->height = 1080;

3、gop_size:指定关键帧(I帧)的间隔大小,即每隔多少帧就产生一个I帧。该参数的大小通常与视频的帧率和编码器的性能有关,过小的间隔可能会导致视频质量下降,而过大的间隔可能会导致视频播放时的卡顿现象。

pCodecCtx->gop_size = 25;

4、max_b_frames:指定编码器允许使用的最大B帧数量。B帧通常是在前后两个关键帧之间插入的一种帧类型,可以提高视频压缩比和画面流畅度,但同时也增加了编码器的计算量。

pCodecCtx->max_b_frames = 2;

5、pix_fmt:指定输入数据的像素格式,例如AV_PIX_FMT_YUV420P表示420格式的YUV数据。

pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;

6、time_base:指定视频的时间基准,即每个时间单位表示多少秒。通常为分数形式,例如{1, 25}表示每个时间单位为1/25秒。

pCodecCtx->time_base = (AVRational){1, 25};

7、workaround_bugs:用于指定编码器是否需要尝试解决一些已知的编码器或解码器的问题。将该参数设置为FF_BUG_AUTODETECT表示编码器会自动检测和解决一些已知的问题,包括一些标准不一致或解码器错误等问题。
在实际应用中,是否需要设置取决于具体的应用场景。如果您的应用程序需要处理多种不同的视频源或使用多种不同的解码器进行解码,那么可以考虑设置该参数,以确保编码器能够处理各种不同的视频源和解码器。但如果您的应用程序只处理特定类型的视频源或使用特定的解码器进行解码,可以考虑禁用该参数以提高编码效率。

pCodecCtx->workaround_bugs = FF_BUG_AUTODETECT; // 0是禁用

8、thread_count:指定编码器使用的线程数量,用于提高编码器的并行处理能力。线程数的大小应根据实际情况和计算资源进行设置。

pCodecCtx->thread_count = 4;

9、refs:表示编码器在编码过程中可以参考的前向帧的数量。参考帧的数量对编码效果和编码速度都有影响。一般情况下,参考帧数量越多,编码效果越好,但编码速度也越慢。
对于参考帧的设置,需要综合考虑编码效果和编码速度。如果您的视频场景比较简单,可以将ref设置为1,这样可以提高编码速度,但可能会影响编码质量。如果您需要更好的编码质量,可以将ref设置为2或更高,但这样会降低编码速度。
所以,具体的ref设置要根据您的具体场景和需求来进行选择。如果您的视频场景比较简单,可以将ref设置为1,否则可以考虑适当增加ref的值。
需要注意的是,ref的值不能超过编码器支持的最大值,否则会导致编码器初始化失败。对于H.264编码器,默认的最大参考帧数是16,因此将ref设置为16或更高可能会导致编码器初始化失败。

pCodecCtx->refs = 1;

10、flags:指定编码器的各种标志,用于控制编码器的行为和性能。

// 生成全局头信息,在某些情况下非常有用,例如将H.264码流封装为MP4或MKV格式时,可以让封装器更容易地处理。
// 具体来说,全局头包含编码器的配置参数和SPS/PPS信息,这些信息对于解码器的正确解码非常重要。
// 因此,如果您需要将编码后的视频流进行封装,建议设置该标志。如果您仅仅需要将编码后的视频流保存为裸流,则不必设置该标志。
pCodecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

// 表示输出的视频流可能会被截断。在一些情况下,输出的视频流可能会被截断,例如网络传输过程中发生了丢包,
//或者存储设备写入速度不足等。如果输出的视频流被截断,那么解码器可能无法正确解码视频流,导致输出的视频出现异常。
// 因此,是否需要设置取决于具体的应用场景。如果您的应用程序需要保证输出的视频流的完整性,那么可以考虑设置该参数。但如果您的应用程序对视频流的完整性要求不高,可以不设置该参数。
pCodecCtx->flags |= AV_CODEC_FLAG_TRUNCATED;

11、bit_rate_tolerance:参数则是指编码器输出比特率的容差,单位同样为bit/s。它表示编码器在进行比特率控制时,可以允许的比特率偏差范围。如果比特率超过了这个容差范围,编码器会尝试进行调整,以保持输出比特率在指定范围内。通常情况下,bit_rate_tolerance参数应该设置为bit_rate参数的10%左右;
需要注意的是,在设置bit_rate参数时,还需要考虑到编码器支持的最大比特率。如果bit_rate超过了编码器支持的最大比特率,编码器会自动进行调整。因此,为了避免比特率过高导致编码器自动调整输出比特率,可以查询编码器支持的最大比特率,并根据其进行设置

pCodecCtx->bit_rate_tolerance = pCodecCtx->bit_rate / 10;

12、profile:影响编码器的性能和输出视频的兼容性,H.264编码器支持多个profile,包括baseline、main、high等。不同的profile对应着不同的编码复杂度和输出视频兼容性。其中,baseline编码速度快,但是编码复杂度和输出视频兼容性一般,high则刚好相反;
对于H.264编码器而言,默认情况下profile参数的值是未定义的,需要手动设置。一般来说,如果不进行设置,编码器会根据当前的参数自动选择一个合适的profile。但是,在特定的应用场景下,手动设置profile参数可以带来更好的编码效果和更高的兼容性。

pCodecCtx->profile = FF_PROFILE_H264_BASELINE;

constrained profile可以使得输出的视频更加稳定、兼容性更好,但是可能会影响视频的质量。在一些情况下,constrained profile会限制一些高级的编码特性,使得编码效果与非constrained profile相比略有差别,如果您的应用程序对视频质量要求较高,且不希望受到任何限制,那么可以不设置该参数。但如果您希望输出的视频具有更高的兼容性、更加稳定,那么可以考虑设置该参数

pCodecCtx->profile |= FF_PROFILE_H264_CONSTRAINED;

13、priv_data:私有数据,即编码器的私有数据,用于存储编码器的内部状态和数据结构;
可以注意到,有些参数可以直接通过AVCodecContext的成员赋值,有些是通过priv_data来赋值,两个设置作用一样,理论上只需要通过一种方式设置就行;但两者之间还是有所差异,可以根据自己的需要进行设置,具体如下:

1、有些参数必须使用priv_data来设置,比如字符串类型的;或者AVCodecContext的成员没有的;
2、将配置参数设置到编码器的私有数据中,可以确保这些参数只对该编码器实例有效,并且不会对其他编码器实例产生影响。这对于同时处理多个视频流的应用程序非常重要,因为每个流可能需要不同的编码参数。此外,使用私有数据设置参数可以避免在参数传递过程中出现不必要的副作用或错误,提高代码的健壮性和可维护性

preset:参数是x264编码器的一种预设选项,用于设置编码速度和编码质量的平衡。不同的预设选项可以在编码速度和编码质量之间进行权衡,通常可以选择的预设选项包括:ultrafast、superfast、veryfast、faster、fast、medium、slow、slower、veryslow、placebo,其中ultrafast是最快的预设,placebo是最慢但是编码效果最好的预设;
如果对编码速度没有过高的要求,可以设置preset参数为slow或者slower,这样可以获得更好的编码质量。但是需要注意的是,使用更慢的预设会增加编码时间和计算资源的消耗,对于某些场景可能会影响实时性能。
因此,如果在实时场景中需要编码视频,并且需要保证编码速度和实时性能,那么可以考虑设置预设为ultrafast或superfast。如果需要更高的编码质量,并且可以承受一定的延迟,可以选择更慢的预设

av_opt_set(pCodecCtx->priv_data, "preset", "slow", 0);

slice-max-size:用于指定编码器在生成码流时,切片(slice)的最大大小(字节数)。这个参数的默认值是0,表示不限制切片大小。
如果设置了slice-max-size参数,则编码器将尝试将生成的每个切片大小限制在指定的最大大小内,从而可以控制编码输出码率的变化范围。通常情况下,设置slice-max-size参数可以有效地控制输出码率和码流大小,并提高编码输出的质量和稳定性。
在具体应用中,是否需要设置slice-max-size参数取决于视频源的特点和应用需求。如果您的视频源较为稳定,码率变化不大,可以不设置该参数。但是如果您的视频源波动较大,码率变化较大,则可以考虑设置slice-max-size参数以控制输出码率和码流大小。

// 我这边用的rtp封包发送,减去头部12
av_opt_set_int(pCodecCtx->priv_data, "slice-max-size", 1500-12, 0);

crf:设置 H.264 编码时使用的 Constant Rate Factor (CRF) 的值。CRF 是一种自适应比特率控制方法,可以在保持视频质量不变的情况下,自动调整输出码率。具体来说,CRF 值越小,输出的视频质量越高,但输出的码率也会越大。CRF 值一般取值范围为 0-51,其中 0 表示无损编码,而 51 则表示最低质量的编码。一般来说,推荐的 CRF 值为 18-28 之间。默认值为 23。因此,如果你希望自适应控制视频码率,同时又想保持较高的视频质量,可以设置一个较小的 CRF 值,如 18。如果你想在保证视频质量的情况下控制视频码率,可以将 CRF 值调大一些,如 28。
注意,如果你设置了 CRF 值,那么其他控制比特率的参数(如 bit_rate 和 bit_rate_tolerance)将会被忽略,因为 CRF 会自动调整输出码率

av_opt_set_int(pCodecCtx->priv_data, "crf", 18, 0);

16、level:参数用于控制输出视频的级别。H.264标准规定了不同级别的输出视频所能达到的最大帧率、最大码率、最大分辨率等参数,因此选择合适的级别可以确保输出视频的兼容性和质量;

pCodecCtx->level = 41;

2.视频解码器

之前开发的时候,也是先创建上下文解码器,赋值相关参数,然后打开解码器才开始解码;但是一直解码失败,估计是上下文某个参数赋值不对;这边需要和安卓对接,安卓走的硬编解码,对我的参考意义不大,加上项目很急,后面就用回调方式解决了解码问题,对端的编码信息已经包含了解码器说需要的参数,不需要再给上下文赋值,后面有空再补充视频解码器的上下文赋值


3.音频编码器

1、codec_id(编解码器ID):

类型:enum AVCodecID
默认值:AV_CODEC_ID_NONE
作用:指定要使用的音频编解码器的ID。例如,对于AAC编码,可以将其设置为AV_CODEC_ID_AAC。该参数告诉FFmpeg选择正确的编解码器进行音频编码。

2、codec_type(编解码器类型):

类型:enum AVMediaType
默认值:AVMEDIA_TYPE_UNKNOWN
作用:指定音频流的类型。对于音频编码,该参数应设置为AVMEDIA_TYPE_AUDIO。这有助于FFmpeg正确识别流的类型并应用适当的编码器

3、bit_rate(比特率):

类型:int64_t
默认值:0
作用:控制编码后音频的比特率(即每秒传输的位数),通常以比特/秒(bps)为单位,一般为(sample_rate * 8 * channel_num)。较高的比特率可提供更好的音频质量,但会产生较大的文件大小。

4、sample_fmt(采样格式):

类型:enum AVSampleFormat
默认值:AV_SAMPLE_FMT_NONE
作用:指定音频采样数据的格式。常见的采样格式包括有符号整数(如AV_SAMPLE_FMT_S16)、无符号整数、浮点数等。选择合适的采样格式与源音频数据的格式相匹配,以确保编码器能够正确处理音频数据。

5、sample_rate(采样率):

类型:int
默认值:0
作用:定义每秒钟采样的样本数。常见的采样率包括 44100 Hz(CD音质)、48000 Hz(DVD音质)等。较高的采样率可提供更好的音频质量,但也会增加文件大小。

6、channels(声道数):

类型:int
默认值:0
作用:定义音频数据的声道数,常见的值为 1(单声道)和 2(立体声)。根据源音频数据的声道数,选择适当的值以确保编码器能正确处理声道布局

7、frame_size(帧大小):

类型:int
默认值:0
作用:指定音频编码器的输出帧大小。帧大小表示每个编码帧中包含的采样点数。对于某些编码器,帧大小需要是固定值。指定正确的帧大小可以确保编码器按预期处理音频数据。

8、time_base.num 和 time_base.den(时间基准):

类型:int
默认值:0
作用:time_base 表示时间的基准单位,用于指定音频流的时间基准。time_base.num 表示每秒钟的时间单位数,time_base.den 表示每个时间单位的时长。这些参数影响音频的时间戳和持续时间的计算。

9、bit_rate_tolerance(比特率容差):

类型:int
默认值:0
作用:指定比特率容差的上限。比特率容差表示编码器在尽力满足指定比特率的同时,允许的最大比特率偏差量。较大的容差值可能会导致音频质量的损失,但也有助于适应比特率变化的情况。

10、channel_layout(声道布局):

类型:uint64_t
默认值:0
作用:指定音频流的声道布局。声道布局描述音频数据中各声道的位置和顺序。例如,对于立体声,可以使用AV_CH_LAYOUT_STEREO。正确的声道布局设置有助于确保编码器正确处理音频流中的声道信息。

4.音频解码器

1、codec_id(编解码器ID):

类型:enum AVCodecID
默认值:AV_CODEC_ID_NONE
作用:指定要使用的音频解码器的ID。例如,对于AAC解码,可以将其设置为AV_CODEC_ID_AAC。该参数告诉FFmpeg选择正确的解码器进行音频解码。

2、codec_type(编解码器类型):

类型:enum AVMediaType
默认值:AVMEDIA_TYPE_UNKNOWN
作用:指定音频流的类型。对于音频解码,该参数应设置为AVMEDIA_TYPE_AUDIO。这有助于FFmpeg正确识别流的类型并应用适当的解码器。

3、request_sample_fmt(采样格式):

类型:enum AVSampleFormat
默认值:AV_SAMPLE_FMT_NONE
作用:是用于向解码器请求特定的采样格式,而不是实际指定解码后的采样格式。解码器将音频数据转换为指定的采样格式,以便后续处理和播放。常见的采样格式包括有符号整数(如AV_SAMPLE_FMT_S16)、无符号整数、浮点数等。

4、sample_rate(采样率):

类型:int
默认值:0
作用:指定解码后音频数据的采样率。解码器将音频数据重新采样为指定的采样率,以匹配后续处理或播放的要求。通常,采样率应与解码前音频数据的采样率一致。

5、channels(声道数):

类型:int
默认值:0
作用:指定解码后音频数据的声道数。解码器将音频数据转换为指定的声道布局,以匹配后续处理或播放的需求。通常,声道数应与解码前音频数据的声道数一致。

6、request_channel_layout(声道布局):

类型:uint64_t
默认值:0
作用:是用于向解码器请求特定的声道布局,而不是实际指定解码后的声道布局。声道布局描述音频数据中各声道的位置和顺序。解码器将音频数据转换为指定的声道布局,以便后续处理或播放。

7、flags(标志):

类型:int

默认值:0
作用:用于设置解码器的一些选项和标志。例如,可以启用解码器低延迟模式(pCodecCtx->flags |= AV_CODEC_FLAG_LOW_DELAY); 将解码器上下文的 flags 参数
设置为 AV_CODEC_FLAG_LOW_DELAY,在音频实时解码的情况下,可以尽快地产生解码后的音频输出。它在实时应用中很有用,例如实时通信、语音聊天、语音识别等场景,
其中延迟是关键因素。通过启用低延迟模式,解码器可能会采取一些策略来减小延迟,例如减少内部缓冲区的大小、禁用一些处理步骤或优化算法等。这可以使解码器更加敏捷地响应输入数据,
并尽快输出解码后的音频。但请注意,低延迟模式可能会对解码器的质量产生一定的影响,因为某些优化步骤可能会受到限制。因此,在使用低延迟模式时需要权衡实时性和音频质量之间的平衡。

二、编解码流程

1.编码流程

1、avcodec_find_decoder:

函数原型:AVCodec *avcodec_find_decoder(enum AVCodecID id);
作用:查找给定编解码器ID的解码器。
参数:
    id:要查找的解码器的编解码器ID,例如,音频AAC(AV_CODEC_ID_AAC)/视频H264(AV_CODEC_ID_H264)
返回值:返回解码器的指针,如果找不到解码器,则返回NULL。

2、avcodec_alloc_context3:

函数原型:AVCodecContext *avcodec_alloc_context3(const AVCodec *codec);
作用:FFmpeg 中用于分配和初始化 AVCodecContext 结构体的函数。
参数:
    codec:表示要分配的上下文将与哪个编解码器相关联。
返回值:返回一个指向新分配的 AVCodecContext 结构体的指针,失败返回NULL。

在使用 avcodec_alloc_context3 分配的上下文后,你可以根据需要修改其字段以进行个性化配置。下面是一些常用的字段及其作用:

codec_id:表示编解码器的 ID。
bit_rate:表示比特率,即每秒传输的比特数。
sample_fmt:表示采样格式,例如 AV_SAMPLE_FMT_S16 表示有符号16位整数。
sample_rate:表示采样率,即每秒采样的次数。
channels:表示声道数,例如 2 表示立体声。
channel_layout:表示声道布局,例如 AV_CH_LAYOUT_STEREO 表示立体声布局。
time_base.num 和 time_base.den:表示时间基准,用于计算时间戳和持续时间。

通过修改这些字段,你可以根据需要对编解码器上下文进行个性化设置,以满足特定的编码或解码需求

3、avcodec_open2:

函数原型:int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
作用:初始化编解码器上下文并打开解码器。
参数:
    avctx:要打开的编解码器上下文。
    codec:解码器。
    options:附加选项,可为NULL。 你也可以这样使用,比如: AVDictionary *opts = NULL; av_dict_set(&opts, "request_sample_fmt", "s16", 0); 这里传参 &opts 即可; 
返回值:返回零表示成功,负值表示错误。

4、avcodec_send_packet:

函数原型:int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);
作用:将原始压缩数据包发送给解码器进行解码。
参数:
    avctx:解码器上下文。
    avpkt:包含要解码的压缩数据的AVPacket。
返回值:返回零表示成功,负值表示错误。

5、avcodec_receive_frame:

函数原型:int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);
作用:从解码器中接收解码后的帧数据。
参数:
    avctx:解码器上下文。
    frame:接收解码后帧数据的AVFrame。
返回值:返回零

2.解码流程

1、avcodec_find_decoder:

    函数原型:AVCodec *avcodec_find_decoder(enum AVCodecID id);
    返回值:返回解码器的指针,如果找不到解码器,则返回 NULL。
    参数:
        id:要查找的解码器的编解码器ID。

2、avcodec_alloc_context3:

    函数原型:AVCodecContext *avcodec_alloc_context3(const AVCodec *codec);
    返回值:返回一个指向新分配的 AVCodecContext 结构体的指针。
    参数:
        codec:要分配的上下文将与哪个编解码器相关联。

3、avcodec_open2:

    函数原型:int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
    返回值:返回零表示成功,负值表示错误。
    参数:
        avctx:要打开的编解码器上下文。
        codec:解码器。
        options:附加选项,可为 NULL。

4、avcodec_send_packet:

    函数原型:int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);
    返回值:返回零表示成功,负值表示错误。
    参数:
        avctx:解码器上下文。
        avpkt:包含要解码的压缩数据的 AVPacket。

5、avcodec_receive_frame:

    函数原型:int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);
    返回值:返回零表示成功,负值表示错误。
    参数:
        avctx:解码器上下文。
        frame:接收解码后帧数据的 AVFrame。
  • 6
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Li_Zhi_Yao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值