AVFormatContext 再分析一

说明 :将 avfromatContext 的变量依次打印分析,根据ffmpeg 给的说明,猜测,结合网上的文章字节写测试代码分析。

从常用到不常用依次分析

1. unsigned int nb_streams;

代表 avfromatContext 中 AVStream **streams 的个数

    /**
     * Number of elements in AVFormatContext.streams.
     *
     * Set by avformat_new_stream(), must not be modified by any other code.
     */
    unsigned int nb_streams;

//打印在解复用时候,avformatContext的主要参数
void PrintfDeMuxingAVFormatContextMainParamter(AVFormatContext* avformatContext) {
    if (avformatContext == NULL) {
        cout << "func PrintfDeMuxingAVFormatContextMainParamter error because AVFormatContext == nullptr  " << endl;
        return;
    }

    cout << "avformat中有的avstream的个数为:avformatContext->nb_streams = " << avformatContext->nb_streams << endl;


}

2. int64_t bit_rate;

在 AVFormatContext 结构体中,‌bit_rate 字段表示媒体文件的全局平均比特率‌(单位为 bps,即 bits per second)

    /**
     * Total stream bitrate in bit/s, 0 if not
     * available. Never set it directly if the file_size and the
     * duration are known as FFmpeg can compute it automatically.
     */
    int64_t bit_rate;

2.1 含义再说明1

从说明中可以看出来,代表的是所有 stream 的 平均比特率。啥意思呢?比如这是一个mp4文件,既有视频也有音频,我们假设有一个视频两个音频(粤语和普通话),假设视频是300kbps,普通话音频是128kbps,粤语音频是100kbps。那么 AVFormatContext 中的 bit_rate就应该等于 300 +128+100 = 628kbps,注意单位是kbps。

// 遍历所有流,计算总比特率
int64_t total_bit_rate = 0;
for (int i = 0; i < avformat_ctx->nb_streams; i++) {
    AVStream *stream = avformat_ctx->streams[i];
    total_bit_rate += stream->codecpar->bit_rate;
}

2.2 含义再说明2,在TS下无效

0 if not available

这个意思是说,这个值有可能是0,代表不可使用。

部分封装格式(如 TS 流)可能不记录全局比特率,此时 bit_rate 字段无效

2.3 含义再说明3,当文件大小和duration都知道的时候,user不要设置,ffmepg会自动计算

Never set it directly if the file_size and the duration are known as FFmpeg can compute it automatically.

2.5 动态码率场景


VBR 编码的文件中,该字段仅代表平均值,无法反映瞬时码率波动

CBR 是 (恒定比特率)

VBR(可变比特率),

mp4文件只能是 VBR。

也就是说:如果我们解析的是mp4文件,那么这个值是平均值。无法反映瞬时码率波动

2.6 典型应用场景

  • 带宽估算‌:
    结合容器和流的比特率,判断网络传输是否满足实时播放需求56。
  • 文件分析工具‌:
    统计媒体文件的码率分布,辅助编码参数优化6。

3. int64_t duration

只能用于解复用,

是 ‌AVFormatContext 结构体‌的关键成员,用于表示 ‌媒体文件的总时长‌。其数据类型为 int64_t,单位为 ‌AV_TIME_BASE‌(即微秒的倒数,通常为 1,000,000)。

该字段在 ‌成功解析媒体文件流信息后‌(调用 avformat_find_stream_info())才会被正确赋值

    /**
     * Duration of the stream, in AV_TIME_BASE fractional
     * seconds. Only set this value if you know none of the individual stream
     * durations and also do not set any of them. This is deduced from the
     * AVStream values if not set.
     *
     * Demuxing only, set by libavformat.
     */
    int64_t duration;

主要使用场景,通过 duration 计算文件时长。

换算为秒‌:

double duration_sec = (double)avformatContext->duration / AV_TIME_BASE;
 

转换为时分秒格式‌:

int64_t total_us = avformatContext->duration + 5000;  // 四舍五入修正
int hours = total_us / (3600 * AV_TIME_BASE);
int mins = (total_us % (3600 * AV_TIME_BASE)) / (60 * AV_TIME_BASE);
int secs = (total_us % (60 * AV_TIME_BASE)) / AV_TIME_BASE;

常见问题与解决方案

问题原因解决方案
返回负值或极大值未正确解析流信息或文件不完整调用 avformat_find_stream_info() 前设置 max_analyze_duration 参数
单位换算错误未使用 AV_TIME_BASE 进行转换确保除以 AV_TIME_BASE(或使用 av_rescale_q() 函数)
时间精度丢失直接截断未四舍五入添加 5000 微秒偏移(如 +5000)后再计算

AVFormatContext *fmt_ctx = NULL;
avformat_open_input(&fmt_ctx, filename, NULL, NULL);  // 打开文件
fmt_ctx->max_analyze_duration = 5 * AV_TIME_BASE;     // 限制解析时长避免卡顿
avformat_find_stream_info(fmt_ctx, NULL);              // 解析流信息

if (fmt_ctx->duration != AV_NOPTS_VALUE) {
    int64_t duration = fmt_ctx->duration + 5000;      // 修正精度
    int hours = duration / (3600 * AV_TIME_BASE);
    int mins = (duration % (3600 * AV_TIME_BASE)) / (60 * AV_TIME_BASE);
    int secs = (duration % (60 * AV_TIME_BASE)) / AV_TIME_BASE;
    printf("Duration: %02d:%02d:%02d\n", hours, mins, secs);
} else {
    printf("Duration unavailable\n");
}
avformat_close_input(&fmt_ctx);                       // 释放资源

适用场景

  • 媒体信息分析工具‌:如 ffprobe 使用该字段输出文件时长。
  • 播放器开发‌:用于显示进度条总时长。
  • 流媒体处理‌:结合 AVStream 中各流时长进行同步控制。

注意‌:在网络流或实时流中,duration 可能无法获取(值为 AV_NOPTS_VALUE),需动态计算

cout << "avformat中duration为:avformatContext->duration = " << avformatContext->duration << endl;
    
double duration_sec = (double)avformatContext->duration / AV_TIME_BASE;
cout << "avformat中秒数为:duration_sec = " << duration_sec << endl;

if (avformatContext->duration != AV_NOPTS_VALUE) {
        int64_t duration = avformatContext->duration + 5000;      // 修正精度
        int hours = duration / (3600 * AV_TIME_BASE);
        int mins = (duration % (3600 * AV_TIME_BASE)) / (60 * AV_TIME_BASE);
        int secs = (duration % (60 * AV_TIME_BASE)) / AV_TIME_BASE;
        printf("Duration: %02d:%02d:%02d\n", hours, mins, secs);
}else {
        printf("Duration unavailable\n");
}


avformat中duration为:avformatContext->duration = 60024000
avformat中秒数为:duration_sec = 60.024
Duration: 00:01:00

4. char *url;

    /**
     * input or output URL. Unlike the old filename field, this field has no
     * length restriction.
     *
     * - demuxing: set by avformat_open_input(), initialized to an empty
     *             string if url parameter was NULL in avformat_open_input().
     * - muxing: may be set by the caller before calling avformat_write_header()
     *           (or avformat_init_output() if that is called first) to a string
     *           which is freeable by av_free(). Set to an empty string if it
     *           was NULL in avformat_init_output().
     *
     * Freed by libavformat in avformat_free_context().
     */
    char *url;

和之前的filename不同,url是没有长度限制的。

在解码时,通过 avformat_open_input 方法 会将url 记录到 AVFormatContext ,可能会nullptr。

在编码时,需要在 调用 avformat_write_header 方法之前设置。

   char * url =  avformatContext->url;
   cout << "avformat中duration为 url = " << url << endl;

结果为:avformat中duration为 url = ./120/400_300_25.mp4

5. int64_t start_time;

    /**
     * Position of the first frame of the component, in
     * AV_TIME_BASE fractional seconds. NEVER set this value directly:
     * It is deduced from the AVStream values.
     *
     * Demuxing only, set by libavformat.
     */
    int64_t start_time;

组件第一帧的位置,以AV_TIME_BASE  为单位。 
切勿直接设置此值:它是从AVStream值推断出来的。

这玩意有啥用呢?表示该avformatContext 第一帧的开始时间,那么应该都是0。

可能的点:todo

如果我们从文件的中间位置读取的,那么这个值就不是0?

在网络流的时候用?

   int64_t starttime = avformatContext->start_time;
   cout << "avformat中duration为 starttime = " << starttime << endl;

avformat中duration为 starttime = 0

6. 接下来都是非重点 AVCodec* audio_codec;

    /**
     * Forced audio codec.
     * This allows forcing a specific decoder, even when there are multiple with the same codec_id.
     * Demuxing: Set by user
     */
    AVCodec *audio_codec;


 * 强制 音频 编解码器,
 * 在解复用的时候,允许强制使用特定的解码器,即使存在多个具有相同codec_id的解码器

我们先看一下在解析mp4的时候,打印结果:是为nullptr的。

