Simple Analysis of FFmpeg source code: avformat_write_header (),

Source: Internet
Author: User
Tags flv file

Simple Analysis of FFmpeg source code: avformat_write_header (),
I plan to write two articles to analyze the three functions used to write FFmpeg files: avformat_write_header (), av_write_frame (), and av_write_trailer (). Av_write_frame () is used to write Video data, avformat_write_header () is used to write the video file header, and av_write_trailer () is used to write the end of the video file.
This article first analyzes avformat_write_header ().
PS:
Note that although these three functions are compatible, their prefixes are different. The function prefix for Writing File Header headers is "avformat _", the prefix of the other two functions is "av _" (I don't quite understand the reason ).
The avformat_write_header () declaration is in libavformat \ avformat. h, as shown below.

/** * Allocate the stream private data and write the stream header to * an output media file. * * @param s Media file handle, must be allocated with avformat_alloc_context(). *          Its oformat field must be set to the desired output format; *          Its pb field must be set to an already opened AVIOContext. * @param options  An AVDictionary filled with AVFormatContext and muxer-private options. *                 On return this parameter will be destroyed and replaced with a dict containing *                 options that were not found. May be NULL. * * @return 0 on success, negative AVERROR on failure. * * @see av_opt_find, av_dict_set, avio_open, av_oformat_next. */int avformat_write_header(AVFormatContext *s, AVDictionary **options);
Briefly explain the meaning of its parameters:
S: AVFormatContext for output.
Options: additional options, which have not been studied in depth yet, are generally NULL.
After the function is executed normally, the return value is equal to 0.

The most typical example of this function can be referred:
The simplest Video Encoder Based on FFMPEG (YUV encoding: H.264)

Function call Relationship Diagram

Shows the call relationship of avformat_write_header.



Avformat_write_header () is defined in libavformat \ mux. c, as shown below.
int avformat_write_header(AVFormatContext *s, AVDictionary **options){    int ret = 0;    if (ret = init_muxer(s, options))        return ret;    if (s->oformat->write_header) {        ret = s->oformat->write_header(s);        if (ret >= 0 && s->pb && s->pb->error < 0)            ret = s->pb->error;        if (ret < 0)            return ret;        if (s->flush_packets && s->pb && s->pb->error >= 0 && s->flags & AVFMT_FLAG_FLUSH_PACKETS)            avio_flush(s->pb);    }    if ((ret = init_pts(s)) < 0)        return ret;    if (s->avoid_negative_ts < 0) {        av_assert2(s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_AUTO);        if (s->oformat->flags & (AVFMT_TS_NEGATIVE | AVFMT_NOTIMESTAMPS)) {            s->avoid_negative_ts = 0;        } else            s->avoid_negative_ts = AVFMT_AVOID_NEG_TS_MAKE_NON_NEGATIVE;    }    return 0;}

From the source code, we can see that avformat_write_header () has completed the following work:
(1) Call init_muxer () to initialize the multipleer.
(2) Call the write_header () of AVOutputFormat ()
Let's take a look at these two functions.

Init_muxer () is used to initialize a multiplead. Its definition is as follows.
static int init_muxer(AVFormatContext *s, AVDictionary **options){    int ret = 0, i;    AVStream *st;    AVDictionary *tmp = NULL;    AVCodecContext *codec = NULL;    AVOutputFormat *of = s->oformat;    AVDictionaryEntry *e;    if (options)        av_dict_copy(&tmp, *options, 0);    if ((ret = av_opt_set_dict(s, &tmp)) < 0)        goto fail;    if (s->priv_data && s->oformat->priv_class && *(const AVClass**)s->priv_data==s->oformat->priv_class &&        (ret = av_opt_set_dict2(s->priv_data, &tmp, AV_OPT_SEARCH_CHILDREN)) < 0)        goto fail;#if FF_API_LAVF_BITEXACT    if (s->nb_streams && s->streams[0]->codec->flags & CODEC_FLAG_BITEXACT)        s->flags |= AVFMT_FLAG_BITEXACT;#endif    // some sanity checks    if (s->nb_streams == 0 && !(of->flags & AVFMT_NOSTREAMS)) {        av_log(s, AV_LOG_ERROR, "No streams to mux were specified\n");        ret = AVERROR(EINVAL);        goto fail;    }    for (i = 0; i < s->nb_streams; i++) {        st    = s->streams[i];        codec = st->codec;#if FF_API_LAVF_CODEC_TBFF_DISABLE_DEPRECATION_WARNINGS        if (!st->time_base.num && codec->time_base.num) {            av_log(s, AV_LOG_WARNING, "Using AVStream.codec.time_base as a "                   "timebase hint to the muxer is deprecated. Set "                   "AVStream.time_base instead.\n");            avpriv_set_pts_info(st, 64, codec->time_base.num, codec->time_base.den);        }FF_ENABLE_DEPRECATION_WARNINGS#endif        if (!st->time_base.num) {            /* fall back on the default timebase values */            if (codec->codec_type == AVMEDIA_TYPE_AUDIO && codec->sample_rate)                avpriv_set_pts_info(st, 64, 1, codec->sample_rate);            else                avpriv_set_pts_info(st, 33, 1, 90000);        }        switch (codec->codec_type) {        case AVMEDIA_TYPE_AUDIO:            if (codec->sample_rate <= 0) {                av_log(s, AV_LOG_ERROR, "sample rate not set\n");                ret = AVERROR(EINVAL);                goto fail;            }            if (!codec->block_align)                codec->block_align = codec->channels *                                     av_get_bits_per_sample(codec->codec_id) >> 3;            break;        case AVMEDIA_TYPE_VIDEO:            if ((codec->width <= 0 || codec->height <= 0) &&                !(of->flags & AVFMT_NODIMENSIONS)) {                av_log(s, AV_LOG_ERROR, "dimensions not set\n");                ret = AVERROR(EINVAL);                goto fail;            }            if (av_cmp_q(st->sample_aspect_ratio, codec->sample_aspect_ratio)                && FFABS(av_q2d(st->sample_aspect_ratio) - av_q2d(codec->sample_aspect_ratio)) > 0.004*av_q2d(st->sample_aspect_ratio)            ) {                if (st->sample_aspect_ratio.num != 0 &&                    st->sample_aspect_ratio.den != 0 &&                    codec->sample_aspect_ratio.num != 0 &&                    codec->sample_aspect_ratio.den != 0) {                    av_log(s, AV_LOG_ERROR, "Aspect ratio mismatch between muxer "                           "(%d/%d) and encoder layer (%d/%d)\n",                           st->sample_aspect_ratio.num, st->sample_aspect_ratio.den,                           codec->sample_aspect_ratio.num,                           codec->sample_aspect_ratio.den);                    ret = AVERROR(EINVAL);                    goto fail;                }            }            break;        }        if (of->codec_tag) {            if (   codec->codec_tag                && codec->codec_id == AV_CODEC_ID_RAWVIDEO                && (   av_codec_get_tag(of->codec_tag, codec->codec_id) == 0                    || av_codec_get_tag(of->codec_tag, codec->codec_id) == MKTAG('r', 'a', 'w', ' '))                && !validate_codec_tag(s, st)) {                // the current rawvideo encoding system ends up setting                // the wrong codec_tag for avi/mov, we override it here                codec->codec_tag = 0;            }            if (codec->codec_tag) {                if (!validate_codec_tag(s, st)) {                    char tagbuf[32], tagbuf2[32];                    av_get_codec_tag_string(tagbuf, sizeof(tagbuf), codec->codec_tag);                    av_get_codec_tag_string(tagbuf2, sizeof(tagbuf2), av_codec_get_tag(s->oformat->codec_tag, codec->codec_id));                    av_log(s, AV_LOG_ERROR,                           "Tag %s/0x%08x incompatible with output codec id '%d' (%s)\n",                           tagbuf, codec->codec_tag, codec->codec_id, tagbuf2);                    ret = AVERROR_INVALIDDATA;                    goto fail;                }            } else                codec->codec_tag = av_codec_get_tag(of->codec_tag, codec->codec_id);        }        if (of->flags & AVFMT_GLOBALHEADER &&            !(codec->flags & CODEC_FLAG_GLOBAL_HEADER))            av_log(s, AV_LOG_WARNING,                   "Codec for stream %d does not use global headers "                   "but container format requires global headers\n", i);        if (codec->codec_type != AVMEDIA_TYPE_ATTACHMENT)            s->internal->nb_interleaved_streams++;    }    if (!s->priv_data && of->priv_data_size > 0) {        s->priv_data = av_mallocz(of->priv_data_size);        if (!s->priv_data) {            ret = AVERROR(ENOMEM);            goto fail;        }        if (of->priv_class) {            *(const AVClass **)s->priv_data = of->priv_class;            av_opt_set_defaults(s->priv_data);            if ((ret = av_opt_set_dict2(s->priv_data, &tmp, AV_OPT_SEARCH_CHILDREN)) < 0)                goto fail;        }    }    /* set muxer identification string */    if (!(s->flags & AVFMT_FLAG_BITEXACT)) {        av_dict_set(&s->metadata, "encoder", LIBAVFORMAT_IDENT, 0);    } else {        av_dict_set(&s->metadata, "encoder", NULL, 0);    }    for (e = NULL; e = av_dict_get(s->metadata, "encoder-", e, AV_DICT_IGNORE_SUFFIX); ) {        av_dict_set(&s->metadata, e->key, NULL, 0);    }    if (options) {         av_dict_free(options);         *options = tmp;    }    return 0;fail:    av_dict_free(&tmp);    return ret;}

The init_muxer () code is very long, but the work it does is relatively simple. It can be summarized into two words: Check. The function flow can be summarized into the following steps:
(1) set the passed options in AVDictionary form to AVFormatContext
(2) traverse each AVStream in AVFormatContext and perform the following checks:
A) whether the time_base of AVStream is set correctly. If the time_base of AVStream is not set, avpriv_set_pts_info () is called for setting.
B) for audio, check whether the sampling rate is set correctly. For video, check the width, height, and width/height ratio.
C) Other checks are not detailed.

