ffmpeg-muxing原理分析

ffmpeg-muxing原理分析

前言:
源码文件所在路径:/doc/example/muxing.c
muxing产生一个人工合成的音频流和视频流,编码后合成为一个输出文件。输出文件格式由文件后缀确定。(flv,avi,mp4,ts等格式)
例子使用方法:
./muxing 1.avi //会产生一个带音视频同步的视频
生成的AVI视频
muxing流程图

1、简介

muxing.c实现的功能就是凭空造出一个视频文件,完全是无中生有

2、程序流程

2、1 avformat_alloc_output_context2

avformat_alloc_output_context2(AVFormatContext **ctx, AVOutputFormat *oformat,const char *format_name, const char *filename); 
该函数通常是第一个调用的函数,函数可以初始化一个用于输出的AVFormatContext结构体

2、2 add_stream添加视频和音频流

这里做的操作其实就是指定了视频编码器和音频编码器的参数

2、3 open_video/open_audio

这里执行的操作就是打开编码器和分配一些需要的存储空间

2、5 写入视频头av_write_header(oc);

根据输出文件名,选择合适的封装和编解码器

2、6 写入音视频数据

1、写视频write_video_frame

1、1 get_video_frame()//动态生成一帧视频数据frame yuv420格式

    2	static AVFrame *get_video_frame(OutputStream *ost)  
    3	{  
    4	    AVCodecContext *c = ost->enc;  
    5	  
    6	    /* check if we want to generate more frames */ 
    7	//比较当前时间是否是否大于设定时间 next_pts*time_base > STREAM_DURATION*1  
    8	//相当于只产生10s的视频
    9	    if (av_compare_ts(ost->next_pts, c->time_base,  
    10	                      STREAM_DURATION, (AVRational){ 1, 1 }) >= 0)  
    11	        return NULL;  
    12	  
    13	    /* when we pass a frame to the encoder, it may keep a reference to it 
    14	     * internally; make sure we do not overwrite it here */
    15	//确保AVFrame是可写的,尽可能避免数据的复制。如果AVFrame不是可写的,将分配新的buffer和复制数据  
    16	    if (av_frame_make_writable(ost->frame) < 0)  
    17	        exit(1);  
    18	  
    19	    if (c->pix_fmt != AV_PIX_FMT_YUV420P) {  
    20	        /* as we only generate a YUV420P picture, we must convert it 
    21	         * to the codec pixel format if needed */  
    22	        if (!ost->sws_ctx) {  
    23	            ost->sws_ctx = sws_getContext(c->width, c->height,  
    24	                                          AV_PIX_FMT_YUV420P,  
    25	                                          c->width, c->height,  
    26	                                          c->pix_fmt,  
    27	                                          SCALE_FLAGS, NULL, NULL, NULL);  
    28	            if (!ost->sws_ctx) {  
    29	                fprintf(stderr,  
    30	                        "Could not initialize the conversion context\n");  
    31	                exit(1);  
    32	            }  
    33	        }  
    34	//格式缩放重采样
    35	        fill_yuv_image(ost->tmp_frame, ost->next_pts, c->width, c->height);  
    36	        sws_scale(ost->sws_ctx, (const uint8_t * const *) ost->tmp_frame->data,  
    37	                  ost->tmp_frame->linesize, 0, c->height, ost->frame->data,  
    38	                  ost->frame->linesize);  
    39	    } else {  
    40	//根据next_pts 参数不一样,动态生成不同画面的frame 
    41	        fill_yuv_image(ost->frame, ost->next_pts, c->width, c->height);  
    42	    }  
    43	  
    44	    ost->frame->pts = ost->next_pts++;  
    45	  //pts 自增 
    46	    return ost->frame;  
    47	}   

1、2 avcodec_encode_video2()将frame数据编码成pkt编码后的数据

