Android Multimedia框架总结(九)Stagefright框架之数据处理及到OMXCodec过程

不知不觉到第九篇了,感觉还有好多好多没有写,路漫漫其修远兮 ,吾将上下而求索。先说福利吧,此前在关于我,

曾说过,不定期搞活动,vip,书啥的,都可以有,不要问我那儿来,

感谢大家的支持最重要,如果大家有想投稿的话,也可以后台留言给我。所有权都是属于原作者,各种福利都会优先发放投稿人,分享不易,好了,废话不多说,今天是送爱奇艺VIP会员卡月卡,方式很简单,文章下方留言,被点赞最多的前三名(时间:截至到今天晚上12:00),12点后准时送VIP会员卡,这个还是很容易获得的,举个我自己的例子:大学时我是转专业的,大二时一方面补大一的课程,一边学大二的课,我就稍微努了把力,就拿了个二等奖学金(虽然比较水,还挂科拿的。),简直我自己都不信,有个段子叫:我发起疯来,连我自己都打,哈哈。还不至于那样。

以上都是题外话,下面开始今天的多媒体框架第九篇

上篇主要介绍了Stagefright框架及AwesomePlayer的数据解析器,最后我们说道,涉及parse及decode部分,将在本篇中介绍,看下今天的Agenda:

  • 两张图看数据走向
  • AwesomePlayer中prepare过程
  • AwesomePlayer初始化音视频解码器过程
  • Stagefright的Decode过程
  • Stagefright处理数据过程
  • 数据由源到最终解码后的流程

两张图看数据走向

一切从MediaSource中来,一切又从MediaSource中去 Audio:

Video:

AwesomePlayer中prepare过程

首先我们开始看下AwesomePlayer的prepare的执行过程:

以上代码总结为:prepare过程调用了prepareAsync_l函数,在prepareAsync_l中执行new AwesomeEvent,并将AwesomePlayer调用onPrepareAsyncEvent的结果返回AwesomeEvent的构造作为参数。 接着分析AwesomeEvent的过程: 启动mQueue,作为event handler

上面new AwesomeEvent会执行onPrepareAsyncEvent函数,我们看下该函数的做了什么?

以上代码总结为:会将AV(音视频)进行分处理,于是有了AwesomePlayer::initVideoDecoder及AwesomePlayer::initAudioDecoder()函数。 本文出自逆流的鱼:http://blog.csdn.net/hejjunlin/article/details/52532085

初始化音视频解码器过程

我们先看下initVideoDecoder,即初始化视频解码器:

接着看下初始化音频解码器,看下几个变量的声明:

接着看代码如下:

对上面代码进行总结: Stagefright调用AwesomePlayer的prepare后,AwesomePlayer调用自身的prepareAsync进行初始化化音视频解码器,这两个方法里面都会OMXCodec::Create,接下来看下这个过程。

Stagefright的Decode过程

经过“数据流的封装”得到的两个MediaSource,其实是两个OMXCodec。AwesomePlayer和mAudioPlayer都是从MediaSource中得到数据进行播放。AwesomePlayer得到的是最终需要渲染的原始视频数据,而mAudioPlayer读取的是最终需要播放的原始音频数据。也就是说,从OMXCodec中读到的数据已经是原始数据了。 OMXCodec是怎么把数据源经过parse、decode两步以后转化成原始数据的。从OMXCodec::Create这个构造方法开始,下面看它的代码:

以上代码总结为:对应参数分析:

  • IOMX &omx指的是一个OMXNodeInstance对象的实例。
  • MetaData &meta这个参数由MediaSource.getFormat获取得到。这个对象的主要成员就是一个KeyedVector(uint32_t, typed_data) mItems,里面存放了一些代表MediaSource格式信息的名值对。
  • bool createEncoder指明这个OMXCodec是编码还是解码。
  • MediaSource &source是一个MediaExtractor(数据解析器)。
  • char *matchComponentName指定一种Codec用于生成这个OMXCodec。 先使用findMatchingCodecs寻找对应的Codec,找到以后为当前IOMX分配节点并注册事件监听器:omx->allocateNode(componentName, observer, &node)。最后,把IOMX封装进一个OMXCodec:

