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

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

先看下今天的Agenda:
(1)一张图回顾数据处理过程
(2)视频渲染器构建过程
(3)音频数据到Buffer过程
(4)AudioPlayer在AwesomePlayer运行过程
(5)音视频同步
(6)音视频输出
(7)一张图看音视频输出


一、一张图回顾数据处理过程

在这里插入图片描述


二、视频渲染器构建过程

在这里插入图片描述
在构造时,new AweSomeEvent时,就开始把AwesomePlayer把onVideoEvent注入进去。
Android Multimedia框架总结(十)Stagefright框架之音视频输出过程_第1张图片
Android Multimedia框架总结(十)Stagefright框架之音视频输出过程_第2张图片
Android Multimedia框架总结(十)Stagefright框架之音视频输出过程_第3张图片
Android Multimedia框架总结(十)Stagefright框架之音视频输出过程_第4张图片
Android Multimedia框架总结(十)Stagefright框架之音视频输出过程_第5张图片
以上代码最会调用initRenderer_l函数
Android Multimedia框架总结(十)Stagefright框架之音视频输出过程_第6张图片
从上面代码来看:
AwesomeRemoteRenderer的本质由OMX::createRenderer会先建立一个hardware
renderer就是:mVideoRenderer = new AwesomeNativeWindowRenderer(mNativeWindow, rotationDegrees);
若失败,则建立new AwesomeLocalRenderer(mNativeWindow, format);

接下来看下:
Android Multimedia框架总结(十)Stagefright框架之音视频输出过程_第7张图片
Android Multimedia框架总结(十)Stagefright框架之音视频输出过程_第8张图片
而另一个AwesomeLocalRenderer在构造时new SoftwareRenderer(nativeWindow)
Android Multimedia框架总结(十)Stagefright框架之音视频输出过程_第9张图片
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方向向下看
Android Multimedia框架总结(十)Stagefright框架之音视频输出过程_第10张图片
Android Multimedia框架总结(十)Stagefright框架之音视频输出过程_第11张图片
创建AudioPlayer
Android Multimedia框架总结(十)Stagefright框架之音视频输出过程_第12张图片
再接着看下startAudioPlayer_l函数,
Android Multimedia框架总结(十)Stagefright框架之音视频输出过程_第13张图片
接下来看下音频mAudioPlayer->start(true)的操作,上面的过程都是在AwesomePlayer中,接下来变到AudioPlayer.cpp类中:
Android Multimedia框架总结(十)Stagefright框架之音视频输出过程_第14张图片
Android Multimedia框架总结(十)Stagefright框架之音视频输出过程_第15张图片

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

具体代码在MediaPlayerservice中
Android Multimedia框架总结(十)Stagefright框架之音视频输出过程_第16张图片
间接地调用到stagefrightplayer->setAudioSink,最终到awesomeplayer中,如下:
在这里插入图片描述
而构造AudioPlayer时用到的就是mAudioSink成员,因此后面分析传入的mAudioSink的操作时,记住实际的对象为AudioOut对象,在MediaPlayerService定义。


四、AudioPlayer在AwesomePlayer运行过程

下面看AudioPlayer构造函数
Android Multimedia框架总结(十)Stagefright框架之音视频输出过程_第17张图片
主要是进行初始化,并将传入的mAudioSink存在成员mAudioSink中
再回到上面的start函数中

总结如下:

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

Android Multimedia框架总结(十)Stagefright框架之音视频输出过程_第18张图片
刚介绍过mAudioSink是AudioOut对象,看下实际的实现(代码在mediaplayerservice.cpp中)

首先mAudioSink->open 需要注意的是传入的参数中有个函数指针 AudioPlayer::AudioSinkCallback ,
其主要作用就是audioout播放pcm的时候会定期调用此回调函数填充数据,具体实现如下
Android Multimedia框架总结(十)Stagefright框架之音视频输出过程_第19张图片
以上代码总结为:

  1. 处理传入的参数,回调函数保存在mCallback中, cookie代表的是AudioPlayer对象指针类型,接下来是根据采样率声道数等计算 frameCount。
  2. 构造AudioTrack对象,并且赋值给t
  3. 将audiotrack对象存储在mTrack成员中

当以上过程完成后,继续分析AudioPlayer.start函数时,最后都会实例化一个AudioTrack对象,然后获取帧大小,比特等信息,然后调用mAudioTrack.start,最后到达mediaplayerservice音频输出start函数。

Android Multimedia框架总结(十)Stagefright框架之音视频输出过程_第20张图片
调用mTrack->start,audiotrack启动后就会周期性的调用 回调函数从解码器获取数据.


五、音视频同步

回到我们前面的问题:音视频如何同步?通过fillBuffer,不断填充buffer。
代码如下:
Android Multimedia框架总结(十)Stagefright框架之音视频输出过程_第21张图片
以上代码总结为:

当callback函数回调AudioPlayer读取解码后的数据时,
AudioPlayer会取得两个时间戳:mPositionTimeMediaUs和mPositionTimeRealUs,

mPositionTimeMediaUs是数据里面所持有的时间戳(timestamp);
mPositionTimeRealUs则是播放此数据的实际时间(依据frame number及sample rate得出)。

Android Multimedia框架总结(十)Stagefright框架之音视频输出过程_第22张图片
以上代码总结为:

  1. 在构造audioplayer的时候会执行mTimeSource = mAudioPlayer,
    即将AudioPlayer作为参考时钟,

  2. 上述代码中成员变量mSeekTimeUs是由如下语句获得:CHECK(mVideoBuffer->meta_data()->findInt64(kKeyTime, &timeUs));

  3. realTimeOffset = getRealTimeUsLocked() - mPositionTimeRealUs; 当显示画面是第一帧时,表示当前的audio的播放时间与第一帧video的时间差值

  4. 其中变量是通过mAudioPlayer->getMediaTimeMapping( int64_t *realtime_us, int64_t *mediatime_us) {
    Mutex::Autolock autoLock(mLock)得到

Android Multimedia框架总结(十)Stagefright框架之音视频输出过程_第23张图片
二者的差值表示这一包pcm数据已经播放了多少。
Stagefright中的video便依据从AudioPlayer得出来之两个时间戳的差值,作为播放的依据


六、音视频输出

最后回到本文开头的onVideoEvent方法中
在这里插入图片描述
这样最终音视频数据通过渲染器就到Surface显示画面,就可看到视频和听到声音了。


七、一张图看音视频输出

Android Multimedia框架总结(十)Stagefright框架之音视频输出过程_第24张图片




本文出自: https://blog.csdn.net/hejjunlin/article/details/52560012

你可能感兴趣的:(07--Android,Media)