1、3 write_frame 将数据写入封装器

    1.	static int write_frame(AVFormatContext *fmt_ctx, const AVRational *time_base, AVStream *st, AVPacket *pkt)  
    2.	{  
    3.	    /* rescale output packet timestamp values from codec to stream timebase */  
    4.	//编码器时间转换,将时间戳由编码器时基AVCodecContext.time_base 转化为流的时基AVStream.time_base
    5.	    av_packet_rescale_ts(pkt, *time_base, st->time_base);  
    6.	    pkt->stream_index = st->index;  
    7.	  
    8.	    /* Write the compressed frame to the media file. */  
    9.	    log_packet(fmt_ctx, pkt);   
    10.	//数据写入封装(缓存几帧自动识别dts写入)
    11.	    return av_interleaved_write_frame(fmt_ctx, pkt);  
    12.	}  

2、写音频write_audio_frame

2、1 get_audio_frame 获取音频帧(原理同上)

    1.	static AVFrame *get_audio_frame(OutputStream *ost)  
    2.	{  
    3.	    AVFrame *frame = ost->tmp_frame;  
    4.	    int j, i, v;  
    5.	    int16_t *q = (int16_t*)frame->data[0];  
    6.	  
    7.	    /* check if we want to generate more frames */  
    8.	//使用avframe中的pts以及时基来计算生成帧的时间,视频步进1/25 音频步进1024/44100
    9.	    if (av_compare_ts(ost->next_pts, ost->enc->time_base,  
    10.	                      STREAM_DURATION, (AVRational){ 1, 1 }) >= 0)  
    11.	        return NULL;  
    12.	  
    13.	    for (j = 0; j <frame->nb_samples; j++) {  
    14.	        v = (int)(sin(ost->t) * 10000);  
    15.	        for (i = 0; i < ost->enc->channels; i++)  
    16.	            *q++ = v;  
    17.	        ost->t     += ost->tincr;  
    18.	        ost->tincr += ost->tincr2;  
    19.	    }  
    20.	  
    21.	    frame->pts = ost->next_pts;  
    22.	    ost->next_pts  += frame->nb_samples;  
    23.	  //音频pts自增
    24.	    return frame;  
    25.	}  

2、2 音频缩放

    	1.     	if (frame) {  
        2.	      /* convert samples from native format to destination codec format, using the resampler */  
        3.	          /* compute destination number of samples */  
        4.	          dst_nb_samples = av_rescale_rnd(swr_get_delay(ost->swr_ctx, c->sample_rate) + frame->nb_samples,  
        5.	                                          c->sample_rate, c->sample_rate, AV_ROUND_UP);  
        6.	          av_assert0(dst_nb_samples == frame->nb_samples);  
        7.	  
        8.	      /* when we pass a frame to the encoder, it may keep a reference to it 
        9.	       * internally; 
        10.	       * make sure we do not overwrite it here 
        11.	       */  
        12.	      ret = av_frame_make_writable(ost->frame);  
        13.	      if (ret < 0)  
        14.	          exit(1);  
        15.	  
        16.	      /* convert to destination format */  
        17.	//音频重采样
        18.	      ret = swr_convert(ost->swr_ctx,  
        19.	                        ost->frame->data, dst_nb_samples,  
        20.	                        (const uint8_t **)frame->data, frame->nb_samples);  
        21.	      if (ret < 0) {  
        22.	          fprintf(stderr, "Error while converting\n");  
        23.	          exit(1);  
        24.	      }  
        25.	      frame = ost->frame;  
        26.	  //时基转换 从1/采样率 –> 流时基
        27.	      frame->pts = av_rescale_q(ost->samples_count, (AVRational){1, c->sample_rate}, c->time_base);  
        28.	      ost->samples_count += dst_nb_samples;  
        29.	  }  

2、3 avcodec_encode_audio2编码音频帧

2、4 写音频编码数据到封装write_frame (原理同上)

2、7 写入视频尾av_write_trailer(oc);

2、8 释放资源

    1.	/* Close each codec. */  
    2.	 if (have_video)  
    3.	      close_stream(oc, &video_st);  
    4.	 if (have_audio)  
    5.	      close_stream(oc, &audio_st);  
    6.	  
    7.	 if (!(fmt->flags & AVFMT_NOFILE))  
    8.	   /* Close the output file. */  
    9.	    avio_closep(&oc->pb);  
    10.	  
    11.	/* free the stream */  
    12.	    avformat_free_context(oc);

3、 音频时间戳pts,dts,duration变化

1、 Pts,dts来源与变化

1、1 frame.pts 的生成

write_audio_frame->get_audio_frame()

