音视频开发七:ffmpeg开发相关API和结构体

文章目录

    • 常用API
      • avformat_open_input
      • avformat_find_stream_info
      • av_find_best_stream
      • avfromat_close_input
      • avformat_alloc_context
      • avformat_free_context
      • av_guess_format
      • avformat_alloc_output_context2
      • avio_open2
      • avformat_new_stream
      • avcodec_parameters_copy
      • avformat_write_header
      • av_read_frame
      • av_seek_frame
      • av_rescale_q_rnd
      • av_interleaved_write_frame
      • av_rescale_q
      • av_packet_rescale_ts
      • av_packet_unref
      • av_packet_free
      • av_packet_move_ref
      • av_calloc
      • av_fifo_alloc2
      • av_fifo_read
      • av_fifo_write
      • av_fifo_freep2
      • avcodec_alloc_context3
      • avcodec_free_context
      • avcodec_find_encoder
      • avcodec_find_encoder_by_name
      • av_frame_alloc
      • av_frame_get_buffer
        • **注意 av_frame_alloc和av_frame_get_buffer之间的关系**
      • avcodec_send_frame
      • avcodec_receive_frame
      • av_frame_free
      • avcodec_find_decoder
      • avcodec_send_packet
      • avcodec_receive_packet
      • avcodec_open2
      • av_frame_make_writeable
      • avchannel_layout_copy
      • avcodec_parameters_to_context
      • sws_getContext
      • sws_scale
      • swr_alloc
      • swr_init
      • swr_alloc_set_opts2
      • swr_convert
      • swr_free
      • av_samples_get_buffer_size
      • av_fast_malloc
      • av_guess_sample_aspect_ratio
      • av_guess_frame_rate
      • av_get_bytes_per_sample
      • av_strdup
      • AVRational
    • 常用数据结构
      • AVFormatContext
      • AVStream
      • AVPacket
      • AVFifo
      • AVIOContext
      • AVOutputFormat
      • AVCodecParameters
        • AVCodecParameters和AVCodecContext的区别
      • AVCodec
      • AVCodecContext
      • AVFrame
      • AVRational
      • SwsContext
      • BITMAPFILEHEADER 和BITMAPINFOHEADER
      • SwrContext
    • 其他
      • 1. 怎样理解out_stream->codecpar->codec_tag = 0 ?
      • 2. pkt.pts * av_q2d(in_stream1->time_base)

常用API

avformat_open_input

avformat_open_input是FFmpeg中用于打开输入文件并分配和初始化AVFormatContext结构体的函数。它的函数原型如下:

int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options);

下面是对该函数参数的解释:

  • ps:指向指向AVFormatContext结构体的指针。
  • url:输入文件的URL或文件路径。
  • fmt:指定输入文件的格式上下文,通常为NULL,根据文件名的后缀自动检测。(存在这种情况:文件的后缀和实际包装的内容不同,例如:文件后缀是flv,但是里面存储的是mp4文件。)
  • options:指定打开输入流的额外选项,没有额外参数,给它默认值为NULL

该函数的返回值为0表示成功,否则表示失败。如果该函数返回一个负数错误代码,您可以使用av_err2str转换函数将其转换为相应的错误消息。

下面是示例代码:

AVFormatContext *fmt_ctx = NULL;
const char *filename = "input.mp4";
int ret = 0;

// 打开输入文件并分配AVFormatContext结构体
if ((ret = avformat_open_input(&fmt_ctx, filename, NULL, NULL)) < 0) {
	// av_err2str是FFmpeg中的一个函数,用于将错误代码转换为错误消息的字符串表示形式。 它的函数原型如下:
    printf("Could not open input file '%s' (error '%s')\n", filename, av_err2str(ret));
    return ret;
}

// 获取文件流信息
if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
    printf("Could not find stream information\n");
    return -1;
}

// 操作文件内容
// ...

// 最后,释放分配的内存
avformat_close_input(&fmt_ctx);

在上面的代码中,我们首先使用avformat_open_input函数打开了输入文件并分配了一个新的AVFormatContext结构体。然后,我们使用avformat_find_stream_info函数获取了文件流的信息。接下来,我们可以使用AVFormatContext变量fmt_ctx中存储的信息处理该文件的数据。最后,我们释放了分配的内存并关闭了输入文件。

avformat_find_stream_info

avformat_find_stream_info函数是用来获取音视频文件中所有流的详细信息的函数,包括流的类型、编码信息、帧率、分辨率等。

函数原型:

int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);

参数说明:

  • ic:AVFormatContext类型的指针,表示音视频文件的上下文。
  • options:AVDictionary类型的指针,表示额外的选项参数,可以为NULL。

函数返回值为0表示成功,否则表示失败。函数执行后,AVFormatContext中的成员变量AVStream中存储了音视频文件中所有流的详细信息。

av_find_best_stream

av_find_best_stream函数的作用是自动从多个流中找到某种类型的最佳的音视频流。它可以用于查找视频流、音频流、字幕流等。

“最佳的音视频流”是指在音视频文件中,最适合用来播放或者处理的音视频流。具体选取哪个流作为最佳流,取决于用户的需求和使用场景,通常需要考虑以下因素:

  • 流类型:音频流、视频流、字幕流等。
  • 媒体质量:码率、分辨率、帧率等。
  • 编码格式:常见的音频编码格式有MP3、AAC等,常见的视频编码格式有H.264、H.265等,编码格式不同,需要使用不同的解码器进行解码。

一般情况下,用户希望选取一个媒体质量较高,编码格式与设备兼容的流作为最佳流。

函数原型:

int av_find_best_stream(AVFormatContext *ic, enum AVMediaType type, int wanted_stream_nb, int related_stream, AVCodec **decoder_ret, int flags);

参数说明:

  • ic:输入的AVFormatContext。
  • type:所需的AVMediaType。(获取音频流、视频流和媒体流等)
  • wanted_stream_nb:需要查找的流的索引,如果为-1,表示不知道需要找哪个流,则会返回查找第一个的流的索引。
  • related_stream:这个参数在查找时可以作为参考流,这可能有助于匹配某些媒体文件,设为-1则不会发生关联流。(通常设置为-1)
  • decoder_ret:AVCodec类型的指针,表示要使用的解码器。如果为NULL,则会自动选择解码器。
  • flags:此参数在查找过程中提供了附加提示。设置为AVFIND_BEST_STREAM_SKIP_STREAM时,函数将跳过第一个零帧数的流。设置为AVFIND_BEST_STREAM_AUTOISO时,函数将自动选择最佳ISO码率设置。(通常设置0)

函数返回值为找到的流的索引,-1表示未找到。

示例代码:

int video_stream_index = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &decoder, 0);
if (video_stream_index < 0) {
    printf("Failed to find a video stream\n");
    return -1;
}

在这个示例中,我们使用av_find_best_stream函数查找输入文件中的最佳视频流,并将其索引存储在video_stream_index变量中。在该函数中,我们指定了所需的媒体类型(视频),并将其它参数设置为默认值。如果找到了一个合适的流,则该函数将填充该流的解码器信息(如果有的话)。

avfromat_close_input

是一个用于关闭输入封装格式上下文的函数,函数原型如下:

void avformat_close_input(AVFormatContext **ps);
  • ps:指向待关闭的输入格式上下文的指针的指针。

该函数用于关闭一个打开的输入格式上下文,并释放其内存空间。函数会自动释放所有被该上下文对象所占用的资源,如打开的文件、解码器、各种缓冲区等。因此,当不再需要使用输入格式上下文对象的时候,应该调用该函数来释放其内存空间,避免内存泄漏。

如果传入的ps参数为NULL,则该函数不会执行任何操作。

举例来说,下面的代码可以用于关闭打开的输入格式上下文:

AVFormatContext *fmtCtx;

avformat_open_input(&fmtCtx, "input.mp4", NULL, NULL);
// ... do something ...
avformat_close_input(&fmtCtx);

该代码打开了一个名为"input.mp4"的文件,并对其进行一些操作后,使用avformat_close_input函数来关闭输入格式上下文。

avformat_alloc_context

avformat_alloc_context函数的作用是为AVFormatContext类型的结构体分配空间并初始化

该函数的原型如下:

AVFormatContext *avformat_alloc_context(void);

调用该函数返回一个分配好的指向AVFormatContext结构体的指针。

avformat_free_context

avformat_free_context是一个用于释放格式上下文内存空间的函数,函数原型如下:

void avformat_free_context(AVFormatContext *s);
  • s:需要释放内存空间的格式上下文对象。

该函数用于释放一个格式上下文对象所占用的内存空间,并将该对象置为NULL。与avformat_close_input函数不同的是,该函数只释放格式上下文本身所占用的内存空间,而不会释放其他由该对象占用的资源,如打开的文件、解码器、各种缓冲区等。因此,当不再需要使用格式上下文对象的时候,如果不需要释放其他资源,则可以直接调用该函数释放其内存空间。

如果传入的s参数为NULL,则该函数不会执行任何操作。

举例来说,下面的代码可以用于释放格式上下文对象所占用的内存空间:

AVFormatContext *fmtCtx;

avformat_open_input(&fmtCtx, "input.mp4", NULL, NULL);
// ... do something ...
avformat_free_context(fmtCtx);

该代码打开了一个名为"input.mp4"的文件,并对其进行一些操作后,使用avformat_free_context函数来释放其内存空间。

av_guess_format

av_guess_format函数用于自动猜测指定文件的输入或输出格式。它通过文件扩展名或已知的文件格式进行猜测。通常情况下,它用于在打开文件之前自动检测文件格式,以便为AVFormatContext结构体设置正确的demuxer或muxer。

函数原型:

AVOutputFormat *av_guess_format(const char *short_name,
                                const char *filename,
                                const char *mime_type);

函数参数说明:

  • short_name:短格式名称,如“mp4”、“mpeg”等。
  • filename:要猜测文件格式的文件名,为NULL则表示只利用short_name来猜测格式。
  • mime_type:用于可选的mime类型,为NULL则表示只利用short_name和filename来猜测格式。

函数返回一个指向AVOutputFormat结构体的指针,如果无法匹配到输入或输出格式,函数将返回NULL。

现在,我们来看一下av_guess_format的行为:

  1. 如果short_name不为空,则根据该名称猜测文件格式,如果能够找到该格式,则返回指向该文件格式的指针;否则,返回NULL。
  2. 如果filename不为空,则根据文件名的扩展名猜测文件格式,如果能够找到该格式,则返回指向该文件格式的指针;否则,返回NULL。
  3. 如果mime_type不为空,则根据文件的MIME类型猜测文件格式,如果能够找到该格式,则返回指向该文件格式的指针;否则,返回NULL。
  4. 如果short_namefilenamemime_type都为空,则返回NULL。
  5. AVOutputFormat结构体中的name字段表示该格式的名称,extensions字段表示该格式所有支持的扩展名,mime_type字段表示该格式的MIME类型。

因此,可以根据返回的AVOutputFormat结构体来获取该格式的信息,例如格式名称、支持的扩展名、MIME类型等等。

avformat_alloc_output_context2

avformat_alloc_output_context2是FFmpeg库的一个函数,用于生成一个带有输出格式的AVFormatContext对象,以用于将音视频编码数据写入到文件或其他媒体输出流中。

相当于(avformat_alloc_context和av_guess_format的结合)

函数的原型为:

int avformat_alloc_output_context2(AVFormatContext **ctx,
                                   AVOutputFormat *oformat,
                                   const char *format_name,
                                   const char *filename);

