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

来源:互联网 发布:dnf一键任务源码 2016 编辑:程序博客网 时间:2024/05/17 08:19

播放一个视频开始时要调用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();
}

原创粉丝点击