frame.pts从0开始,每次递增nb_samples;

1.	frame->pts = ost->next_pts;  
2.	ost->next_pts  += frame->nb_samples; 

1、2 frame.pts转化

在编码之前,需要将frame.pts从1/sample_rate时基转成编码器时基(一般1/sample_rate与编码时基一致)。

frame->pts = av_rescale_q(ost->samples_count, (AVRational){1, c->sample_rate}, c->time_base); 

在编码后pkt.pts、pkt.dts使用AVCodecContext->time_base为单位,需要调用该接口,将时基转换为流时基st->time_base(在本例子中初始化ost->st->time_base = (AVRational){ 1, c->sample_rate },根据不同容器情况来看)

3.	av_packet_rescale_ts(pkt, *time_base, st->time_base);  

再将数据通过av_interleaved_write_frame写入到容器中。

2、 pkt的pts,dts,duration来源

write_audio_frame->avcodec_encode_audio2(AVPacket *avpkt,const AVFrame *frame,);
4if (avpkt->pts == AV_NOPTS_VALUE)  
5、	    avpkt->pts = frame->pts; //将frame的pts,dts赋值给pkt的pts,dts  
6if (!avpkt->duration)  		//	pkt的duration获取nb_samples(AAC 1024)
7、	  avpkt->duration = ff_samples_to_time_base(avctx, frame->nb_samples);  
8、	avpkt->dts = avpkt->pts;  

对于MP4来说,avcodec_encode_audio2后的音频pkt.pts会自动生成以1024为递增的pts,转换成流时基st->time_base对应的增量是1024;(1/44100)(1/44100)

对于FLV来说,avcodec_encode_audio2后的音频pkt.pts会自动生成以1152为递增的pts,转换成流时基st->time_base对应的增量是40 (1/44100)(1/1000)

不同音频编码模式,nb_sample大小不一样参考链接:
https://blog.csdn.net/byxdaz/article/details/80713402

3、关于容器的流time_base设置

参考链接:
https://blog.csdn.net/leixiaohua1020/article/details/44116215
avformat_write_header()用于写视频文件头
(1)调用init_muxer()初始化复用器
init_muxer()代码很长,但是它所做的工作比较简单,可以概括成两个字:检查。函数的流程可以概括成以下几步:
(1)将传入的AVDictionary形式的选项设置到AVFormatContext
(2)遍历AVFormatContext中的每个AVStream,并作如下检查:
a)AVStream的time_base是否正确设置。如果发现AVStream的time_base没有设置,则会调用avpriv_set_pts_info()进行设置。
b)对于音频,检查采样率设置是否正确;对于视频,检查宽、高、宽高比。
c)其他一些检查,不再详述。

(2)调用AVOutputFormat的write_header()

avformat_write_header()中最关键的地方就是调用了AVOutputFormat的write_header()。write_header()是AVOutputFormat中的一个函数指针,指向写文件头的函数。不同的AVOutputFormat有不同的write_header()的实现方法。在这里我们举例子看一下FLV封装格式对应的AVOutputFormat,它的定义位于libavformat\flvenc.c,如下所示。

1.	AVOutputFormat ff_flv_muxer = {  
2.	    .name           = "flv",  
3.	    .long_name      = NULL_IF_CONFIG_SMALL("FLV (Flash Video)"),  
4.	    .mime_type      = "video/x-flv",  
5.	    .extensions     = "flv",  
6.	    .priv_data_size = sizeof(FLVContext),  
7.	    .audio_codec    = CONFIG_LIBMP3LAME ? AV_CODEC_ID_MP3 : AV_CODEC_ID_ADPCM_SWF,  
8.	    .video_codec    = AV_CODEC_ID_FLV1,  
9.	    .write_header   = flv_write_header,  
10.	    .write_packet   = flv_write_packet,  
11.	    .write_trailer  = flv_write_trailer,  
12.	    .codec_tag      = (const AVCodecTag* const []) {  
13.	                          flv_video_codec_ids, flv_audio_codec_ids, 0  
14.	                      },  
15.	    .flags          = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS |  
16.	                      AVFMT_TS_NONSTRICT,  
17.	};  

其中flv_write_header()函数下

