转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/52560012

前言:上篇文中最后介绍了数据解码放到Buffer过程,今天分析的是stagefright框架中音视频输出过程:
先看下今天的Agenda:

  • 一张图回顾数据处理过程
  • 视频渲染器构建过程
  • 音频数据到Buffer过程
  • AudioPlayer在AwesomePlayer运行过程
  • 音视频同步
  • 音视频输出
  • 一张图看音视频输出

一张图回顾数据处理过程

视频渲染器构建过程

在构造时,new AweSomeEvent时,就开始把AwesomePlayer把onVideoEvent注入进去。





以上代码最会调用initRenderer_l函数

从上面代码来看:AwesomeRemoteRenderer的本质由OMX::createRenderer会先建立一个hardware renderer就是:mVideoRenderer =
new AwesomeNativeWindowRenderer(mNativeWindow, rotationDegrees);若失败,则建立new AwesomeLocalRenderer(mNativeWindow, format);
接下来看下:


而另一个AwesomeLocalRenderer在构造时new SoftwareRenderer(nativeWindow)

AwesomeLocalRender的本质上是由OMX:createRenderer,createRenderer会建立一个渲染器。如果video decoder是software component,则建立一个AwesomeLocalRenderer作为mVideoRenderer
AwesomeLocalRenderer的constructor会呼叫本身的init函数,其所做的事和OMX::createRenderer一模一样。可以理解为把read的数据显示在渲染器中。
渲染器渲染出画面后,我们可能会想,MediaExtractor把音视频进行分开,那音频呢?谁来让他们保持同步的呢?

音频数据到Buffer过程

无论是音频也好,还是视频,都是bufferdata,音频或视频总有一个来维持时间线的流。举个例子:我们看过双簧,一个人说话,一个人演示动作,动作快了不行,话说快,动作跟不上也不行。中间在联系台词时,自然有一些停顿或暗号。在OpenCore中,设置了一个主clock,而audio和video就分别以此作为输出的依据。而在Stagefright中,audio的输出是透过callback函式来驱动,video则根据audio的timestamp来做同步。在这之前,我们得了解下音频相关playback过程:
Stagefright框架中,audio的部分是交由AudioPlayer来处理,它是在AwesomePlayer::play_l中被建立的。贴一段以前分析过的代码:只不过当时没有向AudioPlayer方向向下看


创建AudioPlayer

再接着看下startAudioPlayer_l函数,

接下来看下音频mAudioPlayer->start(true)的操作,上面的过程都是在AwesomePlayer中,接下来变到AudioPlayer.cpp类中:


这里首先要介绍一下mAudioSink ,当mAudioSink不为NULL的时候,AudioPlayer会将其传入构造函数。
而且AudioPlayer中的播放操作都会依考mAudioSink来完成。
此处mAudioSink是从MediaPlayerService注册而来的AudioOut对象。具体代码在MediaPlayerservice中

间接地调用到stagefrightplayer->setAudioSink,最终到awesomeplayer中,如下:

而构造AudioPlayer时用到的就是mAudioSink成员,因此后面分析传入的mAudioSink的操作时,记住实际的对象为AudioOut对象,在MediaPlayerService定义。
本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/52560012

AudioPlayer在AwesomePlayer运行过程

下面看AudioPlayer构造函数

主要是进行初始化,并将传入的mAudioSink存在成员mAudioSink中
再回到上面的start函数中
总结如下:

  • 调用mSource->read 启动解码,解码第一帧相当于启动了解码循环
  • 获取音频参数:采样率、声道数、以及量化位数(这里只支持PCM_16_BIT)
  • 启动输出:这里若mAudioSink非空,则启动mAudioSink进行输出,否则构造一个AudioTrack进行音频输出,这里AudioTrack是比较底层的接口 AudioOut是AudioTrack的封装。
  • 在start方法中主要是调用mAudioSink进行工作,主要代码如下:

刚介绍过mAudioSink是AudioOut对象,看下实际的实现(代码在mediaplayerservice.cpp中)

首先mAudioSink->open 需要注意的是传入的参数中有个函数指针 AudioPlayer::AudioSinkCallback ,其主要作用就是audioout播放pcm的时候会定期调用此回调函数填充数据,具体实现如下

以上代码总结为:

  • 1、处理传入的参数,回调函数保存在mCallback中, cookie代表的是AudioPlayer对象指针类型,接下来是根据采样率声道数等计算 frameCount。
  • 2、构造AudioTrack对象,并且赋值给t
  • 3、将audiotrack对象存储在mTrack成员中
    当以上过程完成后,继续分析AudioPlayer.start函数时,最后都会实例化一个AudioTrack对象,然后获取帧大小,比特等信息,然后调用mAudioTrack.start,最后到达mediaplayerservice音频输出start函数。

调用mTrack->start,audiotrack启动后就会周期性的调用 回调函数从解码器获取数据.
本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/52560012

音视频同步

回到我们前面的问题:音视频如何同步?通过fillBuffer,不断填充buffer。
代码如下:

以上代码总结为:当callback函数回调AudioPlayer读取解码后的数据时,AudioPlayer会取得两个时间戳:mPositionTimeMediaUs和mPositionTimeRealUs,mPositionTimeMediaUs是数据里面所持有的时间戳(timestamp);mPositionTimeRealUs则是播放此数据的实际时间(依据frame number及sample rate得出)。

