Android平臺通過C++的MediaPlayer進行多媒體播放在呼叫prepare時死鎖問題
阿新 • • 發佈:2018-12-31
先來看下下面的測試程式碼:
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,更詳細的分析可以參考網路上的文章。