18.	avpriv_set_pts_info(s->streams[i], 32, 1, 1000); /* 32 bit pts in ms */  
19.

修改了流time_base的时基为1/1000;所以FLV格式会发现在muxing.c的主函数下,
avformat_write_header前后打印的st->time_base不一样,初始化为1/sample_rate,在设置头信息后,变成了1/1000.

4、 视频时间戳pts,dts,duration变化

1、Pts,dts来源与变化

1、1 frame.pts 的生成

write_video_frame->get_video_frame ()

frame.pts从0开始,每次递增1;

1 . ost->frame->pts = ost->next_pts++; 

1、2 frame.pts转化

在编码后pkt.pts、pkt.dts使用AVCodecContext->time_base为单位,需要调用该接口,将时基转换为流时基st->time_base(在本例子中ost->st->time_base = (AVRational){ 1, c->sample_rate },根据不同容器情况来看)

2.	av_packet_rescale_ts(pkt, *time_base, st->time_base);  

再将数据通过av_interleaved_write_frame写入到容器中。

2、 pkt的pts,dts,duration来源

write_video_frame->avcodec_encode_video2 (AVPacket *avpkt,const AVFrame *frame,);
9if (!*got_packet_ptr)
10、	        avpkt->size = 0;
11else if (!(avctx->codec->capabilities & AV_CODEC_CAP_DELAY))
12、	      avpkt->pts = avpkt->dts = frame->pts;  

对于MP4来说,avcodec_encode_video2后的音频pkt.pts会自动生成以1为递增的pts,生成的pts顺序打乱了,(4,3,1,2),转时基后的pak.pts为pts*512
转换成流时基st->time_base对应的增量是512;(1/25)(1/12800)

对于FLV来说,avcodec_encode_video2后的音频pkt.pts会自动生成以1为递增的pts,转换成流时基st->time_base对应的增量是40 (1/25)(1/1000)

注意:printf(“frame.pts=%s\n”,av_ts2str(frame.pts));某段时间会出段错误

5、附件

1、mp4 的pts、time_base信息打印

1.	86 pts:18432 pts_time:0.417959 dts:18432 dts_time:0.417959 duration:1024 duration_time:0.02322 stream_index:1  
2.	  87 --------video-    pkt.pts=NOPTS--------------  
3.	  88 -----   st.num=1 st.den=12800  codec.num=1 codec.den=25  got_packet=0-----------  
4.	  89 --------- audio    pkt.pts=19456--------------  
5.	  90 ------ost->samples_count =21504  audio frame_size:=1024  st.num=1 st.den=44100  codec.num=1 codec.den=44100  -----------  
6.	  91 pts:19456 pts_time:0.441179 dts:19456 dts_time:0.441179 duration:1024 duration_time:0.02322 stream_index:1  
7.	  92 --------- audio    pkt.pts=20480--------------  
8.	  93 ------ost->samples_count =22528  audio frame_size:=1024  st.num=1 st.den=44100  codec.num=1 codec.den=44100  -----------  
9.	  94 pts:20480 pts_time:0.464399 dts:20480 dts_time:0.464399 duration:1024 duration_time:0.02322 stream_index:1  
10.	  95 --------video-    pkt.pts=0--------------  
11.	  96 -----   st.num=1 st.den=12800  codec.num=1 codec.den=25  got_packet=1-----------  
12.	  97 pts:0 pts_time:0 dts:-1024 dts_time:-0.08 duration:0 duration_time:0 stream_index:0  
13.	  98 --------- audio    pkt.pts=21504--------------  
14.	  99 ------ost->samples_count =23552  audio frame_size:=1024  st.num=1 st.den=44100  codec.num=1 codec.den=44100  -----------  
15.	 100 pts:21504 pts_time:0.487619 dts:21504 dts_time:0.487619 duration:1024 duration_time:0.02322 stream_index:1  
16.	 101 --------- audio    pkt.pts=22528--------------  
17.	 102 ------ost->samples_count =24576  audio frame_size:=1024  st.num=1 st.den=44100  codec.num=1 codec.den=44100  -----------  
18.	 103 pts:22528 pts_time:0.510839 dts:22528 dts_time:0.510839 duration:1024 duration_time:0.02322 stream_index:1  
19.	 104 --------video-    pkt.pts=4--------------  
20.	 105 -----   st.num=1 st.den=12800  codec.num=1 codec.den=25  got_packet=1-----------  
21.	 106 pts:2048 pts_time:0.16 dts:-512 dts_time:-0.04 duration:0 duration_time:0 stream_index:0  
22.	 107 --------- audio    pkt.pts=23552--------------  
23.	 108 ------ost->samples_count =25600  audio frame_size:=1024  st.num=1 st.den=44100  codec.num=1 codec.den=44100  -----------  
24.	 109 pts:23552 pts_time:0.534059 dts:23552 dts_time:0.534059 duration:1024 duration_time:0.02322 stream_index:1  
25.	 110 --------video-    pkt.pts=2--------------  
26.	 111 -----   st.num=1 st.den=12800  codec.num=1 codec.den=25  got_packet=1-----------

