04FFMPEG的AVStream结构体分析

04FFMPEG的AVStream结构体分析

概述:
该结构体位于libavformat库中的avformat.h中。

1 AVStream结构体

typedef struct AVStream {
    int index;    /**< stream index in AVFormatContext */
    /**
     * 具体格式的stream ID.
     * decoding: 由libavformat库设置。
     * encoding: 由用户设置。 如果未设置,则使用libavformat替换
     */
    int id;
#if FF_API_LAVF_AVCTX
    /**
     * 将被分离,未来将使用codecpar结构代替,即下面的codecpar字段。
     */
    attribute_deprecated
    AVCodecContext *codec;
#endif
    void *priv_data;

    /**
     * 这是表示帧时间戳的基本时间单位(秒)。
     * 即(解)封装上下文中的流的时基,该时基字段非常重要,是我们转换时基时的重要字段。例如输入文件为flv,输出文件为ts,那么flv时基为{1,1000},ts为{1,90000},我们转换时基就是将时间戳从输入流的时基单位转换成输出流的时基单位。
     * 例如此时输入(视频)流的某个画面的pts=80,那么经转时基函数转换后:80/1000=x/90000,x=7200即转换成输出流的时基单位的时间戳pts=7200,这就是我们时基的作用。
     *
     * decoding解码: 由libavformat自行设置。
     * 
     * encoding编码: 调用者可以在avformat_write_header()之前设置,以向muxer提供所需时间基的提示。在avformat_write_header()中,
     * muxer将用实际用于写入输出文件的时间戳的时基覆盖该字段(它可能与用户提供的时间戳相关,也可能与用户提供的时间戳无关,这取决于格式)。
     * 即编码时,muxer复用器会根据写入输出文件的时间戳大概求出对应的时基,然后再判断是否覆盖该字段,所以说明即使你赋值了该时基也可能在写头函数中被改变(一般时基低于10000时有可能被改变)。
     * 
     */
    AVRational time_base;

    /**
     * Decoding解码:流中首帧的显示时间(即流首帧的pts),单位为上面的时基time_base。
     * 只有当你绝对100%确定你设置的值确实是第一帧的pts时才设置这个。
     * 
     * 该值可能是未定义的(AV_NOPTS_VALUE)。
     * 注意:ASF报头不包含正确的start_time, ASF demuxer不能设置这个。
     * 封装上下文AVFormatContext也有一个相同的字段start_time,AVFormatContext中的该字段是由该AVStream的start_time值经过推导后,然后赋值给它的。
     */
    int64_t start_time;

    /**
     * Decoding解码: 流的duration,单位为流中的time_base。
     * 如果一个源文件没有指定持续时间,但是指定了比特率,这个值将根据比特率和文件大小来估计。(有值的话该单位为时基,我们想要转成秒(s)的话需要除以该时基的分母。)
     * 码率:一帧的信息量(字节大小)。所以猜想它大概算法为:文件大小=视频流时长*码率/8。当时长为单位时长时即1s,码率=1(s)*帧率*一帧的大小(单位位字节B)=1(s)*帧率*一帧大小*8(单位为位bit)。
     *
     * Encoding编码:可以由调用者在avformat_write_header()之前设置,以向muxer提供关于估计持续时间的提示。
     * 
     * 注意:该时长为视频流的总时长,一般不用我们设置,注意区别pkt.duration,该时长为两帧的时间差,并非视频流的总时长。
     * 且封装上下文的duration从这里取值,这里的单位为流的时基,封装上下文的为内部时基。
     */
    int64_t duration;

    int64_t nb_frames;                 ///< 如果是已知或者0的话,它代表为流中的帧数。
    int disposition; /**< AV_DISPOSITION_* bit field */

    enum AVDiscard discard; ///< 选择那些数据包可以随意被丢弃而不需要解复用的。
    /**
     * 样品长径比(未知时为0)
     * - encoding编码: 由用户设置。
     * - decoding解码: 由libavformat库设置。
     */
    AVRational sample_aspect_ratio;

    AVDictionary *metadata;

    /**
     * 平均帧速率。
     *
     * - demuxing解复用: 可以在创建流时由libavformat库设置,或者在调用avformat_find_stream_info()时设置。
     * - muxing复用: 调用者可以在avformat_write_header()之前设置。
     */
    AVRational avg_frame_rate;

    /**
     * 对于具有AV_DISPOSITION_ATTACHED_PIC宏配置的流,该数据包将包含附加图片。
     *
     * decoding解码: 由libavformat库设置,必须不能被用户修改。
     * encoding编码: 未使用。
     */
    AVPacket attached_pic;

    /**
     * 一组应用于整个流的边数据(side data)。(即容器不允许它在包与包之间改变)。
     * 
     * 在这个数组中的边数据和包中的边数据之间可能没有重叠。
     * 即给定一边数据被导出要么是由muxer复用器(demuxing,这里应该写错了,应改成muxing复用)或者由调用者(muxing复用)设置在这个数组,那么它不会出现在包中;要么边数据被导出通过在包被发送时(总是在第一个包的值成为已知或更改),那么它就不会出现数组中。 
     * 上面这句话好难啊...看不懂就算了没关系,因为我已经尽量通俗翻译了。
     * 
     *
     * - demuxing解复用: 在创建流时由libavformat库设置。
     * - muxing复用: 调用者可以在avformat_write_header()之前设置。
     *
     * 在libavformat库中由avformat_free_context()函数释放。
     *
     * 可查看av_format_inject_global_side_data()函数。
     */
    AVPacketSideData *side_data;
    
    /**
     * AVStream.side_data边数据数组中的元素数量。
     */
    int nb_side_data;

    /**
     * 用户检查流中发生的事件的标志。在处理事件之后,用户必须清除标志。
     * 
     * 和AVSTREAM_EVENT_FLAG_*是一个组合(与其相关)。
     */
    int event_flags;
    
#define AVSTREAM_EVENT_FLAG_METADATA_UPDATED 0x0001 ///< 调用产生了更新的元数据。

    /**
     * 真实基本的流的帧率。
     * 
     * 这是所有时间戳可以被准确表示的最低帧率(流中所有帧率的最小公倍数)。注意,这个值只是一个猜测!
     * 例如,如果时基是1/90000,并且所有帧大约有3600或者1800个计时器滴答声,那么r_frame_rate将是50/1。
     * 
     * 解析上面例子(它这里应该是以1800作运算结果了):
     * 帧率的计算方法,先以平时时基即1s为例,由于两帧时间差(x)=1/帧率(y),例flv的,x=1/25=0.04,所以我们根据该式子转换成求帧率的:y=1/x。
     * 由于1代表我们的时基单位,x代表两帧时间差,对应上面1就是90000(以分母运算),两帧时间差为流的总计算器滴答。古90000/1800=50的帧率。
     * 所以当我们求帧率时都用上面的公式,别用求pts的式子,防止混乱。
     * 
     * 
     * 注意:由于上面的时基time_base有可能会在写头时被FFmpeg内部重置(time_base小于10000时会不断被乘以2,但不一定真的重置,只是有可能,因为我试过低于10000也帧率也是正常的),导致的结果是帧率变得非常大画面变卡,这一点是需要我们注意的,实际上我们可以通过设置宏让FFmpeg内部必须采用我们的帧率。
     */
    AVRational r_frame_rate;

#if FF_API_LAVF_FFSERVER
    /**
     * 字符串,包含描述推荐编码器配置的键值对。
     * 键值对由','逗号分隔。
     * 键与值之间用'='分隔。
     *
     * 被分离了,不使用。
     */
    attribute_deprecated
    char *recommended_encoder_configuration;
#endif

    /**
     * 与此流关联的编解码器参数。分别由libavformat库中的avformat_new_stream()和avformat_free_context()函数分配和释放。
     *
     * - demuxing解复用: 在创建流时由libavformat库填充或在avformat_find_stream_info()中填充(反正就是解复用时它们不需要我们处理,它们自动处理,下面的链接解复用处理感觉就多余了)。
     * - muxing复用: 由调用者在avformat_write_header()之前填充。
     * 
     * 
     * 注意(个人编码经验,并非原文译码):该字段是新的编解码上下文,代替上面的旧的codec编解码上下文(虽然现在仍可以用,但未来版本将被舍弃,最好用这个)。解复用时不需要我们处理,复用时由我们自定义初始化旧的codec后,
     * 再通过avcodec_parameters_from_context给新的编解码上下文赋值。
     * 不过我觉得应该是可以直接给新的codecpar自定义初始化赋值的,我暂时还没试过,不过我猜可能会有点问题,因为其他库也还在使用旧的编解码上下文,可能还需要经过几个版本才能完全舍弃旧的吧。
     */
    AVCodecParameters *codecpar;

    /*****************************************************************
     * 这一行下面的所有字段都不是公共API的一部分。它们不能在libavformat库之外使用,可以随意更改和删除。
     * 内部提示:请注意,物理(身体,大概是用户自行的意思吧)删除这些字段将破坏ABI。
     * 将删除的字段替换为虚拟字段,并将新字段添加到AVStreamInternal。
     *****************************************************************
     */

#define MAX_STD_TIMEBASES (30*12+30+3+6)
    /**
     * avformat_find_stream_info()内部使用的流信息。
     */
    struct {
        int64_t last_dts;
        int64_t duration_gcd;
        int duration_count;
        int64_t rfps_duration_sum;
        double (*duration_error)[2][MAX_STD_TIMEBASES];
        int64_t codec_info_duration;
        int64_t codec_info_duration_fields;
        int frame_delay_evidence;

        /**
         * 0  -> 解码器还未被查找。
         * >0 -> 找到解码器。
         * <0 -> decoder with codec_id == -查找的解码器是未被查找到的。
         */
        int found_decoder;

        int64_t last_duration;

        /**
         * 这些是用于平均帧率的估计。
         */
        int64_t fps_first_dts;
        int     fps_first_dts_idx;
        int64_t fps_last_dts;
        int     fps_last_dts_idx;

    } *info;

    int pts_wrap_bits; /**< pts中的位数(用于包装控制) */

    // 时间戳一代支持:
    /**
     * 与最后一个dts同步点对应的时间戳。
     * 
     * 当AVCodecParserContext.dts_sync_point >= 0并且从底层容器接收DTS时被初始化。否则默认设置为AV_NOPTS_VALUE。
     */
    int64_t first_dts;
    int64_t cur_dts;
    int64_t last_IP_pts;
    int last_IP_duration;

    /**
     * 用来缓冲编解码(codec)探测的数据包数。
     */
    int probe_packets;

    /**
     * 在avformat_find_stream_info()过程中已经被解复用的帧数。
     */
    int codec_info_nb_frames;

    /* av_read_frame() support */
    enum AVStreamParseType need_parsing;
    struct AVCodecParserContext *parser;

    /**
     * muxing复用时packet_buffer中此流的最后一个包。
     */
    struct AVPacketList *last_in_packet_buffer;
    
    AVProbeData probe_data;
#define MAX_REORDER_DELAY 16
    int64_t pts_buffer[MAX_REORDER_DELAY+1];

    AVIndexEntry *index_entries; /**< 只使用在原本格式不支持seeking位移的时候。*/
    int nb_index_entries;
    unsigned int index_entries_allocated_size;

    /**
     * 流标识符。
     * 这是MPEG-TS流标识符+1。
     * 0意味着未知。
     */
    int stream_identifier;

    /**
     * 详细可查看创建此流的MPEG-TS程序。
     */
    int program_num;
    int pmt_version;
    int pmt_stream_idx;

    int64_t interleaver_chunk_size;
    int64_t interleaver_chunk_duration;

    /**
     * 流探测状态。
     * -1   -> 探测完成。
     *  0   -> 没有探测要求。
     * rest -> 执行探测,request_probe是要接受的最低分数。
     */
    int request_probe;
    /**
     * 指示到下一个关键帧之前的所有内容都应该被丢弃。
     */
    int skip_to_keyframe;

    /**
     * 从下一个包被解码的帧开始时要跳过的样本数。
     */
    int skip_samples;

    /**
     * If not 0, the number of samples that should be skipped from the start of
     * the stream (the samples are removed from packets with pts==0, which also
     * assumes negative timestamps do not happen).
     * 如果不是0,则这个样本数应该被跳过从这个流的开始(从pts==0的包中删除样本,这也假设不发生负的时间戳。)
     * 目的(有意)使用的格式,如mp3与ad-hoc无间隙的音频支持。
     */
    int64_t start_skip_samples;

    /**
     * 如果不是0,那么第一个音频样本应该从该流中被丢弃。
     * 这是由设计打破(需要全局采样计数),但不能避免的设计格式,如mp3与ad-hoc无间隙的音频支持。
     */
    int64_t first_discard_sample;

    /**
     * The sample after last sample that is intended to be discarded after
     * first_discard_sample. Works on frame boundaries only. Used to prevent
     * early EOF if the gapless info is broken (considered concatenated mp3s).
     * 在first_discard_sample之后准备丢弃的最后一个样本之后的样本。只对帧边界起作用。如果无间隙信息被破坏(考虑连接mp3),用于防止早期的EOF。
     */
    int64_t last_discard_sample;

    /**
     * 内部解码帧的数量,在libavformat内部使用,不能访问它的生存期,不同于info,这就是为什么它不在那个结构中。
     */
    int nb_decoded_frames;

    /**
     * 在muxing复用之前添加到时间戳中的时间戳偏移量。
     */
    int64_t mux_ts_offset;

    /**
     * 检查(wrapping应该翻译成实时吧)时间戳的内部数据。
     */
    int64_t pts_wrap_reference;

    /**
     * 检测到wrap时的行为选项。
     *
     * 由AV_PTS_WRAP_值定义。
     *
     * 如果启用了校正,有两种可能性:
     * 如果第一个时间戳靠近换行点(wrap point),则换行偏移量(wrap offset)将被减去,这将创建负的时间戳。
     * 否则将添加偏移量。
     *  
     * (说实话那个wrap没有一个好的翻译,哭了,到底是实时还是换行。。。。。笔者这个字段不管了,反正这不是公共的api。)
     */
    int pts_wrap_behavior;

    /**
     * 内部数据,以防止两次执行update_initial_durations()。
     */
    int update_initial_durations_done;

    /**
     * 根据pts生成dts的内部数据。
     */
    int64_t pts_reorder_error[MAX_REORDER_DELAY+1];
    uint8_t pts_reorder_error_count[MAX_REORDER_DELAY+1];

    /**
     * 内部数据分析DTS和检测错误的mpeg流。
     */
    int64_t last_dts_for_order_check;
    uint8_t dts_ordered;
    uint8_t dts_misordered;

    /**
     * 内部数据注入到全局边数据。
     */
    int inject_global_side_data;

    /**
     * 显示长宽比(未知时为0)。
     * - encoding编码: 未使用。
     * - decoding: 通过libavformat库设置去内部计算sample_aspect_ratio。
     */
    AVRational display_aspect_ratio;

    /**
     * 一个libavformat库内部使用的不透明字段。
     * 调用者不得以任何方式访问。
     */
    AVStreamInternal *internal;
} AVStream;

