本文所使用的是FFmpeg n4.4的源码,所有分析均来自博主瞎猜,如果有误,欢迎批评指正。
/**
* 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.
*/
int av_write_frame(AVFormatContext *s, AVPacket *pkt);
直接把AVPacket写到格式封装器中,输入的pkt必须有正确的时间戳和stream_index。写入的过程中不会有经过buffer和重排序。一些封装器可能在实现的内部对pkt增加引用。
/**
* Write a packet to an output media file ensuring correct interleaving.
*
* This function will buffer the packets internally as needed to make sure the
* packets in the output file are properly interleaved in the order of
* increasing dts. Callers doing their own interleaving should call
* av_write_frame() instead of this function.
*
* Using this function instead of av_write_frame() can give muxers advance
* knowledge of future packets, improving e.g. the behaviour of the mp4
* muxer for VFR content in fragmenting mode.
*
* @return 0 on success, a negative AVERROR on error. Libavformat will always
* take care of freeing the packet, even if this function fails.
*
* @see av_write_frame(), AVFormatContext.max_interleave_delta
*/
int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt);
这个函数的作用同上面的函数一样,都是写入pkt,不同之处在于调用av_interleaved_write_frame函数会写入buffer,然后重新根据时间戳设置写入的pkt顺序。
简单来说,就是调用write_packets_common函数,设置interleaved参数为0
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.
* Copying side data is unavoidable as a bitstream filter
* may change it, e.g. free it on errors. */
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;
}
}
}
// av_interleaved_write_frame和av_write_frame函数都会调用这个函数,具体的区别在write_packets_common中实现
ret = write_packets_common(s, pkt, 0/*non-interleaved*/);
fail:
// Uncoded frames using the noninterleaved codepath are also freed here
av_packet_unref(pkt);
return ret;
}
av_interleaved_write_frame 和 av_write_frame源码类似,调用write_packets_common,设置设置interleaved参数为1.
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 {
// interleaved_write_packet刷新,这个函数在write_packet_common中也有调用
av_log(s, AV_LOG_TRACE, "av_interleaved_write_frame FLUSH\n");
return interleaved_write_packet(s, NULL, 1/*flush*/);
}
}
检测pkt,如果有需要,在经过bsf的转化,然后调用write_packet_common函数,进行写操作。
static int write_packets_common(AVFormatContext *s, AVPacket *pkt, int interleaved)
{
AVStream *st;
//检测stream_index和对应的AVStream codec_type是否为AVMEDIA_TYPE_ATTACHMENT
int ret = check_packet(s, pkt);
if (ret < 0)
return ret;
st = s->streams[pkt->stream_index];
//主要是版本兼容
ret = prepare_input_packet(s, st, pkt);
if (ret < 0)
return ret;
//检测oformat是否有实现check_bitstream。
//主要是用来看编码是数据是否符合要求,比如AVCC和ANNEXB
ret = check_bitstream(s, st, pkt);
if (ret < 0)
return ret;
//不符合要求,去要对packet进行操作
if (st->internal->bsfc) {
return write_packets_from_bsfs(s, st, pkt, interleaved);
} else {
//重要函数,通过这个函数写AVPacket
return write_packet_common(s, st, pkt, interleaved);
}
}
主要作用就是对编码后的数据进行处理,比如AVCC到ANNEXB格式等,将处理后的AVPacket送到write_packet_common函数。
static int write_packets_from_bsfs(AVFormatContext *s, AVStream *st, AVPacket *pkt, int interleaved)
{
AVBSFContext *bsfc = st->internal->bsfc;
int ret;
if ((ret = av_bsf_send_packet(bsfc, pkt)) < 0) {
av_log(s, AV_LOG_ERROR,
"Failed to send packet to filter %s for stream %d\n",
bsfc->filter->name, st->index);
return ret;
}
do {
ret = av_bsf_receive_packet(bsfc, pkt);
if (ret < 0) {
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return 0;
av_log(s, AV_LOG_ERROR, "Error applying bitstream filters to an output "
"packet for stream #%d: %s\n", st->index, av_err2str(ret));
if (!(s->error_recognition & AV_EF_EXPLODE) && ret != AVERROR(ENOMEM))
continue;
return ret;
}
av_packet_rescale_ts(pkt, bsfc->time_base_out, st->time_base);
//主要是这个函数,在write_packets_common函数中,也有调用,也就是说最终还是要调用write_packet_common
ret = write_packet_common(s, st, pkt, interleaved);
if (ret >= 0 && !interleaved) // a successful write_packet_common already unrefed pkt for interleaved
av_packet_unref(pkt);
} while (ret >= 0);
return ret;
}
根据是否interleaved,分别调用interleaved_write_packet函数和write_packet函数
static int write_packet_common(AVFormatContext *s, AVStream *st, AVPacket *pkt, int interleaved)
{
int ret;
if (s->debug & FF_FDEBUG_TS)
av_log(s, AV_LOG_DEBUG, "%s size:%d dts:%s pts:%s\n", __FUNCTION__,
pkt->size, av_ts2str(pkt->dts), av_ts2str(pkt->pts));
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);
}
}
static int interleaved_write_packet(AVFormatContext *s, AVPacket *pkt, int flush)
{
for (;; ) {
AVPacket opkt;
//写入buffer,然后从buffer中读取最前面的pkt
int ret = interleave_packet(s, &opkt, pkt, flush);
if (ret <= 0)
return ret;
pkt = NULL;
//把获取的pkt写入
ret = write_packet(s, &opkt);
av_packet_unref(&opkt);
if (ret < 0)
return ret;
}
}
static int interleave_packet(AVFormatContext *s, AVPacket *out, AVPacket *in, int flush)
{
// oformat中自己实现了buffer和重排序函数
if (s->oformat->interleave_packet) {
return s->oformat->interleave_packet(s, out, in, flush);
} else
// 公共oformat的buffer,重排序函数,重排序算法暂时就不分析了
return ff_interleave_packet_per_dts(s, out, in, flush);
}
static int write_packet(AVFormatContext *s, AVPacket *pkt)
{
int ret;
// If the timestamp offsetting below is adjusted, adjust
// ff_interleaved_peek similarly.
// 输出偏移调整,output_ts_offset最终是在写pkt的时候发挥作用
if (s->output_ts_offset) {
AVStream *st = s->streams[pkt->stream_index];
int64_t offset = av_rescale_q(s->output_ts_offset, AV_TIME_BASE_Q, st->time_base);
if (pkt->dts != AV_NOPTS_VALUE)
pkt->dts += offset;
if (pkt->pts != AV_NOPTS_VALUE)
pkt->pts += offset;
}
// 根据avoid_negative_ts字段,调整要写入的pts和dts
if (s->avoid_negative_ts > 0) {
AVStream *st = s->streams[pkt->stream_index];
int64_t offset = st->internal->mux_ts_offset;
int64_t ts = s->internal->avoid_negative_ts_use_pts ? pkt->pts : pkt->dts;
if (s->internal->offset == AV_NOPTS_VALUE && ts != AV_NOPTS_VALUE &&
(ts < 0 || s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_MAKE_ZERO)) {
s->internal->offset = -ts;
s->internal->offset_timebase = st->time_base;
}
if (s->internal->offset != AV_NOPTS_VALUE && !offset) {
offset = st->internal->mux_ts_offset =
av_rescale_q_rnd(s->internal->offset,
s->internal->offset_timebase,
st->time_base,
AV_ROUND_UP);
}
if (pkt->dts != AV_NOPTS_VALUE)
pkt->dts += offset;
if (pkt->pts != AV_NOPTS_VALUE)
pkt->pts += offset;
if (s->internal->avoid_negative_ts_use_pts) {
if (pkt->pts != AV_NOPTS_VALUE && pkt->pts < 0) {
av_log(s, AV_LOG_WARNING, "failed to avoid negative "
"pts %s in stream %d.\n"
"Try -avoid_negative_ts 1 as a possible workaround.\n",
av_ts2str(pkt->pts),
pkt->stream_index
);
}
} else {
av_assert2(pkt->dts == AV_NOPTS_VALUE || pkt->dts >= 0 || s->max_interleave_delta > 0);
if (pkt->dts != AV_NOPTS_VALUE && pkt->dts < 0) {
av_log(s, AV_LOG_WARNING,
"Packets poorly interleaved, failed to avoid negative "
"timestamp %s in stream %d.\n"
"Try -max_interleave_delta 0 as a possible workaround.\n",
av_ts2str(pkt->dts),
pkt->stream_index
);
}
}
}
//这里是最主要的实现,一般来说都是编码后的数据,走s->oformat->write_packet,write_packet函数是对应各个封装器的实现。
if ((pkt->flags & AV_PKT_FLAG_UNCODED_FRAME)) {
AVFrame **frame = (AVFrame **)pkt->data;
av_assert0(pkt->size == sizeof(*frame));
ret = s->oformat->write_uncoded_frame(s, pkt->stream_index, frame, 0);
} else {
ret = s->oformat->write_packet(s, pkt);
}
if (s->pb && ret >= 0) {
flush_if_needed(s);
if (s->pb->error < 0)
ret = s->pb->error;
}
if (ret >= 0)
s->streams[pkt->stream_index]->nb_frames++;
return ret;
}