我们这里将 avformatContext中的audio_code_id也打印出来,结果为0.

   cout << "-------------------------" << endl;
   AVCodec* audioavcodec = avformatContext->audio_codec;
   if (audioavcodec == nullptr) {
       cout << "audioavcodec == nullptr" << endl;
   }
   else {
       cout << "audioavcodec != nullptr  audioavcodec->id = " << audioavcodec->id << endl;
   }
   AVCodecID audioavcodecid = avformatContext->audio_codec_id;
   cout << "audioavcodecid =" << audioavcodecid << endl;

   cout << "-------------------------" << endl;


log 为:

audioavcodec == nullptr
audioavcodecid =0

我们按照翻译中字面的意思,在decoder的时候,强制使用特定的解码器 。我们写一个test02测试一下。

参考代码如下,从例子中可以看出来,我们强行使用mp3 去解析视频文件中的音频,一定是会报错的。如果要对,那么前提是开发者知道 音频文件的格式,采样率,声道等信息。如果不知道,我们可以从 stream[audio_index]->codepar->codec_id 中获得真正的 audio decoder,按照正常的流程解码

那么这个audio_codec 再我看来,在解码过程中很鸡肋。这里记录一下流程,可能在其他自己还没有想到的场景下用到。测试也说明,我们下一个要学习的

void test02() {
    const char* url_400 = "./120/400_300_25.mp4";

    //这里指定avformatContext 的 audiocodec 就是mp3格式的
    AVFormatContext* avformatContext = avformat_alloc_context();
    AVCodec *audio_avcodec_MP3 = avcodec_find_decoder(AV_CODEC_ID_MP3);
    avformatContext->audio_codec = audio_avcodec_MP3;


    int ret = avformat_open_input(&avformatContext, url_400, nullptr, nullptr);
    
    if (ret < 0) {
        cout << "func avformat_open_input error" << endl;
        PrintErr(ret);
        return;
    }
    cout << "avformatContext->audio_codec = " << avformatContext->audio_codec << endl;// 79950010
    cout << "avformatContext->audio_codec->id = " << avformatContext->audio_codec->id << endl;// 86017.... 86017 就是 AV_CODEC_ID_MP3

    int audioindex = -1;
    int videoindex = -1;


    // audioindex = av_find_best_stream(avformatContext, AVMediaType::AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0);
    // videoindex = av_find_best_stream(avformatContext, AVMediaType::AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);


     //audioindex = av_find_best_stream(avformatContext, AVMediaType::AVMEDIA_TYPE_AUDIO, -1, -1, &audio_avcodec_mp3, 0);
     //videoindex = av_find_best_stream(avformatContext, AVMediaType::AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);

     for (int i = 0; i < avformatContext->nb_streams; i++) {


         if (avformatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
             videoindex = i;
         }
         if (avformatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
             audioindex = i;
         }

     }



    cout << "audioindex = " << audioindex << endl;
    cout << "videoindex = " << videoindex << endl;

    //这里打印stream[audioindex] 中的 codeid,发现是 AAC 的。。注意是 stream[audioindex]->codecpar->codec_id
    cout << "avformatContext->streams[audioindex]->codecpar->codec_id = " << avformatContext->streams[audioindex]->codecpar->codec_id << endl; // 86018 ...... 86018 就是 AV_CODEC_ID_AAC
    cout << "avformatContext->streams[videoindex]->codecpar->codec_id = " << avformatContext->streams[videoindex]->codecpar->codec_id << endl;
    
    // 试图打印 stream[audioindex]->codec,AVCodecContext *codec;attribute_deprecated 发现这个已经废弃,有build error
    //avformatContext->streams[audioindex]->codec->codec_id;// build error,提示 codec 已经废弃

    //按照 avformatContext 中 关于 AVCodec *audio_codec 的说明,在解码的时候用到,那么先回想一下解码时候的一般写法
    //AVPacket* avpacket = av_packet_alloc();
    //AVFrame* avframe = av_frame_alloc();
    //AVCodec* mp3avcodec111 = avcodec_find_decoder(AV_CODEC_ID_MP3);
    //AVCodecContext* avcodeccontext111 = avcodec_alloc_context3(mp3avcodec111);
    //avcodec_open2(avcodeccontext111, mp3avcodec111, nullptr);
    //avcodec_send_packet(avcodeccontext111, avpacket);
    //avcodec_receive_frame(avcodeccontext111, avframe);

    //按照 avformatContext 中 关于 AVCodec *audio_codec 的说明,在解码的时候用到.
    // 这里是为了解码做准备
    //如果我们设定了 avformatContext->audio_codec = audio_avcodec_mp3;,那么解码的时候肯定是要用这试一试
    AVCodecContext* avcodeccontext222 = avcodec_alloc_context3(avformatContext->audio_codec);
    avcodeccontext222->sample_fmt = AV_SAMPLE_FMT_S16;
    avcodeccontext222->sample_rate = 44100;
    avcodeccontext222->channels = 2;
    avcodeccontext222->channel_layout = 2;
    avcodec_open2(avcodeccontext222, avformatContext->audio_codec, nullptr);

    //这里理论上要把 AVCodecParameters  拷贝到 AVCodecContext,但是 AVCodecParameters 是 aac的,avcodeccontext222 是mp3的
    //那么就有问题了了,这里我们的就需要再次设置 audio的参数 ,返回到上一层,通过 avcodeccontext222设置 sample_fmt,sample_rate,channels,注意,这里的前提是:你怎么知道你要解码音频文件的 三要素,否则会出错。

    //AVCodecParameters* audio_AVCodecParameters = avformatContext->streams[audioindex]->codecpar;
    //avcodec_parameters_to_context(avcodeccontext222, audio_AVCodecParameters);



    cout << "理论上到这里我们就设置完成了" << endl;

    AVPacket* avpacket = av_packet_alloc();
    AVFrame* avframe = av_frame_alloc();


    while (true) {
        ret = av_read_frame(avformatContext, avpacket);
        if (ret <0 ) {
            cout << "av_read_frame error return " << endl;
            break;
        }
        if (avpacket->stream_index == audioindex) {
            // 读取到的是 音频的avpacket,
            cout << "avpacket->size = " << avpacket->size << endl;
            //ret = avcodec_send_packet(avformatContext->streams[audioindex]->codec, avpacket);//build error 提示 codec 已经废弃 
            ret = avcodec_send_packet(avcodeccontext222, avpacket);
            if (ret < 0) {
                cout << "avcodec_send_packet error return " << endl; //
                PrintErr(ret);
                continue;
            }
            else {
                while (true) {
                    ret = avcodec_receive_frame(avcodeccontext222, avframe);
                    if (ret < 0) {
                        cout << "avcodec_receive_frame error return " << endl;
                        PrintErr(ret);

                        break;
                    }
                    else if (ret == 0) {
                        //pcm 数据
                        cout << "read pcm data" << endl;
                    }
                }
            }
        }
        else if (avpacket->stream_index == videoindex) {
            // 读取到的是 视频的avpacket

        }
        av_packet_unref(avpacket);
    }


    av_packet_free(&avpacket);
    av_frame_free(&avframe);


    cout << "指定avfromatContext 的 audio_avcodec 是AV_CODEC_ID_AAC, end" << endl;
}

7. enum AVCodecID audio_codec_id;

    /**
     * Forced audio codec_id.
     * Demuxing: Set by user.
     */
    enum AVCodecID audio_codec_id;

 我们先看一下在解析mp4的时候,打印结果:是为nullptr的。

我们这里将 avformatContext中的audio_code_id也打印出来,结果为0.

   cout << "-------------------------" << endl;
   AVCodec* audioavcodec = avformatContext->audio_codec;
   if (audioavcodec == nullptr) {
       cout << "audioavcodec == nullptr" << endl;
   }
   else {
       cout << "audioavcodec != nullptr  audioavcodec->id = " << audioavcodec->id << endl;
   }
   AVCodecID audioavcodecid = avformatContext->audio_codec_id;
   cout << "audioavcodecid =" << audioavcodecid << endl;

   cout << "-------------------------" << endl;


log 为:

audioavcodec == nullptr
audioavcodecid =0

8. AVCodec *video_codec;

    /**
     * Forced video codec.
     * This allows forcing a specific decoder, even when there are multiple with
     * the same codec_id.
     * Demuxing: Set by user
     */
    AVCodec *video_codec;

看这个说明和 6 很像,应该也是鸡肋一样的存在

9.enum AVCodecID video_codec_id

    /**
     * Forced video codec_id.
     * Demuxing: Set by user.
     */
    enum AVCodecID video_codec_id;

10 AVCodec *subtitle_codec;

    /**
     * Forced subtitle codec.
     * This allows forcing a specific decoder, even when there are multiple with
     * the same codec_id.
     * Demuxing: Set by user
     */
    AVCodec *subtitle_codec;

11.enum AVCodecID subtitle_codec_id;

    /**
     * Forced subtitle codec_id.
     * Demuxing: Set by user.
     */
    enum AVCodecID subtitle_codec_id;

6--11 总结

在解码mp4的时候,log 如下,都是0或者nullptr

都是可以指定的具体的 音频/视频/字幕 解码器,