2、flv 的pts、time_base信息打印

1.	29 ------ost->samples_count =6912  audio frame_size:=1152  st.num=1 st.den=1000  codec.num=1 codec.den=44100  -----------  
2.	  30 --------video-    pkt.pts=4--------------  
3.	  31 -----   st.num=1 st.den=1000  codec.num=1 codec.den=25  got_packet=1-----------  
4.	  32 pts:160 pts_time:0.16 dts:160 dts_time:0.16 duration:0 duration_time:0 stream_index:0  
5.	  33 --------- audio    pkt.pts=2351--------------  
6.	  34 ------ost->samples_count =8064  audio frame_size:=1152  st.num=1 st.den=1000  codec.num=1 codec.den=44100  -----------  
7.	  35 pts:53 pts_time:0.053 dts:53 dts_time:0.053 duration:26 duration_time:0.026 stream_index:1  
8.	  36 --------video-    pkt.pts=5--------------  
9.	  37 -----   st.num=1 st.den=1000  codec.num=1 codec.den=25  got_packet=1-----------  
10.	  38 pts:200 pts_time:0.2 dts:200 dts_time:0.2 duration:0 duration_time:0 stream_index:0  
11.	  39 --------- audio    pkt.pts=3503--------------  
12.	  40 ------ost->samples_count =9216  audio frame_size:=1152  st.num=1 st.den=1000  codec.num=1 codec.den=44100  -----------  
13.	  41 pts:79 pts_time:0.079 dts:79 dts_time:0.079 duration:26 duration_time:0.026 stream_index:1  
14.	  42 --------- audio    pkt.pts=4655--------------  
15.	  43 ------ost->samples_count =10368  audio frame_size:=1152  st.num=1 st.den=1000  codec.num=1 codec.den=44100  -----------  
16.	  44 pts:106 pts_time:0.106 dts:106 dts_time:0.106 duration:26 duration_time:0.026 stream_index:1  
17.	  45 --------video-    pkt.pts=6--------------  
18.	  46 -----   st.num=1 st.den=1000  codec.num=1 codec.den=25  got_packet=1-----------  
19.	  47 pts:240 pts_time:0.24 dts:240 dts_time:0.24 duration:0 duration_time:0 stream_index:0  
20.	  48 --------- audio    pkt.pts=5807--------------  
21.	  49 ------ost->samples_count =11520  audio frame_size:=1152  st.num=1 st.den=1000  codec.num=1 codec.den=44100  -----------  
22.	  50 pts:132 pts_time:0.132 dts:132 dts_time:0.132 duration:26 duration_time:0.026 stream_index:1  
23.	  51 --------video-    pkt.pts=7--------------  
24.	  52 -----   st.num=1 st.den=1000  codec.num=1 codec.den=25  got_packet=1-----------  
25.	  53 pts:280 pts_time:0.28 dts:280 dts_time:0.28 duration:0 duration_time:0 stream_index:0  
26.	  54 --------- audio    pkt.pts=6959--------------  
27.	  55 ------ost->samples_count =12672  audio frame_size:=1152  st.num=1 st.den=1000  codec.num=1 codec.den=44100  -----------  
28.	  56 pts:158 pts_time:0.158 dts:158 dts_time:0.158 duration:26 duration_time:0.026 stream_index:1 

6、参考链接:

参考链接1:https://blog.csdn.net/lmhuanying1012/article/details/78369043

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值