2 AVStream结构体中重要的成员
AVStream是非常重要的结构体,目前我列出了编码时常用的8个重要成员,如下,解释已经很详细了。

typedef struct AVStream {
#if FF_API_LAVF_AVCTX
    /**
     * @deprecated use the codecpar struct instead
     */
    attribute_deprecated
    AVCodecContext *codec;//将被废弃的编解码上下文。
#endif

	/**
     * 这是表示帧时间戳的基本时间单位(秒)。
     * 即(解)封装上下文中的流的时基,该时基字段非常重要,是我们转换时基时的重要字段。例如输入文件为flv,输出文件为ts,那么flv时基为{1,1000},ts为{1,90000},我们转换时基就是将时间戳从输入流的时基单位转换成输出流的时基单位。
     * 例如此时输入(视频)流的某个画面的pts=80,那么经转时基函数转换后:80/1000=x/90000,x=7200即转换成输出流的时基单位的时间戳pts=7200,这就是我们时基的作用。
     *
     * decoding解码: 由libavformat自行设置。
     * 
     * encoding编码: 调用者可以在avformat_write_header()之前设置,以向muxer提供所需时间基的提示。在avformat_write_header()中,
     * muxer将用实际用于写入输出文件的时间戳的时基覆盖该字段(它可能与用户提供的时间戳相关,也可能与用户提供的时间戳无关,这取决于格式)。
     * 即编码时,muxer复用器会根据写入输出文件的时间戳大概求出对应的时基,然后再判断是否覆盖该字段,所以说明即使你赋值了该时基也可能在写头函数中被改变(一般时基低于10000时有可能被改变)。
     * 
     * 我们常用时基的转换就是将单位为输入流的时基的时间戳转成单位为输出流的时基的时间戳,这一点理解非常重要。
     */
	AVRational time_base;//视频流的时基。
	
	/**
     * Decoding解码:流中首帧的显示时间(即流首帧的pts),单位为上面的时基time_base。
     * 
     * 只有当你绝对100%确定你设置的值确实是第一帧的pts时才设置这个。
     * 
     * 该值可能是未定义的(AV_NOPTS_VALUE)。
     * 
     * 注意:ASF报头不包含正确的start_time, ASFdemuxer解复用时不能设置这个。
     * 封装上下文AVFormatContext也有一个相同的字段start_time,AVFormatContext中的该字段是由该AVStream的start_time值经过推导后,然后赋值给它的。
     */
	int64_t start_time;
	
	/**
     * Decoding解码: 流的duration,单位为流中的time_base。
     * 如果一个源文件没有指定持续时间,但是指定了比特率,这个值将根据比特率和文件大小来估计。
     * 码率:一帧的信息量(字节大小)。所以猜想它大概算法为:文件大小除以码率=共有多少帧。然后再根据两帧的时间差乘以多少帧=估计的视频流时长。
     *
     * Encoding编码:可以由调用者在avformat_write_header()之前设置,以向muxer提供关于估计持续时间的提示。
     * 
     * 注意:该时长为视频流的总时长,一般不用我们设置,注意区别pkt.duration,该时长为两帧的时间差,并非视频流的总时长。
     */
	int64_t duration;
	
    int64_t nb_frames;//流中帧的个数。

	/**
     * 平均帧速率。
     *
     * - demuxing解复用: 可以在创建流时由libavformat库设置,或者在调用avformat_find_stream_info()时设置。
     * - muxing复用: 调用者可以在avformat_write_header()之前设置。
     */
    AVRational avg_frame_rate;//平均帧率。

	/**
     * 真实基本的流的帧率。
     * 
     * 这是所有时间戳可以被准确表示的最低帧率(流中所有帧率的最小公倍数)。注意,这个值只是一个猜测!
     * 
     * 例如,如果时基是1/90000,并且所有帧大约有3600或者1800个计时器滴答声,那么r_frame_rate将是50/1。
     * 
     * 解析上面例子(它这里应该是以1800作运算结果了):
     * 帧率的计算方法,先以平时时基即1s为例,由于两帧时间差(x)=1/帧率(y),例flv的,x=1/25=0.04,所以我们根据该式子转换成求帧率的:y=1/x。
     * 由于1代表我们的时基单位,x代表两帧时间差,对应上面1就是90000(以分母运算),两帧时间差为流的总计算器滴答。古90000/1800=50的帧率。
     * 所以当我们求帧率时都用上面的公式,别用求pts的式子,防止混乱。
     * 
     * 
     * 注意:由于上面的时基time_base有可能会在写头时被FFmpeg内部重置(time_base小于10000时会不断被乘以2,但不一定真的重置,只是有可能,因为我试过低于10000也帧率也是正常的),导致的结果是帧率变得非常大画面变卡,这一点是需要我们注意的,实际上我们可以通过设置宏让FFmpeg内部必须采用我们的帧率。
     */
    AVRational r_frame_rate;//实时帧率。
    
    /**
     * 与此流关联的编解码器参数。分别由libavformat库中的avformat_new_stream()和avformat_free_context()函数分配和释放。
     *
     * - demuxing解复用: 在创建流时由libavformat库填充或在avformat_find_stream_info()中填充(反正就是解复用时它们不需要我们处理,它们自动处理,下面的链接解复用处理感觉就多余了)。
     * - muxing复用: 由调用者在avformat_write_header()之前填充。
     * 
     * 
     * 注意(个人编码经验,并非原文译码):该字段是新的编解码上下文,代替上面的旧的codec编解码上下文(虽然现在仍可以用,但未来版本将被舍弃,最好用这个)。解复用时不需要我们处理,复用时由我们自定义初始化旧的codec后,
     * 再通过avcodec_parameters_from_context给新的编解码上下文赋值。
     * 不过我觉得应该是可以直接给新的codecpar自定义初始化赋值的,我暂时还没试过,不过我猜可能会有点问题,因为其他库也还在使用旧的编解码上下文,可能还需要经过几个版本才能完全舍弃旧的吧。
     */
    AVCodecParameters *codecpar;//新版本的编解码上下文。
    
	/*其余剩下没有的在libavformat库内部的API,非公共API,不能在其它地方使用*/
	
}AVStream;

