Android源碼分析之Framework的MediaPlayer

來源:互聯網
上載者:User

標籤:android   music   mediaplayer   

在Android中MediaPlayer用來播放音頻和視頻檔案,在這裡分析下在Framework層中MediaPlayer是如何調用的,MediaPlayer的代碼位於:./frameworks/base/media/java/android/media/MediaPlayer.java   下面用到的代碼是基於Android 4.4

開啟後有一個靜態代碼塊是載入庫檔案的,只要這個類被建立就會載入庫。

    static {        System.loadLibrary("media_jni");        native_init();    }
libmedia_jni.so的原始碼位於:./frameworks/base/media/jni

在jni這個檔案夾中有個makefile檔案 Android.mk

LOCAL_PATH:= $(call my-dir)include $(CLEAR_VARS)LOCAL_SRC_FILES:=     android_media_ImageReader.cpp     android_media_MediaCrypto.cpp     android_media_MediaCodec.cpp     android_media_MediaCodecList.cpp     android_media_MediaDrm.cpp     android_media_MediaExtractor.cpp     android_media_MediaMuxer.cpp     android_media_MediaPlayer.cpp     android_media_MediaRecorder.cpp     android_media_MediaScanner.cpp     android_media_MediaMetadataRetriever.cpp     android_media_ResampleInputStream.cpp     android_media_MediaProfiles.cpp     android_media_AmrInputStream.cpp     android_media_Utils.cpp     android_mtp_MtpDatabase.cpp     android_mtp_MtpDevice.cpp     android_mtp_MtpServer.cpp LOCAL_SHARED_LIBRARIES :=     libandroid_runtime     libnativehelper     libutils     libbinder     libmedia     libskia     libui     liblog     libcutils     libgui     libstagefright     libstagefright_foundation     libcamera_client     libmtp     libusbhost     libexif     libstagefright_amrnb_common LOCAL_REQUIRED_MODULES :=     libjhead_jniLOCAL_STATIC_LIBRARIES :=     libstagefright_amrnbencLOCAL_C_INCLUDES +=     external/libexif/     external/tremor/Tremor     frameworks/base/core/jni     frameworks/av/media/libmedia     frameworks/av/media/libstagefright     frameworks/av/media/libstagefright/codecs/amrnb/enc/src     frameworks/av/media/libstagefright/codecs/amrnb/common     frameworks/av/media/libstagefright/codecs/amrnb/common/include     $(TOP)/mediatek/external/amr     frameworks/av/media/mtp     frameworks/native/include/media/openmax     $(call include-path-for, libhardware)/hardware     system/media/camera/include     $(PV_INCLUDES)     $(JNI_H_INCLUDE)     $(call include-path-for, corecg graphics)ifeq ($(strip $(MTK_TB_DEBUG_SUPPORT)),yes)LOCAL_C_INCLUDES +=     $(MTK_PATH_SOURCE)/frameworks/base/include endififeq ($(strip $(MTK_HIGH_QUALITY_THUMBNAIL)),yes)LOCAL_CFLAGS += -DMTK_HIGH_QUALITY_THUMBNAILendififeq ($(strip $(MTK_USE_ANDROID_MM_DEFAULT_CODE)),yes)LOCAL_CFLAGS += -DANDROID_DEFAULT_CODEendifLOCAL_CFLAGS +=LOCAL_LDLIBS := -lpthreadLOCAL_MODULE:= libmedia_jniinclude $(BUILD_SHARED_LIBRARY)# build libsoundpool.so# build libaudioeffect_jni.soinclude $(call all-makefiles-under,$(LOCAL_PATH))
LOCAL_MODULE:= libmedia_jni 編譯後會產生libmedia_jni庫

開啟檔案有四個重載函數

setDataSource (String path)
setDataSource (FileDescriptor fd)
setDataSource (Context context, Uri uri)
setDataSource (FileDescriptor fd, long offset, long length)

