ffmpeg的 Mux 主要分为三步操作:
avformat_write_header: 写文件头。
av_write_frame/av_interleaved_write_frame: 写packet。
av_write_trailer: 写文件尾。
av_write_frame()用于输出一帧视音频数据,它的声明位于libavformat\avformat.h,直接将包写进Mux,没有缓存和重新排序,一切都需要用户自己设置。
av_interleaved_write_frame将对 packet 进行缓存和 pts 检查,这是区别于 av_write_frame 的地方。
av_write_frame
从源代码可以看出,av_write_frame()主要完成了以下几步工作:
(1)调用check_packet()做一些简单的检测。
从代码中可以看出,check_packet()的功能比较简单:首先检查一下输入的AVPacket是否为空,如果为空,则是直接返回;然后检查一下AVPacket的stream_index(标记了该AVPacket所属的AVStream)设置是否正常,如果为负数或者大于AVStream的个数,则返回错误信息;
(2)调用compute_pkt_fields2()设置AVPacket的一些属性值。
从代码中可以看出,compute_pkt_fields2()主要有两方面的功能:一方面用于计算AVPacket的duration, dts等信息;另一方面用于检查pts、dts这些参数的合理性(例如PTS是否一定大于DTS)。
(3)调用write_packet()写入数据。
write_packet()函数最关键的地方就是调用了AVOutputFormat中写入数据的方法。如果AVPacket中的flag标记中包含AV_PKT_FLAG_UNCODED_FRAME,就会调用AVOutputFormat的write_uncoded_frame()函数;如果不包含那个标记,就会调用write_packet()函数。write_packet()实际上是一个函数指针,指向特定的AVOutputFormat中的实现函数。例如,我们看一下FLV对应的AVOutputFormat,位于libavformat\flvenc.c。
av_interleaved_write_frame
intav_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt)
{
int ret, flush = 0, i;
//对包预处理
ret = prepare_input_packet(s, pkt);
if (ret < 0)
goto fail;
//bitstreamfilters (过滤器,也是区别于av_write_frame 的地方)
if (pkt) {
AVStream *st = s->streams[pkt->stream_index];
if (s->oformat->check_bitstream) {
if (!st->internal->bitstream_checked) {
if ((ret =s->oformat->check_bitstream(s, pkt)) < 0)
goto fail;
else if (ret == 1)
st->internal->bitstream_checked = 1;
}
}
for (i = 0; i < st->internal->nb_bsfcs; i++) {
AVBSFContext *ctx = st->internal->bsfcs[i];
if (i > 0) {
AVBSFContext* prev_ctx =st->internal->bsfcs[i - 1];
if(prev_ctx->par_out->extradata_size != ctx->par_in->extradata_size){
if ((ret =avcodec_parameters_copy(ctx->par_in, prev_ctx->par_out)) < 0)
goto fail;
}
}
// TODO: when any bitstream filter requires flushing at EOF, we'll needto
// flush each stream's BSF chain on write_trailer.
if ((ret = av_bsf_send_packet(ctx, pkt)) < 0) {
av_log(ctx, AV_LOG_ERROR,
"Failed to sendpacket to filter %s for stream %d",
ctx->filter->name,pkt->stream_index);
goto fail;
}
// TODO: when any automatically-added bitstream filter is generatingmultiple
// output packets for a single input one, we'll need to call this in aloop
// and write each output packet.
if ((ret = av_bsf_receive_packet(ctx, pkt)) < 0) {
if (ret == AVERROR(EAGAIN) ||ret == AVERROR_EOF)
return 0;
av_log(ctx, AV_LOG_ERROR,
"Failed to sendpacket to filter %s for stream %d",
ctx->filter->name,pkt->stream_index);
goto fail;
}
if (i == st->internal->nb_bsfcs - 1) {
if(ctx->par_out->extradata_size != st->codecpar->extradata_size) {
if ((ret = avcodec_parameters_copy(st->codecpar,ctx->par_out)) < 0)
goto fail;
}
}
}
if (s->debug & FF_FDEBUG_TS)
av_log(s, AV_LOG_TRACE, "av_interleaved_write_frame size:%d dts:%spts:%s\n",
pkt->size,av_ts2str(pkt->dts), av_ts2str(pkt->pts));
#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))
goto fail;
#endif
if (pkt->dts == AV_NOPTS_VALUE && !(s->oformat->flags& AVFMT_NOTIMESTAMPS)) {
ret = AVERROR(EINVAL);
goto fail;
}
}else {
av_log(s, AV_LOG_TRACE, "av_interleaved_write_frame FLUSH\n");
flush = 1;
}
//写包
for (;; ) {
AVPacket opkt;
//此处也是与av_write_frame的区别
int ret = interleave_packet(s, &opkt, pkt, flush);
if (pkt) {
memset(pkt, 0, sizeof(*pkt));
av_init_packet(pkt);
pkt = NULL;
}
if (ret <= 0) //FIXME cleanup needed for ret<0 ?
return ret;
//写入
ret = write_packet(s, &opkt);
if (ret >= 0)
s->streams[opkt.stream_index]->nb_frames++;
av_packet_unref(&opkt);
if (ret < 0)
return ret;
if(s->pb && s->pb->error)
return s->pb->error;
}
fail:
av_packet_unref(pkt);
return ret;
}
prepare_input_packet 函数与av_write_frame 的一致,主要是 bitstream filters 和 interleave_packet 的区别:
static intinterleave_packet(AVFormatContext *s, AVPacket *out, AVPacket *in, int flush)
{
if (s->oformat->interleave_packet) {
int ret = s->oformat->interleave_packet(s, out, in, flush);
if (in)
av_packet_unref(in);
return ret;
}else
return ff_interleave_packet_per_dts(s, out, in, flush);
}