3 对上面重要结构体成员部分使用代码讲解
1)AVCodecContext *codec
对于拥有两个AVFormatContext即一个输入AVFormatContext封装上下文和一个输出AVFormatContext封装上下文的时候:
解复用时由avformat_open_input赋值,复用时通过avformat_new_stream和avcodec_copy_context函数赋值即可。

ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0))//解复用时

//复用时,其初始化赋值需要经过开辟空间和赋值。
AVStream *out_stream = avformat_new_stream(ofmt_ctx, ifmt_ctx->streams[i]->codec->codec);//两个AVFormatContext复用时的初始化赋值,i代表可能有多个流需要被创建进行拷贝
avcodec_copy_context(out_stream->codec, ifmt_ctx->streams[i]->codec);

对于只有一个输出封装上下文AVFormatContext的时候:
由于没有输入封装上下文的流中的编解码器可以拷贝,所以创建流后只能通过自己初始化out_stream->codec编解码上下文了。

AVStream *out_stream = avformat_new_stream(ofmt_ctx, ifmt_ctx->streams[i]->codec->codec);//两个AVFormatContext复用时的初始化赋值,i代表可能有多个流需要被创建进行拷贝

//自己对out_stream->codec编解码上下文的内容进行初始化,例如,:
	//注:pCodecCtxEnc=out_stream->codec
	pCodecCtxEnc->codec_id = AV_CODEC_ID_H264;
	pCodecCtxEnc->flags = 0;
	pCodecCtxEnc->codec_type = AVMEDIA_TYPE_VIDEO;
	//。。。还有许多需要自己初始化

