ffmpeg-muxing原理分析
前言:
源码文件所在路径:/doc/example/muxing.c
muxing产生一个人工合成的音频流和视频流,编码后合成为一个输出文件。输出文件格式由文件后缀确定。(flv,avi,mp4,ts等格式)
例子使用方法:
./muxing 1.avi //会产生一个带音视频同步的视频
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,);
4、 if (avpkt->pts == AV_NOPTS_VALUE)
5、 avpkt->pts = frame->pts; //将frame的pts,dts赋值给pkt的pts,dts
6、 if (!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.youkuaiyun.com/byxdaz/article/details/80713402
3、关于容器的流time_base设置
参考链接:
https://blog.youkuaiyun.com/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,);
9、 if (!*got_packet_ptr)
10、 avpkt->size = 0;
11、 else 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.youkuaiyun.com/lmhuanying1012/article/details/78369043