1 TimedEventQueue
来源:互联网 发布:模架编程 编辑:程序博客网 时间:2024/05/03 01:27
目录
1、概述
2、一个典型的使用TimedEventQueue的例子
3、TimedEventQueue类介绍
1概述
TimeEventQueue是android多媒体系统中实现异步操作的一种手段
在Awesomeplayer中,依赖mVideoEvent、mStreamDoneEvent、mBufferingEvent、mCheckAudioStatusEvent等几个事件驱动着整个播放流程
可以认为TimeEventQueue 是一个调度系统,调度的对象是事件Event,一个TimeEventQueue 可以同时处理多个事件
本篇只分析TimeEventQueue 类结构及其用法
2、一个典型的使用TimedEventQueue的例子
一个简单的使用TimedEventQueue 的序列如下
mEventRead = new dtEvent(this,&dtPlayer::onReadEvent); mQueue.start(); mQueue.postEvent(mEventRead); mQueue.stop();
【说明】
首先建立一个事件
启动TimedEventQueue
通过postEvent触发事件,事件触发后会执行mEventRead对应的fire函数
最后结束
我们首先建立一个事件类
structdtEvent : publicTimedEventQueue::Event { dtEvent(dtPlayer *player,void(dtPlayer::*method)()): mPlayer(player), mMethod(method) { } protected: virtual~dtEvent() {} virtualvoid fire(TimedEventQueue *queue, int64_t/* now_us */) { (mPlayer->*mMethod)(); } private: dtPlayer *mPlayer; void(dtPlayer::*mMethod)(); dtEvent(constdtEvent &); dtEvent &operator=(constdtEvent &); };
这里处理方法与awesomeplayer等类似,由于Event是抽象类,不能直接使用,因此需要建立一个继承自Event的子类,并重载fire方法
这里构造函数中传入了dtPlayer::*method 参数,作为fire中执行的函数体。这样就保证了对于每一个事件,通过传入不同的mMethod方法,便可以做出不同的反应
类似Awesomeplayer,我们需要为TimedEventQueue建立一个载体类dtPlayer
class dtPlayer{ public: dtPlayer(); virtual ~dtPlayer(){}; private: TimedEventQueue mQueue; bool mQueueStarted; sp<TimedEventQueue::Event> mEventRead; sp<TimedEventQueue::Event> mEventWrite; sp<TimedEventQueue::Event> mEventSearch; voidonReadEvent(); voidonWriteEvent(); voidonSearchEvent(); public: voidstart(); voidstop(); bool isplaying(); voidpostRandomEvent(); private: dtPlayer(constdtPlayer &); dtPlayer &operator=(constdtPlayer &); };
在载体类中,我们有
TimedEventQueue 对象mQueue,
三个Event对象,分别是mEventRead mEventWrite mEventSearch
三个方法,分别是传入给上面三个事件的参数,是void onReadEvent(); void onWriteEvent(); void onSearchEvent();
状态机:start stop 等
postRandomEvent 方法:封装的触发事件的方法
下面看下具体实现
dtPlayer::dtPlayer() { mEventRead =new dtEvent(this,&dtPlayer::onReadEvent); mEventWrite =new dtEvent(this,&dtPlayer::onWriteEvent); mEventSearch =new dtEvent(this,&dtPlayer::onSearchEvent); mQueueStarted=false; }
先看下构造函数,构造函数主要是生成三个事件,并将参数传递进去,这里可以看到调用的是重载的event的构造函数
并设置mQueue状态
void dtPlayer::onReadEvent() { ALOGI("--read event occur-- read something \n"); } void dtPlayer::onWriteEvent() { ALOGI("--write event occur-- write something \n"); } void dtPlayer::onSearchEvent() { ALOGI("--search event occur-- search something \n"); }
对应的实现,只加了打印,表示执行过了
void dtPlayer::start() { if(mQueueStarted==false) mQueue.start(); mQueueStarted=true; printf("dtplayer start \n"); } void dtPlayer::stop() { if(mQueueStarted==true) mQueue.stop(true); mQueueStarted=false; } bool dtPlayer::isplaying() { return(mQueueStarted==true); }
状态机代码,主要是控制mQueue的状态
void dtPlayer::postRandomEvent() { mQueue.postEvent(mEventRead); }
事件触发函数,这里可以看到触发方法便是调用mQueue的postEvent方法,并且将事件对象作为参数传入。这样便可以执行对应事件的fire方法了
extern "C"int dt_test() { ALOGE("--post event start \n"); dtPlayer *player=newdtPlayer(); player->start(); player->postRandomEvent(); player->stop(); ALOGE("--post event end\n"); sleep(5); return0; }
这里实现了测试code,只是简单的触发一个事件然后返回
【说明】具体的例子源代码已经打包放在了附件中
测试方法:
(1)将压缩包拷贝至framework/av/media/libstagefright目录下
(2)解压缩并进入_timeeventqueue_test文件夹
(3)mm
(4)将可执行文件拷贝到目标板执行即可
通过上面的例子可以看到,TimedEventQueue 的使用时比较简单的,只要新建一个Event子类,并重载fire方法
之后通过TimedEventQueue 的postEvent等就可以出发执行了
而且这里一个TimedEventQueue 可以支持多个事件
下面看下TimedEventQueue 具体是如何实现这一机制的
3、TimedEventQueue类介绍
2.1 event事件
TimedEventQueue调度的对象是事件
先看下事件的定义,事件是定义在TimedEventQueue 结构体内部的结构体
typedef int32_t event_id; struct Event : public RefBase { Event() : mEventID(0) { } virtual ~Event() {} event_id eventID() { return mEventID; } protected: virtual void fire(TimedEventQueue *queue, int64_t now_us) = 0; private: friend class TimedEventQueue; event_id mEventID; void setEventID(event_id id) { mEventID = id; } Event(const Event &); Event &operator=(const Event &); };
说明:
mEventID 事件ID
fire 纯虚函数,事件触发时调度执行的函数
因此Event不能直接使用,需继承并重载fire方法后使用
2.2 TimedEventQueue 一些重要方法解析
挑一下比较重要的解释下
void TimedEventQueue::start() { if(mRunning) { return; } mStopped =false; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); pthread_create(&mThread, &attr, ThreadWrapper,this); pthread_attr_destroy(&attr); mRunning =true; }
这里调用mQueue->start 后主要是启动一个线程,看下线程代码
// static void *TimedEventQueue::ThreadWrapper(void *me) { androidSetThreadPriority(0, ANDROID_PRIORITY_FOREGROUND); static_cast<TimedEventQueue *>(me)->threadEntry(); returnNULL; }
这里线程实现方法的主要作用是调用mQueue->threadEntry 方法,因为上面传入的参数是this指针
代码比较多我们分段来看
void TimedEventQueue::threadEntry() { prctl(PR_SET_NAME, (unsigned long)"TimedEventQueue",0, 0,0); for(;;) { int64_t now_us =0; sp<Event> event; { Mutex::Autolock autoLock(mLock); if(mStopped) { break; } while(mQueue.empty()) { mQueueNotEmptyCondition.wait(mLock); }
首先判断mStopped,确定是否要退出
然后判断mQueue中是否为空,若未空表示没有要处理的事件,则等待
event_id eventID = 0; for(;;) { if(mQueue.empty()) { // The only event in the queue could have been cancelled // while we were waiting for its scheduled time. break; } 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; } staticint64_t kMaxTimeoutUs = 10000000ll; // 10 secs booltimeoutCapped = false; if(delay_us > kMaxTimeoutUs) { ALOGW("delay_us exceeds max timeout: %lld us", delay_us); // We'll never block for more than 10 secs, instead // we will split up the full timeout into chunks of // 10 secs at a time. This will also avoid overflow // when converting from us to ns. 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; } }
若mQueue事件列表非空,则有要处理的事件,进入for loop进行处理,主要工作有
从mQueue的event列表中获取一个event
判断是否执行,如果时间到了(delay_us <= 0),则跳出循环执行事件处理函数,否则若需等待时间低于10s,则mQueueHeadChangedCondition.waitRelative等待。若高于10s,等10s后重新轮询
当条件满足时,通过event = removeEventFromQueue_l(eventID);获取事件对象,并调用event->fire(this, now_us);
这里有几个问题
(1)对mQueue,每个event都有一个唯一eventID,mQueue通过eventID来获取事件对象,eventID如何计算的
此问题的答案在mQueue->postEvent中。看下代码
TimedEventQueue::event_id TimedEventQueue::postEvent(constsp<Event> &event) { // Reserve an earlier timeslot an INT64_MIN to be able to post // the StopEvent to the absolute head of the queue. returnpostTimedEvent(event, INT64_MIN + 1); }
TimedEventQueue::event_id TimedEventQueue::postTimedEvent( constsp<Event> &event, int64_t realtime_us) { Mutex::Autolock autoLock(mLock); event->setEventID(mNextEventID++); 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); mQueueNotEmptyCondition.signal(); returnevent->eventID(); }
从代码可以看出,postEvent会调用postTimedEvent,而在postTimedEvent中第一步操作就是设置eventID,这也就保证了每个event都有唯一一个eventID标识
另外,会将事件通过mQueue.insert(it, item); 插入到TimedEventQueue->mQueue中,如下定义
struct QueueItem { sp<Event> event; int64_t realtime_us; };
mQueue是一个QueueItem列表,存储了事件对象及要触生的时间
(2)每次mQueue都从列表中取出第一个事件进行处理,但有个问题,若此事件阻塞的时候,其他事件需要触发,怎么处理?
玄机同样在postTimedEvent中,在插入列表之前,会首先查找要插入到TimedEventQueue->mQueue 中的位置,按照触发时间排序
2.3 其他方法介绍
TimedEventQueue 中还有一些接口扩展使得其功能更加强大,主要有
event_id postEventToBack(const sp<Event> &event); 插入到事件列表末尾
TimedEventQueue::event_id TimedEventQueue::postEventWithDelay(const sp<Event> &event, int64_t delay_us) ; 注册事件同时设置delay时间
bool TimedEventQueue::cancelEvent(event_id id);注销某个事件
void TimedEventQueue::cancelEvents(*);注销所有事件
- 1 TimedEventQueue
- TimedEventQueue分析
- TimedEventQueue分析
- stagefright 架构分析(二) TimedEventQueue
- AwesomePlayer中TimedEventQueue的实现
- Android多媒体开发【7】-- AwesomePlayer中TimedEventQueue-event事件调度器
- Android多媒体开发【7】-- AwesomePlayer中TimedEventQueue-event事件调度器
- Android多媒体开发【7】-- AwesomePlayer中TimedEventQueue-event事件调度器
- 1
- 1
- 1
- 1
- 1
- 1
- 1》
- 1
- 1
- 1
- hdu1124-Factorial
- Android图片异步加载与本地缓存的实现
- [QT]VS2010+QT4.8.4开发环境搭建与测试
- linux history 命令详解
- 同步和异步的区别
- 1 TimedEventQueue
- Hadoop namenode无法启动
- poj1182-食物链
- cocos2d-x中文显示问题解决方法(使用cocos2dx中自带的iconv实现)
- TASK_INTERRUPTIBLE 和TASK_UNINTERRUPTIBLE 的区别
- Oracle存储过程基本语法
- 每天一题(48) - C++实现Singleton模式
- 奇偶校验
- IOS NSPredicate 查询、搜索(详解)