MediaPlayer视频播放流程

MediaPlayer视频播放流程(基于Android8.0)

  • 1. MediaPlayer源码分析

    • 1.0

      public class MediaPlayer extends PlayerBase implements 
                  SubtitleController.Listener, VolumeAutomation, AudioRouting
      

      MediaPayer继承自PlayerBase类,分别实现了SubtitleController VolumeAutomation AudioRouting接口

      • frameworks/base/media/java/android/media/PlayerBase.java

        PlayeBase是一个抽象类,封装了常规的播放器操作,如start pause stop release

        baseRegisterPlayer(),获取AppOpsService对象,并通过startWatchingMode对播放音乐的权限OP_PLAY_AUDIO进行监听

        protected void baseRegisterPlayer() {
                 
            int newPiid = AudioPlaybackConfiguration.PLAYER_PIID_INVALID;
            //获取AppOpsService对象
            IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
            mAppOps = IAppOpsService.Stub.asInterface(b);
            // initialize mHasAppOpsPlayAudio
            //检查权限
            updateAppOpsPlayAudio();
            // register a callback to monitor whether the OP_PLAY_AUDIO is still allowed
            mAppOpsCallback = new IAppOpsCallbackWrapper(this);
            try {
                 
                //注册了权限的监听
                mAppOps.startWatchingMode(AppOpsManager.OP_PLAY_AUDIO,
                        ActivityThread.currentPackageName(), mAppOpsCallback);
            } catch (RemoteException e) {
                 
                Log.e(TAG, "Error registering appOps callback", e);
                mHasAppOpsPlayAudio = false;
            }
            try {
                 
                //调用了AudioService的 trackPlayer,主要将mediaplayer统一管理起来,已方便提供 duck,mediaplayer的状态监听啊,以及外部对mediaplayer控制等功能
                newPiid = getService().trackPlayer(
                        new PlayerIdCard(mImplType, mAttributes, new IPlayerWrapper(this)));
            } catch (RemoteException e) {
                 
                Log.e(TAG, "Error talking to audio service, player will not be tracked", e);
            }
            mPlayerIId = newPiid;
        }
        

        在MediaPlayer()构造函数中调用

        public MediaPlayer() {
                 
            //通过super在playerBase创建一个默认的AudioAttributes,以及player类             
                 //PLAYER_TYPE_JAM_MEDIAPLAYER 表示MediaPlayer 
            super(new AudioAttributes.Builder().build(),
                    AudioPlaybackConfiguration.PLAYER_TYPE_JAM_MEDIAPLAYER);
        
            //获取当前线程的looper,如果当前线程没有lopper那么就获取主线程的looper
            Looper looper;
            if ((looper = Looper.myLooper()) != null) {
                 
                //创建一个handler主要用于控制mediaplayer的播放 
                mEventHandler = new EventHandler(this, looper);
            } else if ((looper = Looper.getMainLooper()) != null) {
                 
                mEventHandler = new EventHandler(this, looper);
            } else {
                 
                mEventHandler = null;
            }
        
            //控制播放时间和播放进度的相关
            mTimeProvider = new TimeProvider(this);
            //创建了一个InputStream向量集合 
            mOpenSubtitleSources = new Vector<InputStream>();
        
            //通过jni向下调用,初始化
            native_setup(new WeakReference<MediaPlayer>(this));
        
            baseRegisterPlayer();
         }
        
      • frameworks/base/media/java/android/media/SubtitleController.java

        frameworks/base/media/java/android/media/SubtitleTrack.java
        frameworks/base/media/java/android/media/SubtitleData.java

        用于为用户显示字幕,允许指定要显示的轨迹、定位点,还允许添加外部带外字幕曲目

      • frameworks/base/media/java/android/media/VolumeAutomation.java

        frameworks/base/media/java/android/media/VolumeShaper.java
        frameworks/base/media/jni/android_media_VolumeShaper.h等

        主要用于音频应用的淡入,淡出,淡入淡出,淡入淡出以及其他短暂的自动音量转换,音量控制通过VolumeShaper.Configuration实现,必须要在8.0及以后才能使用。

    • 2.0

      加载so库

      static {
             
          System.loadLibrary("media_jni");
          native_init();
      }
      
    • 3.0

      初始化

      public MediaPlayer() {
             
          ......
      
          //通过jni向下调用,初始化
          native_setup(new WeakReference<MediaPlayer>(this));
      
          ......
      }
      
      • frameworks/base/media/jni/android_media_MediaPlayer.cpp
        static void
        android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
        {
            ALOGV("native_setup");
            // 创建一个MediaPlayer对象(frameworks/av/media/libmedia/mediaplayer.cpp)
            sp<MediaPlayer> mp = new MediaPlayer();
            if (mp == NULL) {
                jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
                return;
            }
        
            // 为MediaPlayer设置listener,目的就是通过callback的方式将player的事件上传至   java层,以便用户做出对应的处理
            // create new listener and give it to MediaPlayer
            sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
            mp->setListener(listener);
        
            // Stow our new C++ MediaPlayer in an opaque field in the Java object.
            setMediaPlayer(env, thiz, mp);
        }
        
        //JNIMediaPlayerListener类
        class JNIMediaPlayerListener: public MediaPlayerListener
        {
        public:
            JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz);
            ~JNIMediaPlayerListener();
            virtual void notify(int msg, int ext1, int ext2, const Parcel *obj = NULL);
        private:
            JNIMediaPlayerListener();
            jclass      mClass;     // Reference to MediaPlayer class
            jobject     mObject;    // Weak ref to MediaPlayer Java object to call on
        };
        
        ......
        
        //callback事件的传递
        void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2, const Parcel *obj)
        {
            JNIEnv *env = AndroidRuntime::getJNIEnv();
            if (obj && obj->dataSize() > 0) {
                // 创建java层Parcel对象对应的JNI类型对象(JNI本地引用对象)
                jobject jParcel = createJavaParcelObject(env);
                if (jParcel != NULL) {
                    // 然后获取该Java层Parcel对象对应的native层Parcel对象指针(值)
                    // 然后缓存当前obj额外参数值及其大小
                    Parcel* nativeParcel = parcelForJavaObject(env, jParcel);
                    nativeParcel->setData(obj->data(), obj->dataSize());
                    // 最后调用java层事件回调静态方法【fields.post_event】通知java层
                    // fields.post_event对应于MediaPlayer的java方法 postEventFromNative
                    env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
                            msg, ext1, ext2, jParcel);
                    // 释放创建的java层Parcel对象的JNI本地引用对象
                    env->DeleteLocalRef(jParcel);
                }
            } else {
                // 若无额外参数,则直接调用通知java层
                env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
                        msg, ext1, ext2, NULL);
            }
            // 检查【post_event】方法调用是否有异常,若异常则需要清空该异常等处理
            // 备注:当一个JNI函数返回一个明确的错误码时,仍可用ExceptionCheck来检查是否有异常发生。
            // 但是,用返回的错误码来判断比较高效。一旦JNI函数的返回值是一个错误码,
            // 那么接下来调用ExceptionCheck肯定会返回JNI_TRUE。
            if (env->ExceptionCheck()) {
                ALOGW("An exception occurred while notifying an event.");
                LOGW_EX(env);
                env->ExceptionClear();
            }
        }
        
        ......
        
        static void
        android_media_MediaPlayer_native_init(JNIEnv *env)
        {
            jclass clazz;
        
            clazz = env->FindClass("android/media/MediaPlayer");
            if (clazz == NULL) {
                return;
            }
        
            fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
            if (fields.context == NULL) {
                return;
            }
        
            // 获取静态方法postEventFromNative
            fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
                                                    "(Ljava/lang/Object;IIILjava/lang/Object;)V");
            if (fields.post_event == NULL) {
                return;
            }
        
            ......
        }
        
        .....
        
        static sp<MediaPlayer> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer>& player)
        {
            Mutex::Autolock l(sLock);
            sp<MediaPlayer> old = (MediaPlayer*)env->GetLongField(thiz, fields.context);
            if (player.get()) {
                player->incStrong((void*)setMediaPlayer);
            }
            if (old != 0) {
                old->decStrong((void*)setMediaPlayer);
            }
            // 将mediaplayer对象指针给与了java层的mediaplayer的mNativeContext字段
            env->SetLongField(thiz, fields.context, (jlong)player.get());
            return old;
        }
        
      • frameworks/av/media/libmedia/include/media/mediaplayer.h
        // ref-counted object for callbacks
        class MediaPlayerListener: virtual public RefBase
        {
        public:
            virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) = 0;
        };
        
      • framework/av/media/libmedia/mediaplayer.cpp
        status_t MediaPlayer::setListener(const sp<MediaPlayerListener>& listener)
        {
            ALOGV("setListener");
            Mutex::Autolock _l(mLock);
            mListener = listener;
            return NO_ERROR;
        }
        
        ......
        
        void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj)
        {
            ALOGV("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);
            bool send = true;
            bool locked = false;
            
            ......
            
            case MEDIA_ERROR:
                // Always log errors.
                // ext1: Media framework error code.
                // ext2: Implementation dependant error code.
                ALOGE("error (%d, %d)", ext1, ext2);
                mCurrentState = MEDIA_PLAYER_STATE_ERROR;
                // 错误发生时,【mPrepareSync】该标识为true时,
                // 表示当前执行的是同步prepare准备流程,非异步执行prepareAsync流程
                if (mPrepareSync)
                {
                    ALOGV("signal application thread");
                    mPrepareSync = false;
                    mPrepareStatus = ext1;
                    mSignal.signal();
                    // prepare同步执行功能失败时,置为false
                    send = false;
                }
                
            ......
                
            ALOGV("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);
            sp<MediaPlayerListener> listener = mListener;
            if (locked) mLock.unlock();
        
            // this prevents re-entrant calls into client code
            if ((listener != 0) && send) {
                // 此处根据上面的分析可知,listener不为空并且send为true(即当prepare同步执行过程中无错误发生)时,
                // 进行加锁调用通知java层该事件
                Mutex::Autolock _l(mNotifyLock);
                ALOGV("callback application");
                listener->notify(msg, ext1, ext2, obj);
                ALOGV("back from callback");
            }
        }
        
        
    • 4.0

      setDataSource(文件类型)

      public void setDataSource(FileDescriptor fd, long offset, long length)
          throws IOException, IllegalArgumentException, IllegalStateException {
             
          _setDataSource(fd, offset, length);
      }
      
      private native void _setDataSource(FileDescriptor fd, long offset, long length)
              throws IOException, IllegalArgumentException, IllegalStateException;
      
      • frameworks/base/media/jni/android_media_MediaPlayer.cpp
        {"_setDataSource",      "(Ljava/io/FileDescriptor;JJ)V",    (void *)
                android_media_MediaPlayer_setDataSourceFD},
        
        ......
        
        static void
        android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
        {
            // 获取mediaplayer指针对象
            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;
            }
            // 在jni中获取文件描述符
            int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
            ALOGV("setDataSourceFD: fd %d", fd);
        
            // 处理mediaplayer指针对象对于setDataSource的结果
            process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
        }
        
        ......
        
        // 获取java层MediaPlayer对象的mNativeContext字段,转为native层mediaplayer对象
        static sp<MediaPlayer> getMediaPlayer(JNIEnv* env, jobject thiz)
        {
            Mutex::Autolock l(sLock);
            MediaPlayer* const p = (MediaPlayer*)env->GetLongField(thiz, fields.context);
            return sp<MediaPlayer>(p);
        }
        
        ......
        
        // 对setDataSource的结果进行处理
        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) BAD_VALUE ) {
                    jniThrowException(env, "java/lang/IllegalArgumentException", 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);
                    }
                }
            }
        }
        
      • frameworks/av/media/libmedia/mediaplayer.cpp
        status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length)
        {
            // //用于记录framework APIs返回成功与否
            ALOGV("setDataSource(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length);
            status_t err = UNKNOWN_ERROR;
            //getMediaPlayerService()函数定义域framework/av/media/libmedia/ IMediaDeathNotifier.cpp中
            //getMediaPlayerService()返回的是MediaPlayerService服务在客户端的代理对象BpMediaPlayerService
            const sp<IMediaPlayerService> service(getMediaPlayerService());
            if (service != 0) {
                //调用IMediaPlayerService中的create()函数查询服务
                //并返回一个调用IMediaPlayer对象
                sp<IMediaPlayer> player(service->create(this, mAudioSessionId));
                //调用服务端的setDataSource(int fd, int64_t offset, int64_t length)方法
                if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
                    (NO_ERROR != player->setDataSource(fd, offset, length))) {
                    player.clear();
                }
                //重置一些变量,更新MediaPlayer对象
                err = attachNewPlayer(player);
            }
            return err;
        }
        
      • frameworks/av/media/libmedia/IMediaDeathNotifier.cpp
        /*static*/const sp<IMediaPlayerService>
        IMediaDeathNotifier::getMediaPlayerService()
        {
            ALOGV("getMediaPlayerServic
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值