The most important part of AVOutputFormat-> write_header () avformat_write_header () is to call the write_header () of AVOutputFormat (). Write_header () is a function pointer in AVOutputFormat, pointing to the function that writes the file header. Different avoutputformats have different write_header () implementation methods. Here we will take an example to look at the AVOutputFormat corresponding to the FLV Encapsulation Format. Its definition is in libavformat \ flvenc. c, as shown below.
AVOutputFormat ff_flv_muxer = {    .name           = "flv",    .long_name      = NULL_IF_CONFIG_SMALL("FLV (Flash Video)"),    .mime_type      = "video/x-flv",    .extensions     = "flv",    .priv_data_size = sizeof(FLVContext),    .audio_codec    = CONFIG_LIBMP3LAME ? AV_CODEC_ID_MP3 : AV_CODEC_ID_ADPCM_SWF,    .video_codec    = AV_CODEC_ID_FLV1,    .write_header   = flv_write_header,    .write_packet   = flv_write_packet,    .write_trailer  = flv_write_trailer,    .codec_tag      = (const AVCodecTag* const []) {                          flv_video_codec_ids, flv_audio_codec_ids, 0                      },    .flags          = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS |                      AVFMT_TS_NONSTRICT,};

From the definition of ff_flv_muxer, we can see that the function that write_header () points to is flv_write_header (). Let's continue to look at the flv_write_header () function. The definition of flv_write_header () is also in libavformat \ flvenc. c, as shown below.
Static int flv_write_header (AVFormatContext * s) {int I; AVIOContext * pb = s-> pb; FLVContext * flv = s-> priv_data; int64_t data_size; // set the parameter for (I = 0; I <s-> nb_streams; I ++) {AVCodecContext * enc = s-> streams [I]-> codec; FLVStreamContext * SC; switch (enc-> codec_type) {case AVMEDIA_TYPE_VIDEO: if (s-> streams [I]-> avg_frame_rate.den & s-> streams [I]-> avg_frame_rate.num) {// set the frame rate. Copy the value from AVStream to flv-> fr. Amerate = av_q2d (s-> streams [I]-> avg_frame_rate);} if (flv-> video_enc) {av_log (s, AV_LOG_ERROR, "at most one video stream is supported in flv \ n"); return AVERROR (EINVAL);} // AVCodecContext flv-> video_enc = enc; if (enc-> codec_tag = 0) {av_log (s, AV_LOG_ERROR, "Video codec '% s' for stream % d is not compatible with FLV \ n ", avcodec_get_name (enc-> codec_id), I); return AVERROR (EINVAL);} if (e Nc-> codec_id = AV_CODEC_ID_MPEG4 | enc-> codec_id = AV_CODEC_ID_H263) {int error = s-> strict_std_compliance> warning; av_log (s, error? AV_LOG_ERROR: AV_LOG_WARNING, "Codec % s is not supported in the official FLV specification, \ n", avcodec_get_name (enc-> codec_id); if (error) {av_log (s, AV_LOG_ERROR, "use vstrict =-1/-strict-1 to use it anyway. \ n "); return AVERROR (EINVAL) ;}} else if (enc-> codec_id = AV_CODEC_ID_VP6) {av_log (s, AV_LOG_WARNING, "Muxing VP6 in flv will produce flipped video on playback. \ n ") ;}break; case AVM EDIA_TYPE_AUDIO: if (flv-> audio_enc) {av_log (s, AV_LOG_ERROR, "at most one audio stream is supported in flv \ n"); return AVERROR (EINVAL );} // audio-encoded AVCodecContext flv-> audio_enc = enc; if (get_audio_flags (s, enc) <0) return AVERROR_INVALIDDATA; if (enc-> codec_id = audio) av_log (s, AV_LOG_WARNING, "16-bit big-endian audio in flv is valid but most likely unplayable (hardware depende Nt); use s16le \ n "); break; case AVMEDIA_TYPE_DATA: if (enc-> codec_id! = AV_CODEC_ID_TEXT & enc-> codec_id! = AV_CODEC_ID_NONE) {av_log (s, AV_LOG_ERROR, "Data codec '% s' for stream % d is not compatible with FLV \ n", avcodec_get_name (enc-> codec_id ), i); return AVERROR_INVALIDDATA;} flv-> data_enc = enc; break; default: av_log (s, AV_LOG_ERROR, "Codec type '% s' for stream % d is not compatible with FLV \ n", av_get_media_type_string (enc-> codec_type), I); return AVERROR (EINVAL );} avpriv_set_pts_info (s-> streams [I], 32, 1, 1000);/* 32 bit pts in MS */SC = av_mallocz (sizeof (FLVStreamContext); if (! SC) return AVERROR (ENOMEM); s-> streams [I]-> priv_data = SC; SC-> last_ts =-1;} flv-> delay = AV_NOPTS_VALUE; // start writing // Signature avio_write (pb, "FLV", 3); // Version avio_w8 (pb, 1); // "!" It means converting non-0 to 1 // Flags avio_w8 (pb, FLV_HEADER_FLAG_HASAUDIO *!! Flv-> audio_enc + FLV_HEADER_FLAG_HASVIDEO *!! Flv-> video_enc); // Header size avio_wb32 (pb, 9); // Header end // Previous Tag Size avio_wb32 (pb, 0); for (I = 0; I <s-> nb_streams; I ++) if (s-> streams [I]-> codec-> codec_tag = 5) {avio_w8 (pb, 8 ); // message type avio_wb24 (pb, 0); // include flags avio_wb24 (pb, 0); // time stamp avio_wb32 (pb, 0); // reserved avio_wb32 (pb, 11); // size flv-> reserved = 5;} write_metadata (s, 0); for (I = 0; I <s-> nb_st Reams; I ++) {AVCodecContext * enc = s-> streams [I]-> codec; if (enc-> codec_id = AV_CODEC_ID_AAC | enc-> codec_id = Hangzhou | enc-> codec_id = AV_CODEC_ID_MPEG4) {int64_t pos; avio_w8 (pb, enc-> codec_type = AVMEDIA_TYPE_VIDEO? FLV_TAG_TYPE_VIDEO: FLV_TAG_TYPE_AUDIO); avio_wb24 (pb, 0); // size patched later avio_wb24 (pb, 0); // ts avio_w8 (pb, 0 ); // ts ext avio_wb24 (pb, 0); // streamid pos = avio_tell (pb); if (enc-> codec_id = AV_CODEC_ID_AAC) {avio_w8 (pb, get_audio_flags (s, enc); avio_w8 (pb, 0); // AAC sequence header avio_write (pb, enc-> extradata, enc-> extradata_size);} else {avio_w8 (pb, enc-> codec_tag | FLV_FRAME_KEY); // flags avio_w8 (pb, 0); // AVC sequence header avio_wb24 (pb, 0); // composition time ff_isom_write_avcc (pb, enc-> extradata, enc-> extradata_size);} data_size = avio_tell (pb)-pos; avio_seek (pb,-data_size-10, SEEK_CUR); avio_wb24 (pb, data_size ); avio_skip (pb, data_size + 10-3); avio_wb32 (pb, data_size + 11); // previous tag size} return 0 ;}

From the source code, we can see that flv_write_header () completes writing the FLV file header. This function can be divided into the following two parts:
(1) set parameters for FLVContext
(2) write the file header and related tags
The code for writing the file header is very short, as shown below.
avio_write(pb, "FLV", 3);avio_w8(pb, 1);avio_w8(pb, FLV_HEADER_FLAG_HASAUDIO * !!flv->audio_enc +            FLV_HEADER_FLAG_HASVIDEO * !!flv->video_enc);avio_wb32(pb, 9);
You can refer to the definition of the FLV file header in to compare the above Code.

 


Lei Xiaohua
Leixiaohua1020@126.com
Http://blog.csdn.net/leixiaohua1020




Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.