使用ffmpeg做RTMP直播推流,保存为mp4文件,音视频同步原理以及同步方法

相信很多像我一样只要接触过ffmpeg的人,都会对这个强大的音视频库产生很大的兴趣,因为几乎所有的音视频处理都离不开ffmpeg,我们平时自己开发一些小软件时,也可能会用到它。

但是,我们会发现,ffmpeg的使用也分几个层次,最开始的时候,我们完全是糊涂的,这个库怎么使用那么复杂,自己去编译一个android版或者windows版的ffmpeg库,可能折腾个好几天也没有一个好的输出结果。好不容易在网站找到了一个编译的文章,按照他们的方法去一步一步地编译,却发现他们使用的ffmpeg版本都是很老了,因此又去专门找到文章中的版本来编译(因为使用文章 中的编译方法新版的ffmpeg版本根本无法编译通过),好不容易编译通过了,然后写一个helloworld测试程序,发现可以正常调用,于是就非常高兴。可是终于有一天,老板说要增加功能,要在视频中加个图片水印,文字水印什么的,然后你百度,找到很多文章,可是又发现大部分是命令行,哈哈,这个时候,你会发现之前自己千辛万苦编译出来的库根本就不支持上面所说的功能,诶,那可怎么办?这个时候你又会对这个ffmpeg深恶痛决,抱怨这个ffmpeg怎么编译起来这么麻烦,你终于放弃自己去编译的想法,改成在网上找现成的编译好的库来使用。幸运的是,网上确实可以找到现成的,因为电脑的另一端同样有人去研究这个东西。于是你又非常开心,发现寻到宝了,巴不得给分享库的人一个大大的赞,可当你下载好后,放进自己的工程,诶,又是一大包错,一大堆的方法已经不支持了,ffmpeg方法发生了变化,是不是要疯的节奏。

哈哈,说了这么多,因为我也是经历过这样的情况,所以有感而发。好了,言归正传。

例如我们做RTMP的直播推流,我们大概分为几步:

1、摄像头图像的采集

2、麦克风声音的采集

3、图像的压缩

4、声音的压缩

5、为压缩后的packet进行标定pts

6、写入输出

其中12346这几步都很容易,按照ffmpeg中的示例都可以很轻易地做出来,但在第5步中就遇到困难了,这个pts到底该怎么赋值啊,而且经常我们想当然地赋值后,发现视频中的音视频根本无法同步,所以今天就来讲讲ffmpeg中推流(包括保存为文件)音视频如何做到同步的。

我们知道,我们的画面是一幅幅的图像的,一秒内显示多少张画面,那么它的帧率就是多少,比如常见的15帧/秒就代表1秒内要显示15张图片,也就是说每张图片之间的显示间隔为1000/15=66.67毫秒,那么我们在给图像赋pts是如何赋值的呢,首先我们可能想到是用时间来赋值,比如这里的66.7,悲剧,它竟然是一个小数,因为1000除于15根本除不尽啊,那好,于是就想到,为什么不干脆这样来赋值呢1 2 3 4 5 6 7 ....,这个想法是正确的,并且ffmpeg里确实是这样做的,在播放端得到这些画面后,得到他们的pts为1 2 3 4 5 6 7 8 9后,然后根据它的帧率,就可以把这些画面还原到具体的时间上。

很容易地,我们发现从时间到pts实际上是进行了一个转换,而且这个转换不是乱转的,是跟帧率相关的,这个转换系数就是ffmpeg中常常遇到的时间基time_base,它是一个分数,具体的转换过程可以查看一下ffmpeg的例子,就可以加深印象了。

说完视频,再来说说声音,其实也是类似的,声音也许你会好像比较难抽象出帧的概念,其实也是一样的,比如我们常常见到的frame_size实际上就是这个,比如我们在压缩为aac时,要求frame_size就可以理解为一帧声音数据的长度,这一帧声音,播放出来是要一定时间的吧,也就类似于画面中帧与帧之间的时间了,因此给声音写pts也是极其类似的了。

如果你还是想通过看实例的方式来加深对他们的印象的话,可以访问我写的一个结合opencv的ffmpeg直播推流的例子(实现了画面的任易叠加,可以叠加图片,文字,多个摄像头混合等),链接为:https://0x9.me/FSAJV

 

你可能感兴趣的:(使用ffmpeg做RTMP直播推流,保存为mp4文件,音视频同步原理以及同步方法)