StagefrightPlayer也叫做AwesomePlayer,虽然只是一个已经不再使用的组件
AwesomePlayer构造过程
在StagefrightPlayerFactory
创建了一个StagefrightPlayer后,就开始进入StagefrightPlayer的构造函数
在StagefrightPlayer.h
中有如下声明
可看出StagefrightPlayer中封装了AwesomePlayer
,进而进行播放行为相关操作,接着分析下里面的StagefrightPlayer
可以看出它继承了MediaPlayerInterface
抽象基类,这个抽象基类有很多虚函数
同样以setDataSource
为例,通常当我们负责的模块和别人的模块进行交互时,在模块之间相当于一个黑盒,通过返回对应的状态或者数据可以判断已经发生的事件,进而进行下一个环节。StagefrightPlayer只是一层外壳,真正干活的是AwesomePlayer
在调用MediaPlayerService中的setDataSource函数后会到达StagefrightPlayer中的setDataSource函数
所有执行步骤都会调用mPlayer->setDataSource(xxxx)
,而通过前面的分析我们知道,这个mPlayer被定义成AwesomePlayer,虽然AwesomePlayer中也有一些网络流处理的代码,但通常执行不到这段逻辑,而是使用NuPlayer,接下来到达AwesomePlayer的setDataSource函数
总结:AwesomePlayer主要用于本地播放,匹配不同的文件类型,并用数据解析器处理。mime
表示该资源的媒体类型,当出现常见音视频封装格式的时候,Android播放器如果在Manifest.xml中设置了mime对应的类型,就能被播放器识别
常用的mime类型资源
AwesomePlayer使用MediaExtractor进行数据解析的过程
MediaExtractor
即为数据解析器,查看其源码
总结:针对文件解析的不同格式创建Extractor解析器并解析,创建好解析器后回到AwesomePlayer::setDataSource_l
中,继续执行setDataSource_l(extractor)函数,对新建的解析器做处理,实质是做视频的A(音频)/V(视频)分离,解析器的类型在AwesomePlayer::setDataSource_l中创建的时候就定下来了
mVideoTrack
和mAudioTrack
作为创建AwesomePlayer的成员函数,其类型为MPEG4Source,继承自MediaSource
以上过程完成了音视频数据的分离,也就是demux(解复用),对音频和视频资源分开处理,顺序:MediaPlayerService->StagefrighterPlayer->AwesomePlayer->MPEG4Extractor->MPEG4Source
,音频同理
AudioPlayer
为AwesomePlayer的成员,AudioPlayer通过callback
来驱动数据的获取,AwesomePlayer则通过videoevent
来驱动。数据的获取都抽象成mSource->read
来完成,且read内部把parse和decode绑在一起。StageFright A/V同步部分,Audio完全由callback驱动数据流,注意视频部分在onViewEvent里会获取Audio的时间戳,然后、进行音视频时间的比较,计算下一帧间隔多久显示,使时间戳同步
AwesomePlayer的Video主要成员
1.mVideoSource
(解码视频)
2.mVideoTrack
(从多媒体文件中读取视频数据)
3.mVideoRenderer
(对解码好的视频进行格式转换,Android使用的格式为RGB565)
4.mISurface
(重绘图层)
5.mQueue
(event事件队列)
StageFright运行时的Audio流程
1.设置mUri
的路径
2.启动mQueue
,创建一个线程来运行threadEntry(命名为TimedEventQueue),这个线程就是event调度器
3.打开mUri所指定文件的头部,根据类型选择不同的分离器,如MPEG4Extractor
4.使用MPEG4Extractor
对MP4进行音视频轨道的分离,并返回MPEG4Source类型的视频轨道给mVideoTrack
5.根据mVideoTrack中的编码类型来选择解码器,AVC的编码类型会选择AVCDecoder,返回给mVideoSource,并设置mVideoSource中的mSource为mVideoTrack
6.插入onVideoEvent
到Queue中,开始解码播放
7.通过mVideoSource
对象读取解析好的视频Buffer
如果解析好的视频Buffer还没到A/V时间戳同步的时刻,则推迟到下一轮操作
1.mVideoRenderer
为空,则进行初始化(如果不使用OMX,会将mVideoRenderer设置为AwesomeLocalRenderer
)
2.通过mVideoRenderer对象将解析好的视频Buffer转换成RGB565
格式,并发给display
模块进行图像绘制
3.将onVideoEvent重新插入event调度器
来循环
AwesomePlayer解码过程
AwesomePlayer中的prepare过程
总结:prepare过程调用了prepareAsync_l
函数,在其中执行AwesomeEvent构造函数
,并将AwesomePlayer调用onPrepareAsyncEvent
的引用结果返回AwesomeEvent的构造函数作为参数
然后分析AwesomeEvent的过程。首先启动mQueue,作为EventHandler
上面的new AwesomeEvent会执行onPrepareAsyncEvent
函数
总结:会将音视频分别处理,于是有了
AwesomePlayer::initVideoDecoder
和AwesomePlayer::initAudioDecoder
先看initVideoDecoder
AwesomPlayer.h
中几个变量的声明
然后看看初始化音频解码器
总结:在StageFright调用AwesomePlayer的prepare函数后,AwesomePlayer调用自身的prepareAsync初始化音视频解码器,无论是prepare还是prepareAsync都会触发
OMXCode::Create
函数,然后使用OMXCodec
处理对应的业务逻辑
prepareAsync
主要完成3件事
1.Streaming:启动下载数据并缓存
2.初始化并创建音视频解码器
3.通知上层已经处于Prepared状态
使用OMXCodec的解码过程
经过数据流封装得到的是两个MediaSource其实是两个OMXCodec
。AwesomePlayer和mAudioPlayer都是从MediaSource中得到数据并进行播放的。AwesomePlayer得到的是最终要渲染的原始视频数据,而AudioPlayer得到的是最终要播放的原始音频数据,也就是说从OMXCOdec中读到的数据已经是原始数据了
先看构造函数Create
总结
1.IOMX &omx
指的是一个OMXNodeInstance
对象的实例
2.MetaData &meta
这个参数由MediaSource.getFormat
获取得到,这个对象的主要成员是一个KeyedVector(uint32_t,typed_data)mItems,里面存放了一些代表MediaSource格式信息
的键值对
3.bool createEncoder
指明这个OMXCodec是编码还是解码
4.MediaSource &source
是一个MeidaExtractor(数据解析器)
5.char* matchComponentName
指定一种Codec用于生成这个OMXCodec
先使用findMatchingCodecs
寻找对应的Codec,找到以后为当前IOMX分配节点并注册事件监听器
最后把IOMX封装进一个OMXCodec
这样就得到了OMXCodec
在AwesomePlayer中得到了这个OMXCodec后,我们返回初始化解码器的函数中
使mAudioSource=mOmxSource
,然后调用start
进行初始化
OMXCodec主要做以下两件事
1.向OpenMAX发送开始指令,如mOMX->sendCommand(mNode,OMX_CommandStateSet,OMX_StateIdle)
,表示状态就绪了,可以进入预解码阶段
2.调用allocateBufers
函数分配两个缓冲去,存放在变量mPortBuffers[2]
中,分别用于输入输出
然后再initxxxDecoder函数中调用start函数
触发MediaSource的子类VideoSource及AudioSource调用start函数后,它的内部就会开始从数据源获取数据并解析,等到缓冲区满后便停止。在AwesomePlayer里就可以调用MediaSource的read
函数读取解码后的数据
对于mVideoSource
来说,将读取的数据(mVideoSource->read(&mVideoBuffer,&options))
交给显示模块进行渲染(mVideoRenderer->render(mVideoBuffer))
对于mAudioSource来说,用mAudioPlayer
对mAudioSource进行封装,然后由mAudioPlayer负责读取数据和控制播放
AwesomePlayer调用OMXCodec读取ES(基本码流,包含音视频或数据的连续码流),并进行解码处理
OMXCodec调用MediaSource的read函数获取音视频数据,调用Android的IOMX
接口来实现音视频解码操作
这个过程就是prepare
过程,主要用于分离音视频数据,给音视频申请好Buffer空间,做预解码操作
然后当Java层调用start函数时,通过MediaPlayerService
进行IPC通信后,再调用StagefrightPlayer中的start函数,引用AwesomePlayer,这样就调用了AwesomePlayer的play
函数
在AwesomePlayer调用play后,通过mVideoSource->read(&mVideoBuffer,&options)读取数据。mVideoSource->read(&mVideoBuffer,&options)
具体是通过调用OMXCdodec.read
来读取数据的。
而OMXCodec.read通过1.调用drainInputBuffers函数对mPortBuffers[kPortIndexInput]进行填充,这一步完成parse。由OpenMAX从数据源把demux(解复用)后的数据读取到输入缓冲区,作为OpenMAX的输入。2.通过fillOutputBuffers[kPortIndexInput]进行填充,这一步完成解码数据的填充。由OpenMAX对输入缓冲区中的数据进行解码,然后把解码后可以显示的视频数据输出到输出缓冲区
AwesomePlayer通过mVideoRenderer->render(mVideoBuffer)
对经过parse和decode处理的数据进行渲染。一个mVideoRenderer其实就是一个包装了IOMXRenderer的AwesomeRemoteRenderer
(在很早的版本才有,仅作了解)