【学习】从零开始的Android音视频开发(7)——AwesomePlayer的构造、解码过程

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中创建的时候就定下来了

在这里插入图片描述
在这里插入图片描述

mVideoTrackmAudioTrack作为创建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::initVideoDecoderAwesomePlayer::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(在很早的版本才有,仅作了解)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值