其中,参数含义为:

  • ctx:输出格式的AVFormatContext对象指针
  • oformat:输出格式的AVOutputFormat对象,用于指定输出数据的格式。
  • format_name:用于猜测输出格式的名称,如果oformat为NULL,则会根据该参数进行猜测,可以为NULL。,可以是 NULL。
  • filename:字符串参数,描述输出文件的文件名,可以是 NULL。

该函数可以自动猜测输出格式,或者根据oformat参数手动指定输出格式。如果同时指定了oformat参数和format_name参数,则以oformat参数为准

函数的返回值为零,表示成功生成了AVFormatContext对象,否则返回负的错误码。

avio_open2

avio_open2,它的主要作用是绑定AVIOContext对象并与之关联指定URL。函数原型如下:

int avio_open2(AVIOContext **s, const char *url, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options);

这个函数的参数含义如下:

  • **s:指向AVIOContext的指针,输入输出上下文。

  • url:要被打开的URL。可以是本地文件,也可以是网络地址。

  • flags:打开URL的选项标志,可以是以下标志的组合:

    • AVIO_FLAG_READ:以读取模式打开URL。
    • AVIO_FLAG_WRITE:以写入模式打开URL。
    • AVIO_FLAG_READ_WRITE:以读写模式打开URL。
    • AVIO_FLAG_NONBLOCK:以非阻塞模式打开URL。
    • AVIO_FLAG_DIRECT:禁用缓存,直接进行操作。
    • AVIO_FLAG_EXCL:以排它模式打开URL。
    • AVIO_FLAG_CREAT:如果URL不存在,则创建它。
    • AVIO_FLAG_TRUNC:清除URL内容。
    • AVIO_FLAG_APPEND:在URL的末尾进行写入。
    • AVIO_FLAG_NOBUFFER:禁用缓存。
  • int_cb:int_cb是用于检测中断请求的回调函数指针,它允许在读取或写入数据时检测中断信号。如果用户提供了一个非空回调函数,FFmpeg会定期调用它以允许中断。没有设置回调函数,设置为NULL。

  • options:指定的其他选项,是可选的,并经常用于传递应用程序特定的选项,例如需要使用的传输协议、超时时间以及代理设置等。没有设置回调函数,设置为NULL。

// 输出文件上下文(&oFmtCtx->pb)与目标文件(dst)进行绑定
avio_open2(&oFmtCtx->pb, dst, AVIO_FLAG_WRITE, NULL, NULL);
// 写多媒体文件头到目的文件
ret = avformat_write_header(oFmtCtx, NULL);
// 用于将音频数据写入到目标文件中
av_interleaved_write_frame(oFmtCtx, &pkt);
//写多媒体文件尾到文件中
av_write_trailer(oFmtCtx);

我的理解是:通过输出文件上下文与目标文件进行绑定。该VIOContext对象 oFmtCtx->pb被关联到目标文件(即 dst 所指向的文件)。avformat_write_header(oFmtCtx, NULL),av_interleaved_write_frame(oFmtCtx, &pkt);av_write_trailer(oFmtCtx); 就是向目标文件写入。

avformat_new_stream

avformat_new_stream()函数是 FFmpeg 中用于创建一个新的流(Stream)的函数

函数原型:

AVStream *avformat_new_stream(AVFormatContext *ctx, const AVCodec *codec);

函数参数:

  • ctx: AVFormatContext 类型,指向 FFmpeg 的格式上下文,用于读写各种多媒体文件格式。
  • codec: AVCodec 类型,指向编/解码器的 AVCodec 结构体。

函数返回:

  • 成功:新创建的 AVStream 指针。
  • 失败:NULL。

avcodec_parameters_copy

avcodec_parameters_copy函数用于将一个AVCodecParameters结构体中的参数复制到另一个AVCodecParameters结构体中

函数原型:

int avcodec_parameters_copy(AVCodecParameters *dst, const AVCodecParameters *src);

函数参数:

  • dst: AVCodecParameters 类型,指向目标 AVCodecParameters 结构体指针。
  • src: AVCodecParameters 类型,指向源 AVCodecParameters 结构体指针。

src 中存储了编码器的参数信息, dst 用于存储从 src 复制的编码器参数信息。

函数返回:

  • 成功:0。
  • 失败:负数错误码。

函数主要用于将输出的流的编码器参数从输入的流中的编码器参数中复制过来,使得输出文件的编码器参数和输入文件保持一致。在使用该函数复制编码器参数时,需要注意目标AVCodecParmaeters结构体必须已经被分配内存,否则会引发错误

avformat_write_header

avformat_write_header() 函数是 FFmpeg 中用于向目标文件写入多媒体格式头函数,其声明如下:

int avformat_write_header(AVFormatContext *s, AVDictionary **options);

其中,参数 s 是一个指向输出文件的AVFormatContext结构体的指针。参数 options 是一个指向 AVDictionary 字典对象的指针,可选参数,用于传递一些额外的信息,通常情况下可以将其设置为NULL,表示不需要特殊选项、使用默认值。

函数返回值:

​ 函数的返回值是一个错误码,返回 0 表示操作成功,返回负数表示操作失败。

在使用该函数之前,首先需要对 AVFormatContext 结构体进行初始化,包括设置格式参数和媒体流信息等。除此之外,在调用该函数之前需要先使用 avio_open2 函数打开输出文件并将其与封装格式上下文关联起来。

av_read_frame

av_read_frame() 调用该函数的主要目的是读取输入文件中的下一帧数据。该函数会自动从输入文件中解码一帧数据并将其存储在AVPacket结构体中。调用者需要在读取完成后释放pkt数据的内存。其声明如下:

int av_read_frame(AVFormatContext *s, AVPacket *pkt);

其中,参数 s 是一个指向 AVFormatContext 结构体的指针,用于表示输入多媒体格式上下文,即输入文件的基本信息和属性。参数 pkt 是一个指向 AVPacket 数据包结构体的指针,用于表示读取到的数据帧。

以下是该函数的示例代码:

int ret;
AVPacket pkt;

while (av_read_frame(inputFormatContext, &pkt) >= 0) {
    /* 处理pkt中的音频、视频或字幕数据 */
    
    av_packet_unref(&pkt);
}

在以上示例中,使用while循环读取输入文件中的下一帧数据,当函数返回值小于0时,就说明已经读取到文件的末尾或者发生了错误,此时退出循环。每次读取完一帧数据之后,需要调用av_packet_unref释放内存。在处理pkt数据时,可以使用pkt中的stream_index字段来获取数据的类型,然后针对不同类型进行不同的处理。

av_seek_frame

av_seek_frame 是 FFmpeg 库中的一个函数**,用于定位媒体文件中的特定时间戳**。通常用于在视频或音频文件中导航到特定的帧或时间位置。它接受参数,如 AVFormatContext(存储流格式信息)、流索引、时间戳和寻找标志。

函数原型如下:

int av_seek_frame(AVFormatContext *format_context, int stream_index,
                  int64_t timestamp, int flags)
  1. AVFormatContext *format_context:媒体文件的格式上下文结构体,包含了媒体文件的格式信息和媒体流的相关信息。
  2. int stream_index:代表要定位到的媒体流的索引值。
  3. int64_t timestamp:代表要定位到的时间戳,单位是微秒。
  4. int flags:代表定位的标志,用于指定查找模式。可以选择以下值之一:
    • AVSEEK_FLAG_BACKWARD:表示向后查找,即定位到给定时间戳之前的最近帧。
    • AVSEEK_FLAG_BYTE:表示精确查找,即定位到给定时间戳所在的位置。
    • AVSEEK_FLAG_ANY:表示无限制查找,即从任意位置查找给定时间戳。
    • AVSEEK_FLAG_FRAME:表示以帧为单位查找,即定位到最接近给定时间戳的关键帧位置。

最后,函数返回非负整数代表成功,返回错误代码代表失败。该函数通常用于在媒体文件中定位特定的帧或时间位置。

av_rescale_q_rnd

av_rescale_q_rnd() 是 FFmpeg 中用于时间戳值转换的函数。在音视频编解码、滤波等操作中,常常需要将时间戳值从输入信息(如采样率、帧率)的时间基转换为输出信息(如输出采样率、帧率)的时间基。av_rescale_q_rnd() 可以较方便地实现这一过程。

该函数的声明如下:

int64_t av_rescale_q_rnd(int64_t a, AVRational bq, AVRational cq, enum AVRounding rnd);

其中,参数 a 表示需要进行时间戳转换的值,bq为源时间戳的时间基,cq为目标时间戳的时间基,rnd为指定的取整方式。

该函数会将时间戳值 abq 时间基转换为 cq 时间基,并根据取整方式 rnd 进行取整,返回转换后的时间戳值

例如,我们需要将 100 个采样点的时间戳从输入采样率 44100 转换为输出采样率 48000,可以使用下面的代码片段实现:

AVRational in_timebase = {1, 44100};
AVRational out_timebase = {1, 48000};
for (int i = 0; i < 100; i++) {
    int64_t ts = av_rescale_q_rnd(i * in_timebase.num, in_timebase, out_timebase, AV_ROUND_NEAR_INF);
    // 进行相应的处理
    ......
}

在上述代码中,我们首先定义了输入和输出采样率对应的时间基分别为 1/44100 和 1/48000。然后,使用 av_rescale_q_rnd() 函数将采样点的时间戳从输入时间基转换为输出时间基,并将转换后的时间戳存储在 ts 变量中。在转换完成之后,我们可以进行相应的采样处理。

需要注意的是,在使用 av_rescale_q_rnd() 函数进行时间戳转换时,需要定义好输入和输出时间基,并根据具体的场景选择合适的取整方式。取整方式有四种,分别为:

  • AV_ROUND_ZERO:向 0 取整
  • AV_ROUND_INF:向上取整(取离线条最远的整数)
  • AV_ROUND_DOWN:向下取整
  • AV_ROUND_NEAR_INF:根据最近值取整

在进行时间戳转换时,应根据具体场景选择合适的取整方式。

av_interleaved_write_frame

av_interleaved_write_frame函数用于将编码后的音视频数据写入输出文件。该函数的原型如下:

int av_interleaved_write_frame(AVFormatContext *context, AVPacket *packet);

参数说明:

  • context:指向打开的媒体文件的AVFormatContext结构体指针。
  • packet:指向AVPacket结构体的指针,包含待写入的音频或视频帧数据。

函数使用结果不是一个确定的返回值,而是通过查看AVOutputFormat结构体中的write_packet回调函数可以获得多媒体文件输出的状态。如果该回调函数返回负数,则表示写入数据出现了错误。

av_rescale_q

FFmpeg中,av_rescale_q函数用于将两个不同时间基的值进行转换。它可以将一个时间轴上的值按比例转换为另一个时间轴上的值。该函数使用AVRatioal结构体定义时间基。

函数定义:

int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq);

参数解释:

  • a: 要转换的值
  • bq: 要转换的值的时间基
  • cq: 要转换到的时间基

返回值:转换后的值,类型为int64_t

av_packet_rescale_ts

av_packet_rescale_ts()函数可以将AVPacket结构体中的时间戳(pts、dts)从一个时间基转换为另一个时间基。它的函数原型如下:

int av_packet_rescale_ts(AVPacket *pkt, AVRational src_tb, AVRational dst_tb);

其中,参数pkt指向要进行时间戳转换的AVPacket结构体,src_tb表示pkt中时间戳的时间基,dst_tb表示需要将时间戳转换为的时间基。

比av_rescale_q更省事

av_packet_unref

av_packet_unref函数用于释放AVPacket结构体中的资源。

释放 AVPacket 结构体中的资源通常是在一个音视频数据包已经使用完之后,为了回收内存资源而进行的操作。该函数会将 AVPacket 结构体中的所有资源(内存、引用计数-1等等)全部清空,并将 AVPacket 结构体本身保留下来供后续复用,比直接释放完整个结构体更高效。