以上代码总结为:

  • 在构造audioplayer的时候会执行mTimeSource = mAudioPlayer,
    即将AudioPlayer作为参考时钟,
  • 上述代码中成员变量mSeekTimeUs是由如下语句获得:CHECK(mVideoBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
  • realTimeOffset = getRealTimeUsLocked() - mPositionTimeRealUs; 当显示画面是第一帧时,表示当前的audio的播放时间与第一帧video的时间差值
  • 其中变量是通过mAudioPlayer->getMediaTimeMapping( int64_t *realtime_us, int64_t *mediatime_us) {
    Mutex::Autolock autoLock(mLock)得到

二者的差值表示这一包pcm数据已经播放了多少。Stagefright中的video便依据从AudioPlayer得出来之两个时间戳的差值,作为播放的依据

音视频输出

最后回到本文开头的onVideoEvent方法中,

这样最终音视频数据通过渲染器就到Surface显示画面,就可看到视频和听到声音了。
本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/52560012

一张图看音视频输出

第一时间获得博客更新提醒,以及更多android干货,源码分析,欢迎关注我的微信公众号,扫一扫下方二维码或者长按识别二维码,即可关注。

如果你觉得好,随手点赞,也是对笔者的肯定,也可以分享此公众号给你更多的人,原创不易

Android Multimedia框架总结(十)Stagefright框架之音视频输出过程相关推荐

  1. Android MediaScanner MediaProvider流程以及性能优化,音视频扫描

    Android MediaScanner MediaProvider流程以及性能优化,音视频扫描 快速扫描 一.源码解析 github链接 MediaScanner时序图 MediaSacannerR ...

  2. Android 使用ViewPager2+ExoPlayer+VideoCache 实现仿抖音视频翻页播放

    1. 实现效果 效果图中,视频没有铺满 是因为使用了ExoPlayer的RESIZE_MODE_FIT模式, 虽然使用RESIZE_MODE_FILL模式可以填充整个父布局,但是本Demo中使用的视频 ...

  3. Android Multimedia Framework overview(多媒体框架概述)--base on jellybean(一)

    不知为啥今晚突然脑海中闪现出一个念头:写博客!原因有二,其一就是记录下自己的学习所得,巩固自己的知识,温故而知新吧,以前都是光看光想,都没有记录,而且不写下来,领悟估计就差点了,好记忆不如烂笔头啊!其 ...

  4. Android 深入系统完全讲解(二)音视频理解攻略 PDF

    原创PDF |<Android 深入系统完全讲解>,可能价值百万! 最近因为训练营里面,想学习音视频的比较多,当然也有一部分因素是我在给大家讲市场时候,说了,算法和界面,其实是市场的一个底 ...

  5. Android平台基于RTMP或RTSP的一对一音视频互动技术方案探讨

    背景 随着智能门禁等物联网产品的普及,越来越多的开发者对音视频互动体验提出了更高的要求.目前市面上大多一对一互动都是基于WebRTC,优点不再赘述,我们这里先说说可能需要面临的问题:WebRTC的服务 ...

  6. Android音频子系统(十二)------抖音直播功耗问题解析

    你好!这里是风筝的博客, 欢迎和我一起交流. [前提条件] 移动卡纯5G,120HZ,最小亮度,最小音量,开启定位 [操作步骤] 1.从软件商店下载最新版本APK 2.进入抖音并登录账号,点击右上方的 ...

  7. Android手机QQ的UI自动化实践,音视频服务器开发难点

    在上一步环节中,我们虽然确定了自动化框架,但是框架只提供底层的驱动能力,如果无统一封装模式进行规范,随着用例的增多会变得难以维护,所以我们需要一个统一模式来封装细节,可以使 testcase 更稳健, ...

  8. 【WebRTC---入门篇】(十四)WebRTC音视频录制

    <

  9. Android 8.1 如何查看系统支持哪些音视频编解码格式

    代码路径: frameworks/av/media/libstagefright/omx/SoftOMXPlugin.cpp 在SoftOMXPlugin.cpp文件中kComponents[]结构体 ...

  10. ffmpeg制作视频播放器(十五)XPlay2 音视频参数获取和复制

    XDemux.cpp //获取视频参数 返回的空间需要清理 avcodec_parameters_free AVCodecParameters *XDemux::CopyVPara() {mux.lo ...

最新文章

  1. 假设检验_python 重点
  2. nagios和cacti的整合
  3. Spring batch Job define
  4. sql高级语法之IF、IFNULL
  5. sqlserver使用depart获取当前日期月份及通过拼接得到日期时间
  6. 计算机组成原理--白中英版 全部知识点
  7. greenplum 数据库单机部署
  8. 五路循迹传感器的优点_用五路循迹传感器做循迹
  9. 西门子定位器6DR5010-0NN00-0AA0
  10. 【数据库认证】OCM准备及考试经验总结
  11. H无穷控制学习笔记——H无穷/H2控制
  12. python--数据类型
  13. window.open() 被拦截的问题解决
  14. 挪威科技大学计算机硕士,挪威科技大学硕士留学申请条件
  15. 黑马程序员——Java的代理模式
  16. java8 joda_Java基础之如何取舍Joda与 Java8 日期库
  17. 日本互联网的十大知名巨头!你听说过几个?
  18. 路由之HSRP热备份
  19. 用laravel写一个API
  20. 格力(GREE)家用移动空调免安装一体机空调KY-23NK 清灰拆装教程

热门文章

  1. C++语法_深度剖析C++中的inline函数
  2. 深度学习笔记_评分函数/损失函数
  3. 三维重建笔记_三维重建方法导图
  4. Ortholab has been moved to Google Code
  5. [转]Angular: Hide Navbar Menu from Login page
  6. mysql忘记root密码怎么办,几个简单操作步骤找回mysql密码
  7. MySql 数据库安装、环境变量配置 以及 本地连接
  8. eShopOnContainers学习系列(二):数据库连接健康检查
  9. docker镜像创建
  10. 读书笔记——5建造者模式(转)