Android多媒体开发(三)----从StageFright到AwesomePlayer

       上一节我们分析到了MediaPlayer的C/S架构最下层的StagefrightPlayer,今天我们继续往下挖,看看这个东西到底是个什么鬼。

皮包公司StagefrightPlayer

       上一节我们再次止步于C++层的setDateSource,但是我们初窥了StatefrightPlayer,简要了解android高版本对opencore的舍弃,和采用了Stagefright框架。所以我们来看看这个StagefrightPlayer是怎么实现的。位于framework/av/media/libmediaplayerservice/StagefrightPlayer.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
......
StagefrightPlayer::StagefrightPlayer()
: mPlayer(new AwesomePlayer) { //StagefrightPlayer构造方法默认创建了AwesomePlayer代替他完成职能
ALOGV("StagefrightPlayer");

mPlayer->setListener(this);
}

StagefrightPlayer::~StagefrightPlayer() {
ALOGV("~StagefrightPlayer");
reset();

delete mPlayer;
mPlayer = NULL;
}

// Warning: The filedescriptor passed into this method will only be valid until
// the method returns, if you want to keep it, dup it!
status_t StagefrightPlayer::setDataSource(int fd, int64_t offset, int64_t length) {
ALOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
return mPlayer->setDataSource(dup(fd), offset, length);
}



status_t StagefrightPlayer::setVideoSurfaceTexture(
const sp<IGraphicBufferProducer> &bufferProducer) {
ALOGV("setVideoSurfaceTexture");

return mPlayer->setSurfaceTexture(bufferProducer);
}

status_t StagefrightPlayer::prepare() {
return mPlayer->prepare();
}

...省略诸多AwesomePlayer代替他完成职能的方法

......

       我们看到这个StagefrightPlayer其实是个皮包公司,对MediaPlayerInterface接口的实现方法都是由AwesomePlayer这个播放器来实现的。(听着名字都应该是装饰模式关系,StageFright叫舞台恐怖者,Awesome是令人畏惧者,一个前台耍花腔散播恐惧,一个后台实际操控恐怖主义)。
       前面一篇中,分析到mediaplayerservice会调到Stagefright中,进行编码解码操作
在libsstagefright中,预设的多媒体解码是openCore,由于其过于庞大和复杂,需要成本较高,开始引进了另一个框架,也就是stagefright框架,以后默认情况Android选择stagefright,但是并没有完全抛弃opencore,做了一个OMX层,仅仅是对 opencore的omx-component部分做了引用。stagefright是和opencore是并列的。Stagefright在 Android中是以shared library的形式存在(libstagefright.so),其中的module – AwesomePlayer可用来播放video/audio。 AwesomePlayer提供许多API,可以让上层的应用程序(Java/JNI)来调用。

播放器基本模型

       AwesomePlayer不管有多么神秘,说到底还是个播放器。在播放器的基本模型上,他与VCL、mplayer、ffmpeg等开源的结构是一致的。只是组织实现的方式不同。
       深入了解AwesomePlayer 之前,把播放器的基本模型总结一下,然后按照模型的各个部分来深入研究AwesomePlayer 的实现方式。
播放器模型

       播放器大致分为4大部分:source、demux、decoder、output。

  • source数据源:数据源,数据的来源不一定都是本地file,也有可能是网路上的各种协议例如:http、rtsp、HLS等。source的任务就是把数据源抽象出来,为下一个demux模块提供它需要的稳定的数据流。demux不用关信数据到底是从什么地方来的。
  • demux解复用:视频文件一般情况下都是把音视频的ES流交织的通过某种规则放在一起。这种规则就是容器规则。现在有很多不同的容器格式。如ts、mp4、flv、mkv、avi、rmvb等等。demux的功能就是把音视频的ES流从容器中剥离出来,然后分别送到不同的解码器中。其实音频和视频本身就是2个独立的系统。容器把它们包在了一起。但是他们都是独立解码的,所以解码之前,需要把它分别 独立出来。demux就是干这活的,他为下一步decoder解码提供了数据流。
  • decoder解码:解码器—-播放器的核心模块。分为音频和视频解码器。影像在录制后, 原始的音视频都是占用大量空间, 而且是冗余度较高的数据. 因此, 通常会在制作的时候就会进行某种压缩 ( 压缩技术就是将数据中的冗余信息去除数据之间的相关性 ). 这就是我们熟知的音视频编码格式, 包括MPEG1(VCD)\ MPEG2(DVD)\ MPEG4 \ H.264 等等. 音视频解码器的作用就是把这些压缩了的数据还原成原始的音视频数据. 当然, 编码解码过程基本上都是有损的 .解码器的作用就是把编码后的数据还原成原始数据。
  • output输出:输出部分分为音频和视频输出。解码后的音频(pcm)和视频(yuv)的原始数据需要得到音视频的output模块的支持才能真正的让人的感官系统(眼和耳)辨识到。

       所以,播放器大致分成上述4部分。怎么抽象的实现这4大部分、以及找到一种合理的方式将这几部分组织并运动起来。是每个播放器不同的实现方式而已。接下来就围绕这4大部分做深入学习,看看AwesomePlayer 是怎么玩的吧。

