1. 程式人生 > >Android平臺通過C++的MediaPlayer進行多媒體播放在呼叫prepare時死鎖問題

Android平臺通過C++的MediaPlayer進行多媒體播放在呼叫prepare時死鎖問題

先來看下下面的測試程式碼:

mptest.cpp

#include <stdio.h>
#include <assert.h>
#include <limits.h>
#include <unistd.h>
#include <fcntl.h>

#include <binder/Parcel.h>
#include <gui/ISurfaceTexture.h>
#include <gui/Surface.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include "utils/Log.h"
#include <media/mediaplayer.h>
#include <media/MediaPlayerInterface.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
#include <ui/DisplayInfo.h>

#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/foundation/AString.h>

using namespace android;


#define LITERAL_TO_STRING_INTERNAL(x)    #x
#define LITERAL_TO_STRING(x) LITERAL_TO_STRING_INTERNAL(x)

#define CHECK(condition)                                \
    LOG_ALWAYS_FATAL_IF(                                \
            !(condition),                               \
            "%s",                                       \
            __FILE__ ":" LITERAL_TO_STRING(__LINE__)    \
            " CHECK(" #condition ") failed.")

#define MAKE_COMPARATOR(suffix,op)                          \
    template<class A, class B>                              \
    AString Compare_##suffix(const A &a, const B &b) {      \
        AString res;                                        \
        if (!(a op b)) {                                    \
            res.append(a);                                  \
            res.append(" vs. ");                            \
            res.append(b);                                  \
        }                                                   \
        return res;                                         \
    }

MAKE_COMPARATOR(EQ,==)
MAKE_COMPARATOR(NE,!=)
MAKE_COMPARATOR(LE,<=)
MAKE_COMPARATOR(GE,>=)
MAKE_COMPARATOR(LT,<)
MAKE_COMPARATOR(GT,>)


#define CHECK_OP(x,y,suffix,op)                                         \
    do {                                                                \
        AString ___res = Compare_##suffix(x, y);                        \
        if (!___res.empty()) {                                          \
            AString ___full =                                           \
                __FILE__ ":" LITERAL_TO_STRING(__LINE__)                \
                    " CHECK_" #suffix "( " #x "," #y ") failed: ";      \
            ___full.append(___res);                                     \
                                                                        \
            LOG_ALWAYS_FATAL("%s", ___full.c_str());                    \
        }                                                               \
    } while (false)

#define CHECK_EQ(x,y)   CHECK_OP(x,y,EQ,==)
#define CHECK_NE(x,y)   CHECK_OP(x,y,NE,!=)
#define CHECK_LE(x,y)   CHECK_OP(x,y,LE,<=)
#define CHECK_LT(x,y)   CHECK_OP(x,y,LT,<)
#define CHECK_GE(x,y)   CHECK_OP(x,y,GE,>=)
#define CHECK_GT(x,y)   CHECK_OP(x,y,GT,>)


int main(int argc,char *argv[])
{

    sp<SurfaceComposerClient> composerClient;
    sp<SurfaceControl> control;
    sp<Surface> surface;
    
    composerClient = new SurfaceComposerClient;
    CHECK_EQ(composerClient->initCheck(), (status_t)OK);

    sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(
            ISurfaceComposer::eDisplayIdMain));
    DisplayInfo info;
    SurfaceComposerClient::getDisplayInfo(display, &info);
    ssize_t displayWidth = info.w;
    ssize_t displayHeight = info.h;

    ALOGV("display is %ld x %ld\n", displayWidth, displayHeight);

    control = composerClient->createSurface(
            String8("A Surface"),
            displayWidth,
            displayHeight,
            PIXEL_FORMAT_RGB_565,
            0);

    CHECK(control != NULL);
    CHECK(control->isValid());

    SurfaceComposerClient::openGlobalTransaction();
    CHECK_EQ(control->setLayer(INT_MAX), (status_t)OK);
    CHECK_EQ(control->show(), (status_t)OK);
    SurfaceComposerClient::closeGlobalTransaction();

    surface = control->getSurface();
    CHECK(surface != NULL);


    const char *url = "/mnt/sdcard/test.avi";
    sp<MediaPlayer> mp = new MediaPlayer();

    int fd = open(url,O_RDONLY);
    mp->setDataSource(fd,0,0x7fffffffL);

    mp->setVideoSurfaceTexture(surface->getSurfaceTexture());
    mp->prepare();
    mp->start();
    while (mp->isPlaying()) {
        printf("++++++++playing...");
        sleep(1);
    }
    mp->disconnect();
    close(fd);
    return 0;
}

以下是編譯指令碼:

Android.mk

LOCAL_PATH := $(call my-dir)

helloworldservice_module_tags := eng user

include $(CLEAR_VARS)

LOCAL_SRC_FILES := mptest.cpp 

LOCAL_MODULE := mptest

# for now, until I do a full rebuild.
LOCAL_PRELINK_MODULE := false

# LOCAL_LDFLAGS += -llog

LOCAL_SHARED_LIBRARIES := \
	 liblog libutils libbinder libstagefright_foundation \
        libmedia libmedia_native libgui libcutils libui
       

include $(BUILD_EXECUTABLE)


編譯後執行會發現在

mp->prepare()

這行程式碼死鎖了,解決辦法是在main的開始加上:

android::ProcessState::self()->startThreadPool();

下面來分析下具體的原因:

在MediaPlayer.cpp中的prepare中有以下程式碼:

    if (mPrepareSync) {
        mSignal.wait(mLock);  // wait for prepare done
        mPrepareSync = false;
    }

這裡的死鎖的程式碼即為: mSignal.wait(mLock)

這是在等待MEDIA_PREPARE訊息,在MediaPlayer::notify中,有以下程式碼:

    case MEDIA_PREPARED:
        ALOGV("prepared");
        mCurrentState = MEDIA_PLAYER_PREPARED;
        if (mPrepareSync) {
            ALOGV("signal application thread");
            mPrepareSync = false;
            mPrepareStatus = NO_ERROR;
            mSignal.signal();
        }
        break;

這裡的mSignal.signal,即會使prepare函式中的mSignal.wait(mLock)返回。

prepare不返回正是因為MediaPlayer::notify沒有被呼叫。

android::ProcessState::self()->startThreadPool() 會啟動一個binder的資料接收迴圈, 接收MediaPlayerService發過來的資料,並最終會呼叫MediaPlayer::notify,更詳細的分析可以參考網路上的文章。