该函数的原型如下:

void av_packet_unref(AVPacket *pkt);

其中,pkt为AVPacket结构体,需要被释放。

av_packet_free

该函数会将 AVPacket 结构体以及其中的全部资源(内存、引用计数等等)全部释放,并将 AVPacket 结构体本身也一同释放,因此后续无法继续使用该 AVPacket 结构体

该函数的函数原型为:

void av_packet_free(AVPacket **pkt);

av_packet_move_ref

用于对AVPacket数据包对象进行缓冲区引用的转移,。

该函数的原型为:

void av_packet_move_ref(AVPacket *dst, AVPacket *src);

它的参数分别为目标 AVPacket 对象指针 dst 和源 AVPacket 对象指针 src。

作用:将目标AVPacket对象中的缓冲区引用值设置为源AVPacket对象中的缓冲区引用值。释放源src对象所占用的空间,但并没有释放src结构体。

av_calloc

av_calloc是FFmpeg中的内存分配函数,函数原型为:

void *av_calloc (size_t nmemb, size_t size);

该函数的作用是分配一个由nmemb个元素组成,每个元素大小为size字节的内存空间,并将空间中的每一位都初始化为0。av_calloc函数类似于C语言中的calloc函数,但是av_calloc会使用FFmpeg内部的内存管理器进行内存分配,因此可以保证分配出来的内存块是FFmpeg所支持的对齐方式,可以有效避免由于内存对齐问题而导致的读写错误。

使用av_calloc函数分配内存空间的示例代码如下:

int *p = (int *)av_calloc(10, sizeof(int));
if (p == NULL) {
    // 内存分配失败,进行相关处理
} else {
    // 成功分配内存,进行相关操作
    p[0] = 1;
    p[1] = 2;
    // ...
    av_free(p); // 使用完后需要释放空间
}

注意,使用av_calloc函数分配的内存需要使用av_free函数进行释放,并且需要保证释放的内存地址与av_calloc参数中返回的指针地址一致。否则可能会导致内存泄漏或其他问题。

av_fifo_alloc2

函数作用: 分配并初始化AVFifo

函数原型为:

AVFifo* av_fifo_alloc2	(	size_t 	elems,
size_t 	elem_size,
unsigned int 	flags )	

参数:

  • elmes: 队列的原始个数
  • elem_size: 每个元素的字节大小。
  • flags:指定 FIFO 缓冲区的标志,如 AV_FIFO_FLAG_NONBLOCK 表示在缓冲区满或空时不阻塞,而是立即返回错误。而 AV_FIFO_FLAG_AUTO_GROW 表示当 FIFO 缓冲区满时,自动扩展缓冲区的大小,而不会返回错误。

成功时分配AVFIifo 。

av_fifo_read

av_fifo_read() 函数是 FFmpeg 库中用于从 FIFO 队列中读取数据的函数,其函数原型如下:

int av_fifo_read	(	AVFifo * 	f,
void * 	buf,
size_t 	nb_elems 
)		

函数参数意义如下:

  • f:指向要读取数据的 AVFifo 指针。
  • buf:指向可存放读取数据的缓冲区 nb_elems * av_fifo_elem_size(f) bytes will be written into buf on success.。
  • nb_elems:元素个数。

函数返回值为非负值表示成功,负值表示失败。

av_fifo_write

函数作用:将数据写入av_fifo

int av_fifo_write	(	AVFifo * 	f,
const void * 	buf,
size_t 	nb_elems )	

参数:

  • f:待写入的AVfifo队列
  • buf: 待写入的数据。
  • nb_elems:写入的元素个数。

返回值:

​ 成功时返回非负数,失败时返回负数。

av_fifo_freep2

void av_fifo_freep2	(	AVFifo ** 	f	)	

Free an AVFifo and reset pointer to NULL.

  • Parameters

    fPointer to an AVFifo to free. *f == NULL is allowed.

avcodec_alloc_context3

用来分配并初始化编解码器的上下文

avcodec_alloc_context3 函数的原型如下:

AVCodecContext *avcodec_alloc_context3(const AVCodec *codec);

codec 是要使用的编解码器的指针,如果不需要指定特定的编解码器,则设置为 NULL。

返回值是指向已分配并初始化的 AVCodecContext 结构体的指针,如果出现任何错误或无法分配内存,则返回 NULL。

avcodec_free_context

avcodec_free_context是FFmpeg中用于释放AVCodecContext结构体内存的函数

下面是avcodec_free_context函数的声明:

void avcodec_free_context(AVCodecContext **avctx);

该函数会释放AVCodecContext结构体所申请的内存空间,参数avctx是传入AVCodecContext结构体指针的指针。使用该函数需要注意的是,传入的AVCodecContext结构体指针必须是通过avcodec_alloc_context3函数申请的。

avcodec_find_encoder

avcodec_find_encoder() 是 FFmpeg 中的一个函数,用于根据编码器 ID 或名称查找匹配的编码器并返回指针,该函数在使用编码器之前需要先注册编码器

它的函数原型如下:

AVCodec *avcodec_find_encoder(enum AVCodecID id);
  • avcodec_find_encoder() 根据编码器 ID 查找匹配的编码器并返回指针。

其中,idenum AVCodecID 类型的编码器 ID

avcodec_find_encoder_by_name

avcodec_find_encoder_by_name是FFmpeg中用于查找指定编码器的函数,它会根据指定的编码器名称来查找并返回与之对应的AVCodec结构体指针。

以下是avcodec_find_encoder_by_name函数的函数原型:

AVCodec *avcodec_find_encoder_by_name(const char *name);

其中,name参数是需要查找的编码器名字。如果找到了指定名称的编码器,则返回一个指向AVCodec结构体的指针。如果没有找到,则返回NULL。

示例代码:

// 查找x264编码器
AVCodec *codec = avcodec_find_encoder_by_name("libx264");

if (codec == NULL) {
    fprintf(stderr, "Could not find the x264 encoder.\n");
    return -1;
}

// 初始化AVCodecContext结构体
AVCodecContext *codec_context = avcodec_alloc_context3(codec);
if (codec_context == NULL) {
    fprintf(stderr, "Could not allocate codec context.\n");
    return -1;
}

// 设置编码参数
codec_context->bit_rate = 500000;
codec_context->width = 1280;
codec_context->height = 720;
codec_context->time_base = (AVRational) {1, 25};

// ...

在上面的代码中,通过调用avcodec_find_encoder_by_name函数查找指定的编码器(此处为x264编码器)。如果找到了指定名称的编码器,则返回一个指向AVCodec结构体的指针,否则返回NULL。我们可以使用这个指针来获取编码器相关的信息和参数,进而进行后续的编码操作。在实际使用中,我们还需要为AVCodecContext结构体设置一些编码器参数,如上例中所示。

av_frame_alloc

av_frame_alloc 是 用于申请一个 AVFrame结构体内存的函数。它申请AVFrame结构体所需要的内存并进行初始化,返回一个指向AVFrame结构体的指针。一般在使用AVFrame结构体之前都需要先调用av_frame_alloc来进行初始化和内存申请。

它的定义如下:

AVFrame *av_frame_alloc(void);

函数会动态分配一个 AVFrame 结构体,然后将其内存全部清零,并返回一个指向它的指针。

使用 av_frame_alloc 函数分配一个 AVFrame 的示例代码如下:

AVFrame *frame = av_frame_alloc();
if (!frame) {
    // 处理分配内存失败的情况
}

在使用完分配的 AVFrame 后,需要调用 av_frame_free 函数释放内存。

av_frame_get_buffer

av_frame_get_buffer()是FFmpeg中的一个函数,用于为AVFrame结构体中的data域分配内存空间

在使用FFmpeg进行视频编解码时,程序需要使用AVFrame结构体来存储视频帧数据。在使用这个结构体之前,需要先为其分配内存空间。

av_frame_get_buffer()函数可以帮助我们为AVFrame结构体分配内存av_frame_get_buffer函数为AVFrame中每个数据指针分配内存,它的函数原型如下:

int av_frame_get_buffer(AVFrame *frame, int align);

其中,frame是需要分配内存空间的AVFrame结构体,align表示内存对齐的大小,通常设置为0,表示默认内存对齐方式。

函数返回一个整数值,代表是否成功为 AVFrame 分配内存,成功时返回 0,失败时返回 AVERROR。

该函数会为AVFrame结构体分配并初始化内存空间,从而使得接下来对它的操作更加方便。注意,在使用完AVFrame结构体后,需要使用av_frame_free()函数来释放内存空间,避免内存泄漏

注意 av_frame_alloc和av_frame_get_buffer之间的关系

先理解两个概念,申请内存和分配内存。

申请内存:向系统申请一块地方。

分配内存:是在已经申请的地方中某一块地方给它数据。

申请内存相当于是给地主(系统)要了一块地,分配内存是这块地的某一个区域种小麦(分配)。

申请和分配内存的过程经常是相互配合的,即先申请一段内存空间,然后再基于这一段内存空间中分配一些小块的内存空间,并使用这些小块内存来存储数据。这种方式一般可以提高内存的利用率,减少内存碎片的产生。

所以关系就是:

av_frame_alloc是比较常用的内存申请函数,它申请AVFrame结构体所需要的内存并进行初始化,返回一个指向AVFrame结构体的指针。

av_frame_get_buffer是用于给已经申请的AVFrame结构体分配内存的函数,它会根据AVFrame中已经设置好的参数(比如宽度、高度、像素格式、采样格式等)来分配内存。使用完av_frame_get_buffer函数后,AVFrame中的数据区域就可以用于存储采样数据或者像素数据等。

avcodec_send_frame

avcodec_send_frame 函数是 FFmpeg 库中的一个函数,用于向编码器发送帧数据。该函数将视频帧的数据送入编码器,对应的编码器将对该帧进行编码。具体使用方法和相关参数解析如下:

函数原型:

int avcodec_send_frame(AVCodecContext *avctx, const AVFrame *frame);

参数说明:

  • AVCodecContext *avctx:
    编码器的上下文环境。
  • const AVFrame *frame:
    待编码的视频帧数据,包括视频像素数据及音频采样数据等。

返回值:

  • 返回 0 表示成功。
  • 返回其他值则代表发送失败或者发送缓存已经被填满。

函数功能:

avcodec_send_frame 函数用于将待编码的视频帧数据送入编码器,开始进行编码处理。视频帧包括视频像素数据及音频采样数据等。函数执行完后,所有的数据都会被加载到编码器的发送缓冲区中等待编码器进行编码。

avcodec_receive_frame

它的作用是从编解码器的输出队列中获取解码后的输出帧,并将其存储在AVFrame结构体中。下面我们来详细解析一下该函数的用法。

函数原型如下:

int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);

其中,avctx是一个指向AVCodecContext结构体的指针,它包含了编解码器的相关信息,frame是一个指向AVFrame结构体的指针,它用于存储解码后的输出帧。

