NuPlayer基于StagefrightPlayer的基础类构建,利用了底层的ALooper/AHandler
机制来进行异步解码播放,Alooper轮循队列中的消息,把消息推送到AHandler处理,最后通过handleMessage函数回调,做相应的逻辑处理
NuPlayer整体结构
MediaPlayerFactory通过工厂模式创建NuPlayerFactory
,然后通过NuPlayerFactory创建NuPlayerDriver
,接着通过NuPlayerDriver构建一个NuPlayer
,NuPlayer作为播放器,其中设计数据解析、解码、渲染等过程
NuPlayer的构建过程
在上层调用setDataSource函数后,开始到达MediaPlayerService的setDataSource函数,通过getPlayerType
函数获取播放器类型
播放器类型枚举如下
这时再回到MediaPlayerFactory中看看getPlayerType函数
接下来进入一个宏函数,宏的主要作用是替换,提高代码的执行效率,因为其省去了分配和释放栈帧、传参、传返回值等一系列工作
这个宏函数的标示遍历map中存放的播放器工厂类,调用scoreFactory
可以得到播放器的播放能力,如果是NuPlayer,默认有一个0.8的值,返回的ret就是NuPlayerFactory对象,如果得到的值为0.0,就进入getDefaultPlayerType
函数
默认也是NuPlayerFactory
针对NuPlayerFactory不会直接创建NuPlayer,而是在NuPlayerDriver的构造函数中创建一个NuPlayerDriver
在NuPlayerDriver的构造函数中,创建了一个NuPlayer,NuPlayerDriver是对NuPlayer的封装,继承MediaPlayerInterface
接口。通过NuPlayer可以实现播放功能,主要用于通知上面的流程
NuPlayer继承自AHandler
,并且引入了AMessage,通过ALooper来处理消息,比如它的prepareAsync
函数
实际就是发送一个kWhatPrepare
消息。在onMessageReceived
函数中,收到消息并进行处理(该函数太长,仅摘取kWhatPrepare部分分析)
NuPlayer的数据解析模块
解析的模块主要是NuPlayerSource
以及继承它的几个类,如HTTPLiveSource、RTSPSource、GenericSource等
在NuPlayer中调用setDataSourceAsync
函数后
这里会根据不同协议选择不同的Source对象,有了这个Source对象后,发送kWhatSetDataSource
消息(下图同样只是该函数部分截图)
通过具体的Source解析完数据,再把Source强制转换成mSource
给Decoder使用,这时里面就包含了数据相关信息,其中的一个HTTPLiveSource
,主要用于解析HLS协议
当调用HTTPLiveSource的prepareAsync函数时
内部主要构建了一个LiveSession
对象,通过LiveSession内部的connectAsync
函数,创建一个会话
查看onConnect函数
网络请求,开始取播放列表,在取到播放列表后进行回调(下图是回调函数)
上面的代码主要是根据URL返回的M3U文件,获取对应的BandwidthItem
,如果熟悉M3U文件,可以知道M3U文件有一级索引和二级索引,下图是一些举例
其主要作用是动态码率适应,BANDWIDTH
越大,分辨率越高。BINDWIDTH实际上就是带宽
若想获得某个Track的信息,得到视频、音频、字幕相关信息,可以通过LiveSession的getTrackInfo
函数
到此数据解析模块完成,视频源的主要作用都是得到音视频数据信息,最终通过mSource
变量传递给解码器
NuPlayer的解码模块
它引入了NuPlayerDecoderBase
,这是一个基类,真正的解码器逻辑在NuPlayerDecoder.cpp中,它继承了这个基类,下面先查看实例化解码器
上面的代码构造了Decoder
对象,且进行了初始化和构建解码器
由于NuPlayerDecoder继承自NuPlayerDecoderBase
类,所以Configure函数会执行NuPlayerDecoder的Configure
函数,最终回调onConfigure
函数,其位于NuPlayerDecoder.cpp中
从onConfigure函数可以看到,NuPlayerDecoder引入了MediaCodec
作为解码器。通过CreateByType/CreateByComponentName
创建了Codec
对象。
NuPlayer的渲染模块
主要功能
1.将音视频原始数据缓存到队列
2.音频数据消耗播放
3.视频数据消耗显示
4.音视频同步
5.播放器控制
将音视频原始数据缓存到队列,在NuPlayerRenderer.h中,存在一个QueueEntry结构体和两个队列
数据在onQueueBuffer
函数中进行添加
而音频的播放在NuPlayerRenderer
中,会先调用openAudioSink
函数
发送一个kWhatOpenAudioSink
消息,收到消息后进行如下处理
调用onOpenAudioSink
函数
这里打开了AudioSink(音频后端)
。当将音频数据放入音频队列时,会接着调用postDrainAudioQueue_l
函数
上图代码发送了一个kWhatDrainAudioQueue
主要是有一个进行判断的onDrainAudioQueue
函数,判断是否需要重新向AudioSink写入数据
到这里,这个音频播放流程如下:先打开音频后端,然后当向音频队列中发送数据,音频队列同时向音频后端写入数据,以供播放音频
视频显示的话,同样是在视频原始数据进入视频队列后,开始执行postDrainVideoQueue
函数
这里先发送一个kWhatDrainVideoQueue
的消息,然后进行音视频同步,发送消息后会出发调用onDrainVideoQueue
NuPlayer::Renderer
使用的是以视频为基准的同步机制,解码后的音频数据时间戳如果大于视频数据时间戳,直接丢弃音频包,然后直接渲染视频。同步机制主要位于视频缓冲区处理部分的onDrainVideoQueue
和音频缓冲区处理部分的onDrainVideoQueue
中。音视频的渲染都采用类似定时器的机制,只不过视频显示需要依赖于实际解码器,音频播放需要依赖于AudioSink接口