【学习】从零开始的Android音视频开发(9)——NuPlayer

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接口

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值