该函数的返回值具体含义如下:

  1. 返回值为0:正常状态,表示函数成功执行,获取了有效的输出帧;

  2. AVERROR_EOF:表示"End of file",即文件已经结束。它的返回值通常用于描述音视频解码器、编码器等相关函数内部已经处理完数据流的情况,通知调用者已经到达了输入流的末尾,不会再产生更多的数据。

    需要注意的是,输入音视频流中可能存在没有正常结束的情况,例如文件损坏、网络异常等导致数据流被截断。此时,AVERROR_EOF并不是正确的结束信号,而只是表示当前的数据流已经结束

    解码器已经完全解码完输入流中的数据。

  3. AVERROR(EAGAIN):

    AVERROR(EAGAIN)是FFmpeg库中的一个宏定义,表示"Try again",即再试一次。它的返回值通常不是错误,而是表示当前操作需要等待某些已经就绪的操作(如缓存中的数据已经就绪),或者需要等待更多数据到达。

    在FFmpeg中,AVERROR(EAGAIN)通常用于描述解码器、编码器、输入输出设备等相关函数内部缓存区的数据中没有有效数据时的状态。例如,在使用avcodec_receive_frame函数从解码器中获取音视频帧时,如果返回AVERROR(EAGAIN),通常表示解码器缓冲区中已经没有可用帧,需要等待更多的数据输入。此时,可以继续发送数据给解码器,并再次调用avcodec_receive_frame函数,直到下一帧数据解码完成为止

    因此,当遇到AVERROR(EAGAIN)的返回值时,程序应该根据实际情况等待一段时间后再次尝试执行该操作,或者通过其他方式增加数据输入。

  4. 其他错误码:表示解码过程中出现了其他错误,可以通过调用av_strerror函数将错误码转换成错误信息。。

在使用avcodec_receive_frame函数之前,我们需要先通过调用avcodec_send_packet将编码后的数据发送给编解码器,当编解码器解码完成后,avcodec_receive_frame函数会从编解码器的输出队列中获取解码后的输出帧

关于avcodec_receive_frame函数需要注意以下几点:

  1. 在调用avcodec_receive_frame函数之前,必须确定编解码器有待解码的数据包,否则函数将一直等待。
  2. 在解码器队列中没有可用帧的情况下,函数会一直等待,直至队列中有可用帧。
  3. 函数返回值为0表示成功,-EAGAIN表示尚未解码完毕, AVERROR(EINVAL)表示参数不正确,其它返回值表示解码失败。
  4. AVFrame结构体中存储的是解码后的输出帧,包括视频像素数据和音频采样数据等。

总之,avcodec_receive_frame函数是实现对编码器解码后输出帧获取的重要函数,常常和avcodec_send_packet函数一起使用,配合完成FFmpeg中的解码流程。

av_frame_free

av_frame_free()是FFmpeg中用于释放AVFrame结构体内存的函数。

下面是av_frame_free()函数的定义:

void av_frame_free(AVFrame **frame);

函数参数是一个指向AVFrame指针的指针,即需要释放的AVFrame结构体的指针的地址。调用该函数后,指向AVFrame结构体的指针会被设置为NULL,以避免出现悬空指针。

avcodec_find_decoder

用于查找解码器的函数之一,返回匹配指定codec_id的解码器的AVCodec结构体指针

AVCodec结构体定义在FFmpeg的avcodec.h头文件中,包含了解码器相关的信息和函数指针。使用avcodec_find_decoder函数可以根据codec_id查找一个解码器。

下面是avcodec_find_decoder函数的声明:

AVCodec *avcodec_find_decoder(enum AVCodecID id);

其中参数id为AVCodecID类型的解码器ID,定义在AVCodecID枚举类型中。

使用示例:

// 查找H.264解码器
AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_H264);
if (!codec) {
    fprintf(stderr, "未找到解码器\n");
    exit(1);
}

// 根据解码器申请AVCodecContext内存
AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);

在上面的示例代码中,首先使用avcodec_find_decoder查找H.264解码器的AVCodec结构体指针,如果未找到则输出错误信息并退出程序。如果找到解码器,然后使用该解码器申请AVCodecContext内存。

需要注意的是,avcodec_find_decoder函数只能查找已经注册的解码器,因此在使用该函数之前需要确保使用av_register_all函数注册了所有的解码器。在FFmpeg 4.0以前的版本中,也可以使用avcodec_register_all函数快速注册所有的解码器。

avcodec_send_packet

它的作用是将编码后的数据包发送给编解码器进行解码

首先会将数据包中的数据发送到缓冲区,等待编码器从中取出数据。如果缓冲区已经被填满,则avcodec_send_packet会一直等待,直到缓冲区有足够的空间存放这个数据包。

其函数原型如下:

int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);

函数参数说明:

  • avctx:指向已打开的AVCodecContext的指针。
  • avpkt:指向要发送的AVPacket的指针。

其中,avctx是一个指向AVCodecContext结构体的指针,它包含了摄像头、文件格式、编解码器等相关信息,avpkt是一个指向AVPacket结构体的指针,它是包含了编码数据的数据包。

函数返回值是一个整型,代表发送结果:

  • 0:成功发送。
  • AVERROR_EOF:当前输入流已结束。
  • AVERROR(EAGAIN):解码器暂时无法接受输入,需要等待。
  • AVERROR(EINVAL):无效参数。

avcodec_receive_packet

avcodec_receive_packet 函数是 FFmpeg 库中的一个函数,用于获取编码器输出的压缩数据包。该函数从编码器中读取已经编码好的数据包,返回获取到的压缩数据。 具体使用方法及相关参数解析如下:

函数原型:

int avcodec_receive_packet(AVCodecContext *avctx, AVPacket *avpkt);

参数说明:

  • AVCodecContext *avctx:
    编码器的上下文环境。
  • AVPacket *avpkt:
    存储编码好的数据包对象。

返回值:

  • 返回 0 表示获取编码器输出的压缩数据包成功。
  • 返回 AVERROR_EOF 表示编码器输出队列被清空。
  • 返回其他值则代表获取编码器输出的压缩数据包失败。

函数功能:

avcodec_receive_packet 函数用于从编码器中读取已经编码好的数据包,可以获取到压缩后的视频数据包,该数据包可以写入文件、网络等传输。可以在 avcodec_send_frame 函数调用之后,不断循环调用该函数读取输出压缩数据包,直至数据读取完毕。

使用示例:

以下示例使用了 avcodec_receive_packet 函数从编码器中获取一帧压缩数据。

AVCodecContext *codec_ctx;  //编码器的上下文环境
AVPacket *avpkt;            //存储编码好的数据包对象

//从编码器中获取一帧压缩数据
int ret = avcodec_receive_packet(codec_ctx, avpkt);
if (ret == 0) {
    //压缩数据包获取成功
} else if (ret == AVERROR_EOF) {
    //编码器输出队列被清空
} else {
    //压缩数据包获取失败
}

以上示例中,我们从编码器中获取一帧压缩数据包,如果获取成功,则可以对数据包进行处理;如果返回 AVERROR_EOF,则表示编码器输出队列已经清空,可以结束循环;如果返回其他值,则代表获取压缩数据包失败,需要进行错误处理。注意,必须先初始化编码器上下文环境与压缩数据包存储对象才能使用此函数。

avcodec_open2

avcodec_open2是FFmpeg中用于打开解码器的函数之一,,它将AVCodecContext和解码器绑定(也就是让AVCodecContext结构体的解码器参数和解码器相结合,让解码器知道怎么解压),初始化AVCodecContext和解码器,打开解码器以准备解码的过程。

下面是avcodec_open2函数的声明:

int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);

其中参数avctx是AVCodecContext结构体指针,存储了解码器的参数信息,codec是AVCodec结构体指针,表示要打开的解码器的信息,而options则是选项的字典。

使用示例:

// 查找H.264解码器
AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_H264);
if (!codec) {
    fprintf(stderr, "未找到解码器\n");
    exit(1);
}

// 根据解码器申请AVCodecContext内存
AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);

// 设置AVCodecContext参数
// ...

// 打开解码器
if (avcodec_open2(codec_ctx, codec, NULL) < 0) {
    fprintf(stderr, "无法打开解码器\n");
    exit(1);
}

在上面的示例代码中,首先使用avcodec_find_decoder函数查找H.264解码器并获取AVCodec结构体指针codec。然后根据该解码器申请AVCodecContext内存,并设置其参数。最后使用avcodec_open2函数打开解码器并检查是否成功打开。注意在本例中直接设置options为NULL。

需要注意的是,avcodec_open2函数只能打开一个解码器一次,多次打开相同的解码器将会导致错误。因此,在个别情况下需要使用多个AVCodecContext来打开多个解码器。

av_frame_make_writeable

av_frame_make_writable 是 FFmpeg 中用于确保 AVFrame 可以进行写入操作的函数。

AVFrame 通常在初始化时,使用 av_frame_alloc 函数分配内存空间,该函数只分配 AVFrame 结构体所占用的内存空间,没有分配 AVFrame 数据缓存区(即data域)的内存空间。当需要往 AVFrame 中写入数据时,需要先使用 av_frame_get_buffer 函数为 AVFrame 的数据缓存区分配一定大小的内存空间。然而,在某些场景下,已经分配的内存空间可能并不足够用于存储实际数据,这时就需要使用 av_frame_make_writable 函数确保 AVFrame 可以进行写入操作

函数原型如下:

int av_frame_make_writable(AVFrame *frame);

该函数接受一个指向 AVFrame 结构体的指针 frame,用于修改的 AVFrame 数据缓存区通常是由其他进程或其他线程所修改,这时候就需要调用 av_frame_make_writable 函数确保可写,否则将有可能导致系统崩溃或数据损坏。

调用该函数时,如果当前 AVFrame 已经是可写状态,函数会直接返回 0。如果当前 AVFrame 不是可写状态,则会创建一个新的缓存区,并将 AVFrame 指向该缓存区。在创建新的缓存区时,需要重新分配缓存区空间并进行数据拷贝,这可能会导致一定的性能损失。如果内存分配失败,则函数返回 AVERROR。

总之,av_frame_make_writable 的作用是确保 AVFrame 可以进行写入操作。如果已经是可写状态,则直接返回 0,否则会进行新的缓存区的分配工作。调用该函数时需要注意内存分配可能会导致性能损失,如果分配失败将返回 AVERROR。

函数作用,确保frame中的data域它的空间可用的。我们进行编码的时候,需要将frame传给编码器,编码器拿到frame之后,锁定frame中的data,然后拿这个数据进行编码,锁定的时候,如果我们想使用这个frame再向编码器传送一帧数据,就会发生冲突。

怎么做呢?

调用av_frame_make_writeable函数

这个函数会检测frame中的data域,看这个data域是否被锁定,若被锁定,就会重新会frame中data域分配一个buffer,如果没有锁定,说明现在这个data域是可写的。这样后面就可以向这个data域写入数据。

//9. 生成视频内容 这个循环中,每一次循环,产生一帧数据
    for(int i=0; i<25; i++){
        ret = av_frame_make_writable(frame);
        if(ret < 0) {
            break;
        }
        产生帧数据。。。
      }

所以对于第一轮循环来说是没有问题的。因为我们在前面已经通过getBuffer来为frame中的data域分配了空间。

当第二轮,将一帧数据传给编码器的就有可能被锁定了。所以通过这个方法解决。

avchannel_layout_copy

avchannel_layout_copy() 是 FFmpeg 中的一个函数,用于将一个音频通道布局(AV_CH_LAYOUT_XXX)从源中复制到目标中

函数原型如下:

uint64_t av_channel_layout_copy(uint64_t *dst, const uint64_t *src);

其中,dst 是目标音频通道布局的指针,src 是源音频通道布局的指针。

使用示例:

AVCodecContext *ctx;
// 给 ctx 赋值
AVCodecContext temp_ctx;
uint64_t dst_layout = 0;
int ret;

temp_ctx = *ctx;
ret = av_channel_layout_copy(&dst_layout, &temp_ctx.channel_layout);
if (ret < 0) {
    fprintf(stderr, "Failed to copy audio channel layout: %s\n", av_err2str(ret));
    // 错误处理
}

