Android2.3时引入流媒体框架,而流媒体框架的核心是NuPlayer。Android4.0之后HttpLive和RTSP协议开始使用NuPlayer播放器,Android5.0(L版本)之后本地播放也开始使用NuPlayer播放器。
VideoTrack与AudioTrack指的是Extractor(即demux)的两个通道,从这里输出的分别就是单纯的解复用后的Video和Audio流。再经过Decoder后输出的就是音、视频的输出了:
初始化NuPlayer对象,并进入NuPlayer播放流程,其实NuPlayer实现的是Ahandle机制:
NuPlayer获取DataSource通过URI的前缀判断媒体类型,比如http、rtsp,还是本地播放,然后创建相应的DataSource,走入相应流程。根据URI创建相应的DataSource,再进一步的利用DataSource创建MediaExtractor做A/V分离。
OnMessageReceived是Nuplayer的核心部分,大部分消息的实现均在OnMessageReceived中完成
环境及相应DataSource都准备好以后,上层发送start开始播放流程以后,开始创建解码器
创建解码器
OMX的创建干了两件事,第一,初始化OMXMaster加载第三方解码器;第二,初始化节点数,为分配正在可用节点。
当收到解码器kWhatDrainThisBuffer消息时,NuPlayer会调用到renderBuffer函数,renderBuffer直接调用mRenderer中的queueBuffer函数。
queueBuffer主要发送kWhatQueueBuffer消息,并在接收到kWhatQueueBuffer消息后调用onQueueBuffer函数,函数onQueueBuffer中对于音频和视频数据队列进行push_back和postDrainQueue操作,push_back就是往renderer中的数据队列中存放数据, postDrainQueue就是从renderer的数据队列中取数据,往外送显,因此可以看出renderer中的数据队列实际上只是个中转站,就是为了实现音视频同步。
音视频同步的函数postDrainVideoQueue()(视频early情况)
判断repost的时间是10ms,还是500ms,根据下面分支判断(上一个时间戳序号减去当前时间戳序号) 。mLastAudioBufferDrained - entry.mBufferOrdinal) <= 0,一般情况下这个值均小于0,所以NuPlayer里面一般repost时间为500ms。
delayUs = realTimeUs – nowUs
realTimeUs = (mediaTimeUs - currentPositionUs) + nowUs
currentPositionUs = (nowUs - mAnchorTimeRealUs) + mAnchorTimeMediaUs
==》realTimeUs = mediaTimeUs – mAnchorTimeMediaUs +
mAnchorTimeRealUs
mediaTimeUs为传入的时间, mAnchorTimeMediaUs为音频上一帧的时间戳,mAnchorTimeRealUs为播放的起锚时间戳,前2者相减获取当前播放的时间长度,再加上起锚时间从而得到当前的系统时间戳。
nowUs是当前时间,通过函数GetNowUs()获取,每次重新获取的时间都不一样,若不重新获取,一直沿用前面获取的时间值。
如果现在的时间点比预计要播放的时间点提前了500ms以上,就要等,则延时10ms再发送kWhatPostDrainVideoQueue消息,接收到这个消息后继续走回postDrainVideoQueue函数中,即做了10ms的等待。
音视频同步的函数onDrainVideoQueue()(视频late情况)
Too Late用来标记是否视频数据现在播放的时间比预期播放的时间晚40ms以上,如果超过40ms,说明现在播放这段数据的时间太晚,要是继续播放这段数据就会主观感觉到视频画面滞后,所以丢弃这段视频数据。
简单总结:RTSP发起、终结流媒体,RTP媒体数据载体 、RTCP对RTP进行控制、同步
*framework/av/media
framework/base/media
vendor/hisi/ap/hardware/vcodec
vendor/hisi/thirdparty/vcodec/chipmedia/
/vendor/opensource/ffmpeg/
vendor/thirdparty/videocodec/helix/*