AwesomePlayer基础

AwesomePlayer事件调度器————TimedEventQueue-event

       在分析AwesomePlayer的主要流程前,我们先了解一下它的时间调度机制,为以后分析主流程打好基础。
       视频处理过程中有很多都是十分耗时的,如果都放在一个大的线程空间中。用户体验的效果可想而知。所以通常都是做异步操作。

       AwesomePlayer是通过event事件调度来实现这些功能之间的驱动和调用的,类似于上层的Handler-Looper。AwesomePlayer中的内部变量TimedEventQueue mQueue;位于framework/av/media/libstagefright/include/AwesomePlayer.h中:

1
TimedEventQueue mQueue;

       这个mQueue就是AwesomePlayer的事件队列,也是事件调度器。从他类型的名字上就能很清楚的看出他是以时间为基础事件队列。接下来看看它是怎么玩转的。
事件调度器

       1. 我们看看TimedEventQueue的内部结构,位于framework/av/media/libstagefright/include/TimedEventQueue.h中。TimedEventQueue内部有一个List< QueueItem >,每个QueueItem包含enent和触发事件时间,还有一个是否持有唤醒锁的标志位。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
List<QueueItem> mQueue;//触发事件队列
pthread_t mThread;//独立调度线程
typedef int32_t event_id;//这里是个typedef,下面Event结构体的事件id会用到
event_id mEventID;
//事件队列单项结构体
struct QueueItem {
sp<Event> event;//事件
int64_t realtime_us;//触发时间
bool has_wakelock;
};
//封装事件结构体
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;//每个事件自动分配的id,初始值为1,每多一个事件id会+1
void setEventID(event_id id) {
mEventID = id;
}
Event(const Event &);
Event &operator=(const Event &);

       这里有一个独立线程mThread,它主要负责事件调度。是在TimedEventQueue::start被创建,TimedEventQueue::stop被销毁的。位于framework/av/media/libstagefright/TimedEventQueue.cpp中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
void TimedEventQueue::start() {
if (mRunning) {
return;
}

mStopped = false;

pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
//创建调度线程,同时调用ThreadWrapper方法
pthread_create(&mThread, &attr, ThreadWrapper, this);

pthread_attr_destroy(&attr);

mRunning = true;
}

void TimedEventQueue::stop(bool flush) {
if (!mRunning) {
return;
}

if (flush) {
postEventToBack(new StopEvent);
} else {
postTimedEvent(new StopEvent, INT64_MIN);
}

void *dummy;
pthread_join(mThread, &dummy);

// some events may be left in the queue if we did not flush and the wake lock
// must be released.
releaseWakeLock_l(true /*force*/);
mQueue.clear();

mRunning = false;

       2. 这里创建了调度线程,然后执行了ThreadWrapper方法,我们继续看看这个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// static
void *TimedEventQueue::ThreadWrapper(void *me) {
androidSetThreadPriority(0, ANDROID_PRIORITY_FOREGROUND);
//调用了threadEntry方法
static_cast<TimedEventQueue *>(me)->threadEntry();
return NULL;
}

void TimedEventQueue::threadEntry() {
prctl(PR_SET_NAME, (unsigned long)"TimedEventQueue", 0, 0, 0);

for (;;) {
int64_t now_us = 0;
sp<Event> event;
bool wakeLocked = false;

{
Mutex::Autolock autoLock(mLock);
//如果停止标志则退出轮询
if (mStopped) {
break;
}
//事件队列为空,则线程等待
while (mQueue.empty()) {
mQueueNotEmptyCondition.wait(mLock);
}

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) {//如果等待时间小于0或者是int_64最大值,则将等待时间置为0
delay_us = 0;
} else {//正常等待
delay_us = when_us - now_us;
}
//等待时间小于等于0,则跳出这次事件轮询
if (delay_us <= 0) {
break;
}
//等待的最大超时时间为10秒
static int64_t kMaxTimeoutUs = 10000000ll; // 10 secs
bool timeoutCapped = false;
if (delay_us > kMaxTimeoutUs) {
ALOGW("delay_us exceeds max timeout: %" PRId64 " 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.
//如果等待时长大于10秒,则强制让它10秒后执行。
//上面注释说这样避免了从毫秒到纳秒转化后的值溢出int_64最大范围。
//然后将超时标签置为true
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;
}
}

// The event w/ this id may have been cancelled while we're
// waiting for its trigger-time, in that case
// removeEventFromQueue_l will return NULL.
// Otherwise, the QueueItem will be removed
// from the queue and the referenced event returned.
//如果在等待时间过程中,事件被移除了,则返回NULL
//如果正常情况,则返回这个事件,同时从队列中移除这个事件
event = removeEventFromQueue_l(eventID, &wakeLocked);
}

if (event != NULL) {
// Fire event with the lock NOT held.
//发射事件,执行事件方法
event->fire(this, now_us);
if (wakeLocked) {
Mutex::Autolock autoLock(mLock);
releaseWakeLock_l();
}
}
}
}
//如果在等待时间过程中,事件被移除了,则返回NULL
//如果正常情况,则返回这个事件,同时从队列中移除这个事件
sp<TimedEventQueue::Event> TimedEventQueue::removeEventFromQueue_l(
event_id id, bool *wakeLocked) {
for (List<QueueItem>::iterator it = mQueue.begin();
it != mQueue.end(); ++it) {
if ((*it).event->eventID() == id) {
sp<Event> event = (*it).event;
event->setEventID(0);
*wakeLocked = (*it).has_wakelock;
mQueue.erase(it);
return event;
}
}

ALOGW("Event %d was not found in the queue, already cancelled?", id);

return NULL;
}

       这一段代码比较长,其实就是讲List目的就是按照延时时间维护一个event事件队列,threadEntry线程就是不断的从队列的头取出一个event,然后通过 event->fire(this, now_us); 回调到这个event事件提前注册好的相对应功能函数。这一部分和Handler-Looper很像,应该不难理解。