该函数会将源音频通道布局中的值复制到目标音频通道布局中,并返回复制的通道个数。如果复制失败,函数将返回负值,该函数的错误信息可以使用 av_err2str() 获取。

avcodec_parameters_to_context

在FFmpeg中,AVCodecContext结构体用于描述编解码器上下文。AVCodecParameters结构体则用于描述编解码器参数,包括音频流的采样率、视频流的宽度和高度等参数。

函数作用将从AVFormatContext中获取的AVCodecParameters结构体中的参数赋值给AVCodecContext结构体

函数原型如下:

int avcodec_parameters_to_context(AVCodecContext *codec, const AVCodecParameters *par);

其中,codec表示要被更新的AVCodecContext结构体,par表示从中获取编解码器参数的AVCodecParameters结构体。

示例代码如下:

AVFormatContext *fmt_ctx;
AVCodec *dec;
AVCodecContext *codec_ctx;
AVCodecParameters *codecpar;
int stream_index;

// 打开文件
if (avformat_open_input(&fmt_ctx, "input.mp4", NULL, NULL) < 0) {
    return;
}

// 查找音频流
if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
    return;
}
stream_index = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, &dec, 0);

// 获取AVCodecParameters结构体
codecpar = fmt_ctx->streams[stream_index]->codecpar;

// 分配AVCodecContext结构体
codec_ctx = avcodec_alloc_context3(dec);

// 将参数拷贝到AVCodecContext结构体中
if (avcodec_parameters_to_context(codec_ctx, codecpar) < 0) {
    return;
}

// 后续操作...

在以上示例中,首先打开一个mp4文件,并查找了其中的音频流。通过获取音频流对应的AVCodecParameters结构体,可以创建一个AVCodecContext结构体,并通过avcodec_parameters_to_context函数将参数赋值给它。接下来,我们就可以使用codec_ctx进行音频的解码操作等。

sws_getContext

用于初始化一个SwsContext结构体。它是一个图像缩放和格式转换的上下文

函数的一般形式如下:

struct SwsContext *sws_getContext(int srcW, 
                                  int srcH, 
                                  enum AVPixelFormat srcFormat,
                                  int dstW, 
                                  int dstH, 
                                  enum AVPixelFormat dstFormat,
                                  int flags, 
                                  SwsFilter *srcFilter, 
                                  SwsFilter *dstFilter, 
                                  const double *param);

其中,参数列表的含义如下:

  • srcWsrcH:源图像的宽度和高度;
  • srcFormat:源图像的像素格式;
  • dstWdstH:目标图像的宽度和高度;
  • dstFormat:目标图像的像素格式;
  • flags:选项标志(可以为0);
  • srcFilterdstFilter:源图像和目标图像的过滤器;
  • param:可选参数。

函数返回值是一个指向SwsContext结构体的指针,该结构体包含用于图像转换的所有必要信息。

一旦上下文被初始化,它就可以用于执行各种图像操作。例如,可以使用sws_scale函数将图像帧从源格式缩放到目标格式(以进行格式转换和大小调整),或者使用sws_getCachedContext函数从上下文缓存中获取更快的转换速度。

总的来说,sws_getContext函数是FFmpeg库中重要的图像处理函数之一,可以满足各种图像转换的需求。

sws_scale

sws_scaleFFmpeg中用于图像缩放和格式转换的函数。它可以将一个输入图像按照指定的参数进行缩放和格式转换,输出一个新的图像。

函数签名如下:

int sws_scale(SwsContext *c, const uint8_t *const srcSlice[], const int srcStride[], int srcSliceY, int srcSliceH,
              uint8_t *const dst[], const int dstStride[]);

其中,主要参数含义如下:

  • SwsContext *c:缩放和转换的上下文,由sws_getContext函数创建。
  • const uint8_t *const srcSlice[]:输入图像中所有行的指针的数组。每个元素是指向一行数据的指针。
  • const int srcStride[]:输入图像中每行数据的字节数,是一个数组,与srcSlice中的元素一一对应。
  • int srcSliceY:输入图像的起始行数。
  • int srcSliceH:输入图像的高度,从起始行开始计算。
  • uint8_t *const dst[]:输出缩放后图像中所有行的指针的数组,每个元素是指向一行数据的指针。
  • const int dstStride[]:输出缩放后图像中每行数据的字节数,是一个数组,与dst中的元素一一对应。

该函数会对输入图像指定的区域进行缩放和色彩空间转换,生成一个新的图像。具体的缩放和转换方式由上下文决定,通常通过设定特定的参数来实现,例如指定目标图像的大小、目标图像的色彩空间等等。

sws_scale函数的返回值为输出的图像行数,如果发生错误则返回负值。

以下是使用sws_scale函数进行图像缩放和格式转换的简单示例代码:

#include 

int main()
{
    // 输入图像的参数
    int srcWidth = 1920;
    int srcHeight = 1080;
    int srcChannels = 3;
    uint8_t *srcData = // 输入图像数据
    int srcStride[] = {srcWidth * srcChannels}; // 每行数据的字节数

    // 输出图像的参数
    int dstWidth = 1280;
    int dstHeight = 720;
    int dstChannels = 2; // 输出图像的色彩空间为YUV420格式
    int dstStride[] = {dstWidth * dstChannels}; // 每行数据的字节数
    uint8_t *dstData = (uint8_t*)malloc(dstStride[0] * dstHeight); // 分配输出图像数据的空间

    // 创建sws_scale上下文
    SwsContext *swsContext = sws_getContext(srcWidth, srcHeight, AV_PIX_FMT_BGR24,
                                            dstWidth, dstHeight, AV_PIX_FMT_YUV420P,
                                            SWS_BICUBIC, NULL, NULL, NULL);

    // 进行缩放和格式转换
    sws_scale(swsContext, &srcData, srcStride, 0, srcHeight, &dstData, dstStride);

    // 释放空间,并关闭上下文
    free(dstData);
    sws_freeContext(swsContext);
  
    return 0;
}

上述代码实现了将一张BGR格式的1920x1080的图像缩放为YUV420格式的1280x720的图像。需要注意的是,输入和输出图像的参数需要与创建上下文时设定的参数一致,否则可能无法正常转换并输出图像。另外,如果需要将输出的图像保存为文件,还需要使用特定方式进行编码和写入

swr_alloc

swr_alloc 函数是用于创建 SwrContext 结构体的函数,其定义如下:

SwrContext *swr_alloc(void);

返回值为创建的 SwrContext 结构体的指针,如果创建失败,则返回 NULL

调用 swr_alloc 函数可以创建一个 SwrContext 结构体,并为其分配内存空间。需要注意的是,创建该结构体后需要使用 swr_alloc_set_opts 函数来设置音频的输入和输出参数,否则不能进行音频重采样。

使用完 SwrContext 结构体后,需要调用 swr_free 函数来释放内存空间。

swr_init

swr_init 函数是用于初始化 SwrContext 结构体的函数,其定义如下:

int swr_init(SwrContext *s);

其中,s 参数为要进行初始化的 SwrContext 结构体,返回值为初始化结果,成功返回 0,失败返回负数。

调用 swr_init 函数后,SwrContext 中的各种参数会根据之前通过 swr_alloc_set_opts 函数设置的输入输出参数进行初始化,包括音频采样格式、样本率、声道布局等参数。初始化后即可调用 swr_convert 函数进行音频重采样。

swr_alloc_set_opts2

根据需要分配SwrContext,并设置/重置公共参数

该函数不需要使用swr_alloc分配。另一方面,swr_alloc0可以使用swr_alloc_set opts2来设置已分配上下文的参数。

它的定义如下:

SwrContext *swr_alloc_set_opts2(SwrContext *s,
                                int64_t out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate,
                                cint64_t in_ch_layout, enum AVSampleFormat in_sample_fmt, int in_sample_rate,
                                int log_offset, void *log_ctx);

该函数的参数含义如下:

  • s: SwrContext 结构体指针,如果为NULL,则会创建一个新的SwrContext对象,否则会更新现有的SwrContext对象。
  • out_ch_layout:输出通道布局;
  • out_sample_fmt:输出采样格式;
  • out_sample_rate:输出采样率;
  • in_ch_layout:输入通道布局;
  • in_sample_fmt:输入采样格式;
  • in_sample_rate:输入采样率;
  • log_offset:日志级别;
  • log_ctx:AVClass 上下文指针。

返回值:

成功时为0,错误时为负AVERROR代码。错误时,Swr上下文被释放,*s设置为NULL。

swr_convert

swr_convert 函数是 SwrContext 结构体中用于进行音频重采样的核心函数。其函数原型如下:

int swr_convert(SwrContext *s, uint8_t **out, int out_count, const uint8_t **in, int in_count);

其中,参数说明如下:

  • s:要进行音频重采样的 SwrContext 结构体;
  • out:输出音频数据的缓冲区地址;(重采样后的数据)
  • out_count:每个通道可用于输出的可用空间的数量;(输出数据中每个通道的采样个数)
  • in:输入音频数据的缓冲区地址;(需要进行重采样的数据)
  • in_count:一个通道中可用的输入样本数量。(输入数据中每个通道的采样个数)

函数的返回值为每个通道输出的样本数量,如果返回值为负数,则表示转换失败。

swr_convert 函数为音频数据进行重采样,**输入缓冲区中的音频数据会被重采样为指定格式的输出缓冲区所需的样本数

swr_free

swr_free 函数是用于释放 SwrContext 结构体内存的函数,其定义如下:

void swr_free(SwrContext **s);

其中,s 参数为指向 SwrContext 结构体指针的指针,表示要释放的结构体。该函数没有返回值,释放成功后,SwrContext 结构体指针被设置为 NULL

av_samples_get_buffer_size

函数 av_samples_get_buffer_size() 是 FFmpeg 提供的计算音频数据缓冲区大小的函数,其函数原型如下:

int av_samples_get_buffer_size(int *linesize, int nb_channels, int nb_samples,
                               enum AVSampleFormat sample_fmt, int align);

函数参数说明:

  • linesize:指向缓冲区每行数据的大小,如果为 NULL 则不计算;

  • nb_channels:音频通道数;

  • nb_samples:每个通道的采样数;

  • sample_fmt:采样格式,例如 AV_SAMPLE_FMT_S16AV_SAMPLE_FMT_FLT 等;

  • align:缓冲区对齐方式,通常为 0。

    • 0 = default”,常用于提供冗余的大小,此函数之后常常跟着的是类malloc函数。

    • 1 = no alignment”,不对齐,其实即是按1字节对齐,也就是说,求的是音频数据的真实大小,常常用来计算出刚刚(调用此函数之前)转码出来的数据大小

该函数的功能是计算给定采样格式、通道数、采样数等参数下,音频数据缓冲区需要的总大小。

av_fast_malloc

av_fast_malloc() 是 FFmpeg 提供的快速分配内存的函数,其函数原型如下:

void av_fast_malloc(void *ptr, unsigned int *size, size_t min_size);

函数参数说明:

  • ptr:指向当前已分配内存的指针;
  • size:实例分配的大小。
  • min_size:期望分配的大小。

av_guess_sample_aspect_ratio

av_guess_sample_aspect_ratio是FFmpeg库中的一个函数,用于猜测给定帧的样本宽高比

函数原型如下:

AVRational av_guess_sample_aspect_ratio(AVFormatContext *format_context, AVStream *stream, AVFrame *frame);

参数说明:

  • format_context:输入文件的上下文。
  • stream:帧所在的流。
  • frame:需要猜测样本宽高比的帧。

返回值:

  • 返回猜测出的样本宽高比。

av_guess_frame_rate

用于猜测视频帧率。其函数原型如下:

