转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼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多媒体框架总结(1) - 利用MediaMuxer合成音视频数据流程分析

    场景介绍: 设备端通过服务器传向客户端(Android手机)实时发送视频数据(H.264)和音频数据(g711a或g711u), 需要在客户端将音视频数据保存为MP4文件存放在本地,用户可以通过APP ...

  2. Android IOS WebRTC 音视频开发总结(七十)-- 移动端音视频技术优化的七个方向

    最近直播很火,很多朋友对背后的技术比较感兴趣,所以今天我们整理一篇关于移动端视频优化的文章,这篇文章是我朋友在一个技术大会上分享过的,更多内容请关注我们的微信公众号:rtcblacker 视频直播为什 ...

  3. 转:Android IOS WebRTC 音视频开发总结 (系列文章集合)

    随笔分类 - webrtc   Android IOS WebRTC 音视频开发总结(七八)-- 为什么WebRTC端到端监控很关键? 摘要: 本文主要介绍WebRTC端到端监控(我们翻译和整理的,译 ...

  4. 堪称教科书级别的Android音视频入门进阶学习手册,开源分享!

    概述 随着整个互联网的崛起,数据传递的形式也在不断升级变化,总的流行趋势如下: 纯文本的短信,QQ -> 空间,微博,朋友圈的图片文字结合 -> 微信语音 -> 各大直播软件 -&g ...

  5. Android Multimedia框架总结(十五)Camera框架之Camera2补充

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

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

    转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼:http://blog.csdn.net/hejjunlin/article/details/52532085 不知不觉到第九篇了,感觉还有 ...

  7. Android Multimedia框架总结(八)Stagefright框架之AwesomePlayer及数据解析器

    转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼:http://blog.csdn.net/hejjunlin/article/details/52503057 前言:前面一篇分析了medi ...

  8. Android Multimedia框架总结(二十四)MediaMuxer实现手机屏幕录制成gif图

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

  9. Android Multimedia框架总结(十一)CodeC部分之AwesomePlayer到OMX服务

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

随机推荐

  1. servlet之session设置

    商品对象,购物车对象,servlet的实现 商品: package app02d;public class Product {    private int id;    private String ...

  2. 什么是HTTP Referer?

    什么是Referer? Referer是 HTTP请求header 的一部分,当浏览器(或者模拟浏览器行为)向web 服务器发送请求的时候,头信息里有包含 Referer.比如我在www.sojson ...

  3. codesforces 671D Roads in Yusland

    Mayor of Yusland just won the lottery and decided to spent money on something good for town. For exa ...

  4. C++traits——STL源码剖析

    有时候我们希望知道迭代器所指的元素类型. 以迭代器所指声明对象: template<typename Iterator, typename T> void func_impl(Iterat ...

  5. 【QAQ的Minecraft】

    树套树被QAQ用木斧挖了,只剩二维RMQ了. 题目:      QAQ最近爱上了一款很平凡的游戏,叫做<Minecraft>.目前游戏更新到了1.12版本,他发现了一条新的指令:/fill ...

  6. [BZOJ]1018 堵塞的交通(SHOI2008)

    一道有点神的线段树. Description 有一天,由于某种穿越现象作用,你来到了传说中的小人国.小人国的布局非常奇特,整个国家的交通系统可以被看成是一个2行C列的矩形网格,网格上的每个点代表一个城 ...

  7. bzoj 1046: [HAOI2007]上升序列

    Description 对于一个给定的S={a1,a2,a3,…,an},若有P={ax1,ax2,ax3,…,axm},满足(x1 < x2 < … < xm)且( ax1 < ...

  8. 如何查看cisco 生成树状态

    如何查看cisco 生成树状态  Role(角色): 1.Desg(指定端口)   2.Root(根端口)    3.Altn(替换端口) Sts(状态): 1.FWD(转发)      2.BLK( ...

  9. springboot解决跨域问题(Cors)

    1.对于前后端分离的项目来说,如果前端项目与后端项目部署在两个不同的域下,那么势必会引起跨域问题的出现. 针对跨域问题,我们可能第一个想到的解决方案就是jsonp,并且以前处理跨域问题我基本也是这么处 ...

  10. Socket网络编程详解

    一,socket的起源 socket一词的起源 在组网领域的首次使用是在1970年2月12日发布的文献IETF RFC33中发现的, 撰写者为Stephen Carr.Steve Crocker和Vi ...