请选择 进入手机版 | 继续访问电脑版
设为首页收藏本站
开启辅助访问
切换到宽版

ChinaFFmpeg

 找回密码
 立即注册

QQ登录

只需一步,快速开始

用新浪微博登录

只需一步,快速搞定

搜索
热搜: 活动 交友 discuz
查看: 347|回复: 1

[其他] FFmpeg Muxer结构体解析

[复制链接]
扫一扫,手机访问本帖
发表于 2018-1-19 19:38:22 | 显示全部楼层 |阅读模式
在FFmpeg里面封装、也叫复用、也叫Muxer的时候,主要是用的是AVOutputFormat作为封装模块的机构,抽象了几个通用的接口,下面看一下代码注释
文件: libavformat/avformat.h
[C] 纯文本查看 复制代码
/**
 * @addtogroup lavf_encoding
 * @{
 */
typedef struct AVOutputFormat {
    const char *name;
    /**
     * Descriptive name for the format, meant to be more human-readable
     * than name. You should use the NULL_IF_CONFIG_SMALL() macro
     * to define it.
     */
    const char *long_name;
    const char *mime_type;
    const char *extensions; /**< comma-separated filename extensions */
    /* output support */
    enum AVCodecID audio_codec;    /**< default audio codec */
    enum AVCodecID video_codec;    /**< default video codec */
    enum AVCodecID subtitle_codec; /**< default subtitle codec */
    /**
     * can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER,
     * AVFMT_GLOBALHEADER, AVFMT_NOTIMESTAMPS, AVFMT_VARIABLE_FPS,
     * AVFMT_NODIMENSIONS, AVFMT_NOSTREAMS, AVFMT_ALLOW_FLUSH,
     * AVFMT_TS_NONSTRICT, AVFMT_TS_NEGATIVE
     */
    int flags;

    /**
     * List of supported codec_id-codec_tag pairs, ordered by "better
     * choice first". The arrays are all terminated by AV_CODEC_ID_NONE.
     */
    const struct AVCodecTag * const *codec_tag;


    const AVClass *priv_class; ///< AVClass for the private context

    /*****************************************************************
     * No fields below this line are part of the public API. They
     * may not be used outside of libavformat and can be changed and
     * removed at will.
     * New public fields should be added right above.
     *****************************************************************
     */
    struct AVOutputFormat *next;
    /**
     * size of private data so that it can be allocated in the wrapper
     */
    int priv_data_size;

    int (*write_header)(struct AVFormatContext *);
    /**
     * Write a packet. If AVFMT_ALLOW_FLUSH is set in flags,
     * pkt can be NULL in order to flush data buffered in the muxer.
     * When flushing, return 0 if there still is more data to flush,
     * or 1 if everything was flushed and there is no more buffered
     * data.
     */
    int (*write_packet)(struct AVFormatContext *, AVPacket *pkt);
    int (*write_trailer)(struct AVFormatContext *);
    /**
     * Currently only used to set pixel format if not YUV420P.
     */
    int (*interleave_packet)(struct AVFormatContext *, AVPacket *out,
                             AVPacket *in, int flush);
    /**
     * Test if the given codec can be stored in this container.
     *
     * @return 1 if the codec is supported, 0 if it is not.
     *         A negative number if unknown.
     *         MKTAG('A', 'P', 'I', 'C') if the codec is only supported as AV_DISPOSITION_ATTACHED_PIC
     */
    int (*query_codec)(enum AVCodecID id, int std_compliance);

    void (*get_output_timestamp)(struct AVFormatContext *s, int stream,
                                 int64_t *dts, int64_t *wall);
    /**
     * Allows sending messages from application to device.
     */
    int (*control_message)(struct AVFormatContext *s, int type,
                           void *data, size_t data_size);

    /**
     * Write an uncoded AVFrame.
     *
     * See av_write_uncoded_frame() for details.
     *
     * The library will free *frame afterwards, but the muxer can prevent it
     * by setting the pointer to NULL.
     */
    int (*write_uncoded_frame)(struct AVFormatContext *, int stream_index,
                               AVFrame **frame, unsigned flags);
    /**
     * Returns device list with it properties.
     * @see avdevice_list_devices() for more details.
     */
    int (*get_device_list)(struct AVFormatContext *s, struct AVDeviceInfoList *device_list);
    /**
     * Initialize device capabilities submodule.
     * @see avdevice_capabilities_create() for more details.
     */
    int (*create_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps);
    /**
     * Free device capabilities submodule.
     * @see avdevice_capabilities_free() for more details.
     */
    int (*free_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps);
    enum AVCodecID data_codec; /**< default data codec */
    /**
     * Initialize format. May allocate data here, and set any AVFormatContext or
     * AVStream parameters that need to be set before packets are sent.
     * This method must not write output.
     *
     * Return 0 if streams were fully configured, 1 if not, negative AVERROR on failure
     *
     * Any allocations made here must be freed in deinit().
     */
    int (*init)(struct AVFormatContext *);
    /**
     * Deinitialize format. If present, this is called whenever the muxer is being
     * destroyed, regardless of whether or not the header has been written.
     *
     * If a trailer is being written, this is called after write_trailer().
     *
     * This is called if init() fails as well.
     */
    void (*deinit)(struct AVFormatContext *);
    /**
     * Set up any necessary bitstream filtering and extract any extra data needed
     * for the global header.
     * Return 0 if more packets from this stream must be checked; 1 if not.
     */
    int (*check_bitstream)(struct AVFormatContext *, const AVPacket *pkt);
} AVOutputFormat;
/**
 * @}
 */