AVRational av_guess_frame_rate(AVFormatContext *ctx, AVStream *stream, AVFrame *frame);

函数参数说明如下:

  • ctx:输入的AVFormatContext上下文,指向包含此视频的容器格式上下文。
  • stream:输入的AVStream结构体,指向视频流。
  • frame:视频帧(AVFrame结构体)。

函数返回值为一个AVRational结构体,表示猜测的帧率。如果无法猜测,则返回一个缺省的帧率。

av_guess_frame_rate根据流的时基和帧的时间戳猜测视频的帧率。如果输入的视频流中存在无效的帧率数据,那么猜测的结果可能不准确。

需要注意的是,av_guess_frame_rate只是一个猜测函数,无法保证结果的准确性。如果需要精确的帧率信息,可以从AVStream结构体中的r_frame_rate字段中获取。

av_get_bytes_per_sample

av_get_bytes_per_sample是FFmpeg中的一个函数,用于计算指定采样格式的每个采样所占用的字节数。它的定义如下:

int av_get_bytes_per_sample(enum AVSampleFormat sample_fmt);

该函数的参数sample_fmt是一个枚举类型AVSampleFormat,表示采样格式。它的返回值是一个整数,表示每个采样所占用的字节数。

例如,如果我们想要计算16位有符号整数类型的采样每个采样所占用的字节数,可以这样调用av_get_bytes_per_sample函数:

int bytes_per_sample = av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);

这个调用将返回2,表示每个采样占用2个字节。

av_strdup

av_strdup 函数是 FFmpeg 中的一个字符串操作函数,用于将一个字符串复制到新的内存空间中,并返回新的字符串指针。其原型如下:

char *av_strdup(const char *s);

参数说明:

  • s:要复制的字符串。

返回值为新的字符串指针,如果分配内存失败,则返回 NULL。

av_strdup 函数的作用类似于 C 标准库中的 strdup 函数,但是 av_strdup 函数使用的是 FFmpeg 中的内存分配函数 av_malloc 和 av_free,因此可以避免一些内存管理上的问题。另外,av_strdup 函数还可以用于复制 FFmpeg 中 AV 字符串(AVString)类型的字符串。

AVRational

用于将 AVRational 类型的有理数转换为双精度浮点数。

AVRational 类型是 FFmpeg 库中用于表示有理数的结构体,包含了分子和分母。在视频编解码中,常常用到分数来表示帧率、码率等信息。

函数原型如下:

double av_q2d (AVRational a);

参数 a 是一个 AVRational 类型的有理数。

该函数返回一个双精度浮点数,表示将有理数 a 转换为浮点数后的值。

例如,如果有一个 AVRational 类型变量 r,可以使用 av_q2d 函数将其转换为双精度浮点数,如下所示:

double d = av_q2d(r);

该函数的具体实现如下:

static inline double av_q2d(AVRational a)
{
    return a.num / (double) a.den;
}

函数内部首先将分子转换为双精度浮点数,然后除以分母,得到有理数的浮点数值。

常用数据结构

AVFormatContext

它用于表示一个封装格式上下文,包含了读取或写入音视频文件所需要的全部信息,如音视频流、时间戳、媒体信息等。

AVFormatContext结构体的成员变量如下:

  1. AVInputFormat* iformat:输入格式,如果是读取文件时会指定该参数。
  2. AVOutputFormat* oformat:输出格式,如果是写入文件时会指定该参数。
  3. void* priv_data:私有数据,FFmpeg模块内部使用。
  4. AVIOContext* pb:IO上下文,用于读取或写入文件。
  5. int nb_streams:流的数量。
  6. AVStream** streams:音视频流数组,包含了所有的音视频流。
  7. char* filename:文件名。
  8. int64_t start_time:文件的开始时间,单位为微秒。
  9. int64_t duration:文件的持续时间,单位为微秒。
  10. int bit_rate:总比特率。
  11. AVDictionary* metadata:元数据,包含了文件的各种信息。
  12. int n_programs:程序数量。
  13. AVProgram** programs:程序数组,用于单独处理一组流。

在解码和编码过程中,我们需要使用AVFormatContext结构体来读取或写入不同类型的音视频文件。对于需要读取文件的情况,在调用avformat_open_input函数获取AVFormatContext结构体之后,需要通过av_read_frame函数来读取每个音视频流的AVPacket。

而对于需要写入文件的情况,我们需要先调用avformat_alloc_output_context2函数来初始化AVFormatContext结构体,然后使用avformat_new_stream函数来创建音视频流,并通过av_write_frame函数来写入每个音视频流的AVPacket。

AVStream

AVStream结构体用于表示一个音视频流。在FFmpeg中,一个多媒体文件通常包含多个音频流和视频流,每个流都有对应的AVStream结构体。

AVStream结构体的成员变量非常丰富,下面分别进行介绍:

  1. index:流的索引,表示在多媒体文件中该流的位置。
  2. codecpar:该流的编解码器参数,通过AVCodecParameters结构体包含了编解码器的具体信息。
  3. time_base:时间基准,表示单位时间内的时间戳数量。
  4. r_frame_rate:帧率,表示每秒钟的帧数。
  5. id:流的ID,在多媒体文件中唯一。
  6. metadata:流的元数据,包含了流的各种相关信息。
  7. nb_frames:流中帧的数量。
  8. start_time:流的开始时间。
  9. duration:流的持续时间。
  10. disposition:流的位置信息,如前置流、后置流等。
  11. avg_frame_rate:平均帧率。
  12. recommended_encoder_configuration:推荐的编码器配置信息。
  13. codec_info_duration:编解码器信息的持续时间。

AVStream结构体的成员变量非常丰富,这些成员变量可以用来描述一个音视频流的各种属性和信息,方便我们在使用FFmpeg进行音视频处理和编解码时对流进行正确的处理。

AVPacket

在 FFmpeg 中,AVPacket 结构体用于存储编码后的音视频数据帧,是 FFmpeg 中传递数据的基本单元

下面是 AVPacket 结构体的定义:

typedef struct AVPacket {
    int64_t pts;            // 表示该帧的显示时间戳
    int64_t dts;            // 表示该帧的解码时间戳
    uint8_t *data;          // 数据指针,指向存储音视频帧数据的缓冲区
    int size;               // 音视频帧数据的大小
    int stream_index;       // 音视频流的索引,表示该帧数据属于哪个流
    int flags;              // 标志位,用于记录该帧数据的一些状态。
    struct AVPacket *next;  // 下一帧数据指针
    int64_t pos;            // 数据在输入文件中的位置
    int64_t duration;       // 帧时长
} AVPacket;

AVPacket 结构体中包含了以下一些重要的字段:

  • ptsdtspts 表示显示时间戳,用于表示音视频帧在整个数据流中的播放时间;dts 表示解码时间戳,用于表示音视频帧在整个数据流中的解码时间。
  • datasizedata 是指向音视频帧数据的指针,size 表示数据长度。
  • stream_index:表示当前音视频帧所属的流索引。
  • flags:表示当前帧的一些特殊属性,例如是否为关键帧、是否为有效帧等。
  • pos:表示音视频帧在输入文件中的位置。
  • duration:表示帧时长,仅在某些情况下有意义,例如针对视频帧的场景。

值得注意的是,AVPacket 可能包含多帧数据,因此它不仅表示单个数据帧,同时也可以表示多个数据帧组成的数据包。多帧数据可以通过 AVPacket 中的 sizedata 字段进行访问,而每个数据帧的时间戳和时长则可以使用 ptsdtsduration 字段进行访问。

AVPacket作为音视频编解码过程中的中转数据格式,它在各个步骤中都发挥着重要的作用。在解封装时,AVPacket用于存储从容器中读取的音视频压缩数据;在编码时,AVPacket用于存储编码后的压缩数据。而在编解码操作之间,AVPacket又作为数据传输的载体,通过FFmpeg的数据传输接口进行传递。

AVFifo

一种FIFO(First In First Out)先进先出的数据结构。

struct AVFifo {
     uint8_t *buffer;
  
     size_t elem_size, nb_elems;
     size_t offset_r, offset_w;
     // distinguishes the ambiguous situation offset_r == offset_w
     int    is_empty;
  
     unsigned int flags;
     size_t       auto_grow_limit;
 };

AVIOContext

AVIOContext是FFmpeg中的一个结构体,它是输出文件上下文,它可用于向文件中写入数据,

AVIOContext包含了一些重要的字段:

  1. buffer:缓存指针,指向内存空间。
  2. buffer_size:缓存大小,单位为字节。
  3. buf_ptr:当前指针,表示缓存中数据当前的位置。
  4. eof_reached:标志位,表示当前缓存是否读取完毕,1表示读取完毕,0表示未读取完毕。
  5. read_packet:读取数据包的回调函数指针。
  6. write_packet:写入数据包的回调函数指针。
  7. seek:流定位函数的回调函数指针。
  8. opaque:不透明指针,用于在回调函数中传递参数。

在FFmpeg中,许多模块都是使用AVIOContext来实现数据输入输出的操作。因此,熟练掌握AVIOContext的使用对于掌握FFmpeg的输入输出和数据处理流程非常重要。

AVOutputFormat

AVOutputFormat是ffmpeg中定义输出格式的结构体,代表一个音视频输出格式,包括格式名称、文件扩展名、音视频编解码器等信息。每种输出格式都有一个对应的AVOutputFormat结构体,

AVOutputFormat结构体定义如下:

typedef struct AVOutputFormat {
    const char *name;        /* 格式名称 */
    const char *long_name;   /* 格式全名 */
    const char *mime_type;   /* mime类型 */
    const char *extensions;  /* 文件扩展名 */

    int flags;               /* 格式标志 */
    const AVCodecTag * const *codec_tag; /* 音视频编码器标记 */

    const struct AVOutputFormat *next;
    /* ... */
} AVOutputFormat;
  • name:格式名称,用于标识某种输出格式的简写名称,如“mp4”、“mpeg”等。
  • long_name:格式全名,用于显示某种输出格式的全称,如“MPEG-4 Part 14”。
  • mime_type:输出格式的mime类型,用于表示该格式的媒体类型,如“video/mp4”。
  • extensions:该格式支持的文件扩展名,以空格分隔,如“mp4 m4v”。
  • flags:格式标志,用于指示特定格式的属性,如“AVFMT_NOFILE”表示该格式不需要底层文件句柄。
  • codec_tag:音视频编码器标记,用于指定音视频编解码器的标识。该标记包含了四个字符的编码器ID和一个与之对应的压缩编码器ID。
  • next:指向下一个AVOutputFormat结构体的指针,用于构建输出格式链表。

以MP4格式为例,其对应的AVOutputFormat结构体如下:

AVOutputFormat ff_mp4_muxer = {
    .name              = "mp4",
    .long_name         = NULL_IF_CONFIG_SMALL("MP4 (MPEG-4 Part 14)"),
    .mime_type         = "video/mp4",
    .extensions        = "mp4",

    .video_codec       = AV_CODEC_ID_MPEG4,
    .audio_codec       = AV_CODEC_ID_AAC,

    /* ... */

    .codec_tag         = (const struct AVCodecTag *const []) {
        ff_mp4_obj_type, 0
    },

    /* ... */
};

AVCodecParameters

AVCodecParameters它用于描述音视频编解码器的相关参数,包括编解码格式、宽高、采样率等等。

