前言
前文讲到Android音视频开发框架中的上半段:音视频的创建,编码,保存,这个属于音视频资源生产端的过程。在消费端,还需要经历读取,解码,播放这三个节点。
音视频读取
在前文中,我们可以打通从摄像头+麦克风-编码数据-保存文件这个过程,假如一切顺利,那么可以在磁盘中保存一个MP4文件。但是想要消费这段影片,首先要做的就是提取文件里的编码过的音频和视频信息。这个工作主要依赖于MediaExtractor类。
MediaExtractor的主要方法如下:
1 | scss复制代码// 设置数据源 |
我们可以把MediaExtrtactor看作是MediaMuxer的逆过程,后者是把音频视频封装写入文件,前者是读取文件,解封装获取独立的音频和视频。
音频和视频分别是独立线程编解码的,那么读取自然在分在两个线程中分别读取互不干扰。而且由于操作的相似性,我们可以对它的操作进行一定的封装:
1 | kotlin复制代码class MExtractor(filePath:String) { |
以上基本上就是MediaExtractor的全部了,他往往需要配合其他的组件使用。
音视频解码
有了MediaExtractor的帮助,我们已经可以 从文件中获取数据源,接着我们还是使用异步模式来开启解码过程
视频
1 | kotlin复制代码private val videoHandlerThread: HandlerThread = HandlerThread("video-thread").apply { start() } |
对于视频的解码过程,输出端我们仍然可以使用Surface来简化我们的输出操作,MediaCodec提供了直接输出数据到Surface的过程,因此我们把播放端的SurfaceView或者TextureView中的surface传入进来,那么数据就可以直接打通了。
音频
音频的解码过程和视频解码大差不差
1 | kotlin复制代码private val audioHandlerThread: HandlerThread = HandlerThread("audio-thread").apply { start() } |
音视频播放
音视频播放其实是完全不同的路径,视频播放依赖TextureView等的view展示,而音频播放则是依赖音频设备。
对于视频而言,我们需要在UI中插入TextureView(SurfaceView也一样),然后在TextureView中设置SurfaceTextureListener,等待SUrface的创建成功,接着把SUrface传入解码器
1 | kotlin复制代码dataBinding.textureview.surfaceTextureListener = object :SurfaceTextureListener{ |
这样,解码的视频帧就可以显示在textureView上了。
但是音频的播放过程则完全在后台进行
1 | scss复制代码// 创建音频播放设备 |
以上就是音频播放设备的使用方式。
你以为这样就结束了么?天真。
如果按照正常操作视频的解码速度会很快,你会发现视频像走马灯一样播放完了,音频还在播放,因此我们需要对音视频进行同步。
音视频同步
由于每一帧音频或者视频数据都有PTS,也就是说已经设定好了这一帧数据应该播放的时间点,而音视频同步要做的就是,当解码出来的帧的时间戳还没到播放的时间节点时,我们需要等待,一直等到播放的时间节点到来。
音视频同步的方法不止一种,我选择大家比较容易理解的一种来描述:选择一条独立的时间轴,每次音频或者视频解码出来之后的时间戳与独立时间轴的当前时间戳进行比较,如果大于当前时间戳,表示该帧数据还没有到展示的时候,需要等待,否则就直接展示。
如何实现呢?比较简单,在开始解码时的时间设为独立时间轴的起点startPresentationTimeUs,后续的解码回调中和这个时间起点进行比较即可
1 | kotlin复制代码// 开始解码时调用,并记录一下时间起点 |
这就实现了一个简单的音视频同步的逻辑了,我相信理解起来没有太大的难度。当然,如果系统有支持的方法我们自然不必亲自实现同步逻辑,在Android体系中,有MediaSync可以帮助我们实现音视频播放同步的逻辑,使用起来不算太复杂,不过它也同样深度嵌套到音视频的解码过程中去了,这个留给大家去熟悉吧。
除了音视频同步这个重要内容外,其实还有播放/暂停,这个过程也会影响到音视频同步的逻辑,因为播放暂停时,每帧数据的显示时间戳PTS不会变,但是我们建立的独立时间轴的时间会继续流逝,等恢复之后,在比较时间戳就完全错误了,因此我们需要在暂停和恢复时记录一下暂停的时长,然后在比较时减去这段时间,又或者直接把独立时间轴的起点时间往后挪动暂停时长即可。
此外,播放过程中获取预览图,播放进度条等内容也是基本内容,我认为它们并没有比音视频同步更难以理解,因此不一一说明了。
Android当然有支持较好的播放器可以同时播放音频和视频,而且还能自动帮助我们解码数据,这些我相信大家是更了解的。
总结
到此,Android的音视频开发框架基本描述完整了,它涵盖了音视频的创建,编码,保存,提取,解码,播放的全过程,当然每个部分只是囫囵吞枣的介绍,代码也不是完整,其实这里里面很多内容都可以单列一章来讲,细节颇多,不过我认为作为一个简介性质的文章深度是够了的,主要侧重于介绍概念和使用方法。后续深入研究还靠自己,本身的水平也有限。
本文转载自: 掘金