比较鸡肋。建议忽略

   cout << "-------------------------" << endl;
   AVCodec* audioavcodec = avformatContext->audio_codec;
   if (audioavcodec == nullptr) {
       cout << "audioavcodec == nullptr" << endl;
   }
   else {
       cout << "audioavcodec != nullptr  audioavcodec->id = " << audioavcodec->id << endl;
   }
   AVCodecID audioavcodecid = avformatContext->audio_codec_id;
   cout << "audioavcodecid =" << audioavcodecid << endl;

   cout << "-------------------------" << endl;

   AVCodec* videoavcodec = avformatContext->video_codec;
   if (videoavcodec == nullptr) {
       cout << "videoavcodec == nullptr" << endl;
   }
   else {
       cout << "videoavcodec != nullptr  videoavcodec->id = " << videoavcodec->id << endl;
   }
   AVCodecID videoavcodecid = avformatContext->video_codec_id;
   cout << "videoavcodecid =" << videoavcodecid << endl;
   cout << "-------------------------" << endl;


   AVCodec* subtitleavcodec = avformatContext->subtitle_codec;
   if (subtitleavcodec == nullptr) {
       cout << "subtitleavcodec == nullptr" << endl;
   }
   else {
       cout << "subtitleavcodec != nullptr  subtitleavcodec->id = " << subtitleavcodec->id << endl;
   }
   AVCodecID subtitleavcodecid = avformatContext->subtitle_codec_id;
   cout << "subtitleavcodecid =" << subtitleavcodecid << endl;
   cout << "-------------------------" << endl;

12 int audio_preload; 

    /**
     * Audio preload in microseconds.
     * Note, not all formats support this and unpredictable things may happen if it is used when not supported.
     * - encoding: Set by user
     * - decoding: unused
     */
    int audio_preload;

中文翻译

*音频预加载(微秒)。
*请注意,并非所有格式都支持此功能,如果在不支持的情况下使用,可能会发生不可预测的事情。
*-编码:由用户设置

解码:不使用

应用场景未知。

   cout << "avformatContext->audio_preload = " << avformatContext->audio_preload << endl;// 结果为0

13 int avio_flags;

    /**
     * avio flags, used to force AVIO_FLAG_DIRECT.
     * - encoding: unused
     * - decoding: Set by user
     */
    int avio_flags;

中文翻译

*avio标志,用于强制avio_FLAG_DIRECT。
*-编码:未使用
*-解码:由用户设置
     */


‌avio_flags在AVFormatContext中用于控制输入/输出流的缓冲方式‌。具体来说,avio_flags是一个标志位,用于指定输入/输出流的缓冲行为。

avio_flags 的作用和设置方法
avio_flags可以设置不同的标志来控制缓冲方式,常见的标志包括:

‌'direct'‌:减少缓冲,提高数据传输效率。

应用场景 未知,但是从说明看起来 应该是 在 解码时,不需要缓冲 的场景时可以设置

   avformatContext->avio_flags;
   cout << "avformatContext->avio_flags = " << avformatContext->avio_flags << endl;// 当前结果为0