这样就得到了OMXCodec。

  • AwesomePlayer中得到这个OMXCodec后,接着看initVideoDecoder/initAudioDecoder,这里看initAudioDecoder方法,是把 mAudioSource = mOmxSource,赋值,接着调用mAudioSource->start()进行初始化。 OMXCodec初始化主要是做两件事:
    • 向OpenMAX发送开始命令。mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle)
    • 调用allocateBuffers()分配两个缓冲区,存放在Vector mPortBuffers[2]中,分别用于输入和输出。
  • 然后在现个initxxxDecoder方法中会调用(mAudioSource->start()/mVideoSource->start())

触发MediaSource的子类VideoSource及AudioSource调用start()方法后,它的内部就会开始从数据源获取数据并解析,等到缓冲区满后便停止。在AwesomePlayer里就可以调用MediaSource的read方法读取解码后的数据。

  • 对于mVideoSource来说,读取的数据:mVideoSource->read(&mVideoBuffer, &options)交给显示模块进行渲染,mVideoRenderer->render(mVideoBuffer);
  • 对mAudioSource来说,用mAudioPlayer对mAudioSource进行封装,然后由mAudioPlayer负责读取数据和播放控制。
  • AwesomePlayer调用OMXCode读取ES数据,并且进行解码的处理
  • OMXCodec调用MediaSource的read函数来获取音视频的数据
  • OMXCodec调用Android的IOMX接口,其实就是Stagefrightdecode中的 OMX实现

本文出自逆流的鱼:http://blog.csdn.net/hejjunlin/article/details/52532085

这个过程就是prepare的过程,重点是解码把流放到Buffer中 接下来,当java层调用start方法时,通过mediaplayerservice,再传到StagefrightPlayer中,引用AwesomePlayer,这样就调到AwesomePlayer的play方法,看代码:

  • 当AwesomePlayer调用play后,通过mVideoSource->read(&mVideoBuffer, &options)读取数据。mVideoSource->read(&mVideoBuffer, &options)具体是调用OMXCodec.read来读取数据。而OMXCodec.read主要分两步来实现数据的读取:
  • (1) 通过调用drainInputBuffers()对mPortBuffers[kPortIndexInput]进行填充,这一步完成 parse。由OpenMAX从数据源把demux后的数据读取到输入缓冲区,作为OpenMAX的输入。
  • (2) 通过fillOutputBuffers()对mPortBuffers[kPortIndexOutput]进行填充,这一步完成 decode。由OpenMAX对输入缓冲区中的数据进行解码,然后把解码后可以显示的视频数据输出到输出缓冲区。 AwesomePlayer通过mVideoRenderer->render(mVideoBuffer)对经过parse和decode 处理的数据进行渲染。一个mVideoRenderer其实就是一个包装了IOMXRenderer的AwesomeRemoteRenderer:

Stagefright处理数据过程

  • Audioplayer为AwesomePlayer的成员,audioplayer通过callback来驱动数据的获取,awesomeplayer则是通过 videoevent来驱动。二者有个共性,就是数据的获取都抽象成mSource->read()来完成,且read内部把parse和decode绑在一起。Stagefright AV同步部分,audio完全是callback驱动数据流,注意是video部分在onVideoEvent里会获取audio的时间戳,是传统的AV时间戳做同步。
  • AwesomePlayer的Video主要有以下几个成员:
    • mVideoSource(解码视频)
    • mVideoTrack(从多媒体文件中读取视频数据)
    • mVideoRenderer(对解码好的视频进行格式转换,android使用的格式为RGB565)
    • mISurface(重绘图层)
    • mQueue(event事件队列)
  • stagefright运行时的Audio流程如下:
    • 首先设置mUri的路径
    • 启动mQueue,创建一个线程来运行 threadEntry(命名为TimedEventQueue,这个线程就是event调度器)
    • 打开mUri所指定的文件的头部,则会根据类型选择不同的分离器(如MPEG4Extractor)
    • 使用 MPEG4Extractor对MP4进行音视频轨道的分离,并返回MPEG4Source类型的视频轨道给mVideoTrack
    • 根据 mVideoTrack中的编码类型来选择解码器,avc的编码类型会选择AVCDecoder,并返回给mVideoSource,并设置mVideoSource中的mSource为mVideoTrack
    • 插入onVideoEvent到Queue中,开始解码播放
    • 通过mVideoSource对象来读取解析好的视频buffer

如果解析好的buffer还没到AV时间戳同步的时刻,则推迟到下一轮操作

