FFmpeg在libavformat模块提供音视频的muxer封装与demuxer解封装。其中muxer封装文件包括avformat_write_header()、av_write_frame()和av_write_trailer()。本文主要探讨av_write_frame函数如何写入音视频帧数据,包括音视频交错与音视频非交错两种情况。
av_write_frame函数位于libavformat/mux.c,写音视频帧的流程如下:
1、av_write_frame
av_write_frame的声明位于libavformat/avformat.h,具体如下:
/**
* Write a packet to an output media file.
*
* This function passes the packet directly to the muxer, without any buffering
* or reordering. The caller is responsible for correctly interleaving the
* packets if the format requires it. Callers that want libavformat to handle
* the interleaving should call av_interleaved_write_frame() instead of this
* function.
*
* @param s media file handle
* @param pkt The packet containing the data to be written. Note that unlike
* av_interleaved_write_frame(), this function does not take
* ownership of the packet passed to it (though some muxers may make
* an internal reference to the input packet).
* @return < 0 on error, = 0 if OK, 1 if flushed and there is no more data to flush
*
* @see av_interleaved_write_frame()
*/
int av_write_frame(AVFormatContext *s, AVPacket *pkt);
大致翻译为:写一个数据包到输出媒体文件。这个函数直接传递数据包到封装器,没有任何缓存或重排序。如果格式需要,调用者要负责把数据包正确排列。如果调用者期望处理交错的数据包,应该调用av_interleaved_write_frame()函数而不是当前这个函数。
下面来看看无交错写音视频av_write_frame()函数的实现,位于libavformat/mux.c:
int av_write_frame(AVFormatContext *s, AVPacket *in)
{
AVPacket *pkt = s->internal->pkt;
int ret;
if (!in) {
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;
}
if (in->flags & AV_PKT_FLAG_UNCODED_FRAME) {
pkt = in;
} else {
/* We don't own in, so we have to make sure not to modify it.
* The following avoids copying in's data unnecessarily. */
av_packet_unref(pkt);
pkt->buf = NULL;
pkt->data = in->data;
pkt->size = in->size;
ret = av_packet_copy_props(pkt, in);
if (ret < 0)
return ret;
if (in->buf) {
pkt->buf = av_buffer_ref(in->buf);
if (!pkt->buf) {
ret = AVERROR(ENOMEM);
goto fail;
}
}
}
ret = write_packets_common(s, pkt, 0/*non-interleaved*/);
fail:
av_packet_unref(pkt);
return ret;
}
再看看有交错写音视频帧av_interleaved_write_frame()函数的实现:
int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt)
{
int ret;
if (pkt) {
ret = write_packets_common(s, pkt, 1/*interleaved*/);
if (ret < 0)
av_packet_unref(pkt);
return ret;
} else {
av_log(s, AV_LOG_TRACE, "av_interleaved_write_frame FLUSH\n");
return interleaved_write_packet(s, NULL, 1/*flush*/);
}
}
经过对比,内部都是调用write_packets_common()函数,区别在于av_write_frame()传参是无交错,而av_interleaved_write_frame()传参是有交错。
2、write_packets_common
接下来看看write_packets_common()函数的实现:
static int write_packets_common(AVFormatContext *s, AVPacket *pkt, int interleaved)
{
AVStream *st;
// 检查pkt的stream_index和codec_type
int ret = check_packet(s, pkt);
if (ret < 0)
return ret;
st = s->streams[pkt->stream_index];
// 检查pkt的pts和dts
ret = prepare_input_packet(s, st, pkt);
if (ret < 0)
return ret;
// 调用s->oformat->check_bitstream()检查码流
ret = check_bitstream(s, st, pkt);
if (ret < 0)
return ret;
if (st->internal->bsfc) {
// 经过bitstream filter处理再写数据包
return write_packets_from_bsfs(s, st, pkt, interleaved);
} else {
// 通用方式写数据包
return write_packet_common(s, st, pkt, interleaved);
}
}
由源码可知,主要是分4个步骤处理:
- 检查pkt的stream_index和codec_type;
- 检查pkt的pts和dts;
- 调用s->oformat->check_bitstream()检查码流;
- 调用write_packets_from_bsfs或write_packet_common写数据包;
3、write_packet_common
write_packets_from_bsfs()函数主要是经过bitstream filter处理,比如h264要处理startcode起始码。我们主要探讨write_packet_common()函数的实现:
static int write_packet_common(AVFormatContext *s, AVStream *st, AVPacket *pkt, int interleaved)
{
int ret;
// 猜测pkt数据包的时长
guess_pkt_duration(s, st, pkt);
#if FF_API_COMPUTE_PKT_FIELDS2 && FF_API_LAVF_AVCTX
if ((ret = compute_muxer_pkt_fields(s, st, pkt)) < 0 && !(s->oformat->flags & AVFMT_NOTIMESTAMPS))
return ret;
#endif
if (interleaved) {
// 有交错写数据包
if (pkt->dts == AV_NOPTS_VALUE && !(s->oformat->flags & AVFMT_NOTIMESTAMPS))
return AVERROR(EINVAL);
return interleaved_write_packet(s, pkt, 0);
} else {
// 无交错写数据包
return write_packet(s, pkt);
}
}
由此可见,主要根据i