​   //AVIO_FLAG_DIRECT; 0x8000

    AVFormatContext*ic = avformat_alloc_context();
    ic->avio_flags = AVIO_FLAG_DIRECT; // 设置直接缓冲方式
    

    /**

    * Use direct mode.

    * avio_read and avio_write should if possible be satisfied directly

    instead of going through a buffer, and avio_seek will always

    call the underlying seek function directly.

    */

    #define AVIO_FLAG_DIRECT 0x8000

    /**
    *使用直接模式。
    *如果可能的话,应直接满足avio_read和avio_write的要求
    而不是通过缓冲区,avio_seek将始终
    直接调用底层seek函数。
    */
     

    14 int avoid_negative_ts

    该成员变量在 复用 过程中使用。

    有三个值AVFMT_AVOID_NEG_TS_AUTO, AVFMT_AVOID_NEG_TS_MAKE_NON_NEGATIVE , AVFMT_AVOID_NEG_TS_MAKE_ZERO                  

    该值在在使用av_interleaved_write_frame时有效。(interleave_packet_per_dts正在使用中)

        /**
         * Avoid negative timestamps during muxing.
         * Any value of the AVFMT_AVOID_NEG_TS_* constants.
         * Note, this only works when using av_interleaved_write_frame. (interleave_packet_per_dts is in use)
         * - muxing: Set by user
         * - demuxing: unused
         */
        int avoid_negative_ts;
    #define AVFMT_AVOID_NEG_TS_AUTO             -1 ///< Enabled when required by target format
    #define AVFMT_AVOID_NEG_TS_MAKE_NON_NEGATIVE 1 ///< Shift timestamps so they are non negative
    #define AVFMT_AVOID_NEG_TS_MAKE_ZERO         2 ///< Shift timestamps so that they start at 0

    *在复用过程中避免使用负时间戳。
    *AVFMT_AVOID_NEG_TS_*常数的任何值。
    *注意,这仅在使用av_interleaved_write_frame时有效。(interleave_packet_per_dts正在使用中)
    *-复用:由用户设置
    *-解复用:未使用
         */
    int avoid_negative_ts;
    #定义AVFMT_AVID_NEG_TS_AUTHO-1///<目标格式要求时启用
    #定义AVFMT_AVIOD_NEG_TS_MAKE_NON_NEGATIVE 1///<移位时间戳,使其为非负
    #定义AVFMT_AVIOD_NEG_TS_MAKE_ZERO 2///<移位时间戳,使其从0开始

    要明白应用场景,就要知道 av_interleaved_write_frame 函数是干啥的

    原型:

    int av_interleaved_write_frame(AVFormatContext *fmt_ctx, AVPacket *pkt);
     

    1. 核心功能

    • 数据排序与同步
      该函数将编码后的 AVPacket 按时间戳(DTS)排序后写入输出文件或流,确保音视频数据的正确交错(interleaving)和播放顺序。
    • 自动缓冲机制
      内部通过缓存和调整数据包的时序,避免因乱序写入导致播放异常

    2. 参数说明

    • fmt_ctx: 输出文件的格式上下文(AVFormatContext),包含输出流和容器格式信息23。
    • pkt: 待写入的 AVPacket,需包含编码后的数据及时间戳(PTS/DTS)等元信息23。

    3. 返回值

    • 0 表示成功,负值为错误码(如 AVERROR_EOF 或 AVERROR(EAGAIN))24。

    4. 内部工作机制

    • 时间戳处理
      自动修正数据包的 dts 和 pts,确保输出文件的时间戳连续性。
    • 比特流过滤
      调用 do_packet_auto_bsf 函数处理比特流过滤逻辑(如 H.264 的 Annex B 格式转换)。
    • 交错排序算法
      若容器未实现自定义交错逻辑,默认使用 ff_interleave_packet_per_dts 按 DTS 排序。

    5. 典型使用场景

    • 流媒体输出
      与 avio_open 结合使用,支持 RTMP/RTSP 等协议的实时推流5。
    • 多轨道媒体文件
      适用于包含音视频混合的 MP4/MKV 等格式,防止因写入顺序错误导致播放器解析失败24。
    • 网络传输优化
      在服务器断开时需检查编码参数或网络连接状态5。

    6. 与 av_write_frame 的区别

    特性av_interleaved_write_frameav_write_frame
    数据排序自动按 DTS 排序并缓冲直接写入,无排序逻辑
    适用场景需严格时序的多轨道文件或流媒体用户自行管理交错顺序的场景
    性能开销较高(需缓存和排序)较低
    引用计数处理自动接管 AVPacket 引用所有权需手动管理引用34

    7. 注意事项

    • 引用计数
      若 AVPacket 是引用计数的,函数会接管其所有权并在适当时机释放37。
    • 错误处理
      返回 AVERROR(EAGAIN) 时需继续调用直至数据写入完成4。
    • 头尾写入
      需在 avformat_write_header 和 av_write_trailer 之间调用此函数48。

    此方法一看就是要有了avpacket的时候,就调用一次,我们在写入文件的时候测试一下。 todo

    15 AVChapter **chapters;

        /**
         * Number of chapters in AVChapter array.
         * When muxing, chapters are normally written in the file header,
         * so nb_chapters should normally be initialized before write_header
         * is called. Some muxers (e.g. mov and mkv) can also write chapters
         * in the trailer.  To write chapters in the trailer, nb_chapters
         * must be zero when write_header is called and non-zero when
         * write_trailer is called.
         * - muxing: set by user
         * - demuxing: set by libavformat
         */
        unsigned int nb_chapters;
        AVChapter **chapters;

    在 FFmpeg 的 AVFormatContext 结构中,AVChapter **chapters 成员的作用及相关特性如下:


    1. 基本定义

    • 类型‌:双重指针 AVChapter**,指向动态数组的指针,存储媒体文件中的章节信息6。
    • 内存管理‌:需通过 avformat_alloc_context() 初始化 AVFormatContext 后访问此成员3。

    2. 章节数据结构

    每个 AVChapter 包含以下核心字段:

    • 时间范围‌:start 和 end 时间戳(基于 time_base 时间基)6。
    • 元数据‌:通过 AVDictionary 存储章节标题、语言等附加信息6。
    • 唯一标识‌:id 字段标识章节的索引顺序6。

    3. 典型应用场景

    • 多媒体文件处理‌:
      用于解析或生成 MKV、MP4 等支持章节标记的格式文件25。
    • 交互式播放控制‌:
      播放器可通过章节信息实现快速跳转或分段播放5。
    • 元数据编辑‌:
      允许修改章节标题、时间范围等属性后重新封装文件2。

    4. 操作示例

    AVFormatContext *fmt_ctx = avformat_alloc_context(); // 初始化上下文
    
    // 遍历章节
    for (int i = 0; i < fmt_ctx->nb_chapters; i++) {
        AVChapter *chapter = fmt_ctx->chapters[i];
        // 获取章节开始时间(秒)
        double start_sec = chapter->start * av_q2d(chapter->time_base);
    }
    


    5. 注意事项

    • 生命周期‌:chapters 内存由 AVFormatContext 自动管理,调用 avformat_close_input() 或 avformat_free_context() 时释放37。
    • 格式支持‌:并非所有容器格式均支持章节(如 FLV 不支持),需通过 AVOutputFormat 的 flags 校验26。

    该成员是处理带章节的多媒体文件时的重要扩展功能,需结合具体容器格式特性使用25。

    应用场景:猜想的,看到bilibili  中有跳转到某一个具体位置,猜测就是这个方法在调转。

       unsigned int nb_chapters = avformatContext->nb_chapters;
       AVChapter** chapters = avformatContext->chapters;
       cout << "avformatContext->nb_chapters = " << nb_chapters << endl;
       if (chapters == nullptr) {
           cout << "avformatContext->chapters = nullptr"  << endl;
    
       }
       else {
           for (int i = 0; i < nb_chapters; i++ ) {
               int64_t start = chapters[i]->start;
               int64_t end = chapters[i]->end;
               AVRational time_base = chapters[i]->time_base;
               int id = chapters[i]->id;
               AVDictionary* metadata = chapters[i]->metadata;
           }
       }

    16 unsigned int nb_chapters;

    参考15,表示AVChapter **chapters的个数,

    17 char *codec_whitelist;

        /**
         * ',' separated list of allowed decoders.
         * If NULL then all are allowed
         * - encoding: unused
         * - decoding: set by user
         */
        char *codec_whitelist;

    解复用时限制可以选择的解码器,但是baidu发现,应该在 复用 时也可以使用

    关于 AVFormatContext 中的 char *codec_whitelist 成员,其作用及使用场景如下:


    1. 基本定义

    • 类型‌:字符串指针 (char*),用于指定允许使用的编解码器白名单7。
    • 作用范围‌:主要在解复用(demuxing)过程中限制可选择的解码器,或在复用(muxing)时限制编码器类型7。

    2. 主要用途

    • 编解码器过滤‌:
      通过设置以逗号分隔的编解码器名称(如 "h264,aac"),强制 FFmpeg 仅使用白名单内的编解码器处理数据流7。
    • 安全限制‌:
      防止加载潜在不兼容或存在安全风险的编解码器(尤其在处理不可信输入源时)7。
    • 性能优化‌:
      减少编解码器探测时间,提升处理效率(例如在实时推流场景中)7。

    3. 设置方法

    • 初始化时指定‌:
      可通过 av_dict_set 函数设置选项参数,示例:
       

      cCopy Code

      AVFormatContext *fmt_ctx = avformat_alloc_context(); av_dict_set(&fmt_ctx->metadata, "codec_whitelist", "h264,aac", 0);

    • 动态修改‌:
      在打开输入/输出流后,通过修改 fmt_ctx->codec_whitelist 直接赋值(需注意生命周期管理)7。

    4. 注意事项

    • 格式兼容性‌:
      白名单需与容器格式支持的编解码器匹配(如 MP4 容器需指定 h264 而非 libx264)7。
    • 优先级规则‌:
      若同时设置黑名单 (codec_blacklist),白名单优先生效7。
    • 释放内存‌:
      字符串内存需手动管理,避免内存泄漏(通过 av_free 释放)7。

    此成员适用于需要精确控制编解码器选择的场景(如安全敏感型应用或资源受限环境),但需结合具体容器格式特性使用7。

       char* codec_whitelist = avformatContext->codec_whitelist;
       if (codec_whitelist == nullptr) {
           cout << "avformatContext->codec_whitelist = nullptr" << endl;
       }
       else {
           cout << "codec_whitelist = " << codec_whitelist << endl;
       }

    18  av_format_control_message control_message_cb;

    设备和 应用 通讯的callback 函数。

    没有找到例子,ffmpeg 源码中也没有例子,baidu不到相关的信息。

    ffmpeg的声明写的很简单,就是 设备 和 应用程序的 callback。

        /**
         * Callback used by devices to communicate with application.
         */
        av_format_control_message control_message_cb;

    /**
     * Callback used by devices to communicate with application.
     */
    typedef int (*av_format_control_message)(struct AVFormatContext *s, int type,
                                             void *data, size_t data_size);

    查了一下源码,如下的部分是猜测。

    发现在avdevice.c中有实现,看名字,应该是有两个实现,都会用到,一个是 app to dev。一个是dev to app。如果遇到实际情况的case,应该沿着这条 线看下去。

    int avdevice_app_to_dev_control_message(struct AVFormatContext *s, enum AVAppToDevMessageType type,
                                            void *data, size_t data_size)
    {
        if (!s->oformat || !ffofmt(s->oformat)->control_message)
            return AVERROR(ENOSYS);
        return ffofmt(s->oformat)->control_message(s, type, data, data_size);
    }
    
    int avdevice_dev_to_app_control_message(struct AVFormatContext *s, enum AVDevToAppMessageType type,
                                            void *data, size_t data_size)
    {
        if (!s->control_message_cb)
            return AVERROR(ENOSYS);
        return s->control_message_cb(s, type, data, data_size);
    }
       cout << "avformatContext->control_message_cb = " << avformatContext->control_message_cb<<endl;   值为nullptr

    参考28 的字段 int event_flags, 结合起来学习

    19、unsigned int correct_ts_overflow

    在解复用的时候,纠正 单个时间戳 溢出。

        /**
         * Correct single timestamp overflows
         * - encoding: unused
         * - decoding: Set by user
         */
        unsigned int correct_ts_overflow;

       cout << " avformatContext->correct_ts_overflow = " << avformatContext->correct_ts_overflow << endl;  值是1

    问题是:

    1.什么时候有单个 时间戳的溢出?

    2.为了纠正,应该设置什么样的值?

    todo

    20 int ctx_flags;

    看翻译,和 stream info 相关

    标记信号流属性。AVFMTCTX_*的组合。

        /* stream info */
        /**
         * Flags signalling stream properties. A combination of AVFMTCTX_*.
         * Set by libavformat.
         */
        int ctx_flags;

    #define AVFMTCTX_NOHEADER      0x0001 /**< signal that no header is present
                                             (streams are added dynamically) */
    #define AVFMTCTX_UNSEEKABLE    0x0002 /**< signal that the stream is definitely
                                             not seekable, and attempts to call the
                                             seek function will fail. For some
                                             network protocols (e.g. HLS), this can
                                             change dynamically at runtime. */

    打印log 值是0.

       cout << "avformatContext->ctx_flags = " << avformatContext->ctx_flags << endl; // 结果为0

    ​ctx_flags:标志,表示流属性。AVFMTCTX_*的组合。一般在read_header中设置

    AVFMTCTX_NOHEADER  0x0001  表示不存在header(流是动态添加的)

    AVFMTCTX_UNSEEKABLE   0x0002  表示流绝对不可定位,尝试调用seek函数将失败。

    这个是有啥应用场景呢?

    todo

    21  AVCodec *data_codec;

        /**
         * Forced data codec.
         * This allows forcing a specific decoder, even when there are multiple with
         * the same codec_id.
         * Demuxing: Set by user
         */
        AVCodec *data_codec;

    参考6-11,应该也是没啥用的变量

    22 enum AVCodecID data_codec_id

        /**
         * Forced Data codec_id.
         * Demuxing: Set by user.
         */
        enum AVCodecID data_codec_id;

    参考6-11,应该也是没啥用的变量

    23   int debug;

    开启 debug 的选项,打印默认值是0,理论上关闭的。设置为1表示开启,

    ffmpeg给了 宏定义FF_FDEBUG_TS        

        /**
         * Flags to enable debugging.
         */
        int debug;

    #define FF_FDEBUG_TS        0x0001


    默认值打印,结果是0

       int debug = avformatContext->debug;
       cout << "avformatContext->debug = " << avformatContext->debug << endl; ;
    
    

    这个有啥用呢?或者说,开启后是debug 那个模块

    24 uint8_t *dump_separator;

    dump_separator 是用于控制元数据或流信息输出格式的分隔符参数

        /**
         * dump format separator.
         * can be ", " or "\n      " or anything else
         * - muxing: Set by user.
         * - demuxing: Set by user.
         */
        uint8_t *dump_separator;

    打印结果为: avformatContext->dump_separator = ,

       uint8_t* dump_separator = avformatContext->dump_separator;
       if (dump_separator == nullptr) {
           cout << "avformatContext->dump_separator = nullptr"  << endl; ;
    
       }
       else {
           cout << "avformatContext->dump_separator = " << avformatContext->dump_separator << endl; ;
    
       }

    在 FFmpeg 框架中,‌dump_separator 是用于控制元数据或流信息输出格式的分隔符参数‌,主要应用于调试或结构化数据导出场景37。以下是其核心用法及关联机制:


    1. 参数作用与适用场景

    • 元数据格式化
      指定分隔符对 ffmpeg 或 ffprobe 输出的元数据字段进行分割(默认使用换行符或空格)37。
    • 脚本处理适配
      当需要将输出结果导入脚本或数据库时,可自定义分隔符(如逗号 , 或竖线 |)以提高解析效率3。
    • 调试日志增强
      结合 -v verbose 日志级别使用,可清晰区分不同层次的调试信息7。

    2. 命令行调用示例

    通过 -dump_separator 指定分隔符格式:

     
    

    bashCopy Code

    ffmpeg -i input.mp4 -dump_separator " | " -f null -

    此命令将输出流信息时以 | 分隔各字段,例如分辨率、码率等属性37。


    3. 高级配置选项

    • 多字符支持
      允许使用多字符组合作为分隔符(如 "###"),增强可读性7。
    • 转义字符兼容
      支持 \n(换行)、\t(制表符)等转义字符,适配不同平台需求7。
    • 与 -show_entries 联动
      在 ffprobe 中配合 -show_entries 选择输出字段,生成结构化报表:
       

      bashCopy Code

      ffprobe -v error -show_streams -dump_separator "," input.mp4


    4. 注意事项

    • 版本兼容性
      该参数在 FFmpeg 4.0 及以上版本中稳定支持,旧版本可能需通过源码编译启用37。
    • 容器格式限制
      部分封装格式(如 MPEG-TS)的流信息输出可能忽略此分隔符设置3。
    • 编码器差异
      仅影响元数据输出,不影响编码过程中的时间戳或数据包处理7。

    如需更精细控制输出格式,可结合 -print_format 指定 JSON、XML 等结构化格式替代纯文本

    要结合元数据学习,下面就结合元数据的学习一下

    简单说:元数据就是 如下的数据,这些对于视频的管理很重要。对应的 AVFormatContext 中的就是AVDictionary *metadata;

    ‌标题(title)‌: 描述音视频文件的主题或内容。
    ‌艺术家(artist)‌: 创建或表演音视频内容的艺术家或团体的名称。
    ‌专辑(album)‌: 音视频文件所属的专辑或系列名称。
    ‌流派(genre)‌: 描述音视频内容的音乐或视频类型。
    ‌年份(year)‌: 音视频内容的创作或发布年份。
    ‌版权(copyright)‌: 关于版权的信息。
    ‌描述(description)‌: 对音视频内容的简短描述。
    ‌分辨率(resolution)‌: 视频的宽度和高度。
    ‌帧率(frame rate)‌: 视频每秒显示的帧数。
    ‌编码格式(codec)‌: 音视频数据的编码方式。
    ————————————————

    ffmpeg 元数据-CSDN博客

    25  AVDictionary *metadata;

        /**
         * Metadata that applies to the whole file.
         *
         * - demuxing: set by libavformat in avformat_open_input()
         * - muxing: may be set by the caller before avformat_write_header()
         *
         * Freed by libavformat in avformat_free_context().
         */
        AVDictionary *metadata;

    适用于整个文件的元数据。解复用:由libavformat在avformat_open_input()中设置
    , 复用:可以在avformat_write_header()之前由用户设置,由libavformat在avformat_free_context()中释放。

    在 FFmpeg 的 AVFormatContext 结构中,‌AVDictionary *metadata 字段用于存储文件级别的元数据信息‌,包含视频、音频或容器格式的附加描述信息35。以下是其核心机制与操作方式:


    1. 数据结构与作用

    • 全局元数据存储
      metadata 以键值对(key-value)形式保存容器格式的全局元数据,如标题、作者、版权信息等。
    • 与流级元数据区分
      每个 AVStream 结构体也有独立的 metadata 字段,用于存储该特定流(如音频流、视频流)的元数据。

    2. 常见元数据键名

    FFmpeg 支持标准化的元数据键名,例如:

    • "title":媒体标题
    • "artist":创作者或艺术家
    • "album":所属专辑
    • "date":创建日期
    • "copyright":版权声明
    • "comment":注释信息

    3. 元数据操作接口

    • 元数据访问
      使用 av_dict_get() 函数遍历元数据:
       

      cCopy Code

      AVDictionaryEntry *tag = NULL; while ((tag = av_dict_get(fmt_ctx->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) { printf("%s: %s\n", tag->key, tag->value); }

    • 元数据修改
      通过 av_dict_set() 动态增删或修改键值对:
       

      cCopy Code

      av_dict_set(&fmt_ctx->metadata, "author", "Custom Author", 0);


    4. 应用场景

    • 媒体信息展示
      提取元数据用于播放器界面显示媒体属性(如时长、分辨率、比特率)3。
    • 转码保留元数据
      在转码或复用(remux)时,通过复制 metadata 保留原始文件的描述信息6。
    • 自定义元数据注入
      生成新文件时添加自定义元数据(如 GPS 坐标、设备型号)5。

    5. 注意事项

    • 编码器与容器支持
      部分编码器或容器格式(如 MP4、FLV)可能限制特定元数据的写入或读取56。
    • 字符编码兼容性
      元数据内容需统一使用 UTF-8 编码,避免乱码问题3。
    • 内存管理
      AVFormatContext 销毁时(如调用 avformat_close_input()),metadata 会自动释放,无需手动清理37。

    示例:读取文件元数据

       //遍历
       AVDictionary* metadata = avformatContext->metadata;
       int count = av_dict_count(metadata);
       cout << "av_dict_count(metadata)" << count << endl;
    
       if (metadata == nullptr) {
           cout << "avformatContext->metadata = nullptr" << endl;
       }
       else {
           AVDictionaryEntry* entry = NULL;
           while ((entry = av_dict_get(metadata, "", entry, AV_DICT_IGNORE_SUFFIX))) {
               printf("Key: %s, Value: %s\n", entry->key, entry->value);
           }
       }
    
    
    
       //找到具体的某一个值
       cout << "-----111-------" << endl;
    
       AVDictionary* dict = NULL;
       av_dict_set(&dict, "video_codec", "h264", 0);
       av_dict_set(&dict, "audio_codec", "aac", 0);
    
       AVDictionaryEntry* entry = NULL;
       while ((entry = av_dict_get(dict,"video_codec", entry, AV_DICT_IGNORE_SUFFIX))) {
           printf("Key: %s, Value: %s\n", entry->key, entry->value);
       }
    
       av_dict_free(&dict);
       cout << "-----222-------" << endl;

    av_dict_count(metadata)4
    Key: major_brand, Value: isom
    Key: minor_version, Value: 512
    Key: compatible_brands, Value: isomiso2avc1mp41
    Key: encoder, Value: Lavf58.45.100
    -----111-------
    Key: video_codec, Value: h264
    -----222-------

    完整学习参考 

    av_dict_get,av_dict_set,av_dict_set_int-CSDN博客

    26 enum AVDurationEstimationMethod duration_estimation_method;

    和duration的计算方式有关。先参考 3 int64_t duration 回顾 duration 的用处。

    duration 是 这视频的长度,ffmpeg 内部在计算 duration的时候,有多种参数可以使用,这个枚举就是让我们选择用啥参数

    视频的持续时间可以通过各种方式估算,并且可以使用此枚举

        /**
         * The duration field can be estimated through various ways, and this field can be used
         * to know how the duration was estimated.
         * - encoding: unused
         * - decoding: Read by user
         */
        enum AVDurationEstimationMethod duration_estimation_method;

    /**
     * The duration of a video can be estimated through various ways, and this enum can be used
     * to know how the duration was estimated.
     */
    enum AVDurationEstimationMethod {
        AVFMT_DURATION_FROM_PTS,    ///< Duration accurately estimated from PTSes  0 
        AVFMT_DURATION_FROM_STREAM, ///< Duration estimated from a stream with a known duration  1  
        AVFMT_DURATION_FROM_BITRATE ///< Duration estimated from bitrate (less accurate) 2
    };

    AVFMT_DURATION_FROM_PTS,/// 根据PTS准确估计的持续时间
    AVFMT_DURATION_FROM_STREAM,/// 根据已知持续时间的流估计的持续时间
    AVFMT_DURATION_FROM_BITRATE/// 根据比特率估计的持续时间(精度较低)

    当前 mp4 打印出来结果为1,根据AVFMT_DURATION_FROM_STREAM计算出来的

       cout << "avformatContext->duration_estimation_method = " << avformatContext->duration_estimation_method << endl;
    

    27 int error_recognition;

    解复用前面,可以通过设置这个值,检测到更多的错误。,但是可能会导致误检测,感觉没啥用

        /**
         * Error recognition; higher values will detect more errors but may
         * misdetect some more or less valid parts as errors.
         * Demuxing only, set by the caller before avformat_open_input().
         */
        int error_recognition;

    *错误识别;较高的值将检测到更多的错误,但可能会将一些或多或少有效的部分误检测为错误。
    *仅限解复用,由调用者在avformat_open_input()之前设置。

    默认值是1

       cout << "avformatContext->error_recognition = " << avformatContext->error_recognition << endl;
    

    28 int event_flags;

        /**
         * Flags for the user to detect events happening on the file. Flags must
         * be cleared by the user once the event has been handled.
         * A combination of AVFMT_EVENT_FLAG_*.
         */
        int event_flags;
    #define AVFMT_EVENT_FLAG_METADATA_UPDATED 0x0001 ///< The call resulted in updated metadata.

    打印默认值,结果为1

       cout << "avformatContext->event_flags = " << avformatContext->event_flags << endl; //结果为1

    AVFMT_EVENT_FLAG_METADATA_UPDATED 的功能解析

    AVFMT_EVENT_FLAG_METADATA_UPDATED 是 FFmpeg 中用于标识 ‌媒体元数据动态更新‌ 的事件标志,常见于流媒体或实时封装格式(如 MPEG-TS、FLV)的解封装过程中78。以下是其核心特性与使用场景:


    1. **标志作用与触发条件
    • 元数据变更通知‌:当解封装器(Demuxer)在解析过程中检测到元数据(如标题、分辨率、语言轨道等)发生变化时,触发此标志7。
    • 动态媒体支持‌:适用于直播流或动态调整媒体参数的场景,例如:
      • 直播中切换分辨率或码率;
      • 多语言音轨的动态加载8。

    2. **关联接口与使用逻辑
    • 事件回调机制‌:
      通过注册 AVFormatContext 的 ‌事件回调函数‌(如 av_format_control_message)捕获此事件,具体流程如下:

      1. 初始化 AVFormatContext 并设置回调函数;
      2. 在解封装循环中,通过 av_read_frame() 读取数据包;
      3. 当元数据变更时,FFmpeg 内部触发 AVFMT_EVENT_FLAG_METADATA_UPDATED 事件,回调函数接收通知7。
    • 代码示例片段‌:

    // 定义事件回调函数  
    static int metadata_event_callback(AVFormatContext *s, int type, void *data, size_t data_size) {  
        if (type & AVFMT_EVENT_FLAG_METADATA_UPDATED) {  
            printf("Metadata updated!\n");  
            // 重新读取元数据(如 s->metadata)  
        }  
        return 0;  
    }  
    
    // 初始化时绑定回调  
    AVFormatContext *format_ctx = avformat_alloc_context();  
    format_ctx->control_message_callback = metadata_event_callback;  
    

    3. **典型应用场景
    • 直播流自适应‌:实时检测分辨率或编码参数变化,动态调整解码器或渲染逻辑8。
    • 多语言/字幕切换‌:当媒体流新增音轨或字幕时,通知应用程序更新可选轨道列表7。
    • 动态广告插入‌:直播流中插入广告时,元数据可能变更,需同步更新播放器信息8。

    4. **兼容性与限制
    • 格式支持差异‌:
      • 部分封装格式(如 MP4)通常静态存储元数据,此事件可能不会触发;
      • 流媒体协议(如 HLS、RTMP)更易触发此事件78。
    • 资源消耗‌:频繁的元数据更新可能增加回调处理的开销,需优化逻辑避免性能问题。

    总结

    AVFMT_EVENT_FLAG_METADATA_UPDATED 是 FFmpeg 处理动态元数据变更的关键机制,通过与事件回调结合,可实现对实时媒体流的精细化控制。开发者需根据封装格式特性合理设计回调逻辑,并注意性能优化78。

    29 char filename[1024]; 已废弃,有build error

       avformatContext->filename; //attribute_deprecated char filename[1024];已废弃,有build error,已经使用url 替代
     

    30 int flags;

        /**
         * Flags modifying the (de)muxer behaviour. A combination of AVFMT_FLAG_*.
         * Set by the user before avformat_open_input() / avformat_write_header().
         */
        int flags;

    打印结果为 2097152

       cout << "avformatContext->flags = " << avformatContext->flags << endl;

    对应的16进制为:

    #define AVFMT_FLAG_AUTO_BSF   0x200000 ///< Add bitstream filters as requested by the muxer
     

    AVFormatContext 的 flags 字段解析

    AVFormatContext 的 ‌flags‌ 字段用于控制封装/解封装过程中的行为模式,其取值由多个标志位组合而成,直接影响媒体文件的解析与生成逻辑8。以下是关键信息整理:


    1. **核心功能
    • 流程控制‌:通过标志位定义输入输出的处理策略,例如:
      • 是否自动生成缺失的时间戳(AVFMT_FLAG_GENPTS)8;
      • 是否启用快速非精确的 Seek 模式(AVFMT_FLAG_FAST_SEEK)8;
      • 是否允许网络协议的非阻塞模式(AVFMT_FLAG_NONBLOCK)8。
    • 格式适配‌:针对特定封装格式的兼容性调整,如 TS 流的拼接模式(AVFMT_FLAG_FLAT_PES)8。

    2. **常用标志位
    标志位名称作用描述
    AVFMT_FLAG_GENPTS自动生成缺失的 PTS(Presentation Time Stamp)值,确保时间戳连续性8。
    AVFMT_FLAG_FAST_SEEK启用快速 Seek 模式(牺牲精度换取速度,适用于实时流场景)8。
    AVFMT_FLAG_NONBLOCK设置 I/O 操作为非阻塞模式,避免因网络延迟导致的线程阻塞8。
    AVFMT_FLAG_FLAT_PES将 PES 包数据扁平化处理,适用于 TS 流拼接8。
    AVFMT_FLAG_KEEP_SIDE_DATA保留流中的附加元数据(如 H.264 SEI 信息)8。

    3. **典型场景与设置示例
    • 直播流处理‌:
      组合使用 AVFMT_FLAG_NONBLOCK 和 AVFMT_FLAG_FAST_SEEK,优化实时性要求高的场景8。

       

      cCopy Code

      format_ctx->flags |= AVFMT_FLAG_NONBLOCK | AVFMT_FLAG_FAST_SEEK;

    • 修复时间戳问题‌:
      启用 AVFMT_FLAG_GENPTS 自动填充缺失的 PTS/DTS:

       

      cCopy Code

      avformat_open_input(&format_ctx, filename, NULL, NULL); format_ctx->flags |= AVFMT_FLAG_GENPTS;


    4. **注意事项
    • 默认值‌:大多数场景下,FFmpeg 会根据封装格式自动设置合理标志位,无需手动干预8。
    • 冲突处理‌:标志位可能存在互斥性(如网络协议与本地文件操作),需结合具体协议类型设置8。

    总结

    AVFormatContext 的 flags 字段通过标志位组合控制媒体处理的底层行为,需根据协议类型、实时性需求等场景灵活配置,以确保解析与封装的高效性和兼容性8

    31int flush_packets

        /**
         * Flush the I/O context after each packet.
         * - encoding: Set by user
         * - decoding: unused
         */
        int flush_packets;

       //打印值为 -1
       cout << "avformatContext->flush_packets = " << avformatContext->flush_packets << endl;

    从翻译的看来,是在复用的时候使用,但是baidu发现可能在解复用的时候也能用到。

    AVFormatContext 的 flush_packets 字段解析

    flush_packets 是 AVFormatContext 中控制 ‌解封装(Demux)过程中内部缓存刷新行为‌ 的字段,主要用于优化 Seek 操作或实时流场景下的数据一致性46。


    1. **核心功能
    • 缓存刷新控制‌:
      • flush_packets = 1‌:强制立即清空解封装器内部的缓存数据包(如执行 Seek 操作后丢弃旧数据,确保后续读取的数据来自新位置)46。
      • flush_packets = 0‌(默认):保留缓存数据,复用未处理的包以提高效率(适用于静态文件或非实时场景)48。

    2. **典型应用场景
    • 精确 Seek 操作‌:
      在跳转播放位置时,避免残留的旧数据包干扰新位置的数据读取,例如视频播放器中拖动进度条后需要立即刷新缓存68。
    • 低延迟实时流处理‌:
      强制丢弃过期的数据包,减少因缓存累积导致的延迟(如直播流中需要实时处理最新帧)48。

    3. **代码示例
    AVFormatContext *format_ctx = avformat_alloc_context();  
    format_ctx->flush_packets = 1;  // 启用强制刷新  
    
    // Seek 到指定时间戳  
    av_seek_frame(format_ctx, -1, target_ts, AVSEEK_FLAG_BACKWARD);  
    
    // 后续读取的数据将基于新位置  
    AVPacket pkt;  
    av_read_frame(format_ctx, &pkt);  
    


    4. **注意事项
    • 性能影响‌:
      频繁启用 flush_packets 会增加 I/O 开销(需重新解析数据),降低解封装效率,建议仅在必要场景下开启48。
    • 默认行为‌:
      大多数静态文件(如 MP4、MKV)默认禁用此选项,以复用缓存数据优化性能48。
    • 兼容性‌:
      对实时流协议(如 RTMP、HLS)支持较好,部分本地文件格式可能不支持动态刷新逻辑68。

    总结

    flush_packets 通过控制内部缓存的刷新机制,解决了 Seek 和实时流场景下的数据一致性问题,开发者需根据具体场景权衡性能与实时性需求46。

    32 int format_probesize; 用于识别 format。在 avformat_open_input方法中使用

        /**
         * number of bytes to read maximally to identify format.
         * - encoding: unused
         * - decoding: set by user
         */
        int format_probesize;

    在 avformat_open_input 方法中使用,用于识别 format,也就是音频 的格式 AAC 或者视频的 格式 YUV420P 。 

    当前打印结果为1m,是当前的默认值,这个默认值是根据系统不同决定的,有可能在linux下是5m。

       //avformatContext->format_probesize = 1048576  单位是字节,1048576字节/1024 = 1024 kb ;1024kb /1024 = 1M,也就是说,默认,可以探测的最大字节为1M
       cout << "avformatContext->format_probesize = " << avformatContext->format_probesize << endl;

    format_probesize 字段

    在解复用的时候使用,我们知道 avformat_open_input()方法和avformat_find_stream_info()方法在解复用的开始就需要调用,avformat_open_input()方法是解析 mp4等有文件头的,avformat_find_stream_info()方法是解析 h264等没有文件头的。目的都是找到文件的 元数据信息。

    问题是,这个format_probesize字段的大小,仅是 avformat_open_input方法中生效,还是 avformat_find_stream_info中也生效,还是在哪里生效?

    通过查看源码知道

    format_probesize字段是在avformat_open_input()方法的时候就使用的,也就是说,在解析

    在 avformat_find_stream_info方法实现中没有看到使用。

    format_probesize 是 AVFormatContext 结构体中的一个成员变量,它用于指定在尝试打开文件时,FFmpeg 应该读取的最大字节数来探测文件格式。这个参数对于自动检测文件格式非常重要,特别是在文件头部没有足够的格式信息(如扩展名不正确或者文件损坏)的情况下。

    作用
    • 格式探测:当FFmpeg尝试打开一个文件时,它首先会读取一定数量的字节(由format_probesize指定)来尝试识别文件的格式。例如,如果设置了较大的format_probesize,FFmpeg会读取更多的数据来尝试更准确地确定文件的格式,这有助于处理一些边缘情况,如文件头部有损坏或格式不明显的情况。

    • 性能考虑:增加format_probesize可以提高格式探测的准确性,但同时也会增加读取和处理数据的时间。因此,这个值需要根据具体情况进行调整,以平衡准确性和性能。

    AVFormatContext 的 format_probesize 字段解析

    format_probesize 是 AVFormatContext 中用于 ‌控制媒体格式探测阶段读取的数据量大小‌ 的关键参数,直接影响封装格式的识别精度与效率16。


    1. **核心功能
    • 数据探测范围‌:
      定义 FFmpeg 在初始化解封装时分析的最大数据量(单位:字节),用于判断媒体文件的封装格式(如 MP4、FLV、TS 等)16。
      • 值越大‌:探测范围更广,可提高复杂或分散格式的识别成功率(例如多流混合或元数据分散的媒体文件)6。
      • 值越小‌:减少 I/O 延迟,但可能导致格式探测失败(数据不足无法匹配特征)1。

    2. **默认值与调整
    • 默认值‌:5,000,000 字节(约 5MB),适用于绝大多数场景16。
    • 手动设置‌:
       

      cCopy Code

      AVFormatContext *format_ctx = avformat_alloc_context();
      format_ctx->format_probesize = 10 * 1024 * 1024; // 设为 10MB avformat_open_input(&format_ctx, filename, NULL, NULL);


    3. **典型应用场景
    • 网络流探测‌:
      对于实时流(如 RTMP、HLS),若初始数据包中格式信息分散,需增大 format_probesize 以确保正确识别15。
    • 非常规封装格式‌:
      自定义或非标准格式可能需扩展探测范围以提取足够特征6。

    4. **注意事项
    • 性能权衡‌:
      增大此值会延长初始化解封装的时间,尤其在网络传输或高延迟存储中更明显16。
    • 探测失败处理‌:
      若格式识别错误或返回 AVERROR_INVALIDDATA,可尝试逐步增加 format_probesize 并重试6。

    总结

    format_probesize 通过控制探测数据量平衡格式识别的精度与效率,需根据媒体类型(本地文件/网络流)和封装复杂度灵活调整15。

    33 char *format_whitelist;

        /**
         * ',' separated list of allowed demuxers.
         * If NULL then all are allowed
         * - encoding: unused
         * - decoding: set by user
         */
        char *format_whitelist;

    指定 解复用器 白名单,如果为nullptr 表示 所有的 解码器都可以。

    当前测试结果为nullptr

       //测试结果为 format_whitelist == nullptr
       char* format_whitelist =  avformatContext->format_whitelist;
       if (format_whitelist == nullptr) {
           cout << "format_whitelist == nullptr" << endl;
       }
       else {
           cout << "avformatContext->format_whitelist = " << avformatContext->format_whitelist << endl;
       }

    format_whitelist 字段是 AVFormatContext 结构体中的一个属性,它在FFmpeg的版本更新中引入,主要用于限制可以解析和编码的媒体格式。

    功能和用途

    format_whitelist 字段允许用户指定一个格式列表,FFmpeg只会考虑这个列表中的格式进行解码或编码。这对于安全考虑特别有用,比如防止恶意构造的文件导致安全漏洞。例如,如果你的应用只需要处理MP4和MKV文件,你可以通过设置 format_whitelist 来确保FFmpeg不会尝试解析或编码其他类型的文件,这样可以减少潜在的安全风险。

    如何使用

    在FFmpeg的API中,你可以通过设置 AVFormatContext 的 format_whitelist 字段来指定允许的格式。下面是一个简单的示例代码,展示如何设置和使用 format_whitelist

    #include <libavformat/avformat.h>
     
    int main() {
        avformat_network_init(); // 初始化网络组件(如果需要)
     
        AVFormatContext *fmt_ctx = avformat_alloc_context();
        if (!fmt_ctx) {
            fprintf(stderr, "Could not allocate format context\n");
            return -1;
        }
     
        // 设置 format_whitelist
        fmt_ctx->format_whitelist = "mp4,mkv"; // 只允许MP4和MKV格式
     
        // 其他代码,例如打开文件、读取流等
        // ...
     
        avformat_free_context(fmt_ctx); // 释放资源
        avformat_network_deinit(); // 反初始化网络组件(如果需要)
        return 0;
    }

    注意事项

    • 在使用 format_whitelist 前,确保已经初始化了FFmpeg库。

    • 确保在不需要时释放所有分配的资源,以避免内存泄漏。

    • format_whitelist 应该只包含你确实需要处理的格式名称,以避免不必要的性能开销和潜在的安全问题。

    • 从FFmpeg版本4.0开始引入了 format_whitelist 功能。如果你使用的是更早的版本,这个功能可能不可用或者需要通过其他方式实现类似的功能(例如通过命令行参数或环境变量)。

    通过合理使用 format_whitelist,你可以提高你的应用程序处理媒体文件时的安全性和效率。

    34 int fps_probe_size;在解复用中计算video的fps。在avformat_find_stream_info方法中使用

        /**
         * The number of frames used for determining the framerate in
         * avformat_find_stream_info().
         * Demuxing only, set by the caller before avformat_find_stream_info().
         */
        int fps_probe_size;

    打印一下,结果为 avformatContext->fps_probe_size = -1

       cout << "avformatContext->fps_probe_size = " << avformatContext->fps_probe_size << endl;

    在 解复用的时候,设置读取多少帧,才能计算出  video的fps。如果不设置,默认值是-1;为啥是-1?看源码。发现在解复用的时候,真的用多少帧,是由于很多值决定的。且开始的时候,会赋值一个20.

    • 解码时fps_probe_size 用于指定在尝试探测视频流的真实帧率时,应该查看多少帧。这对于某些编码格式,特别是那些不直接在头部信息中明确标明帧率的格式(如 MPEG-TS),非常有用。通过查看连续几帧的时间戳差值,可以估算出实际的帧率。

    • 编码时,不一定有用

      编码时:虽然主要用于解码时的帧率探测,但在某些情况下,编码器也可能使用这个参数来设置期望的帧率(例如,通过设置 muxer 的时间基)。但通常,编码器的帧率设置是通过其他方式(如编码器特定的选项)来控制的。

    核心功能定位
    • fps_probe_size‌:
      用于 ‌帧率(FPS)探测阶段的数据量控制‌,指定在分析视频流时需读取的初始数据量,以计算准确的帧率值。

      • 适用场景‌:当视频流头部未明确包含帧率信息(如某些实时流或自定义封装格式时),需通过一定量的数据样本推导帧率。
      • 调整影响‌:增大此值可能提升帧率计算精度,但会增加初始解析时间。

    参见源码,源码中这块很复杂,

    在 int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)方法中。

    设置为25帧,通过25帧计算 fps,也就是25帧的平均fps

    AVFormatContext* formatCtx = avformat_alloc_context();
    if (!formatCtx) {
        fprintf(stderr, "Could not allocate format context\n");
        exit(1);
    }
     
    // 设置 fps_probe_size 为 25,例如
    formatCtx->fps_probe_size = 25;

    35 int64_t probesize; 在解复用中使用,avformat_find_stream_info 方法中使用

        /**
         * Maximum size of the data read from input for determining
         * the input container format.
         * Demuxing only, set by the caller before avformat_open_input().
         */
        int64_t probesize;

       //avformatContext->probesize = 5000000,单位是字节 。 probe_size(format_probesize)默认约为 5MB(5,000,000 字节)
       cout << "avformatContext->probesize = " << avformatContext->probesize << endl;

    36 int64_t max_analyze_duration; 解复用时使用,在avformat_find_stream_info 方法中使用

        /**
         * Maximum duration (in AV_TIME_BASE units) of the data read
         * from input in avformat_find_stream_info().
         * Demuxing only, set by the caller before avformat_find_stream_info().
         * Can be set to 0 to let avformat choose using a heuristic.
         */
        int64_t max_analyze_duration;

    *从avformat_find_stream_info()中的输入读取数据的最大持续时间(以AV_TIME_BASE为单位)。
    *仅限解复用,由调用者在avformat_find_stream_info()之前设置。
    *可以设置为0,让avformat使用heuristic(启发式方法)进行选择。

    max_analyze_duration 是 FFmpeg 中 AVFormatContext 结构体的关键字段,主要用于控制 ‌流信息探测阶段的时长和数据量‌,直接影响媒体元数据(如编码格式、帧率、分辨率等)的解析效率和准确性17。


    1. ‌核心功能

    • 作用阶段‌:在调用 avformat_find_stream_info() 时生效,用于限制 FFmpeg 解析流信息的最大时间或数据量18。
    • 控制目标‌:决定探测阶段读取的媒体数据时长(单位:微秒),确保在合理时间内获取流参数,避免因数据量过大导致延迟78。

    2. ‌参数设置与影响

    属性说明
    默认值通常为 5 * AV_TIME_BASE(即 5 秒)
    单位微秒(需通过 AV_TIME_BASE 宏转换,例如 5 * AV_TIME_BASE 表示 5 秒)
    调优场景网络流、实时流等需要快速起播的场景,可适当减小值以减少探测时间;非标准格式或复杂流需增大值以提高解析成功率
    负面影响值过小可能导致流信息解析失败(如无法获取帧率或编码参数);值过大可能增加初始延迟78

    3. ‌代码示例

    AVFormatContext *fmt_ctx = avformat_alloc_context();  
    avformat_open_input(&fmt_ctx, input_file, NULL, NULL);  
    
    // 设置最大分析时长(例如设置为 2 秒)  
    fmt_ctx->max_analyze_duration = 2 * AV_TIME_BASE;  
    
    // 启动流信息探测  
    avformat_find_stream_info(fmt_ctx, NULL);  
    

    4. ‌关联参数

    • probesize‌:限制探测阶段读取的最大数据量(字节数),与 max_analyze_duration 共同控制探测行为的资源消耗。
    • fps_probe_size‌(若存在):专用于帧率探测的数据量控制,优先级低于 max_analyze_duration

    5. ‌优化建议

    1. 实时流场景‌:将 max_analyze_duration 设为 1 * AV_TIME_BASE(1 秒)以内,结合 probesize 减小探测数据量,实现快速起播78。
    2. 复杂流处理‌:若流头部信息不完整,需增大此值(如 10 * AV_TIME_BASE)以提高格式识别成功率78。
    3. 已知流格式时‌:直接通过 AVCodecParameters 设置解码器参数,跳过探测阶段以提升效率7。

    总结

    max_analyze_duration 是流媒体解析性能调优的核心参数,需根据场景平衡 ‌解析精度‌ 与 ‌响应速度‌,结合 probesize 参数实现最佳效果17。

    32-36 总结

    int format_probesize; 在解复用中用于识别 format。在 avformat_open_input方法中使用。默认值是1M 或者5M,

    • 值越大‌:探测范围更广,可提高复杂或分散格式的识别成功率(例如多流混合或元数据分散的媒体文件)。
    • 值越小‌:减少 I/O 延迟,但可能导致格式探测失败(数据不足无法匹配特征)。

    int fps_probe_size;在解复用中计算video的fps。在avformat_find_stream_info方法中使用。看源码,一般情况下默认值应该是20.

    • 适用场景‌:当视频流头部未明确包含帧率信息(如某些实时流或自定义封装格式时),需通过一定量的数据样本推导帧率。
    • 调整影响‌:增大此值可能提升帧率计算精度,但会增加初始解析时间。可以调整为25
    • 计算25帧的数据,以计算fps

    int64_t probesize; 在解复用中使用,avformat_find_stream_info 方法中使用。

             默认值 是 5000000字节,也就是大约 5M

            调整影响‌:增大此值在解复用时,多解析字节。但是会有I/O延迟。减小可以快一些。

    int64_t max_analyze_duration; 解复用时使用,在avformat_find_stream_info 方法中使用.

    如果是0,则ffmpeg 使用启发式方式自动设置。

    一般默认值是 通常为 5 * AV_TIME_BASE(即 5 秒)

    调优场景网络流、实时流等需要快速起播的场景,可适当减小值以减少探测时间;非标准格式或复杂流需增大值以提高解析成功率
    负面影响值过小可能导致流信息解析失败(如无法获取帧率或编码参数);值过大可能增加初始延迟

    优化建议

    1. 实时流场景‌:将 max_analyze_duration 设为 1 * AV_TIME_BASE(1 秒)以内,结合 probesize 减小探测数据量,实现快速起播。
    2. 复杂流处理‌:若流头部信息不完整,需增大此值(如 10 * AV_TIME_BASE)以提高格式识别成功率。
    3. 已知流格式时‌:直接通过 AVCodecParameters 设置解码器参数,跳过探测阶段以提升效率7。

    FFmpeg是个用于处理多媒体的开源框架,AVFormatContext结构体是其中个重要的数据结构,用于描述个多媒体文件的格式。下面是该结构体的详细说明: ``` typedef struct AVFormatContext { const AVClass *av_class; // 用于记录日志的类 struct AVInputFormat *iformat; // 输入的AVInputFormat struct AVOutputFormat *oformat; // 输出的AVOutputFormat void *priv_data; // 自定义私有数据 AVIOContext *pb; // IO上下文 int nb_streams; // 流的数量 AVStream **streams; // AVStream结构体数组 char *filename; // 文件名 int64_t start_time; // 文件开始时间 int64_t duration; // 文件时长 int bit_rate; // 文件的码率 unsigned int packet_size; // 读取AVPacket包的大小 int max_delay; // 最大延迟 int flags; // 标志位 int64_t probesize; // 探测的数据大小 int64_t max_analyze_duration; // 最大分析时间 const uint8_t *key; // 传输密钥 int keylen; // 密钥长度 unsigned int nb_programs; // 节目数量 AVProgram **programs; // AVProgram结构体数组 } AVFormatContext; ``` 接下来是各个字段的详细解释: 1. const AVClass *av_class:指向个描述该结构体所属类的AVClass结构体的指针,用于记录日志。 2. struct AVInputFormat *iformat:指向输入文件的AVInputFormat结构体的指针。 3. struct AVOutputFormat *oformat:指向输出文件的AVOutputFormat结构体的指针。 4. void *priv_data:自定义的私有数据,可以是任意类型的数据,通常用于保存些与文件格式相关的信息。 5. AVIOContext *pb:IO上下文,用于读写文件。 6. int nb_streams:该文件中流的数量,包括音频流和视频流等。 7. AVStream **streams:指向AVStream结构体数组的指针,每个元素代表个流。 8. char *filename:文件名。 9. int64_t start_time:文件开始时间。 10. int64_t duration:文件时长。 11. int bit_rate:文件的码率。 12. unsigned int packet_size:读取AVPacket包的大小。 13. int max_delay:最大延迟。 14. int flags:标志位,用于设置些特殊的属性。 15. int64_t probesize:探测的数据大小。 16. int64_t max_analyze_duration:最大分析时间。 17. const uint8_t *key:传输密钥。 18. int keylen:密钥长度。 19. unsigned int nb_programs:节目数量。 20. AVProgram **programs:指向AVProgram结构体数组的指针,每个元素代表个节目。 总之,AVFormatContext结构体是FFmpeg中个非常重要的数据结构,它包含了个多媒体文件的所有相关信息,是FFmpeg进行多媒体处理的必备数据类型。
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值