读书人

Awesomeplayer 里解压跟显示video的有

发布时间: 2013-03-21 10:08:17 作者: rapoo

Awesomeplayer 里解压和显示video的有关分析

播放一个视频开始时要调用prepare , 在prepare 里调用 TimedEventQueue::start() ,即mQueue.start() ,会启动一个线程,然后在TimedEventQueue::threadEntry() 会收事件,处理事件。

TimedEventQueue 是一个按照事件约定时间来执行事件携带动作的类。事件的约定时间存在 QueueItem::realtime_us 里,往TimedEventQueue发事件使用 TimedEventQueue::postTimedEvent ,该方法除this以外的第一个参数是事件,第二个参数是该事件约定的执行时间,它是按顺序往列表里填事件的,请看这段代码:

TimedEventQueue::event_id TimedEventQueue::postTimedEvent() {
....

List<QueueItem>::iterator it = mQueue.begin();
while (it != mQueue.end() && realtime_us >= (*it).realtime_us) {
++it;
}

QueueItem item;
item.event = event;
item.realtime_us = realtime_us;

if (it == mQueue.begin()) {
mQueueHeadChangedCondition.signal();
}

mQueue.insert(it, item);

....

}

在TimedEventQueue::threadEntry()按顺序查事件:

List<QueueItem>::iterator it = mQueue.begin();
eventID = (*it).event->eventID();

按该事件约定时间延时后再取事件:event = removeEventFromQueue_l(eventID);

再执行事件携带的动作:event->fire(this, now_us);

事件携带的动作(Event::fire) 是在AwesomeEvent类里实现的

struct AwesomeEvent : public TimedEventQueue::Event {
AwesomeEvent(
AwesomePlayer *player,
void (AwesomePlayer::*method)())
: mPlayer(player),
mMethod(method)
{
}

protected:
....

virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) {
(mPlayer->*mMethod)();
}

private:
AwesomePlayer *mPlayer;
void (AwesomePlayer::*mMethod)();

...

};

看下面两行代码:

virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) {
(mPlayer->*mMethod)();
}

mPlayer和mMethod 都是AwesomeEvent 的成员变量,(*mMethod)() 表示调用该指针指向的函数, mPlayer-> 表示把mPlayer 指针作为(*mMethod)()的第一个参数:this。

其中有关视频解压和显示的事件是 AwesomePlayer::mVideoEvent。

我们看看该事件是如何创建的:

AwesomePlayer::AwesomePlayer(){

.....

mVideoEvent = new AwesomeEvent(this, &AwesomePlayer::onVideoEvent);

}

可以看出 mVideoEvent 事件创建之后的 mPlayer是AwesomePlayer*, 而动作是AwesomePlayer::onVideoEvent。

我把AwesomePlayer::onVideoEvent()的代码简化和适当注释了一下放在了最后一段。开始通过调用mVideoSource->read(&mVideoBuffer, &options);来解压, 然后mVideoRenderer->render(mVideoBuffer);显示。中间有超时判断。不管哪个分支都有postVideoEvent_l()的调用,也就是说是每10ms就要触发一次解压和显示事件,然后根据视频帧的时戳决定是否延时。

在AwesomePlayer::onVideoEvent 里调用的 postVideoEvent_l(),原型是AwesomePlayer::postVideoEvent_l(int64_t delayUs=-1),声明缺省参数值是-1,所以在AwesomePlayer::onVideoEvent()不带参数调用的话,是以当时后延10ms的时间作为事件约定时间是来调用TimedEventQueue::postTimedEvent方法的。

通过 TimeSource *ts = ((mFlags & AUDIO_AT_EOS) || !(mFlags & AUDIOPLAYER_STARTED)) ? &mSystemTimeSource : mTimeSource;

这一句可以看出视频是用音频的时间作为同步参考时钟的。

void AwesomePlayer::onVideoEvent() {
Mutex::Autolock autoLock(mLock);
if (mSeeking != NO_SEEK) {
if (mVideoBuffer) {
mVideoBuffer->release();
mVideoBuffer = NULL;
}
}

for (;;) {

/// 这里做解压的动作,mVideoSource是一个基于OpenMax的decoder,在里面会通过连接的 DataSource 去读取原始的压缩数据,解压出可以显示的点阵数据。
status_t err = mVideoSource->read(&mVideoBuffer, &options); // mVideoSource is a video decoder
options.clearSeekTo();
break;
}

int64_t timeUs;
CHECK(mVideoBuffer->meta_data()->findInt64(kKeyTime, &timeUs));

mLastVideoTimeUs = timeUs;

TimeSource *ts =
((mFlags & AUDIO_AT_EOS) || !(mFlags & AUDIOPLAYER_STARTED))
? &mSystemTimeSource : mTimeSource; // mTimeSource = mAudioPlayer; so, timeSource is from audio.

if (mFlags & FIRST_FRAME) {
modifyFlags(FIRST_FRAME, CLEAR);
mSinceLastDropped = 0;
mTimeSourceDeltaUs = ts->getRealTimeUs() - timeUs;
}

int64_t realTimeUs, mediaTimeUs;
if (!(mFlags & AUDIO_AT_EOS) && mAudioPlayer != NULL
&& mAudioPlayer->getMediaTimeMapping(&realTimeUs, &mediaTimeUs)) {
mTimeSourceDeltaUs = realTimeUs - mediaTimeUs;
}

if (wasSeeking == NO_SEEK) { //忽略这一帧的显示
// Let's display the first frame after seeking right away.
int64_t nowUs = ts->getRealTimeUs() - mTimeSourceDeltaUs;
int64_t latenessUs = nowUs - timeUs;

if (latenessUs > 500000ll ) {
mVideoBuffer->release(); // delete this mediabuffer
mVideoBuffer = NULL;

mSeeking = SEEK_VIDEO_ONLY;
mSeekTimeUs = mediaTimeUs;

///给自己发一个10ms之后执行的事件。
postVideoEvent_l();
return;
}

if (latenessUs > 40000) { //忽略这一帧的显示
// We're more than 40ms late.
mVideoBuffer->release(); // delete this mediabuffer
mVideoBuffer = NULL;
++mStats.mNumVideoFramesDropped;

///给自己发一个10ms之后执行的事件。
postVideoEvent_l();
return;
}
if (latenessUs < -10000) {
// We're more than 10ms early.

///给自己发一个10ms之后执行的事件。
postVideoEvent_l(10000); // to display after a while
return;
}
}

if ((mNativeWindow != NULL)
&& (mVideoRendererIsPreview || mVideoRenderer == NULL)) {
mVideoRendererIsPreview = false;

initRenderer_l();
}

if (mVideoRenderer != NULL) {
mSinceLastDropped++;

//// 通过渲染库把点阵数据显示出来
mVideoRenderer->render(mVideoBuffer); // display it
}

mVideoBuffer->release();
mVideoBuffer = NULL;

if (wasSeeking != NO_SEEK && (mFlags & SEEK_PREVIEW)) {
modifyFlags(SEEK_PREVIEW, CLEAR);
return;
}

///给自己发一个10ms之后执行的事件。

postVideoEvent_l();
}

读书人网 >移动开发

热点推荐