上篇主要介绍了Stagefright框架及AwesomePlayer的数据解析器,最后我们说道,涉及parse及decode部分,将在本篇中介绍,
看下今天的Agenda:
(1)两张图看数据走向
(2)AwesomePlayer中prepare过程
(3)AwesomePlayer初始化音视频解码器过程
(4)Stagefright的Decode过程
(5)Stagefright处理数据过程
(6)数据由源到最终解码后的流程
一切从MediaSource中来,一切又从MediaSource中去
Audio:
Video:
首先我们开始看下AwesomePlayer的prepare的执行过程:
以上代码总结为:prepare过程调用了prepareAsync_l函数,
在prepareAsync_l中执行new AwesomeEvent,
并将AwesomePlayer调用onPrepareAsyncEvent的结果返回AwesomeEvent的构造作为参数。
接着分析AwesomeEvent的过程: 启动mQueue,作为event handler
上面new AwesomeEvent会执行onPrepareAsyncEvent函数,我们看下该函数的做了什么?
以上代码总结为:
会将AV(音视频)进行分处理,于是有了 AwesomePlayer::initVideoDecoder 及 AwesomePlayer::initAudioDecoder() 函数。
我们先看下initVideoDecoder,即初始化视频解码器:
接着看下初始化音频解码器,看下几个变量的声明:
接着看代码如下:
对上面代码进行总结:
Stagefright 调用 AwesomePlayer的prepare后,AwesomePlayer调用自身的prepareAsync进行初始化化音视频解码器,
这两个方法里面都会OMXCodec::Create,
接下来看下这个过程。
经过“数据流的封装”得到的两个MediaSource,其实是两个OMXCodec。
AwesomePlayer和mAudioPlayer都是从MediaSource中得到数据进行播放。
AwesomePlayer得到的是最终需要渲染的原始视频数据,而mAudioPlayer读取的是最终需要播放的原始音频数据。
也就是说,从OMXCodec中读到的数据已经是原始数据了。
OMXCodec是怎么把数据源经过parse、decode两步以后转化成原始数据的。
从OMXCodec::Create这个构造方法开始,下面看它的代码:
以上代码总结为:对应参数分析:
AwesomePlayer中得到这个OMXCodec后,接着看initVideoDecoder/initAudioDecoder,这里看initAudioDecoder方法,是把 mAudioSource = mOmxSource,赋值,接着调用mAudioSource->start()进行初始化。 OMXCodec初始化主要是做两件事:
然后在现个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实现
这个过程就是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:
Audioplayer为AwesomePlayer的成员,audioplayer通过callback来驱动数据的获取,awesomeplayer则是通过 videoevent来驱动。
二者有个共性,就是数据的获取都抽象成mSource->read()来完成,且read内部把parse和decode绑在一起。
Stagefright AV同步部分,audio完全是callback驱动数据流,
注意是video部分在onVideoEvent里会获取audio的时间戳,是传统的AV时间戳做同步。
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还没到AV时间戳同步的时刻,则推迟到下一轮操作
1、mVideoRenderer为空,则进行初始化(如果不使用 OMX会将mVideoRenderer设置为AwesomeLocalRenderer)
2、通过mVideoRenderer对象将解析好的视频buffer转换成RGB565格式,并发给display模块进行图像绘制
3、将onVideoEvent重新插入event调度器来循环
设置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来提交事件用来停止解码,还可以选择是否继续缓冲数据。
本文出自:http://blog.csdn.net/hejjunlin/article/details/52532085