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;
相关推荐
runing_an_min4 小时前
ffmpeg视频滤镜:网格-drawgrid
ffmpeg·音视频·网格·drawgrid
间彧9 小时前
FFmpeg推流器
ffmpeg
生命几十年3万天11 小时前
ffmpeg常用命令
ffmpeg
qs113798184311 小时前
怎么提取视频里的音频?关于提取视频里音频的几种方法
linux·网络·ffmpeg
cuijiecheng201812 小时前
音视频入门基础:AAC专题(12)——FFmpeg源码中,解码AudioSpecificConfig的实现
ffmpeg·音视频·aac
fensnote1 天前
ffmpeg拉流分段存储到文件-笔记
笔记·ffmpeg
cpp_learners1 天前
Windows环境 ffmpeg 命令使用介绍
windows·ffmpeg·ffmpeg命令
HockerF2 天前
ffmpeg环境
ffmpeg
学前端的小朱3 天前
【视频混剪Demo】FFmpeg的使用【Windows】
windows·ffmpeg·音视频·1024程序员节·更改参数·视频混剪
从后端到QT3 天前
HLS协议之nginx-hls-多码率测试环境搭建
运维·nginx·ffmpeg·音视频·rtmp