从头用脚分析FFmpeg源码 - av_interleaved_write_frame | av_write_frame
本文所使用的是FFmpeg n4.4的源码,所有分析均来自博主瞎猜,如果有误,欢迎批评指正。
av_write_frame 作用
/* 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增加引用。
av_interleaved_write_frame 作用
/* 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顺序。
av_write_frame 源码
简单来说,就是调用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 hereav_packet_unref(pkt);return ret;
}
av_interleaved_write_frame 源码
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*/);}
}
write_packets_common 源码
检测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_ATTACHMENTint 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和ANNEXBret = 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 {//重要函数,通过这个函数写AVPacketreturn write_packet_common(s, st, pkt, interleaved);}
}
write_packets_from_bsfs 源码
主要作用就是对编码后的数据进行处理,比如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_commonret = write_packet_common(s, st, pkt, interleaved);if (ret >= 0 && !interleaved) // a successful write_packet_common already unrefed pkt for interleavedav_packet_unref(pkt);} while (ret >= 0);return ret;
}
write_packet_common 源码
根据是否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_AVCTXif ((ret = compute_muxer_pkt_fields(s, st, pkt)) < 0 && !(s->oformat->flags & AVFMT_NOTIMESTAMPS))return ret;
#endifif (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);}
}
interleaved_write_packet 源码
static int interleaved_write_packet(AVFormatContext *s, AVPacket *pkt, int flush)
{for (;; ) {AVPacket opkt;//写入buffer,然后从buffer中读取最前面的pktint 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;}
}
interleave_packet 源码
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);
}
write_packet 源码
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和dtsif (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;
}