It is intended to write two articles that simply analyze the 3 functions used by FFmpeg's writing Files: Avformat_write_header (), Av_write_frame (), and Av_write_trailer (). where Av_write_frame () is used to write video data, Avformat_write_header () is used to write video file headers, while Av_write_trailer () is used to write video file endings.
This paper first analyzes Avformat_write_header ().
PS:
It is important to note that although these 3 function functions are matched, they do not have the same prefix, the function prefix of the header header is "Avformat_", and the other two function prefixes are "av_" (the reason is not quite clear).
The declaration of Avformat_write_header () is located 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 is allocated with Avformat_alloc_context (). * Its oformat field must is set to the desired output format; * Its PB field must is set to an already opened AVIO Context. * @param options an avdictionary filled with avformatcontext and muxer-private options. * On return this parameter would be destroyed and replaced with a dict containing * options, were not found. May is 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);
Simply explain the meaning of its parameters:
S: Avformatcontext for output.
Options: Additional options, not currently studied in depth, are generally null.
The return value is equal to 0 after the function executes normally.
The most typical examples of this function can be consulted:
The simplest video encoder based on FFmpeg (YUV encoded as H.
Function call graph
The invocation relationship of Avformat_write_header () is as shown.
The definition of Avformat_write_header () Avformat_write_header () is located 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;}
As you can see from the source code, Avformat_write_header () has completed the following tasks:
(1) Call Init_muxer () initialization multiplexer
(2) Call Avoutputformat's Write_header ()
Let's take a look at these two functions.
Init_muxer () Init_muxer () is used to initialize the multiplexer, which is defined 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 && C Odec->time_base.num) {Av_log (S, av_log_warning, "Using AVStream.codec.time_base as a" "t Imebase hint to the muxer is deprecated. Set "" Avstream.time_base instead.\n "); Avpriv_set_pts_info (St, 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) avpri V_set_pts_info (St, 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_q2 D (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_asp Ect_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->sa Mple_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 and can be summed up in two words: check. The process of a function can be summed up in the following steps:
(1) Set the option of the incoming avdictionary form to Avformatcontext
(2) iterate through each avstream in the Avformatcontext and check for the following:
a) Avstream time_base is set correctly. If the Avstream time_base is found to be not set, then Avpriv_set_pts_info () is called to set.
B) for audio, check that the sampling rate is set correctly, and for video, check the width, height, and aspect ratio.
c) Some other checks are no longer detailed.
The most critical place in Avoutputformat->write_header () Avformat_write_header () is the invocation of Avoutputformat's Write_header (). Write_header () is a function pointer in Avoutputformat that points to the function that writes the header of the file. Different avoutputformat have different implementation methods of Write_header (). Here we give an example of the Avoutputformat in the FLV package format, which is defined 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< c24/>= Avfmt_globalheader | Avfmt_variable_fps | Avfmt_ts_nonstrict,};
As can be seen from the definition of ff_flv_muxer, the function pointed to by Write_header () is Flv_write_header (). Let's continue to look at the Flv_write_header () function. The definition of Flv_write_header () is also located 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 parameters 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 frame rate, copy from Avstream Flv->fra Merate = av_q2d (s->streams[i]->avg_frame_rate); } if (Flv->video_enc) {Av_log (S, Av_log_error, "at the most one video stre AM is supported in flv\n "); Return Averror (EINVAL); }//video-encoded avcodeccontext FLV->VIDEO_ENC = enc; if (Enc->codec_tag = = 0) {Av_log (S, Av_log_error, "Video codec '%s ' for stream%d are not COmpatible with flv\n ", Avcodec_get_name (enc->codec_id), i); Return Averror (EINVAL); } if (enc->codec_id = = AV_CODEC_ID_MPEG4 | | enc->codec_id = = av_codec_id_h263) {int error = s->strict_std_compliance > Ff_compliance_unoffici AL; Av_log (s), error? Av_log_error:av_log_warning, "Codec%s isn't supported in the official FLV specification,\n", AVC Odec_get_name (enc->codec_id)); if (Error) {Av_log (S, Av_log_error, "use Vstrict=-1/-strict-1 to use it a Nyway.\n "); Return Averror (EINVAL); }} else if (enc->codec_id = = AV_CODEC_ID_VP6) {Av_log (S, av_log_warning, "Muxing VP6 in FLV would produce flipped video on playback.\n"); } break; Case Avmedia_type_audio:if (flV->audio_enc) {Av_log (S, Av_log_error, "at the most one audio stream are 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 = = av_codec_id_pcm_s16be) Av_log (S, av_log_warning, "16-bit big- Endian audio in FLV are valid but most likely unplayable (hardware dependent); Use s16le\n "); Break Case Avmedia_type_data:if (enc->codec_id! = Av_codec_id_text && enc->codec_id! = Av_codec_id_non E) {Av_log (S, Av_log_error, "Data codec '%s ' for stream%d are 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 ' isn't 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); /* The N-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); “!! "meaning to convert non 0 into 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 (P B, 8); MessageType 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_streams; i++) {Avcodeccontext *enc = s->streams[i]->codec; if (enc->codec_id = = AV_CODEC_ID_AAC | | enc->codec_id = av_codec_id_h264 | | 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 can be seen, Flv_write_header () completed the FLV file header writing work. The work of this function can be broadly divided into the following two parts:
(1) Setting parameters for Flvcontext
(2) Write the file header, and the relevant tag
The code to write 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 the above code.
Lei Huawei
[Email protected]
http://blog.csdn.net/leixiaohua1020
FFmpeg source code Simple analysis: Avformat_write_header ()