2)AVRational time_base
这是我们输入文件解复用时的视频流的时基,flv为1000,ts为90000。我们常说时基的转换,就是将单位为输入视频流时基的时间戳转换成单位为输出视频流时基的时间戳。

	//前面需要我们进行pts,dts,duration的运算,并进行必要的延时处理。
	//输入流和输出流的时基转换
	pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
	pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
	pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);

具体关于和输出封装上下文AVFormatContext的处理即这四步:pts的运算、延时处理、时基转换和写包。因为讲到时基不可能单独讲得通的,必须涉及到pts的相关处理。
参考我下面的文章,包含了两个AVFormatContext和只有一个输出封装上下文AVFormatContext。

3)int64_t start_time
其实这个字段不需要我们计算,但是我们需要了解。该字段可以认为只在解复用时有效,因为复用时的首帧时间戳是由自己发包时的pkt.pts控制。
该字段在解复用时为首帧的时间戳,只有当你100%确定输入文件的首帧时间戳你才能设置,否则可能会出现问题。
在这里只想说明一下:由于AVFormatContext中也有同名的start_time,它们两者的关系是:在该流的start_time被经过推导后,然后再赋值给AVFormatContext的start_time字段。

4)int64_t duration
视频流的总时长,不需要我们计算,具体看上面解释即可。
这里需要强调的是区分pkt.duration,这个duration是指一帧的时长,并非整个视频流的时长。