注释写得很详细,下面来简单解析一下:

name 为一个常量,也就是这个模块的名称,例如图片的一般为“image2”, HLS 的为“hls”

long_name 也是一个常量,也就是关于模块的描述,例如图片一般为“image2 sequence”, HLS的为“Apple HTTP Live Streaming”
mime_type 也是一个常量,比如flv,一般为"video/x-flv", ts一般为"video/MP2T", 这类mime_type的理解方式与http服务器中配置的相同
extensions 也是一个常量,是媒体的扩展名描述,比如flv一般是.flv, HLS是.m3u8

然后是三个常见的枚举变量, audio_codec,video_codec,subtitle_codec 分别是音频的编码标识,视频的编码标识,字母的编码标识,类型是 AVCodecID 定义如下:文件:libavformat/internal.h
[C] 纯文本查看 复制代码
/**
 * Identify the syntax and semantics of the bitstream.
 * The principle is roughly:
 * Two decoders with the same ID can decode the same streams.
 * Two encoders with the same ID can encode compatible streams.
 * There may be slight deviations from the principle due to implementation
 * details.
 *
 * If you add a codec ID to this list, add it so that
 * 1. no value of an existing codec ID changes (that would break ABI),
 * 2. it is as close as possible to similar codecs
 *
 * After adding new codec IDs, do not forget to add an entry to the codec
 * descriptor list and bump libavcodec minor version.
 */
enum AVCodecID {
    AV_CODEC_ID_NONE,