在APP中,如原生的Music

 /**         * M: add async prepare to aviod ANR, add information and duration update listener         *          * @param player The mediaplayer         * @param path The data source path         * @param async M: use async prepare if it's set true .         * @return If set data source success, return true, otherwise false.         */        private boolean setDataSourceImpl(MediaPlayer player, String path, boolean async) {            MusicLogUtils.d(TAG, "setDataSourceImpl(" + path + ");async = " + async);            try {                player.reset();                if (async) {                    player.setOnPreparedListener(preparedlistener);                } else {                    player.setOnPreparedListener(null);                }                if (path.startsWith("content://")) {                    player.setDataSource(MediaPlaybackService.this, Uri.parse(path));                } else {// / M: add add for DRM secure flag @{MediaPlayerEx.setContextForSecureFlag(player,MediaPlaybackService.this.getApplicationContext());// / @}player.setDataSource(path);                }                /// M:  Attach auxiliary audio effect only with valid effect id                if (mAuxEffectId > 0) {                    player.attachAuxEffect(mAuxEffectId);                    player.setAuxEffectSendLevel(1.0f);                    mWhetherAttachWhenPause = false;                    MusicLogUtils.d(TAG, "setDataSourceImpl: attachAuxEffect mAuxEffectId = " + mAuxEffectId);                }                player.setAudioStreamType(AudioManager.STREAM_MUSIC);                if (async) {                    player.prepareAsync();                } else {                    player.prepare();                }            } catch (IOException ex) {                // TODO: notify the user why the file couldn't be opened                MusicLogUtils.e(TAG, "setDataSourceImpl: " + ex);                return false;            } catch (IllegalArgumentException ex) {                // TODO: notify the user why the file couldn't be opened                MusicLogUtils.e(TAG, "setDataSourceImpl: " + ex);                return false;            } catch (IllegalStateException ex) {                MusicLogUtils.e(TAG, "setDataSourceImpl: " + ex);                return false;            }            player.setOnCompletionListener(listener);            player.setOnErrorListener(errorListener);            player.setOnInfoListener(infoListener);            player.setOnDurationUpdateListener(durationListener);            sendSessionIdToAudioEffect(false);            return true;        }
不管在App中調用的是哪個函數,最後在MediaPlayer.java中都是調用

 /**     * Sets the data source (FileDescriptor) to use.  The FileDescriptor must be     * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility     * to close the file descriptor. It is safe to do so as soon as this call returns.     *     * @param fd the FileDescriptor for the file you want to play     * @param offset the offset into the file where the data to be played starts, in bytes     * @param length the length in bytes of the data to be played     * @throws IllegalStateException if it is called in an invalid state     */    public void setDataSource(FileDescriptor fd, long offset, long length)            throws IOException, IllegalArgumentException, IllegalStateException {        disableProxyListener();        _setDataSource(fd, offset, length);    }    private native void _setDataSource(FileDescriptor fd, long offset, long length)            throws IOException, IllegalArgumentException, IllegalStateException;
_setDataSource有native修飾,是libmedia_jni.so中的方法,找到 ./frameworks/base/media/jni/android_media_MediaPlayer.cpp

// ----------------------------------------------------------------------------static JNINativeMethod gMethods[] = {    {        "_setDataSource",        "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V",        (void *)android_media_MediaPlayer_setDataSourceAndHeaders    },    {"_setDataSource",       "(Ljava/io/FileDescriptor;JJ)V",    (void *)android_media_MediaPlayer_setDataSourceFD},    {"_setVideoSurface",    "(Landroid/view/Surface;)V",        (void *)android_media_MediaPlayer_setVideoSurface},    {"prepare",             "()V",                              (void *)android_media_MediaPlayer_prepare},    {"prepareAsync",        "()V",                              (void *)android_media_MediaPlayer_prepareAsync},    {"_start",              "()V",                              (void *)android_media_MediaPlayer_start},    {"_stop",               "()V",                              (void *)android_media_MediaPlayer_stop},    {"getVideoWidth",       "()I",                              (void *)android_media_MediaPlayer_getVideoWidth},    {"getVideoHeight",      "()I",                              (void *)android_media_MediaPlayer_getVideoHeight},    {"seekTo",              "(I)V",                             (void *)android_media_MediaPlayer_seekTo},    {"_pause",              "()V",                              (void *)android_media_MediaPlayer_pause},    {"isPlaying",           "()Z",                              (void *)android_media_MediaPlayer_isPlaying},    {"getCurrentPosition",  "()I",                              (void *)android_media_MediaPlayer_getCurrentPosition},    {"getDuration",         "()I",                              (void *)android_media_MediaPlayer_getDuration},    {"_release",            "()V",                              (void *)android_media_MediaPlayer_release},    {"_reset",              "()V",                              (void *)android_media_MediaPlayer_reset},    {"setAudioStreamType",  "(I)V",                             (void *)android_media_MediaPlayer_setAudioStreamType},    {"setLooping",          "(Z)V",                             (void *)android_media_MediaPlayer_setLooping},    {"isLooping",           "()Z",                              (void *)android_media_MediaPlayer_isLooping},    {"setVolume",           "(FF)V",                            (void *)android_media_MediaPlayer_setVolume},    {"native_invoke",       "(Landroid/os/Parcel;Landroid/os/Parcel;)I",(void *)android_media_MediaPlayer_invoke},    {"native_setMetadataFilter", "(Landroid/os/Parcel;)I",      (void *)android_media_MediaPlayer_setMetadataFilter},    {"native_getMetadata", "(ZZLandroid/os/Parcel;)Z",          (void *)android_media_MediaPlayer_getMetadata},    {"native_init",         "()V",                              (void *)android_media_MediaPlayer_native_init},    {"native_setup",        "(Ljava/lang/Object;)V",            (void *)android_media_MediaPlayer_native_setup},    {"native_finalize",     "()V",                              (void *)android_media_MediaPlayer_native_finalize},    {"getAudioSessionId",   "()I",                              (void *)android_media_MediaPlayer_get_audio_session_id},    {"setAudioSessionId",   "(I)V",                             (void *)android_media_MediaPlayer_set_audio_session_id},    {"setAuxEffectSendLevel", "(F)V",                           (void *)android_media_MediaPlayer_setAuxEffectSendLevel},    {"attachAuxEffect",     "(I)V",                             (void *)android_media_MediaPlayer_attachAuxEffect},    {"native_pullBatteryData", "(Landroid/os/Parcel;)I",        (void *)android_media_MediaPlayer_pullBatteryData},    {"setParameter",        "(ILandroid/os/Parcel;)Z",          (void *)android_media_MediaPlayer_setParameter},    {"getParameter",        "(ILandroid/os/Parcel;)V",          (void *)android_media_MediaPlayer_getParameter},    {"native_setRetransmitEndpoint", "(Ljava/lang/String;I)I",  (void *)android_media_MediaPlayer_setRetransmitEndpoint},    {"setNextMediaPlayer",  "(Landroid/media/MediaPlayer;)V",   (void *)android_media_MediaPlayer_setNextMediaPlayer},    {"updateProxyConfig", "(Landroid/net/ProxyProperties;)V", (void *)android_media_MediaPlayer_updateProxyConfig},};
會進入如下函數

static voidandroid_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length){    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);    if (mp == NULL ) {        jniThrowException(env, "java/lang/IllegalStateException", NULL);        return;    }    if (fileDescriptor == NULL) {        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);        return;    }    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);    ALOGV("setDataSourceFD: fd %d", fd);    process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );}
mp->setDataSource(fd, offset, length) //調用開啟檔案方法

