概述
av_write_trailer()用于输出文件尾,它的声明位于libavformatavformat.h,如下所示。
/**
* 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);
av_write_trailer()的定义位于libavformatmux.c,如下所示。
int av_write_trailer(AVFormatContext *s)
{
int ret, i;
for (;; ) {
AVPacket pkt;
ret = interleave_packet(s, &pkt, NULL, 1);
if (ret < 0)
goto fail;
if (!ret)
break;
ret = write_packet(s, &pkt);
if (ret >= 0)
s->streams[pkt.stream_index]->nb_frames++;
av_packet_unref(&pkt);
if (ret < 0)
goto fail;
if(s->pb && s->pb->error)
goto fail;
}
fail:
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);
}
}
if (s->oformat->deinit)
s->oformat->deinit(s);
s->internal->initialized =
s->internal->streams_initialized = 0;
if (s->pb)
avio_flush(s->pb);
if (ret == 0)
ret = s->pb ? s->pb->error : 0;
for (i = 0; i < s->nb_streams; i++) {
av_freep(&s->streams[i]->priv_data);
av_freep(&s->streams[i]->index_entries);
}
if (s->oformat->priv_class)
av_opt_free(s->priv_data);
av_freep(&s->priv_data);
return ret;
}
从源代码可以看出av_write_trailer()主要完成了以下两步工作:
(1)循环调用interleave_packet()以及write_packet(),将还未输出的AVPacket输出出来。
(2)调用AVOutputFormat的write_trailer(),输出文件尾。
其中第一步和av_write_frame()中的步骤大致是一样的(interleave_packet()这一部分在并不包含在av_write_frame()中,而是包含在av_interleaved_write_frame()中,这一部分源代码还没有分析),可以参考文章《FFmpeg源代码简单分析:av_write_frame()》。下面分析一下第二步。
AVOutputFormat->write_trailer()
AVOutputFormat的write_trailer()是一个函数指针,指向特定的AVOutputFormat中的实现函数。我们以FLV对应的AVOutputFormat为例,看一下它的定义,如下所示。
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()函数。
flv_write_trailer()
flv_write_trailer()函数的定义位于libavformatflvenc.c,如下所示。
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 openn");
} 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;
}
从flv_write_trailer()的源代码可以看出该函数做了以下两步工作:
(1)如果视频流是H.264,则添加包含EOS(End Of Stream) NALU的Tag。
(2)更新FLV的时长信息,以及文件大小信息。
其中,put_avc_eos_tag()函数用于添加包含EOS NALU的Tag(包含结尾的一个PreviousTagSize),如下所示。
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 */
}
最后
以上就是鲤鱼火车为你收集整理的ffmpeg源码分析:av_write_trailer()AVOutputFormat->write_trailer()flv_write_trailer()的全部内容,希望文章能够帮你解决ffmpeg源码分析:av_write_trailer()AVOutputFormat->write_trailer()flv_write_trailer()所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复