awesomeplayer里面,最重要的一个函数,就是AwesomePlayer::onVideoEvent
这个函数囊括了AV同步,元数据读取,帧数据显示等很多工作。
我们前面提到过这个函数。现在针对这个函数总结一下他的工作流程
首先总结一下onVideoEvent是如何产生的
前面讲过,status_t AwesomePlayer::prepareAsync_l() 调用的时候,会调用mQueue.start
首先被创建
//一阵视频解码完,openmax component会触发FILL_BUFFER_DONE的onMessage,
//OMXCodec::on_message中会mBufferFilled.signal, 从而使mVideoSource->read跳出等待,继续往下执行
//以下进行AV同步。具体在前面文章介绍过
并且通过控制触发自己下一次执行的时间,实现了AV同步。
并且通过mVideoSource->read实现了元数据读取,emptythisbuffer命令下发
通过mVideoRenderer->render实现了图像显示,
通过mVideoBuffer->release实现了fillthisbuffer命令下发
这个函数囊括了AV同步,元数据读取,帧数据显示等很多工作。
我们前面提到过这个函数。现在针对这个函数总结一下他的工作流程
首先总结一下onVideoEvent是如何产生的
前面讲过,status_t AwesomePlayer::prepareAsync_l() 调用的时候,会调用mQueue.start
status_t AwesomePlayer::prepareAsync_l() {
if (!mQueueStarted) {
mQueue.start();
mQueueStarted = true;
}
}
这里实际上就是创建一个线程
<pre name="code" class="cpp">void TimedEventQueue::start() {
pthread_create(&mThread, &attr, ThreadWrapper, this);
}
void *TimedEventQueue::ThreadWrapper(void *me) {
static_cast<TimedEventQueue *>(me)->threadEntry();
}
void TimedEventQueue::threadEntry() {
for (;;) {
{
event_id eventID = 0;
for (;;) {
List<QueueItem>::iterator it = mQueue.begin();
eventID = (*it).event->eventID();//取事件
now_us = ALooper::GetNowUs();
int64_t when_us = (*it).realtime_us; //获得约定等待时间
int64_t delay_us;
if (when_us < 0 || when_us == INT64_MAX) {
delay_us = 0;
} else {
delay_us = when_us - now_us;
}
if (delay_us <= 0) {
break;
}
static int64_t kMaxTimeoutUs = 10000000ll; // 10 secs
bool timeoutCapped = false;
if (delay_us > kMaxTimeoutUs) {
delay_us = kMaxTimeoutUs;
timeoutCapped = true;
}
status_t err = mQueueHeadChangedCondition.waitRelative(
mLock, delay_us * 1000ll);//按照约定时间进行等待
if (!timeoutCapped && err == -ETIMEDOUT) {
// We finally hit the time this event is supposed to
// trigger.
now_us = ALooper::GetNowUs();
break;
}
}
event = removeEventFromQueue_l(eventID);//等待时间到,取事件,
}
if (event != NULL) {
event->fire(this, now_us); //执行事件的对应函数
}
}
}
virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) {
(mPlayer->*mMethod)();
}
//AwesomePlayer的构造函数里面,执行mVideoEvent = new AwesomeEvent(this, &AwesomePlayer::onVideoEvent);//在AwesomeEvent的构造函数里,将onVideoEvent函数挂接到mMethod指针上。
AwesomeEvent(AwesomePlayer *player, void (AwesomePlayer::*method)())
: mPlayer(player),
mMethod(method)
{
}
mPlayer和mMethod 都是AwesomeEvent 的成员变量,(*mMethod)() 表示调用该指针指向的函数, mPlayer-> 表示把mPlayer 指针作为(*mMethod)()的第一个参数:this。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); //这样就把事件填到了列表里面
....
}
具体到AwesomePlayer::mVideoEvent这个事件,我们做如下说明:
首先被创建
AwesomePlayer::AwesomePlayer(){
mVideoEvent = new AwesomeEvent(this, &AwesomePlayer::onVideoEvent);
}
//mVideoEvent 事件创建之后的 mPlayer是AwesomePlayer*, 动作是AwesomePlayer::onVideoEvent。
首先在AwesomePlayer::play_l里面,会通过调用postVideoEvent_l来触发第一次onVideoEvent
void AwesomePlayer::postVideoEvent_l(int64_t delayUs) {
mVideoEventPending = true;
mQueue.postEventWithDelay(mVideoEvent, delayUs < 0 ? 10000 : delayUs);//默认延时10ms
}
然后onVideoEvent开始进入第一次执行
void AwesomePlayer::onVideoEvent() {
for (;;) {
status_t err = mVideoSource->read(&mVideoBuffer, &options); // mVideoSource is a video decoder
options.clearSeekTo();
break;
}
//前面我们介绍过,awesomeplayer把一帧数据送给decoder后,会在mVideoSource->read(即OMXCodec::read)等待,
//一阵视频解码完,openmax component会触发FILL_BUFFER_DONE的onMessage,
//OMXCodec::on_message中会mBufferFilled.signal, 从而使mVideoSource->read跳出等待,继续往下执行
//以下进行AV同步。具体在前面文章介绍过
<pre name="code" class="cpp"> 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;
}
}
//以上进行AV同步。具体在前面文章介绍过
mVideoRenderer->render(mVideoBuffer); 通过surfaceflinger把数据显示出来
mVideoBuffer->release(); //显示完毕,通过调用signalBufferReturned,掉用fillOutputBuffer
mVideoBuffer = NULL;
///给自己发一个10ms之后执行的事件。触发下一次执行,然后退出
postVideoEvent_l();
}
实际上onVideoEvent是通过一次次的触发自己下一次执行,来实现的一帧帧的解码。
并且通过控制触发自己下一次执行的时间,实现了AV同步。
并且通过mVideoSource->read实现了元数据读取,emptythisbuffer命令下发
通过mVideoRenderer->render实现了图像显示,
通过mVideoBuffer->release实现了fillthisbuffer命令下发