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 (在很早的版本才有,仅作了解)
|