- /**
- * Write the stream trailer to an output media file and free the
- * file private data.
- *
- * May only be called after a successful call to avformat_write_header.
- *
- * @param s media file handle
- * @return 0 if OK, AVERROR_xxx on error
- */
- int av_write_trailer(AVFormatContext *s);
- int av_write_trailer(AVFormatContext *s)
- {
- FFFormatContext *const si = ffformatcontext(s);
- AVPacket *const pkt = si->parse_pkt;
- int ret1, ret = 0;
-
- for (unsigned i = 0; i < s->nb_streams; i++) {
- AVStream *const st = s->streams[i];
- FFStream *const sti = ffstream(st);
- if (sti->bsfc) {
- ret1 = write_packets_from_bsfs(s, st, pkt, 1/*interleaved*/);
- if (ret1 < 0)
- av_packet_unref(pkt);
- if (ret >= 0)
- ret = ret1;
- }
- }
- ret1 = interleaved_write_packet(s, pkt, 1, 0);
- if (ret >= 0)
- ret = ret1;
-
- if (s->oformat->write_trailer) {
- if (!(s->oformat->flags & AVFMT_NOFILE) && s->pb)
- avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_TRAILER);
- if (ret >= 0) {
- ret = s->oformat->write_trailer(s);
- } else {
- s->oformat->write_trailer(s);
- }
- }
-
- deinit_muxer(s);
-
- if (s->pb)
- avio_flush(s->pb);
- if (ret == 0)
- ret = s->pb ? s->pb->error : 0;
- for (unsigned i = 0; i < s->nb_streams; i++) {
- av_freep(&s->streams[i]->priv_data);
- av_freep(&ffstream(s->streams[i])->index_entries);
- }
- if (s->oformat->priv_class)
- av_opt_free(s->priv_data);
- av_freep(&s->priv_data);
- av_packet_unref(si->pkt);
- return ret;
- }
- const 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,
- .init = flv_init,
- .write_header = flv_write_header,
- .write_packet = flv_write_packet,
- .write_trailer = flv_write_trailer,
- .check_bitstream= flv_check_bitstream,
- .codec_tag = (const AVCodecTag* const []) {
- flv_video_codec_ids, flv_audio_codec_ids, 0
- },
- .flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS |
- AVFMT_TS_NONSTRICT,
- .priv_class = &flv_muxer_class,
- };
从FLV对应的AVOutputFormat结构体的定义我们可以看出,write_trailer()指向了flv_write_trailer()函数
- static int flv_write_trailer(AVFormatContext *s)
- {
- int64_t file_size;
- AVIOContext *pb = s->pb;
- FLVContext *flv = s->priv_data;
- int build_keyframes_idx = flv->flags & FLV_ADD_KEYFRAME_INDEX;
- int i, res;
- int64_t cur_pos = avio_tell(s->pb);
-
- if (build_keyframes_idx) {
- FLVFileposition *newflv_posinfo, *p;
-
- avio_seek(pb, flv->videosize_offset, SEEK_SET);
- put_amf_double(pb, flv->videosize);
-
- avio_seek(pb, flv->audiosize_offset, SEEK_SET);
- put_amf_double(pb, flv->audiosize);
-
- avio_seek(pb, flv->lasttimestamp_offset, SEEK_SET);
- put_amf_double(pb, flv->lasttimestamp);
-
- avio_seek(pb, flv->lastkeyframetimestamp_offset, SEEK_SET);
- put_amf_double(pb, flv->lastkeyframetimestamp);
-
- avio_seek(pb, flv->lastkeyframelocation_offset, SEEK_SET);
- put_amf_double(pb, flv->lastkeyframelocation + flv->keyframe_index_size);
- avio_seek(pb, cur_pos, SEEK_SET);
-
- res = shift_data(s);
- if (res < 0) {
- goto end;
- }
- avio_seek(pb, flv->keyframes_info_offset, SEEK_SET);
- put_amf_string(pb, "filepositions");
- put_amf_dword_array(pb, flv->filepositions_count);
- for (newflv_posinfo = flv->head_filepositions; newflv_posinfo; newflv_posinfo = newflv_posinfo->next) {
- put_amf_double(pb, newflv_posinfo->keyframe_position + flv->keyframe_index_size);
- }
-
- put_amf_string(pb, "times");
- put_amf_dword_array(pb, flv->filepositions_count);
- for (newflv_posinfo = flv->head_filepositions; newflv_posinfo; newflv_posinfo = newflv_posinfo->next) {
- put_amf_double(pb, newflv_posinfo->keyframe_timestamp);
- }
-
- newflv_posinfo = flv->head_filepositions;
- while (newflv_posinfo) {
- p = newflv_posinfo->next;
- if (p) {
- newflv_posinfo->next = p->next;
- av_free(p);
- p = NULL;
- } else {
- av_free(newflv_posinfo);
- newflv_posinfo = NULL;
- }
- }
-
- put_amf_string(pb, "");
- avio_w8(pb, AMF_END_OF_OBJECT);
-
- avio_seek(pb, cur_pos + flv->keyframe_index_size, SEEK_SET);
- }
-
- end:
- if (flv->flags & FLV_NO_SEQUENCE_END) {
- av_log(s, AV_LOG_DEBUG, "FLV no sequence end mode open\n");
- } else {
- /* Add EOS tag */
- for (i = 0; i < s->nb_streams; i++) {
- AVCodecParameters *par = s->streams[i]->codecpar;
- FLVStreamContext *sc = s->streams[i]->priv_data;
- if (par->codec_type == AVMEDIA_TYPE_VIDEO &&
- (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4))
- put_avc_eos_tag(pb, sc->last_ts);
- }
- }
-
- file_size = avio_tell(pb);
-
- if (build_keyframes_idx) {
- flv->datasize = file_size - flv->datastart_offset;
- avio_seek(pb, flv->datasize_offset, SEEK_SET);
- put_amf_double(pb, flv->datasize);
- }
- if (!(flv->flags & FLV_NO_METADATA)) {
- if (!(flv->flags & FLV_NO_DURATION_FILESIZE)) {
- /* update information */
- if (avio_seek(pb, flv->duration_offset, SEEK_SET) < 0) {
- av_log(s, AV_LOG_WARNING, "Failed to update header with correct duration.\n");
- } else {
- put_amf_double(pb, flv->duration / (double)1000);
- }
- if (avio_seek(pb, flv->filesize_offset, SEEK_SET) < 0) {
- av_log(s, AV_LOG_WARNING, "Failed to update header with correct filesize.\n");
- } else {
- put_amf_double(pb, file_size);
- }
- }
- }
-
- return 0;
- }
- static void put_avc_eos_tag(AVIOContext *pb, unsigned ts)
- {
- avio_w8(pb, FLV_TAG_TYPE_VIDEO);
- avio_wb24(pb, 5); /* Tag Data Size */
- put_timestamp(pb, ts);
- avio_wb24(pb, 0); /* StreamId = 0 */
- avio_w8(pb, 23); /* ub[4] FrameType = 1, ub[4] CodecId = 7 */
- avio_w8(pb, 2); /* AVC end of sequence */
- avio_wb24(pb, 0); /* Always 0 for AVC EOS. */
- avio_wb32(pb, 16); /* Size of FLV tag */
- }