開啟 ./frameworks/av/media/libmedia/mediaplayer.cpp   setDataSource有三個重載函數,根據參數

status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length){    ALOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);    status_t err = UNKNOWN_ERROR;    const sp<IMediaPlayerService>& service(getMediaPlayerService());    if (service != 0) {        sp<IMediaPlayer> player(service->create(this, mAudioSessionId));        if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||            (NO_ERROR != player->setDataSource(fd, offset, length))) {            player.clear();        }        err = attachNewPlayer(player);    }    return err;}
開啟 ./frameworks/av/media/libmedia/IMediaPlayer.cpp setDataSource也有三個重載函數,根據參數

    status_t setDataSource(int fd, int64_t offset, int64_t length) {        Parcel data, reply;        data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());        data.writeFileDescriptor(fd);        data.writeInt64(offset);        data.writeInt64(length);        remote()->transact(SET_DATA_SOURCE_FD, data, &reply);        return reply.readInt32();    }
這個開啟檔案流程只要是針對本地媒體檔案。

process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." ) //根據返回的結果處理

// If exception is NULL and opStatus is not OK, this method sends an error// event to the client application; otherwise, if exception is not NULL and// opStatus is not OK, this method throws the given exception to the client// application.static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message){    if (exception == NULL) {  // Don't throw exception. Instead, send an event.        if (opStatus != (status_t) OK) {            sp<MediaPlayer> mp = getMediaPlayer(env, thiz);            if (mp != 0) mp->notify(MEDIA_ERROR, opStatus, 0);        }    } else {  // Throw exception!        if ( opStatus == (status_t) INVALID_OPERATION ) {            jniThrowException(env, "java/lang/IllegalStateException", NULL);        } else if ( opStatus == (status_t) PERMISSION_DENIED ) {            jniThrowException(env, "java/lang/SecurityException", NULL);        } else if ( opStatus != (status_t) OK ) {            if (strlen(message) > 230) {               // if the message is too long, don't bother displaying the status code               jniThrowException( env, exception, message);            } else {               char msg[256];                // append the status code to the message               sprintf(msg, "%s: status=0x%X", message, opStatus);               jniThrowException( env, exception, msg);            }        }    }}
其它功能(start‘stop‘play‘seek_to)的流程也差不多,痛點就是JNI技術和APP中的AIDL,當然以上只是一個簡單的調用過程,要更深入瞭解還是要花些時間的。

Android源碼分析之Framework的MediaPlayer

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.