android多媒体本地播放流程video playback--base on jellybean (四)
上一篇我们讲了mediaplayer播放的第一步骤setdataSource,下面我们来讲解preparesync的流程,在prepare前我们还有setDisplay这一步,即获取su易做图cetexture来进行画面的展示
setVideoSu易做图ce(JNIEnv *env, jobject thiz, jobject jsu易做图ce, jboolean mediaPlayerMustBeAlive)
{
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
………
sp<ISu易做图ceTexture> new_st;
if (jsu易做图ce) {
sp<Su易做图ce> su易做图ce(Su易做图ce_getSu易做图ce(env, jsu易做图ce));
if (su易做图ce != NULL) {
new_st = su易做图ce->getSu易做图ceTexture();
---通过su易做图ce获取su易做图ceTexture
new_st->incStrong(thiz);
……….
}………….
mp->setVideoSu易做图ceTexture(new_st);
}
为什么用su易做图ceTexture不用su易做图ce来展示呢?ICS之前都用的是su易做图ceview来展示video或者openGL的内容,su易做图caview render在su易做图ce上,textureview render在su易做图ceTexture,textureview和su易做图ceview 这两者有什么区别呢?su易做图ceview跟应用的视窗不是同一个视窗,它自己new了一个window来展示openGL或者video的内容,这样做有一个好处就是不用重绘应用的视窗,本身就可以不停的更新,但这也带来一些局限性,su易做图ceview不是依附在应用视窗中,也就不能移动、缩放、旋转,应用ListView或者 ScrollView就比较费劲。Textureview就很好的解决了这些问题。它拥有su易做图ceview的一切特性外,它也拥有view的一切行为,可以当个view使用。
获取完su易做图ceTexture,我们就可以prepare/prepareAsync了,先给大伙看个大体时序图吧:
JNI的部分我们跳过,直接进入libmedia下的mediaplayer.cpp的 prepareAsync_l方法,prepare是个同步的过程,所以要加锁,prepareAsync_l后缀加_l就是表面是同步的过程。
status_t MediaPlayer::prepareAsync_l()
{
if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_INITIALIZED | MEDIA_PLAYER_STOPPED) ) ) {
mPlayer->setAudioStreamType(mStreamType);
mCurrentState = MEDIA_PLAYER_PREPARING;
return mPlayer->prepareAsync();
}
ALOGE("prepareAsync called in state %d", mCurrentState);
return INVALID_OPERATION;
}
在上面的代码中,我们看到有个mPlayer,看过前一章的朋友都会记得,就是我们从Mediaplayerservice获得的BpMediaplayer.通过BpMediaplayer我们就可以长驱直入,直捣Awesomeplayer这条干实事的黄龙,前方的mediaplayerservice:client和stagefrightplayer都是些通风报信的料,不值得我们去深入研究,无非是些接口而已。进入了prepareAsync_l方法,我们的播放器所处的状态就是MEDIA_PLAYER_PREPARING了。好了,我们就来看看Awesomeplayer到底做了啥吧.
代码定位于:frameworks/av/media/libstagefright/Awesomeplayer.cpp
先看下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);
mAsyncPrepareEvent = new AwesomeEvent(
this, &AwesomePlayer::onPrepareAsyncEvent);
mQueue.postEvent(mAsyncPrepareEvent);
return OK;
}
这里我们涉及到了TimeEventQueue,即时间事件队列模型,Awesomeplayer里面类似Handler的东西,它的实现方式是把事件响应时间和事件本身封装成一个queueItem,通过postEvent 插入队列,时间到了就会根据事件id进行相应的处理。
首先我们来看下TimeEventQueue的start(mQueue.start();)方法都干了什么:
frameworks/av/media/libstagefright/TimedEventQueue.cpp
void TimedEventQueue::start() {
if (mRunning) {
return;
}
……..
pthread_create(&mThread, &attr, ThreadWrapper, this);
………
}
目的很明显就是在主线程创建一个子线程,可能很多没有写过C/C++的人对ptread_create这个创建线程的方法有点陌生,我们就来分析下:
int pthread_create(pthread_t *thread, pthread_addr_t *arr,
void* (*start_routine)(void *), void *arg);
thread :用于返回创建的线程的ID
arr : 用于指定的被创建的线程的属性
start_routine : 这是一个函数指针,指向线程被创建后要调用的函数
arg : 用于给线程传递参数
分析完了,我们就看下创建线程后调用的函数ThreadWrapper吧:
// static
void *TimedEventQueue::ThreadWrapper(void *me) {
……
static_cast<TimedEventQueue *>(me)->threadEntry();
return NULL;
}
跟踪到threadEntry:
frameworks/av/media/libstagefright/TimedEventQueue.cpp
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);
}
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();
……&h
补充:移动开发 , Android ,