1、mVideoRenderer为空,则进行初始化(如果不使用 OMX会将mVideoRenderer设置为AwesomeLocalRenderer) 2、通过mVideoRenderer对象将解析好的视频buffer转换成RGB565格式,并发给display模块进行图像绘制 3、将onVideoEvent重新插入event调度器来循环

本文出自逆流的鱼:http://blog.csdn.net/hejjunlin/article/details/52532085

Stagefright数据由源到最终解码后的流程

可以对照《Android Multimedia框架总结(八)Stagefright框架之AwesomePlayer及数据解析器》中那个图,这里不贴了,

  • 设置DataSource,数据源可以两种URI和FD。URI可以http://,rtsp://等。FD是一个本地文件描述符,能过FD,可以找到对应的文件。
  • 由DataSource生成MediaExtractor。通过sp extractor = MediaExtractor::Create(dataSource);来实现。 MediaExtractor::Create(dataSource)会根据不同的数据内容创建不同的数据读取对象。
  • 通过调用setVideoSource由MediaExtractor分解生成音频数据流(mAudioTrack)和视频数据流(mVideoTrack)。
  • onPrepareAsyncEvent()如果DataSource是URL的话,根据地址获取数据,并开始缓冲,直到获取到mVideoTrack和mAudioTrack。mVideoTrack和mAudioTrack通过调用initVideoDecoder()和initAudioDecoder()来生成 mVideoSource和mAudioSource这两个音视频解码器。然后调用postBufferingEvent_l()提交事件开启缓冲。
  • 数据缓冲的执行函数是onBufferingUpdate()。缓冲区有足够的数据可以播放时,调用play_l()开始播放。play_l()中关键是调用了postVideoEvent_l(),提交了 mVideoEvent。这个事件执行时会调用函数onVideoEvent()。这个函数通过调用 mVideoSource->read(&mVideoBuffer, &options)进行视频解码。音频解码通过mAudioPlayer实现。
  • 视频解码器解码后通过mVideoSource->read读取一帧帧的数据,放到mVideoBuffer中,最后通过 mVideoRenderer->render(mVideoBuffer)把视频数据发送到显示模块。当需要暂停或停止时,调用cancelPlayerEvents来提交事件用来停止解码,还可以选择是否继续缓冲数据。

原文发布于微信公众号 - 何俊林(DriodDeveloper)

原文发表时间:2016-09-14

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏歪先生_自留地

Python test2

1063
来自专栏北京马哥教育

shell十三问,为linux学习打基础(一)

本文整理并转自CU上的帖子[学习共享] shell 十三問?,此贴是2003年发表的,但却是相当不错的linux基础知识汇集贴,原帖主使用的台湾风格,本文加以简...

3784
来自专栏CRPER折腾记

CSS-Next : CSS预处理器简单写法的替代者, 想了解下么?

借助相关的插件我们可以把新的特性降级到 css3乃至一些特性降级到css2.1...无缝过渡

1102
来自专栏牛客网

京东提前批内推一面

1353
来自专栏数值分析与有限元编程

Python IDLE关联.py文件

为进一步提升Python IDLE可操作性,本文介绍如何在windows操作系统下默认使用python自带的IDLE编辑器关联后缀名为.py的文件。

5531
来自专栏水击三千

UML学习-状态图

1.状态图概述 状态图(Statechart Diagram)主要用于描述一个对象在其生存期间的动态行为,表现为一个对象所经历的状态序列,引起状态转移的事件(E...

27810
来自专栏图形学与OpenGL

WebGL绘制三角形

1372
来自专栏安恒网络空间安全讲武堂

浙江省首届网络安全大赛决赛Write Up

直接打开pwd.docx,发现是空的,那么将其作为zip 解压,在其中找到了一张二维码

1412
来自专栏SeanCheney的专栏

Python模拟登陆 —— 征服验证码 8 微信网页版

微信登录界面 微信网页版使用了UUID含义是通用唯一识别码来保证二维码的唯一性。 先用一个伪造的appid获得uuid。 params = { ...

5429
来自专栏Spark学习技巧

Flink DataStream编程指南及使用注意事项。

Flink中的DataStream程序是对数据流进行转换的常规程序(例如,过滤,更新状态,定义窗口,聚合)。数据流的最初的源可以从各种来源(例如,消息队列,套接...

1.9K7

扫码关注云+社区

领取腾讯云代金券