FFmpeg源码:avformat_new_stream函数分析

一、avformat_new_stream函数的声明

avformat_new_stream函数定义在FFmpeg源码(本文演示用的FFmpeg源码版本为7.0.1)的头文件libavformat/avformat.h中:

cpp 复制代码
/**
 * Add a new stream to a media file.
 *
 * When demuxing, it is called by the demuxer in read_header(). If the
 * flag AVFMTCTX_NOHEADER is set in s.ctx_flags, then it may also
 * be called in read_packet().
 *
 * When muxing, should be called by the user before avformat_write_header().
 *
 * User is required to call avformat_free_context() to clean up the allocation
 * by avformat_new_stream().
 *
 * @param s media file handle
 * @param c unused, does nothing
 *
 * @return newly created stream or NULL on error.
 */
AVStream *avformat_new_stream(AVFormatContext *s, const struct AVCodec *c);

该函数作用是:给一个媒体文件增加新流。即给形参s的streams数组增加新的元素,新元素会被初始化。

形参s:输出型参数,指向一个AVFormatContext对象。AVFormatContext结构体中有一个成员变量streams,为指针的指针。streams指向一个AVStream指针数组,该数组的每个元素都指向一个AVStream结构,每一个AVStream结构都存贮媒体文件/媒体流中一路流的信息,流的类型可以为视频流、音频流、字幕流等。

AVFormatContext结构体中的nb_streams成员存放该媒体文件/媒体流中流的个数,也是AVStream指针数组(形参s的streams数组)的元素个数。

执行avformat_new_stream函数后,形参s的streams数组会增加新的元素,也就是会给媒体文件增加新流:

cpp 复制代码
typedef struct AVFormatContext {
//...
    /**
     * Number of elements in AVFormatContext.streams.
     *
     * Set by avformat_new_stream(), must not be modified by any other code.
     */
    unsigned int nb_streams;
    /**
     * A list of all streams in the file. New streams are created with
     * avformat_new_stream().
     *
     * - demuxing: streams are created by libavformat in avformat_open_input().
     *             If AVFMTCTX_NOHEADER is set in ctx_flags, then new streams may also
     *             appear in av_read_frame().
     * - muxing: streams are created by the user before avformat_write_header().
     *
     * Freed by libavformat in avformat_free_context().
     */
    AVStream **streams;
//...
} AVFormatContext;

形参c:该参数没有被用到,可忽略。

返回值:成功返回新流对应的AVStream结构,失败返回NULL。

二、avformat_new_stream函数的定义

avformat_new_stream函数定义在源文件libavformat/options.c中:

cpp 复制代码
AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c)
{
    FFFormatContext *const si = ffformatcontext(s);
    FFStream *sti;
    AVStream *st;
    AVStream **streams;

    if (s->nb_streams >= s->max_streams) {
        av_log(s, AV_LOG_ERROR, "Number of streams exceeds max_streams parameter"
               " (%d), see the documentation if you wish to increase it\n",
               s->max_streams);
        return NULL;
    }
    streams = av_realloc_array(s->streams, s->nb_streams + 1, sizeof(*streams));
    if (!streams)
        return NULL;
    s->streams = streams;

    sti = av_mallocz(sizeof(*sti));
    if (!sti)
        return NULL;
    st = &sti->pub;

    st->av_class = &stream_class;
    st->codecpar = avcodec_parameters_alloc();
    if (!st->codecpar)
        goto fail;

    sti->fmtctx = s;

    if (s->iformat) {
        sti->avctx = avcodec_alloc_context3(NULL);
        if (!sti->avctx)
            goto fail;

        sti->info = av_mallocz(sizeof(*sti->info));
        if (!sti->info)
            goto fail;

#if FF_API_R_FRAME_RATE
        sti->info->last_dts      = AV_NOPTS_VALUE;
#endif
        sti->info->fps_first_dts = AV_NOPTS_VALUE;
        sti->info->fps_last_dts  = AV_NOPTS_VALUE;

        /* default pts setting is MPEG-like */
        avpriv_set_pts_info(st, 33, 1, 90000);
        /* we set the current DTS to 0 so that formats without any timestamps
         * but durations get some timestamps, formats with some unknown
         * timestamps have their first few packets buffered and the
         * timestamps corrected before they are returned to the user */
        sti->cur_dts = RELATIVE_TS_BASE;
    } else {
        sti->cur_dts = AV_NOPTS_VALUE;
    }

    st->index      = s->nb_streams;
    st->start_time = AV_NOPTS_VALUE;
    st->duration   = AV_NOPTS_VALUE;
    sti->first_dts     = AV_NOPTS_VALUE;
    sti->probe_packets = s->max_probe_packets;
    sti->pts_wrap_reference = AV_NOPTS_VALUE;
    sti->pts_wrap_behavior  = AV_PTS_WRAP_IGNORE;

    sti->last_IP_pts = AV_NOPTS_VALUE;
    sti->last_dts_for_order_check = AV_NOPTS_VALUE;
    for (int i = 0; i < MAX_REORDER_DELAY + 1; i++)
        sti->pts_buffer[i] = AV_NOPTS_VALUE;

    st->sample_aspect_ratio = (AVRational) { 0, 1 };
    sti->transferred_mux_tb = (AVRational) { 0, 1 };;

#if FF_API_AVSTREAM_SIDE_DATA
    sti->inject_global_side_data = si->inject_global_side_data;
#endif

    sti->need_context_update = 1;

    s->streams[s->nb_streams++] = st;
    return st;
fail:
    ff_free_stream(&st);
    return NULL;
}