5)int64_t nb_frames
流中帧的个数,同样代码不需要我们处理。

6)AVRational avg_frame_rate
平均帧速率。

  • demuxing解复用: 可以在创建流时由libavformat库设置,或者在调用avformat_find_stream_info()时设置。
  • muxing复用: 调用者可以在avformat_write_header()之前设置。

一般不需要我们进行处理,引用其它博主的原话:
一般解复用(解码)时,遵循一下规则:
视频的帧率,应该从AVStream结构的avg_frame_rate成员获取,而非AVCodecContext结构体。如果avg_frame_rate为{0, 1},那么再尝试从r_frame_rate成员获取。
但是我在解码时并未处理过,因为解复用不需要处理,复用时只处理实时帧率即以下字段。

7)AVRational r_frame_rate
实时帧率。该帧率一般在复用时由自己设置,但这个值只是一个猜测!只是提供给FFmpeg一个参考。

ofmt_ctx->streams[video]->r_frame_rate = {25,1};//一般这样设置即可

8)AVCodecParameters *codecpar
新版本的编解码上下文。
参考以下博主的链接即可。
注:解码时我觉得是不需要进行任何处理的,它的做法将新版本的内容拷贝到旧版本的编解码上下文,但实际上旧版本目前在5.8的动态库仍能使用,只不过会报出警告,但为了防止旧版本的可能没有内容,你最好还是拷贝吧哈哈。

并且需要注意编码时,拷贝必须在打开网络io之后才能进行,否则会有问题。例如你在推hls的时候就会出现问题,live不会。因为我试过这种情况。

https://blog.csdn.net/luotuo44/article/details/54981809
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值