    /* video codecs */
    AV_CODEC_ID_MPEG1VIDEO,
    AV_CODEC_ID_MPEG2VIDEO, ///< preferred ID for MPEG-1/2 video decoding
    AV_CODEC_ID_H261,
    AV_CODEC_ID_H263,
    AV_CODEC_ID_RV10,
    AV_CODEC_ID_RV20,
    AV_CODEC_ID_MJPEG,
    AV_CODEC_ID_MJPEGB,
    AV_CODEC_ID_LJPEG,
    AV_CODEC_ID_SP5X,
    AV_CODEC_ID_JPEGLS,
省略剩余部分



接着是封装相关的flags:
[AppleScript] 纯文本查看 复制代码
 /**
     * can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER,
     * AVFMT_GLOBALHEADER, AVFMT_NOTIMESTAMPS, AVFMT_VARIABLE_FPS,
     * AVFMT_NODIMENSIONS, AVFMT_NOSTREAMS, AVFMT_ALLOW_FLUSH,
     * AVFMT_TS_NONSTRICT, AVFMT_TS_NEGATIVE
     */
    int flags;


例如设置global_header,或者notimestamps, 或者vfps相关的设置。

codec_tag, codec的标签变量,定义如下:
文件:libavformat/internal.h
[C] 纯文本查看 复制代码
typedef struct AVCodecTag {
    enum AVCodecID id;
    unsigned int tag;
} AVCodecTag;


偶尔也许会用到,例如wav封装:
[C] 纯文本查看 复制代码
.codec_tag = (const AVCodecTag* const []){ ff_codec_wav_tags, 0 },


而ff_codec_wav_tags的定义如下:
文件:libavformat/riff.c
[C] 纯文本查看 复制代码
const AVCodecTag ff_codec_wav_tags[] = {
    { AV_CODEC_ID_PCM_S16LE,       0x0001 },
    /* must come after s16le in this list */
    { AV_CODEC_ID_PCM_U8,          0x0001 },

省略一部分内容



priv_class, 模块本身内容私有类

接下来是一系列的抽象接口:
write_header 写封装文件头部分。
write_packet 写封装文件的body内容部分
write_trailer 写封装文件的末尾部分,例如mp4的moov,一般是在这里面写入的
query_codec 查找codec信息
get_output_timestamp 获得写入的时间戳
control_message 做设备操作时从应用发消息到设备
write_uncoded_frame 写未经过编码的帧数居
get_device_list 获得设备列表
create_device_capabilities 初始化设备的功能
free_device_capabilities 释放设备
init 初始化封装,以前没有这个抽象接口,后来将write_header拆分成两部分,一部分是初始化,一部分是写文件头
deinit 结束封装,这个用到的并不多,与init基本对应
check_bitstream, 这个接口比较关键,在旧版本中,从mp4转封装到ts或者hls的时候,需要加上滤镜bsf:v mp4toannexb ,调用api操作的时候也需要用filter来处理一下,封装的时候加上这个接口处理mp4annexb操作的话,就无需那么多步骤进行操作了,因为在ffmpeg的write_frame的时候自动就check这个接口了:
文件:libavformat/mux.c
[AppleScript] 纯文本查看 复制代码
static int do_packet_auto_bsf(AVFormatContext *s, AVPacket *pkt) {
    AVStream *st = s->streams[pkt->stream_index];
    int i, ret;

    if (!(s->flags & AVFMT_FLAG_AUTO_BSF))
        return 1;

    if (s->oformat->check_bitstream) {
        if (!st->internal->bitstream_checked) {
            if ((ret = s->oformat->check_bitstream(s, pkt)) < 0)
                return ret;
            else if (ret == 1)
                st->internal->bitstream_checked = 1;
        }
    }

    for (i = 0; i < st->internal->nb_bsfcs; i++) {
        AVBSFContext *ctx = st->internal->bsfcs[i];
        // TODO: when any bitstream filter requires flushing at EOF, we'll need to
        // flush each stream's BSF chain on write_trailer.
        if ((ret = av_bsf_send_packet(ctx, pkt)) < 0) {
            av_log(ctx, AV_LOG_ERROR,
                    "Failed to send packet to filter %s for stream %d\n",
                    ctx->filter->name, pkt->stream_index);
            return ret;
        }
        // TODO: when any automatically-added bitstream filter is generating multiple
        // output packets for a single input one, we'll need to call this in a loop
        // and write each output packet.
        if ((ret = av_bsf_receive_packet(ctx, pkt)) < 0) {
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                return 0;
            av_log(ctx, AV_LOG_ERROR,
                    "Failed to send packet to filter %s for stream %d\n",
                    ctx->filter->name, pkt->stream_index);
            if (s->error_recognition & AV_EF_EXPLODE)
                return ret;
            return 0;
        }
    }
    return 1;
}
int av_write_frame(AVFormatContext *s, AVPacket *pkt)
{
    int ret;

    ret = prepare_input_packet(s, pkt);
    if (ret < 0)
        return ret;

    if (!pkt) {
        if (s->oformat->flags & AVFMT_ALLOW_FLUSH) {
            ret = s->oformat->write_packet(s, NULL);
            flush_if_needed(s);
            if (ret >= 0 && s->pb && s->pb->error < 0)
                ret = s->pb->error;
            return ret;
        }
        return 1;
    }

    ret = do_packet_auto_bsf(s, pkt);
    if (ret <= 0)
        return ret;

#if FF_API_COMPUTE_PKT_FIELDS2 && FF_API_LAVF_AVCTX
    ret = compute_muxer_pkt_fields(s, s->streams[pkt->stream_index], pkt);

    if (ret < 0 && !(s->oformat->flags & AVFMT_NOTIMESTAMPS))
        return ret;
#endif

    ret = write_packet(s, pkt);
    if (ret >= 0 && s->pb && s->pb->error < 0)
        ret = s->pb->error;

    if (ret >= 0)
        s->streams[pkt->stream_index]->nb_frames++;
    return ret;
}



从上面的代码实现中可以看到,已经自动处理了。


回复

使用道具 举报

发表于 2018-1-19 19:40:51 | 显示全部楼层
学习学习
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|Archiver|ChinaFFmpeg    

GMT+8, 2018-5-20 21:28 , Processed in 0.032103 second(s), 21 queries .

Powered by ChinaFFmpeg

© 2013 - 2018 www.chinaffmpeg.com

快速回复 返回顶部 返回列表