发射执行事件比较简单,我们看看AwesomeEvent这个继承TimedEventQueue::Event的实现类,位于framework/av/media/libstagefright/AwesomePlayer.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct AwesomeEvent : public TimedEventQueue::Event {
AwesomeEvent(
AwesomePlayer *player,
void (AwesomePlayer::*method)())
: mPlayer(player),
mMethod(method) {
}

protected:
virtual ~AwesomeEvent() {}
//发射事件,调用AwesomePlayer的相关方法
virtual void fire(TimedEventQueue * /* queue */, int64_t /* now_us */) {
(mPlayer->*mMethod)();
}

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

AwesomeEvent(const AwesomeEvent &);
AwesomeEvent &operator=(const AwesomeEvent &);

       3. 然后看看AwesomePlayer是怎么用TimedEventQueue,AwesomePlayer会定义很多类型的event事件,并把和这些事件相关的功能函数一定绑定起来。比如AwesomePlayer的构造方法里有这么几个,位于framework/av/media/libstagefright/AwesomePlayer.cpp中:

1
2
3
4
5
6
7
8
9
10
......
mVideoEvent = new AwesomeEvent(this, &AwesomePlayer::onVideoEvent);
mVideoEventPending = false;
mStreamDoneEvent = new AwesomeEvent(this, &AwesomePlayer::onStreamDone);
mStreamDoneEventPending = false;
mBufferingEvent = new AwesomeEvent(this, &AwesomePlayer::onBufferingUpdate);
mBufferingEventPending = false;
mVideoLagEvent = new AwesomeEvent(this, &AwesomePlayer::onVideoLagUpdate);
mVideoLagEventPending = false;
......

       当然还有一些Event是在方法内部构造的。

       原因之前也说了,因为好多音视频处理的功能是十分耗时间的,假如AwesomePlayer 想用某个功能,他并不是直线去调用它,而是抽象成一种AwesomeEvent,将想要调用的功能函数与事件捆绑。通过TimedEventQueue::postTimedEvent(),按照延时的优先顺序把它放到TimedEventQueue的队列之中。然后AwesomePlayer就不管了。TimedEventQueue start之后,自己内部的线程会从队列中依次取出这些事件,然后通过event->fire回调事件的功能函数。这样就达到了AwesomePlayer的目的。

       4. 我们举个栗子,比如prepareAsync过程,我们看看AwesomePlayer的prepareAsync过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
status_t AwesomePlayer::prepareAsync() {
......
return prepareAsync_l();
}

status_t AwesomePlayer::prepareAsync_l() {
if (mFlags & PREPARING) {
return UNKNOWN_ERROR; // async prepare already pending
}

if (!mQueueStarted) {
mQueue.start();
mQueueStarted = true;
}

modifyFlags(PREPARING, SET);
//在这里new了一个AwesomeEvent给onPrepareAsyncEvent事件回调
mAsyncPrepareEvent = new AwesomeEvent(
this, &AwesomePlayer::onPrepareAsyncEvent);

mQueue.postEvent(mAsyncPrepareEvent);

return OK;
}

       他并没有实际的调用onPrepareAsyncEvent()真正的功能函数,他只是把mQueue start之后,然后创建个mAsyncPrepareEvent事件,把它插入到mQueue之中就不管了,具体调用是由mQueue中的threadEntry线程来做。

结语

       本节我们只是做一个铺垫,在分析AwesomePlayer基本框架及播放流程之前的预备知识。这里我们依然止步于AwesomePlayer的setDataSource(这个setDataSource都可以贯穿这么篇幅了,果然不简单),所以下一节将仔细分析这些过程。
妹子

坚持技术分享,您的支持将鼓励我继续创作!