typedef struct AVStream {
    int index;          // 媒体流的索引号,表示在文件中的排列顺序
    int id;             // 媒体流的编号
    AVCodecContext *codec;   // 指向对应的 AVCodecContext 结构体的指针,表示媒体流的编解码器上下文
    void *priv_data;    // 指向私有数据的指针,存储一些额外的信息(如元数据、辅助数据等)
    AVRational time_base;   // 时间基准,表示每个时间单位的长度,例如输入文件中的每个视频帧、音频帧等时间长度
    int64_t start_time; // 媒体流的起始时间,以 AV_TIME_BASE 为单位
    int64_t duration;   // 媒体流的持续时间,以 AV_TIME_BASE 为单位
    int64_t nb_frames;  // 媒体流中的总帧数
    uint32_t disposition;   // 媒体流的处理方式,可以是原始数据、附加数据等
    enum AVDiscard discard; // 丢帧策略
    AVRational sample_aspect_ratio; // 采样长宽比,视频特有
    AVDictionary *metadata;  // 元数据
    AVRational avg_frame_rate;   // 帧率
    AVPacket attached_pic; // 附图
    AVCodecParameters *codecpar; // 媒体流的编解码参数,可以获取媒体流的相关信息,如码率、分辨率、帧率等
    struct AVFrac pts;  // 表示媒体流中最新的PTS值(presentation timestamp),用于时间基准的缩放计算
    AVStreamParseType need_parsing; // 解析媒体流的方式
    struct AVCodecParserContext *parser; // 媒体流解析器上下文,用于解析媒体流
    int64_t cur_dts;    // 媒体流中最新的DTS值(decoding timestamp),表示解码器解码时间,以 AV_TIME_BASE 为单位
    int64_t last_IP_pts;    // 最新的I帧或P帧的时间戳PTS值,依赖于参考(reference)媒体流
    int last_IP_duration;   // 最新的I帧或P帧的持续时间,依赖于参考(reference)媒体流
    char language[4];   // 媒体流的语言编码,ISO 639-2 3字码
    int nb_decoded_frames;   // 编解码后的总帧数
    int64_t mux_ts_offset;  // muxer的时间戳偏移量
    AVStreamSideData **side_data;    // 指向 Side Data 数据的指针,如添加的 Metadata
    int nb_side_data;   // Side Data 数据的个数
    int event_flags;    // 事件标志
} AVStream;

AVCodecParameters结构体的成员变量如下:

  1. enum AVCodecID codec_id:编解码器ID,表示采用的编解码器类型。
  2. enum AVMediaType codec_type:编解码器类型,包括视频、音频、字幕等。
  3. int width:视频宽度。
  4. int height:视频高度。
  5. AVRational sample_aspect_ratio:采样率。
  6. int bits_per_raw_sample:每个采样的比特数。
  7. int format:像素格式。
  8. AVRational time_base:时间基准,表示单位时间内的时间戳数量。
  9. int channels:音频通道数。
  10. int sample_rate:音频采样率。
  11. uint64_t channel_layout:音频通道布局。
  12. int64_t bit_rate:比特率。
  13. AVDictionary* extradata:额外数据,包含了关于编解码器和流的相关信息。

AVCodecParameters结构体中的这些成员变量描述了编解码器的各种参数和属性,它们对于解码和编码过程非常重要。在解码过程中,我们需要使用AVCodecParameters结构体中的信息对解码器进行初始化,并从AVPacket中解码出AVFrame,而在编码过程中,我们也需要使用AVCodecParameters结构体中的信息对编码器进行初始化,并将AVFrame编码成AVPacket。

技术细节:
AVCodecParameters结构体在FFmpeg版本3.0及以后被引入。以前的版本中用的是AVCodecContext结构体,但是该结构体包含了更多与编解码器本身有关的信息。AVCodecParameters被设计用于存储容器格式相关的编解码器信息,而将更多的编解码器相关信息存储在AVCodecContext中。****

AVCodecParameters和AVCodecContext的区别

  1. AVCodecContext结构体是一个重量级的结构体,包含了AVCodecParameters结构体所有信息,同时还包含了一些AVCodecParameters中没有的参数,比如一些与编解码器相关的控制信息;
  2. AVCodecContext结构体通常用于描述音视频的编解码器,可用于解码和编码等操作;
  3. 当进行解码时,通常先从AVCodecParameters结构体中创建一个AVCodecContext结构体,再使用该结构体进行解码。

在使用AVCodecParameters和AVCodecContext时,我们需要明确它们之间的关系。AVCodecContext结构体是AVCodecParameters结构体的一个超集,AVCodecParameters结构体中包含了AVCodecContext结构体中的大部分参数,但是AVCodecContext结构体中还包含了很多AVCodecParameters没有的编解码器相关参数。所以,在一些与编解码器有关的操作中,我们需要使用AVCodecContext,而在仅仅获取音视频流的参数信息时,我们可以使用AVCodecParameters。

AVCodec

AVCodec结构体是FFmpeg中定义的一个编码器结构体。在FFmpeg中,每个媒体格式(如MP4、FLV等)都对应着一个或多个编解码器。当需要解码一个视频文件时,FFmpeg会根据文件格式自动选择对应的编解码器,然后将解码后的数据保存到AVFrame结构体中。

下面对AVCodec结构体的成员变量做一个简单解析:

  1. name:编解码器名称,以字符串形式保存。
  2. type:编解码器类型,包括音频编码器、视频编码器、音频解码器和视频解码器等。
  3. id:编解码器的唯一标识符,以AVCodecID枚举类型表示。
  4. capabilities:编解码器的能力标志,以AV_CODEC_CAP_*常量表示,用于标识编解码器支持的特性和功能。
  5. supported_framerates:支持的帧率列表。
  6. pix_fmts:支持的像素格式列表。
  7. supported_samplerates:支持的采样率列表。
  8. sample_fmts:支持的采样格式列表。
  9. channel_layouts:支持的声道布局列表。
  10. profiles:支持的编解码器配置文件列表。
  11. priv_class:编解码器私有数据的类结构体。
  12. init_static_data:初始化静态数据的函数指针。
  13. init:初始化编解码器的函数指针。
  14. encode2:编码数据的函数指针。
  15. decode:解码数据的函数指针。
  16. flush:刷新编解码器缓存的函数指针。
  17. close:关闭编解码器的函数指针。

在使用AVCodec结构体时,需要根据具体的操作选择适合的函数指针。例如,解码音频数据时,需要用到decode函数,而编码视频数据时,需要用到encode2函数。同时,根据编解码器的支持情况,选择合适的像素格式、采样率、采样格式和声道布局等参数,以便得到更好的编解码效果。

AVCodecContext

AVCodecContext是FFmpeg中用于描述编解码器上下文信息的结构体,它包含了大量的编解码器参数和状态信息,如编解码器类型、帧率、分辨率、编解码器参数、编码质量、音频采样率、通道数、码率等。

下面对AVCodecContext结构体的成员变量做一个简单解析:

  1. av_class:用于访问AVCodecContext结构体的类结构体。

  2. codec_id:编解码器ID,即AVCodecID枚举类型的值。

  3. codec_type:编解码器类型,即AVMediaType枚举类型的值。

  4. bit_rate:比特率,以比特每秒(bps)为单位。

  5. sample_rate:采样率,以赫兹(Hz)为单位。

  6. channels:声道数。

  7. channel_layout:声道布局,可通过FFmpeg提供的API获取。

  8. time_base:时间基准,指定每个刻度代表的时间长度。

  9. ticks_per_frame:每帧所需的刻度数。

  10. width:视频帧宽度。

  11. height:视频帧高度。

  12. pix_fmt:像素格式,即AVPixelFormat枚举类型的值。

  13. sample_fmt:采样格式,即AVSampleFormat枚举类型的值。

  14. delay:编解码器输出的时延。

  15. codec:指向AVCodec的指针,表示使用的编解码器。

  16. codec_tag:编解码器标签。

  17. priv_data:编解码器私有数据。

  18. gop_size: 表示视频帧组(Group of Pictures,简称 GOP)的大小。

    当需要传输视频流时,较小的 gop_size 可以降低网络延迟和带宽消耗。而当需要在本地进行视频剪辑和编辑时,较大的 gop_size 可以提高视频质量和编辑效率

  19. flags:编解码器状态标志。

  20. extradata:编解码器额外数据。

  21. extradata_size:额外数据的长度。

  22. qmin、qmax、qcompress、qblur、global_quality:视频编码参数,如最小/最大量化因子,压缩因子等等。

  23. strict_std_compliance:是否遵循标准的标志,即表示是否忽略一些非标准格式等非关键错误。

  24. level、profile:视频编码级别和配置文件。

  25. keyframe_interval:关键帧间隔。

  26. pkt_timebase:数据包时间基准。

AVFrame

av_frame 是 FFmpeg 中的一个结构体,用于存储音视频帧数据。它包含了一些与帧相关的信息,如帧的时间戳、持续时间、采样率、声道数、像素格式等等。具体来说,av_frame 结构体包含了以下成员:

typedef struct AVFrame {
    uint8_t *data[AV_NUM_DATA_POINTERS];
    int linesize[AV_NUM_DATA_POINTERS];
    uint8_t *buf[AV_NUM_DATA_POINTERS];
    int64_t pts;
    int64_t pkt_dts;
    int64_t best_effort_timestamp;
    int64_t pkt_pos;
    int64_t pkt_duration;
    AVRational sample_aspect_ratio;
    int format;
    int width, height;
    int channels;
    int channel_layout;
    enum AVColorRange color_range;
    enum AVColorSpace color_space;
    enum AVColorPrimaries color_primaries;
    enum AVColorTransferCharacteristic color_trc;
    int64_t opaque;
    struct AVDictionary *metadata;
    int flags;
    int64_t pkt_size;
    void *hw_frames_ctx;
    AVBufferRef *bufs[AV_NUM_DATA_POINTERS];
} AVFrame;
  • data:是一个uint8_t* 类型的指针数组,用于存储音视频帧的数据。

    data数组中的指针指向的是音视频数据的缓冲区。由于一个音视频帧可能包含多个数据,比如一个YUV420P格式的视频帧就包含了Y、U、V三个分量的数据,因此data数组是一个指针数组,每个指针指向音视频帧的一个数据块。

    data数组中的元素对应的数据块是根据像素格式而确定的。例如对于YUV420P格式的视频帧,data[0]指向Y分量的数据块,data[1]指向U分量的数据块,data[2]指向V分量的数据块。对于音频帧,data[0]指向该帧的PCM数据块。

    对于视频帧,data数组中存储的是一帧图像的像素数据;对于音频帧,data数组中存储的则是一帧采样数据。

    需要注意的是,在使用AVFrame结构体时,我们应该先使用av_frame_alloc函数初始化一个AVFrame结构体,然后使用av_frame_get_buffer函数为AVFrame分配数据缓冲区。这一步分配缓冲区的过程会初始化data数组中的指针,每个指针将指向一块数据缓冲区。

    示例代码:

    #include 
    #include 
    
    int main() {
      // 初始化AVFrame结构体
      AVFrame *frame = av_frame_alloc();
      frame->width = 1920;
      frame->height = 1080;
      frame->format = AV_PIX_FMT_YUV420P;
    
      // 分配缓冲区
      int ret = av_frame_get_buffer(frame, 32);
      if (ret < 0) {
        printf("Failed to allocate buffer for AVFrame.\n");
        return 1;
      }
    
      // 将数据存储到data数组中
      uint8_t *y_data = frame->data[0]; // 存储Y分量的数据
      uint8_t *u_data = frame->data[1]; // 存储U分量的数据
      uint8_t *v_data = frame->data[2]; // 存储V分量的数据
    
      // 接下来可以对y_data、u_data和v_data进行处理
    
      // 释放AVFrame结构体
      av_frame_free(&frame);
      return 0;
    }
    

    在上面的代码中,我们使用av_frame_alloc函数初始化AVFrame结构体并设置其宽度、高度和像素格式。接着,我们使用av_frame_get_buffer函数为AVFrame分配数据缓冲区,并将data数组中的指针指向相应的数据块。最后,我们可以对data数组中的数据进行处理,并使用av_frame_free函数释放AVFrame结构体。

  • linesize:int 类型的指针数组,每一行像素数据在内存中所占用的字节数。

    在AVFrame结构体中,linesize数组和data数组一样也是一个指针数组,是用于存储音视频帧数据的行大小

    linesize数组中的元素表示每一行数据的大小(以字节为单位),这个值与像素格式有关,因此不同的像素格式其行大小也不同。AVFrame中的每一行数据长度其实就是这一行数据的像素数 * 每个像素占用的字节数。

    以YUV420P格式的视频帧为例,该格式的视频帧包含Y、U、V三个分量的数据,每个分量的数据都是按照一个平面的存储方式存储的。在每个分量的存储中,每一行数据的长度都是图像宽度大小。因此,对于一个YUV420P格式的视频帧,它的linesize[0]、linesize[1]和linesize[2]分别为图像宽度、图像宽度/2、图像宽度/2。

    需要注意的是,在使用AVFrame结构体时,我们应该先使用av_frame_alloc函数初始化一个AVFrame结构体,然后使用av_frame_get_buffer函数为AVFrame分配数据缓冲区。这一步分配缓冲区的过程会初始化linesize数组中的元素,每个元素表示相应数据的一行大小。

  • extended_data:uint8_t* 类型的数组指针,用于存储额外的数据。通常用于存储多通道音频数据。

  • nb_samples:int 类型,每个通道的音频采样个数。

  • format:int 类型,表示音视频帧的像素格式。

  • width:int 类型,表示视频帧的宽度。

  • height:int 类型,表示视频帧的高度。

  • sample_rate:int 类型,表示音频帧的采样率。

  • channel_layout:uint64_t 类型,表示音频帧的声道布局。

  • pts:int64_t 类型,表示该帧的时间戳(presentation timestamp)以time base为单位。

  • pkt_pts:int64_t 类型,表示该帧所在数据包的时间戳。

  • pkt_dts:int64_t 类型,表示该帧所在数据包的解码时间戳。

  • metadata:AVDictionary* 类型,表示帧的元数据(metadata)信息。

  • pkt_pos:表示该帧数据在媒体文件中的位置,以字节为单位。