三、avformat_new_stream函数的内部实现分析

avformat_new_stream函数内部,首先会判断该媒体文件/媒体流中已经存在的流的个数是否超过"限制的最大数量","限制的最大数量"(s->max_streams)默认是1000,该值可以设置。如果超过,打印日志:"Number of streams exceeds max_streams parameter",avformat_new_stream函数返回NULL:

cpp 复制代码
    if (s->nb_streams >= s->max_streams) {
        av_log(s, AV_LOG_ERROR, "Number of streams exceeds max_streams parameter"
               " (%d), see the documentation if you wish to increase it\n",
               s->max_streams);
        return NULL;
    }

通过下面语句,给新流对应的AVStream指针分配内存,给形参s的streams数组增加新的元素(即增加新流对应的AVStream指针)。注意:这一步只会给AVStream的指针分配内存,不会给AVStream本身分配内存。关于av_realloc_array函数的用法可以参考:《FFmpeg源码:av_malloc_array、av_realloc_array函数分析》:

cpp 复制代码
    streams = av_realloc_array(s->streams, s->nb_streams + 1, sizeof(*streams));
    if (!streams)
        return NULL;
    s->streams = streams;

给新流对应的FFStream和AVStream结构本身分配内存:

cpp 复制代码
    sti = av_mallocz(sizeof(*sti));
    if (!sti)
        return NULL;
    st = &sti->pub;
//...
    s->streams[s->nb_streams++] = st;

给新流对应的AVStream结构的其它成员分配内存和初始化。语句:avpriv_set_pts_info(st, 33, 1, 90000)是给AVStream的time_base设置一个默认值,关于avpriv_set_pts_info函数的用法可以参考:《FFmpeg源码:avpriv_set_pts_info函数分析》:

cpp 复制代码
    st->av_class = &stream_class;
    st->codecpar = avcodec_parameters_alloc();
    if (!st->codecpar)
        goto fail;

    sti->fmtctx = s;

    if (s->iformat) {
        sti->avctx = avcodec_alloc_context3(NULL);
        if (!sti->avctx)
            goto fail;

        sti->info = av_mallocz(sizeof(*sti->info));
        if (!sti->info)
            goto fail;

#if FF_API_R_FRAME_RATE
        sti->info->last_dts      = AV_NOPTS_VALUE;
#endif
        sti->info->fps_first_dts = AV_NOPTS_VALUE;
        sti->info->fps_last_dts  = AV_NOPTS_VALUE;

        /* default pts setting is MPEG-like */
        avpriv_set_pts_info(st, 33, 1, 90000);
        /* we set the current DTS to 0 so that formats without any timestamps
         * but durations get some timestamps, formats with some unknown
         * timestamps have their first few packets buffered and the
         * timestamps corrected before they are returned to the user */
        sti->cur_dts = RELATIVE_TS_BASE;
    } else {
        sti->cur_dts = AV_NOPTS_VALUE;
    }

    st->index      = s->nb_streams;
    st->start_time = AV_NOPTS_VALUE;
    st->duration   = AV_NOPTS_VALUE;
    sti->first_dts     = AV_NOPTS_VALUE;
    sti->probe_packets = s->max_probe_packets;
    sti->pts_wrap_reference = AV_NOPTS_VALUE;
    sti->pts_wrap_behavior  = AV_PTS_WRAP_IGNORE;

    sti->last_IP_pts = AV_NOPTS_VALUE;
    sti->last_dts_for_order_check = AV_NOPTS_VALUE;
    for (int i = 0; i < MAX_REORDER_DELAY + 1; i++)
        sti->pts_buffer[i] = AV_NOPTS_VALUE;

    st->sample_aspect_ratio = (AVRational) { 0, 1 };
    sti->transferred_mux_tb = (AVRational) { 0, 1 };;

#if FF_API_AVSTREAM_SIDE_DATA
    sti->inject_global_side_data = si->inject_global_side_data;
#endif

    sti->need_context_update = 1;
相关推荐
ZouZou老师3 小时前
FFmpeg性能优化经典案例
性能优化·ffmpeg
aqi006 小时前
FFmpeg开发笔记(九十)采用FFmpeg套壳的音视频转码百宝箱FFBox
ffmpeg·音视频·直播·流媒体
齐齐大魔王8 小时前
FFmpeg
ffmpeg
你好音视频9 小时前
FFmpeg RTSP拉流流程深度解析
ffmpeg
IFTICing20 小时前
【环境配置】ffmpeg下载、安装、配置(Windows环境)
windows·ffmpeg
haiy201120 小时前
FFmpeg 编译
ffmpeg
aqi001 天前
FFmpeg开发笔记(八十九)基于FFmpeg的直播视频录制工具StreamCap
ffmpeg·音视频·直播·流媒体
八月的雨季 最後的冰吻1 天前
FFmepg--28- 滤镜处理 YUV 视频帧:实现上下镜像效果
ffmpeg·音视频
ganqiuye1 天前
向ffmpeg官方源码仓库提交patch
大数据·ffmpeg·video-codec
草明1 天前
ffmpeg 把 ts 转换成 mp3
ffmpeg