先来看整个框架图:
NuPlayer基于Stagefright的基础类构建,使用了更底层的ALooper-AHandler-AMessage机制来异步处理消息。
AMessage作为一个消息载体,保存这与这个消息有关的信息;
ALooper是一个循环,它运行着一个后台线程,来循环处理接收到的消息(将信息转给AHandler来处理,它相当于一个中转站);
AHandler作为一个句柄,它是最终对消息进行处理的。
在实现上NuPlayer和Awesomeplayer不同,NuPlayer基于StagefrightPlayer的基础类构建,利用了更底层的ALooper/AHandler机制来异步地处理请求,ALooper列队消息请求,AHandler中去处理,所以有更少的Mutex/Lock在NuPlayer中。Awesomeplayer中利用了omxcodec,而NuPlayer中利用了Acodec。
首先需要分析AHandler,因为在Amessage::setTarget函数中,需要使用AHandler中的const_cast<AHandler *>(this)和mLooper,而这两个值需要在AHandler中初始化。
1. AHandler
先来看AHandler的定义:
android_m6.0.1_2.1.0/frameworks/av/include/media/stagefright/foundation/AHandler.h)
struct AHandler : public RefBase {
AHandler()
: mID(0),
mVerboseStats(false),
mMessageCounter(0) {
}//构造函数,为mID,mVerboseStats,mMessageCounter三个变量赋初始值。
ALooper::handler_id id() const {
return mID;
}
sp<ALooper> looper() const {
return mLooper.promote();
}
wp<ALooper> getLooper() const {
return mLooper;
}
wp<AHandler> getHandler() const {
// allow getting a weak reference to a const handler
return const_cast<AHandler *>(this);
}
protected:
virtual void onMessageReceived(const sp<AMessage> &msg) = 0;
//这是个虚函数,每个继承自AHandler的子类都需要实现这个虚函数,比如在NuPlayer中就实现了这个虚函数,最终对于Message的处理还是在这个函数中。
private:
friend struct AMessage; // deliverMessage()
friend struct ALooperRoster; // setID()
ALooper::handler_id mID;
wp<ALooper> mLooper;
inline void setID(ALooper::handler_id id, wp<ALooper> looper) {
mID = id;
mLooper = looper;
}//这个函数供友元类ALooperRoster来使用。
bool mVerboseStats;
uint32_t mMessageCounter;
KeyedVector<uint32_t, uint32_t> mMessages;
//这里是一个KeyedVector,用来存放Messages,它是一个键值对类型的容器。
void deliverMessage(const sp<AMessage> &msg);
//这个函数是AHandler.cpp需要去实现的一个函数,在里面调用onMessageReceived(msg)函数后再对mMessageCounter进行操作。这个函数是在AMessage::deliver()函数中调用的。
DISALLOW_EVIL_CONSTRUCTORS(AHandler);
};
从上面的接口来看,初步印象是AHandler没有直接对外的接口(只有获取成员变量的接口),基本上只有一个onMessageReceived用于子类的继承,deliverMessage用于给类AMessage使用,setID用于给友元类ALooperRoster来使用。所以从这点来说,真正的代码可能在AMessage中。
那这个AHandler是给谁用的呢?从名字上来看,它是一个句柄,同时也是这个ALooper-AHandler-AMessage机制的一个核心,所以如果想要在某个程序中使用这个消息机制,就要让主体继承自这个AHandler,然后在主体中实现onMessageReceived函数,同时也就可以使用AMessage了。
比如在Media playback中,这个主体就是NuPlayer,所以NuPlayer继承自AHanlder。
1.2 ALooperRoster类
从AHandler中setID函数的实现上面来看,是为AHandler中的的mID,mLooper两个成员赋初始值,而调用这个setID函数的正是ALooperRoster::registerHandler()和ALooperRoster::unregisterHandler()这两个函数,同时Roster的翻译就是花名册,所以,可以理解为ALooperRoster类来将ALooper和AHandler类关联起来,它是用来管理ALooper的。
2. AMessage类
.\libstagefright\foundation\AMessage.cpp
.\media\stagefright\foundation\AMessage.h
struct AMessage : public RefBase {
AMessage();
AMessage(uint32_t what, const sp<const AHandler> &handler);
//两个构造函数,但是第二个是常用的构造函数,通常指定id和需要哪个AHanlder来处理。
static sp<AMessage> FromParcel(const Parcel &parcel);
void writeToParcel(Parcel *parcel) const;
void setWhat(uint32_t what);
uint32_t what() const;
//如果不通过构造函数传入id的话,也可以通过这两个设置。
void setTarget(const sp<const AHandler> &handler);
//这个函数还是蛮重要的,通过这个函数,可以为这个类中的mTarget,mHandler,mLooper赋值,这个函数通常会被这样调用:msg->setTarget(this);通过这一步,就可以为msg设置Handler和Looper。注意这里本身传入的就是一个AHandler。
void clear();
void setInt32(const char *name, int32_t value);
void setInt64(const char *name, int64_t value);
void setSize(const char *name, size_t value);
void setFloat(const char *name, float value);
void setDouble(const char *name, double value);
void setPointer(const char *name, void *value);
void setString(const char *name, const char *s, ssize_t len = -1);
void setString(const char *name, const AString &s);
void setObject(const char *name, const sp<RefBase> &obj);
void setBuffer(const char *name, const sp<ABuffer> &buffer);
void setMessage(const char *name, const sp<AMessage> &obj);
void setRect(
const char *name,
int32_t left, int32_t top, int32_t right, int32_t bottom);
bool contains(const char *name) const;
bool findInt32(const char *name, int32_t *value) const;
bool findInt64(const char *name, int64_t *value) const;
bool findSize(const char *name, size_t *value) const;
bool findFloat(const char *name, float *value) const;
bool findDouble(const char *name, double *value) const;
bool findPointer(const char *name, void **value) const;
bool findString(const char *name, AString *value) const;
bool findObject(const char *name, sp<RefBase> *obj) const;
bool findBuffer(const char *name, sp<ABuffer> *buffer) const;
bool findMessage(const char *name, sp<AMessage> *obj) const;
bool findRect(
const char *name,
int32_t *left, int32_t *top, int32_t *right, int32_t *bottom) const;
status_t post(int64_t delayUs = 0);
//投递消息,新建一个消息后,设置属性,就可以调用post投递了。
status_t postAndAwaitResponse(sp<AMessage> *response);
bool senderAwaitsResponse(sp<AReplyToken> *replyID);
status_t postReply(const sp<AReplyToken> &replyID);
sp<AMessage> dup() const;
size_t countEntries() const;
const char *getEntryNameAt(size_t index, Type *type) const;
protected:
virtual ~AMessage();
private:
friend struct ALooper; // deliver()
uint32_t mWhat;
// used only for debugging
ALooper::handler_id mTarget;
wp<AHandler> mHandler;
wp<ALooper> mLooper;
Item *allocateItem(const char *name);
void freeItemValue(Item *item);
const Item *findItem(const char *name, Type type) const;
void setObjectInternal(
const char *name, const sp<RefBase> &obj, Type type);
size_t findItemIndex(const char *name, size_t len) const;
void deliver();//这个接口给ALooper调用,发送消息的接口。
DISALLOW_EVIL_CONSTRUCTORS(AMessage);
};
从上面的接口可以看出,在使用AMessage时只需要指定消息的id和要处理该消息的AHandler即可,可以通过AMessage的构造函数中指定,也可以单独调用setWhat和setTarget接口。AMessage构造完成之后,可以调用setxxx设置对应的参数,通过findxxx获取传递的参数。最后通过post即可将消息投递到AHandler的消息队列中。
下面就是一个消息从创建到发送的过程:
sp<AMessage> notify = dupNotify();
notify->setInt32("what", kWhatPrepared);
notify->setInt32("err", err);
notify->post();
3. ALooper
(这里插入ALooper类)
ALooper的对外接口比较简单,通常就是NuPlayerDriver构造函数中的调用逻辑,先创建一个ALooper对象,然后调用setName和start接口,之后调用registerHandler设置一个Handler,这样就完成了初始化。
创建过程
mLooper(new ALooper),
mLooper->setName("NuPlayerDriver Looper");
mLooper->start(
false, /* runOnCallingThread */
true, /* canCallJava */
PRIORITY_AUDIO);
mLooper->registerHandler(mPlayer);
在ALooper::start函数中会启动一个线程,并调用ALooper::loop函数,该函数主要实现消息的实际执行,如下所示:
bool ALooper::loop() {
Event event;
{
Mutex::Autolock autoLock(mLock);
if (mThread == NULL && !mRunningLocally) {
return false;
}
if (mEventQueue.empty()) {
mQueueChangedCondition.wait(mLock);
return true;
}
int64_t whenUs = (*mEventQueue.begin()).mWhenUs;
int64_t nowUs = GetNowUs();
if (whenUs > nowUs) {
int64_t delayUs = whenUs - nowUs;
mQueueChangedCondition.waitRelative(mLock, delayUs * 1000ll);
return true;
}
event = *mEventQueue.begin();
mEventQueue.erase(mEventQueue.begin());
}
event.mMessage->deliver();
// NOTE: It's important to note that at this point our "ALooper" object
// may no longer exist (its final reference may have gone away while
// delivering the message). We have made sure, however, that loop()
// won't be called again.
return true;
}
说一下这里的逻辑:首先ALooper中维护着一个Event链表,List<Event> mEventQueue;这个Event结构体中包含一个AMessage消息和一个时间值,在ALooper::loop()函数中会一直监听着这个链表,如果有一个Event的话,就从链表中取出Event,调用event.mMessage->deliver()函数。
在AMessage::deliver()函数中,就会去获取这个AMessage的Ahandler,然后调用对应AHandler的deliverMessage函数:handler->deliverMessage(this)。
在AHandler::deliverMessage(const sp<AMessage> &msg)函数中,就会调用AHandler中的onMessageReceived(msg)函数,如果这时候传入的是AHandler子类的话,就会去调用AHandler子类的onMessageReceived(msg)函数。
4. 实例
void NuPlayer::setVideoSurfaceTextureAsync(
const sp<IGraphicBufferProducer> &bufferProducer) {
sp<AMessage> msg = new AMessage(kWhatSetVideoSurface, this);
if (bufferProducer == NULL) {
msg->setObject("surface", NULL);
} else {
msg->setObject("surface", new Surface(bufferProducer, true /* controlledByApp */));
}
msg->post();
}
5. 上面这些消息都是异步实现的,如果想要同步消息,在这个消息机制中的设计很巧妙,它是新建一个AMessage,然后通过这个新建的AMessage来传输回复的消息,实例如下:
status_t NuPlayer::getPlaybackSettings(AudioPlaybackRate *rate /* nonnull */) {
sp<AMessage> msg = new AMessage(kWhatGetPlaybackSettings, this);
sp<AMessage> response;
status_t err = msg->postAndAwaitResponse(&response);
if (err == OK && response != NULL) {
CHECK(response->findInt32("err", &err));
if (err == OK) {
readFromAMessage(response, rate);
}
}
return err;
}
6.
sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
7.
sp<AMessage> response = new AMessage;
response->setInt32("err", err);
response->postReply(replyID);
8. msg中findObject和setObject的用法:
void NuPlayer::setVideoSurfaceTextureAsync(
const sp<IGraphicBufferProducer> &bufferProducer) {
sp<AMessage> msg = new AMessage(kWhatSetVideoSurface, this);
if (bufferProducer == NULL) {
msg->setObject("surface", NULL);
} else {
msg->setObject("surface", new Surface(bufferProducer, true /* controlledByApp */));
}
msg->post();
}
之后在OnMessageReceived函数中:
case kWhatSetVideoSurface:
{
sp<RefBase> obj;
CHECK(msg->findObject("surface", &obj));
sp<Surface> surface = static_cast<Surface *>(obj.get());
这里通过一个static_cast转换,就能够把这个sp<Surface>结构体通过msg传递过来。
最终总结一句:很多同学到这里就不理解了,为什么这里要使用这么复杂的机制,直接在函数中把这些操作直接顺序写下来不就行了?因为在Media相关的地方,很多操作都是耗时很长的操作,但是用户对于画面的流程度的容忍度很低,举一个很简单的例子,在视频的播放过程中,如果我想直接把进度条拖到中间的位置开始播放,当我点击完毕后,NuPlayer内部就开始执行了seek操作,但是这时候耗时比较长,画面就卡在这里了,用户体验度很差。但是如果我使用类似的机制,画面继续播放之前缓冲的数据,而NuPlayer抓紧去执行seek操作,当seek操作执行完毕后,直接把画面切换到这个位置开始播放,这样的话,画面不会卡住,而用户体验度就会好很多。