总的来说,av_frame 可以看做是一个音视频帧数据的容器,它仅存储数据,而不存储与数据相关的其他信息。这些信息通常存储在与其对应的 AVPacket 中,或者在其他地方进行存储。在数据经过解码后,可以通过访问 av_frame 中的相应成员来获取所需的信息。

AVRational

AVRational是FFmpeg中表示有理数的结构体,用于描述时间基、帧率等涉及有理数的场合。它定义如下:

typedef struct AVRational {
    int num; // 分子
    int den; // 分母
} AVRational;

其中,num表示有理数的分子,den表示有理数的分母。

AVRational结构体常常被用于描述媒体文件中的时间戳、帧率等信息。例如,在解码器中,我们需要根据视频帧的时间戳(PTS)来进行时间基调整、帧率控制等操作。使用AVRational结构体,可以方便地表示时间戳、帧率这类涉及有理数的概念。

在FFmpeg中,AVRational结构体的定义和使用十分广泛。我们可以在很多地方看到它的身影。例如,在编解码器初始化时,需要设置AVCodecContext的time_base字段,这个字段就是一个AVRational结构体类型,用于表示时间基:

AVCodecContext *codec_context = avcodec_alloc_context3(codec);

codec_context->time_base = (AVRational){1, 25}; // 设置时间基为1/25

在上面的代码片段中,我们通过设置AVCodecContext的time_base字段来指定解码器的时间基为1/25。当然,time_base字段并不只是用于解码器,对于编码器来说同样也需要通过它来进行时间基的设置。

SwsContext

SwsContext结构体是FFmpeg库中的一个重要类型,用于图像缩放和格式转换

它的定义如下:

typedef struct SwsContext SwsContext;

其中SwsContext结构体中包含以下重要成员:

  • swScale:指向缩放函数的指针,用于将输入图像转换为输出图像。
  • redBlueTable:存储蓝色和红色通道的指针。
  • greenTable:指向绿色通道的指针。
  • brightnessTable:指向亮度表的指针。
  • contrastTable:指向对比度表的指针。
  • swScaleFlags:用于控制缩放函数的特定标志。

上述成员中**,最重要的是swScale成员,它是一个指向缩放函数的指针,用于将输入图像转换为输出图像**。它的定义如下:

int (*swScale)(struct SwsContext *,
               const uint8_t* const srcSlice[],
               const int srcStride[],
               int srcSliceY, int srcSliceH,
               uint8_t* const dst[], const int dstStride[]);

该函数的参数列表包括:

  • struct SwsContext *:指向上下文结构体的指针;
  • const uint8_t* const srcSlice[]:源图像的行指针数组;
  • const int srcStride[]:源图像每行的字节数组;
  • int srcSliceY:源图像裁剪的起始位置;
  • int srcSliceH:源图像裁剪的高度;
  • uint8_t* const dst[]:目标图像的行指针数组;
  • const int dstStride[]:目标图像每行的字节数组。

swScale成员是SwsContext结构体中最重要的成员之一,它定义了缩放函数的基本框架。不同的缩放函数实现可以使用不同的算法和优化技术。例如,FFmpeg库中提供了多种缩放函数,包括BICUBIC、BILINEAR、NEAREST等。

BITMAPFILEHEADER 和BITMAPINFOHEADER

BITMAPFILEHEADERBITMAPINFOHEADER都是用于描述位图文件的结构体。两者通常一起使用来完整地描述一个位图文件。

BITMAPFILEHEADER是位图文件的文件头,描述整个位图文件的基本信息,包括文件类型、文件大小以及位图数据的偏移量等等。它的作用类似于一个“外壳”,为解析位图文件提供了基本的信息。

BITMAPINFOHEADER是位图文件的图像信息头,用于描述位图文件中的图像信息,包括宽度、高度、位深、颜色平面数等等。它的作用类似于一个“内核”,提供了位图图像的详细信息。

两者的具体区别在于作用不同**。BITMAPFILEHEADER描述的是整个位图文件的基本信息,而BITMAPINFOHEADER描述的是位图图像的信息**。因此,在对位图文件进行处理时,我们需要先读取BITMAPFILEHEADER结构体,获取文件的基本信息,然后再读取BITMAPINFOHEADER结构体,获取图像的详细信息,以便后续的处理。

SwrContext

SwrContext 结构体是用于音频重采样的上下文结构体,它包含了一些必要的参数和缓存,用于存储重采样所需要的状态信息和中间数据。其定义如下:

typedef struct SwrContext SwrContext;
struct SwrContext {
    int swr_flags;

    AVSampleFormat in_sample_fmt;
    AVSampleFormat out_sample_fmt;

    int64_t in_channel_layout;
    int64_t out_channel_layout;

    int in_sample_rate;
    int out_sample_rate;

    int nb_samples;

    struct ResampleContext *resample;

    int async;
    int available_samples;
    AVAudioResampleContext *avr;
    int allocated_samples;
    int8_t **midbuf;

    int matrix_encoding;
    int need_rematrix;
    void (*rematrix)(struct SwrContext *s, int32_t **matrix, int stride,
                     const int32_t *coefs, int channels);
    int rematrix_custom;

    void *log_ctx;
    int log_level_offset;
};

其中,定义的成员变量包含:

  • swr_flags:重采样操作的一些标志,比如控制输出的采样数依赖于输入的采样数。
  • in_sample_fmt/out_sample_fmt:输入和输出音频采样格式,比如 AV_SAMPLE_FMT_S16
  • in_channel_layout/out_channel_layout:输入输出的声道布局,比如 AV_CH_LAYOUT_STEREO
  • in_sample_rate/out_sample_rate:输入输出的采样率,比如 48000
  • nb_samplesSwrContext 内置的 FIFO 缓存的采样数。
  • resample:音频重采样表示状态的结构体。
  • async/available_samples:用于异步重采样的标志和采样数。
  • beta1:存放音频重采样所需要的状态信息和缓存的指针。
  • matrix_encoding:矩阵编码。
  • rematrix:重编码函数。
  • rematrix_custom:自定义重编码函数标记。
  • log_ctx/log_level_offset:用于日志输出的上下文和其偏移量。

SwrContext 中最重要的成员是 resample,它是运行时音频重采样内部操作状态的结构体,其定义如下:

typedef struct ResampleContext ResampleContext;
struct ResampleContext {
    double factor;              // 采样率转换系数
    int phase_shift;            // 重采样器移位
    int in_incr, out_incr;      // 输入输出增量
    int32_t ideal_dst_incr;     // 目标增量
    int dst_incr;               // 实际目标增量
    int16_t *in_buffer;         // 输入缓冲区
    int in_buffer_count;        // 输入缓冲区大小
    int16_t *resample_ptr;      // 重采样指针
    int in_index, out_index;    // 输入输出索引
    int frac;                   // 分数部分
    int resample_slice;         // 重采样大小
    void *filter_bank;          // 滤波器状态结构体的指针
    void (*resample_one)(ResampleContext *c, int16_t *dst, const int16_t *src, int left, int right); // 单样本重采样函数
};

可以看到,ResampleContext 中定义了一些关于重采样的参数和一些用于存储中间数据的变量,比如 factor 表示采样率转换系数,in_buffer 表示输入缓冲区,resample_one 表示单样本重采样函数等。SwrContext 中使用 resample 结构体来实现反映音频重采样操作的状态响应。

其他

1. 怎样理解out_stream->codecpar->codec_tag = 0 ?

作用:根据的多媒体文件,自动适配编解码器。

在进行音视频编码时,需要将输入流的编码格式转换为输出流指定的编码格式,这通常需要使用特定的编解码器。但是,不同的编解码器对应的标签(codec_tag)是不一样的,这会导致输出流中的编码格式被错误地识别。为了解决这个问题,我们可以将输出流的编码格式的标签设置为 0,让 FFmpeg 自动识别输出流的编码格式,从而避免错误。

需要注意的是,将标签设置为 0 并不适用于所有的输出流。有些编解码器要求必须指定正确的标签才能正常工作。此外,在设置标签的过程中,还需要进行相应的判断和错误处理,以确保编码器正常工作。

2. pkt.pts * av_q2d(in_stream1->time_base)

pkt.pts * av_q2d(in_stream1->time_base) 是一个时间单位转换的操作,它将 pts(presentation time stamp)从流的时间基(time base)转换为实际时间单位(单位为秒)

在 ffmpeg 中,输入或输出媒体流中的每个音视频帧都包含一个 pts(显示时间戳)和一个 dts(解码时间戳)。

AVPacket 结构体中的 pts 和 dts 字段存储的是时间戳,需要根据相应的时间基进行转换才能得到实际的时间值。av_q2d(in_stream1->time_base) 的作用是将时间基转换为秒数。具体来说,av_q2d 函数可以将任意一个有理数时间基转换为秒数,其函数定义如下:

double av_q2d(AVRational a);
  • a:一个待转换的有理数时间基。

该函数返回一个浮点数,表示将 a 转换为秒数后得到的值。

因此,pkt.pts * av_q2d(in_stream1->time_base) 的含义是**:将 AVPacket 中存储的 pts 时间戳乘上输入媒体流的时间基转换为秒数,得到音视频帧在实际时间中的显示时间。**

你可能感兴趣的:(音视频开发,ffmpeg,音视频)