zoukankan      html  css  js  c++  java
  • Android源代码分析之Framework的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_jni
    
    LOCAL_STATIC_LIBRARIES := 
        libstagefright_amrnbenc
    
    LOCAL_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 
    endif
    
    ifeq ($(strip $(MTK_HIGH_QUALITY_THUMBNAIL)),yes)
    LOCAL_CFLAGS += -DMTK_HIGH_QUALITY_THUMBNAIL
    endif
    
    ifeq ($(strip $(MTK_USE_ANDROID_MM_DEFAULT_CODE)),yes)
    LOCAL_CFLAGS += -DANDROID_DEFAULT_CODE
    endif
    
    LOCAL_CFLAGS +=
    
    LOCAL_LDLIBS := -lpthread
    
    LOCAL_MODULE:= libmedia_jni
    
    include $(BUILD_SHARED_LIBRARY)
    
    # build libsoundpool.so
    # build libaudioeffect_jni.so
    include $(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 void
    android_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);
                }
            }
        }
    }
    相关文件
    Environment.java
    MediaScannerReceiver.java
    MediaScannerService.java
    
    其他功能(start‘stop‘play‘seek_to)的流程也差点儿相同,难点就是JNI技术和APP中的AIDL。当然以上仅仅是一个简单的调用过程。要更深入了解还是要花些时间的。

  • 相关阅读:
    JavaScript函数基础知识
    The type 'Expression<>' is defined in an assembly that is not referenced.You must add a reference to assembly 'System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.
    不可不知的表达式树(2)创建动态查询
    ABP给WebApi添加性能分析组件Miniprofiler
    不可不知的表达式树(3)定制IQueryProvider
    ABP使用Miniprofiler监测EF
    使用Type.MakeGenericType,反射构造泛型类型
    在ABP的Web层中实现复杂请求跨域访问
    不可不知的表达式树(1)Expression初探
    ajax实现无刷新编辑表格,两个demo
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/5281145.html
Copyright © 2011-2022 走看看