草庐IT

Android MediaPlayer源码分析

小迪vs同学 2024-06-14 原文

【目的】

梳理应用层到framework层,MediaPlayer的处理流程,以便于工作中多媒体相关问题的分析处理,以下是整个流程的时序图,可对照时序图看下面的内容,会更加直观

【案例展示】

布局文件

放一个VideoView作为播放视频的控件

首先初始化VideoView,然后通过setVideoPath()设置本地视频路径,紧接着添加了一个控制器,用于视频的暂停/播放、快进/快退控制,最后调用start()方法开始播放

【代码分析】

先看VideoView.setVideoPath()

先执行openVideo()打开视频,然后执行requestLayout()和invalidate()重绘UI
进入openVideo()方法里面

先执行release(),因为有可能之前已经调用过start()。然后申请获取音频焦点,最后创建了MediaPlayer对象,并对其做了一系列的初始化操作,核心操作有:

  • setDataSource()
  • setDisplay()
  • prepareAsync()

在核心操作之前,在MediaPlayer类的加载期间和对象的创建期间做了一些初始化的准备工作,分别是

  • System.loadLibrary(“media_jni”);
  • native_init();
  • new EventHandler();
  • native_setup();



首先加载了media_jni.so动态库,这里涉及到jni技术,jni是java和c/c++建立沟通的桥梁,java层调用native层的方法必须通过jni访问
动态库加载完毕后执行了native_init()方法,这里动态库的源码在android_media_MediaPlayer.cpp中,进入到该文件查看native_init()方法的实现

首先是方法的注册,jni方法的注册有两种方式,静态注册和动态注册,这里使用的是动态注册,即java中native_init()对应的native方法为android_media_MediaPlayer_native_init(),查看该方法的具体实现

主要是将java中的一些变量值保存到fields_t fields结构体中。
对应的native_init()执行完后接着创建了一个EventHandler对象,该对象的作用主要用于接收native层发送过来的消息

 private class EventHandler extends Handler
   {
       private MediaPlayer mMediaPlayer;
 
       public EventHandler(MediaPlayer mp, Looper looper) {
           super(looper);
           mMediaPlayer = mp;
       }
 
       @Override
       public void handleMessage(Message msg) {
           if (mMediaPlayer.mNativeContext == 0) {
               Log.w(TAG, "mediaplayer went away with unhandled events");
               return;
           }
           switch(msg.what) {
           case MEDIA_PREPARED:
               try {
                   scanInternalSubtitleTracks();
               } catch (RuntimeException e) {
                   // send error message instead of crashing;
                   // send error message instead of inlining a call to onError
                   // to avoid code duplication.
                   Message msg2 = obtainMessage(
                           MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
                   sendMessage(msg2);
               }
 
               OnPreparedListener onPreparedListener = mOnPreparedListener;
               if (onPreparedListener != null)
                   onPreparedListener.onPrepared(mMediaPlayer);
               return;
 
           case MEDIA_DRM_INFO:
               Log.v(TAG, "MEDIA_DRM_INFO " + mOnDrmInfoHandlerDelegate);
 
               if (msg.obj == null) {
                   Log.w(TAG, "MEDIA_DRM_INFO msg.obj=NULL");
               } else if (msg.obj instanceof Parcel) {
                   // The parcel was parsed already in postEventFromNative
                   DrmInfo drmInfo = null;
 
                   OnDrmInfoHandlerDelegate onDrmInfoHandlerDelegate;
                   synchronized (mDrmLock) {
                       if (mOnDrmInfoHandlerDelegate != null && mDrmInfo != null) {
                           drmInfo = mDrmInfo.makeCopy();
                       }
                       // local copy while keeping the lock
                       onDrmInfoHandlerDelegate = mOnDrmInfoHandlerDelegate;
                   }
 
                   // notifying the client outside the lock
                   if (onDrmInfoHandlerDelegate != null) {
                       onDrmInfoHandlerDelegate.notifyClient(drmInfo);
                   }
               } else {
                   Log.w(TAG, "MEDIA_DRM_INFO msg.obj of unexpected type " + msg.obj);
               }
               return;
 
           case MEDIA_PLAYBACK_COMPLETE:
               {
                   mOnCompletionInternalListener.onCompletion(mMediaPlayer);
                   OnCompletionListener onCompletionListener = mOnCompletionListener;
                   if (onCompletionListener != null)
                       onCompletionListener.onCompletion(mMediaPlayer);
               }
               stayAwake(false);
               return;
 
           case MEDIA_STOPPED:
               {
                   TimeProvider timeProvider = mTimeProvider;
                   if (timeProvider != null) {
                       timeProvider.onStopped();
                   }
               }
               break;
 
           case MEDIA_STARTED:
               // fall through
           case MEDIA_PAUSED:
               {
                   TimeProvider timeProvider = mTimeProvider;
                   if (timeProvider != null) {
                       timeProvider.onPaused(msg.what == MEDIA_PAUSED);
                   }
               }
               break;
 
           case MEDIA_BUFFERING_UPDATE:
               OnBufferingUpdateListener onBufferingUpdateListener = mOnBufferingUpdateListener;
               if (onBufferingUpdateListener != null)
                   onBufferingUpdateListener.onBufferingUpdate(mMediaPlayer, msg.arg1);
               return;
 
           case MEDIA_SEEK_COMPLETE:
               OnSeekCompleteListener onSeekCompleteListener = mOnSeekCompleteListener;
               if (onSeekCompleteListener != null) {
                   onSeekCompleteListener.onSeekComplete(mMediaPlayer);
               }
               // fall through
 
           case MEDIA_SKIPPED:
               {
                   TimeProvider timeProvider = mTimeProvider;
                   if (timeProvider != null) {
                       timeProvider.onSeekComplete(mMediaPlayer);
                   }
               }
               return;
 
           case MEDIA_SET_VIDEO_SIZE:
               OnVideoSizeChangedListener onVideoSizeChangedListener = mOnVideoSizeChangedListener;
               if (onVideoSizeChangedListener != null) {
                   onVideoSizeChangedListener.onVideoSizeChanged(
                       mMediaPlayer, msg.arg1, msg.arg2);
               }
               return;
 
           case MEDIA_ERROR:
               Log.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")");
               boolean error_was_handled = false;
               OnErrorListener onErrorListener = mOnErrorListener;
               if (onErrorListener != null) {
                   error_was_handled = onErrorListener.onError(mMediaPlayer, msg.arg1, msg.arg2);
               }
               {
                   mOnCompletionInternalListener.onCompletion(mMediaPlayer);
                   OnCompletionListener onCompletionListener = mOnCompletionListener;
                   if (onCompletionListener != null && ! error_was_handled) {
                       onCompletionListener.onCompletion(mMediaPlayer);
                   }
               }
               stayAwake(false);
               return;
 
           case MEDIA_INFO:
               switch (msg.arg1) {
               case MEDIA_INFO_VIDEO_TRACK_LAGGING:
                   Log.i(TAG, "Info (" + msg.arg1 + "," + msg.arg2 + ")");
                   break;
               case MEDIA_INFO_METADATA_UPDATE:
                   try {
                       scanInternalSubtitleTracks();
                   } catch (RuntimeException e) {
                       Message msg2 = obtainMessage(
                               MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
                       sendMessage(msg2);
                   }
                   // fall through
 
               case MEDIA_INFO_EXTERNAL_METADATA_UPDATE:
                   msg.arg1 = MEDIA_INFO_METADATA_UPDATE;
                   // update default track selection
                   if (mSubtitleController != null) {
                       mSubtitleController.selectDefaultTrack();
                   }
                   break;
               case MEDIA_INFO_BUFFERING_START:
               case MEDIA_INFO_BUFFERING_END:
                   TimeProvider timeProvider = mTimeProvider;
                   if (timeProvider != null) {
                       timeProvider.onBuffering(msg.arg1 == MEDIA_INFO_BUFFERING_START);
                   }
                   break;
               }
 
               OnInfoListener onInfoListener = mOnInfoListener;
               if (onInfoListener != null) {
                   onInfoListener.onInfo(mMediaPlayer, msg.arg1, msg.arg2);
               }
               // No real default action so far.
               return;
 
           case MEDIA_NOTIFY_TIME:
                   TimeProvider timeProvider = mTimeProvider;
                   if (timeProvider != null) {
                       timeProvider.onNotifyTime();
                   }
               return;
 
           case MEDIA_TIMED_TEXT:
               OnTimedTextListener onTimedTextListener = mOnTimedTextListener;
               if (onTimedTextListener == null)
                   return;
               if (msg.obj == null) {
                   onTimedTextListener.onTimedText(mMediaPlayer, null);
               } else {
                   if (msg.obj instanceof Parcel) {
                       Parcel parcel = (Parcel)msg.obj;
                       TimedText text = new TimedText(parcel);
                       parcel.recycle();
                       onTimedTextListener.onTimedText(mMediaPlayer, text);
                   }
               }
               return;
 
           case MEDIA_SUBTITLE_DATA:
               final OnSubtitleDataListener extSubtitleListener;
               final Handler extSubtitleHandler;
               synchronized(this) {
                   if (mSubtitleDataListenerDisabled) {
                       return;
                   }
                   extSubtitleListener = mExtSubtitleDataListener;
                   extSubtitleHandler = mExtSubtitleDataHandler;
               }
               if (msg.obj instanceof Parcel) {
                   Parcel parcel = (Parcel) msg.obj;
                   final SubtitleData data = new SubtitleData(parcel);
                   parcel.recycle();
 
                   mIntSubtitleDataListener.onSubtitleData(mMediaPlayer, data);
 
                   if (extSubtitleListener != null) {
                       if (extSubtitleHandler == null) {
                           extSubtitleListener.onSubtitleData(mMediaPlayer, data);
                       } else {
                           extSubtitleHandler.post(new Runnable() {
                               @Override
                               public void run() {
                                   extSubtitleListener.onSubtitleData(mMediaPlayer, data);
                               }
                           });
                       }
                   }
               }
               return;
 
           case MEDIA_META_DATA:
               OnTimedMetaDataAvailableListener onTimedMetaDataAvailableListener =
                   mOnTimedMetaDataAvailableListener;
               if (onTimedMetaDataAvailableListener == null) {
                   return;
               }
               if (msg.obj instanceof Parcel) {
                   Parcel parcel = (Parcel) msg.obj;
                   TimedMetaData data = TimedMetaData.createTimedMetaDataFromParcel(parcel);
                   parcel.recycle();
                   onTimedMetaDataAvailableListener.onTimedMetaDataAvailable(mMediaPlayer, data);
               }
               return;
 
           case MEDIA_NOP: // interface test message - ignore
               break;
 
           case MEDIA_AUDIO_ROUTING_CHANGED:
                   broadcastRoutingChange();
                   return;
 
           case MEDIA_TIME_DISCONTINUITY:
               final OnMediaTimeDiscontinuityListener mediaTimeListener;
               final Handler mediaTimeHandler;
               synchronized(this) {
                   mediaTimeListener = mOnMediaTimeDiscontinuityListener;
                   mediaTimeHandler = mOnMediaTimeDiscontinuityHandler;
               }
               if (mediaTimeListener == null) {
                   return;
               }
               if (msg.obj instanceof Parcel) {
                   Parcel parcel = (Parcel) msg.obj;
                   parcel.setDataPosition(0);
                   long anchorMediaUs = parcel.readLong();
                   long anchorRealUs = parcel.readLong();
                   float playbackRate = parcel.readFloat();
                   parcel.recycle();
                   final MediaTimestamp timestamp;
                   if (anchorMediaUs != -1 && anchorRealUs != -1) {
                       timestamp = new MediaTimestamp(
                               anchorMediaUs /*Us*/, anchorRealUs * 1000 /*Ns*/, playbackRate);
                   } else {
                       timestamp = MediaTimestamp.TIMESTAMP_UNKNOWN;
                   }
                   if (mediaTimeHandler == null) {
                       mediaTimeListener.onMediaTimeDiscontinuity(mMediaPlayer, timestamp);
                   } else {
                       mediaTimeHandler.post(new Runnable() {
                           @Override
                           public void run() {
                               mediaTimeListener.onMediaTimeDiscontinuity(mMediaPlayer, timestamp);
                           }
                       });
                   }
               }
               return;
 
           case MEDIA_RTP_RX_NOTICE:
               final OnRtpRxNoticeListener rtpRxNoticeListener = mOnRtpRxNoticeListener;
               if (rtpRxNoticeListener == null) {
                   return;
               }
               if (msg.obj instanceof Parcel) {
                   Parcel parcel = (Parcel) msg.obj;
                   parcel.setDataPosition(0);
                   int noticeType;
                   int[] data;
                   try {
                       noticeType = parcel.readInt();
                       int numOfArgs = parcel.dataAvail() / 4;
                       data = new int[numOfArgs];
                       for (int i = 0; i < numOfArgs; i++) {
                           data[i] = parcel.readInt();
                       }
                   } finally {
                       parcel.recycle();
                   }
                   mOnRtpRxNoticeExecutor.execute(() ->
                           rtpRxNoticeListener
                                   .onRtpRxNotice(mMediaPlayer, noticeType, data));
               }
               return;
 
           default:
               Log.e(TAG, "Unknown message type " + msg.what);
               return;
           }
       }
   }

最后执行了native_setup()方法,该方法也是一个native方法,对应在android_media_MediaPlayer.cpp中的实现是android_media_MediaPlayer_native_setup()

主要是创建了一个MediaPlayer对象,MediaPlayer继承自BnMediaPlayerClient和IMediaDeathNotifier

class MediaPlayer : public BnMediaPlayerClient,
                    public virtual IMediaDeathNotifier
{
public:
    MediaPlayer();
    ~MediaPlayer();
            void            died();
            void            disconnect();
 
            status_t        setDataSource(
                    const sp<IMediaHTTPService> &httpService,
                    const char *url,
                    const KeyedVector<String8, String8> *headers);
 
    virtual status_t        setDataSource(int fd, int64_t offset, int64_t length);
            status_t        setDataSource(const sp<IDataSource> &source);
            status_t        setVideoSurfaceTexture(
                                    const sp<IGraphicBufferProducer>& bufferProducer);
            status_t        setListener(const sp<MediaPlayerListener>& listener);
            status_t        getBufferingSettings(BufferingSettings* buffering /* nonnull */);
            status_t        setBufferingSettings(const BufferingSettings& buffering);
            status_t        prepare();
            status_t        prepareAsync();
            status_t        start();
            status_t        stop();
    virtual status_t        pause();
            bool            isPlaying();
            status_t        setPlaybackSettings(const AudioPlaybackRate& rate);
            status_t        getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */);
            status_t        setSyncSettings(const AVSyncSettings& sync, float videoFpsHint);
            status_t        getSyncSettings(
                                    AVSyncSettings* sync /* nonnull */,
                                    float* videoFps /* nonnull */);
            status_t        getVideoWidth(int *w);
            status_t        getVideoHeight(int *h);
            status_t        seekTo(
                    int msec,
                    MediaPlayerSeekMode mode = MediaPlayerSeekMode::SEEK_PREVIOUS_SYNC);
            status_t        notifyAt(int64_t mediaTimeUs);
            status_t        getCurrentPosition(int *msec);
            status_t        getDuration(int *msec);
            status_t        reset();
            status_t        setAudioStreamType(audio_stream_type_t type);
            status_t        getAudioStreamType(audio_stream_type_t *type);
            status_t        setLooping(int loop);
            bool            isLooping();
            status_t        setVolume(float leftVolume, float rightVolume);
    virtual void            notify(int msg, int ext1, int ext2, const Parcel *obj = NULL);
            status_t        invoke(const Parcel& request, Parcel *reply);
            status_t        setMetadataFilter(const Parcel& filter);
            status_t        getMetadata(bool update_only, bool apply_filter, Parcel *metadata);
            status_t        setAudioSessionId(audio_session_t sessionId);
            audio_session_t getAudioSessionId();
            status_t        setAuxEffectSendLevel(float level);
            status_t        attachAuxEffect(int effectId);
            status_t        setParameter(int key, const Parcel& request);
            status_t        getParameter(int key, Parcel* reply);
            status_t        setRetransmitEndpoint(const char* addrString, uint16_t port);
            status_t        setNextMediaPlayer(const sp<MediaPlayer>& player);
 
            media::VolumeShaper::Status applyVolumeShaper(
                                    const sp<media::VolumeShaper::Configuration>& configuration,
                                    const sp<media::VolumeShaper::Operation>& operation);
            sp<media::VolumeShaper::State> getVolumeShaperState(int id);
            // Modular DRM
            status_t        prepareDrm(const uint8_t uuid[16], const Vector<uint8_t>& drmSessionId);
            status_t        releaseDrm();
            // AudioRouting
            status_t        setOutputDevice(audio_port_handle_t deviceId);
            audio_port_handle_t getRoutedDeviceId();
            status_t        enableAudioDeviceCallback(bool enabled);
 
private:
            void            clear_l();
            status_t        seekTo_l(int msec, MediaPlayerSeekMode mode);
            status_t        prepareAsync_l();
            status_t        getDuration_l(int *msec);
            status_t        attachNewPlayer(const sp<IMediaPlayer>& player);
            status_t        reset_l();
            status_t        doSetRetransmitEndpoint(const sp<IMediaPlayer>& player);
            status_t        checkStateForKeySet_l(int key);
 
    sp<IMediaPlayer>            mPlayer;
    thread_id_t                 mLockThreadId;
    Mutex                       mLock;
    Mutex                       mNotifyLock;
    Condition                   mSignal;
    sp<MediaPlayerListener>     mListener;
    void*                       mCookie;
    media_player_states         mCurrentState;
    int                         mCurrentPosition;
    MediaPlayerSeekMode         mCurrentSeekMode;
    int                         mSeekPosition;
    MediaPlayerSeekMode         mSeekMode;
    bool                        mPrepareSync;
    status_t                    mPrepareStatus;
    audio_stream_type_t         mStreamType;
    Parcel*                     mAudioAttributesParcel;
    bool                        mLoop;
    float                       mLeftVolume;
    float                       mRightVolume;
    int                         mVideoWidth;
    int                         mVideoHeight;
    audio_session_t             mAudioSessionId;
    float                       mSendLevel;
    struct sockaddr_in          mRetransmitEndpoint;
    bool                        mRetransmitEndpointValid;
};

该类中的成员基本上与java层中的MediaPlayer是一一对应的。
执行完初始化操作后主要接着执行上文说到的三个核心操作,依次查看三个核心操作的逻辑,先看setDataSource()

这里做了判断,如果是本地视频,则执行setDataSource(),如果是网络视频,则执行nativeSetDataSource(),这里使用的是本地视频,所以走setDataSource(),MediaPlayer里面有很多重载的setDataSource方法,最终都会执行_setDataSource(),注意,前面有个下划线,表示是一个native方法

_setDataSource()对应的native方法为android_media_MediaPlayer_setDataSourceCallback()

主要是获取了MediaPlayer对象,然后调用它的setDataSource()方法,MediaPlayer所有方法的实现均在mediaplayer.cpp中

首先获取到MediaPlayerService服务,然后调用该服务的create()方法创建一个Client客户端,Client结构如下

class Client : public BnMediaPlayer {
        // IMediaPlayer interface
        virtual void            disconnect();
        virtual status_t        setVideoSurfaceTexture(
                                        const sp<IGraphicBufferProducer>& bufferProducer);
        virtual status_t        setBufferingSettings(const BufferingSettings& buffering) override;
        virtual status_t        getBufferingSettings(
                                        BufferingSettings* buffering /* nonnull */) override;
        virtual status_t        prepareAsync();
        virtual status_t        start();
        virtual status_t        stop();
        virtual status_t        pause();
        virtual status_t        isPlaying(bool* state);
        virtual status_t        setPlaybackSettings(const AudioPlaybackRate& rate);
        virtual status_t        getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */);
        virtual status_t        setSyncSettings(const AVSyncSettings& rate, float videoFpsHint);
        virtual status_t        getSyncSettings(AVSyncSettings* rate /* nonnull */,
                                                float* videoFps /* nonnull */);
        virtual status_t        seekTo(
                int msec,
                MediaPlayerSeekMode mode = MediaPlayerSeekMode::SEEK_PREVIOUS_SYNC);
        virtual status_t        getCurrentPosition(int* msec);
        virtual status_t        getDuration(int* msec);
        virtual status_t        reset();
        virtual status_t        notifyAt(int64_t mediaTimeUs);
        virtual status_t        setAudioStreamType(audio_stream_type_t type);
        virtual status_t        setLooping(int loop);
        virtual status_t        setVolume(float leftVolume, float rightVolume);
        virtual status_t        invoke(const Parcel& request, Parcel *reply);
        virtual status_t        setMetadataFilter(const Parcel& filter);
        virtual status_t        getMetadata(bool update_only,
                                            bool apply_filter,
                                            Parcel *reply);
        virtual status_t        setAuxEffectSendLevel(float level);
        virtual status_t        attachAuxEffect(int effectId);
        virtual status_t        setParameter(int key, const Parcel &request);
        virtual status_t        getParameter(int key, Parcel *reply);
        virtual status_t        setRetransmitEndpoint(const struct sockaddr_in* endpoint);
        virtual status_t        getRetransmitEndpoint(struct sockaddr_in* endpoint);
        virtual status_t        setNextPlayer(const sp<IMediaPlayer>& player);
 
        virtual media::VolumeShaper::Status applyVolumeShaper(
                                        const sp<media::VolumeShaper::Configuration>& configuration,
                                        const sp<media::VolumeShaper::Operation>& operation) override;
        virtual sp<media::VolumeShaper::State> getVolumeShaperState(int id) override;
 
        sp<MediaPlayerBase>     createPlayer(player_type playerType);
 
        virtual status_t        setDataSource(
                        const sp<IMediaHTTPService> &httpService,
                        const char *url,
                        const KeyedVector<String8, String8> *headers);
 
        virtual status_t        setDataSource(int fd, int64_t offset, int64_t length);
 
        virtual status_t        setDataSource(const sp<IStreamSource> &source);
        virtual status_t        setDataSource(const sp<IDataSource> &source);
 
 
        sp<MediaPlayerBase>     setDataSource_pre(player_type playerType);
        status_t                setDataSource_post(const sp<MediaPlayerBase>& p,
                                                   status_t status);
 
                void            notify(int msg, int ext1, int ext2, const Parcel *obj);
 
                pid_t           pid() const { return mPid; }
        virtual status_t        dump(int fd, const Vector<String16>& args);
 
                audio_session_t getAudioSessionId() { return mAudioSessionId; }
        // Modular DRM
        virtual status_t prepareDrm(const uint8_t uuid[16], const Vector<uint8_t>& drmSessionId);
        virtual status_t releaseDrm();
        // AudioRouting
        virtual status_t setOutputDevice(audio_port_handle_t deviceId);
        virtual status_t getRoutedDeviceId(audio_port_handle_t* deviceId);
        virtual status_t enableAudioDeviceCallback(bool enabled);
 
    private:
        class AudioDeviceUpdatedNotifier: public AudioSystem::AudioDeviceCallback
        {
        public:
            AudioDeviceUpdatedNotifier(const sp<MediaPlayerBase>& listener) {
                mListener = listener;
            }
            ~AudioDeviceUpdatedNotifier() {}
 
            virtual void onAudioDeviceUpdate(audio_io_handle_t audioIo,
                                             audio_port_handle_t deviceId);
 
        private:
            wp<MediaPlayerBase> mListener;
        };
 
        friend class MediaPlayerService;
                                Client( const sp<MediaPlayerService>& service,
                                        pid_t pid,
                                        int32_t connId,
                                        const sp<IMediaPlayerClient>& client,
                                        audio_session_t audioSessionId,
                                        uid_t uid);
                                Client();
        virtual                 ~Client();
 
                void            deletePlayer();
 
        sp<MediaPlayerBase>     getPlayer() const { Mutex::Autolock lock(mLock); return mPlayer; }
 
 
 
        // @param type Of the metadata to be tested.
        // @return true if the metadata should be dropped according to
        //              the filters.
        bool shouldDropMetadata(media::Metadata::Type type) const;
 
        // Add a new element to the set of metadata updated. Noop if
        // the element exists already.
        // @param type Of the metadata to be recorded.
        void addNewMetadataUpdate(media::Metadata::Type type);
 
        // Disconnect from the currently connected ANativeWindow.
        void disconnectNativeWindow_l();
 
        status_t setAudioAttributes_l(const Parcel &request);
 
        class Listener : public MediaPlayerBase::Listener {
        public:
            Listener(const wp<Client> &client) : mClient(client) {}
            virtual ~Listener() {}
            virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) {
                sp<Client> client = mClient.promote();
                if (client != NULL) {
                    client->notify(msg, ext1, ext2, obj);
                }
            }
        private:
            wp<Client> mClient;
        };
 
        mutable     Mutex                         mLock;
                    sp<MediaPlayerBase>           mPlayer;
                    sp<MediaPlayerService>        mService;
                    sp<IMediaPlayerClient>        mClient;
                    sp<AudioOutput>               mAudioOutput;
                    pid_t                         mPid;
                    status_t                      mStatus;
                    bool                          mLoop;
                    int32_t                       mConnId;
                    audio_session_t               mAudioSessionId;
                    audio_attributes_t *          mAudioAttributes;
                    uid_t                         mUid;
                    sp<ANativeWindow>             mConnectedWindow;
                    sp<IBinder>                   mConnectedWindowBinder;
                    struct sockaddr_in            mRetransmitEndpoint;
                    bool                          mRetransmitEndpointValid;
                    sp<Client>                    mNextClient;
                    sp<MediaPlayerBase::Listener> mListener;
 
        // Metadata filters.
        media::Metadata::Filter mMetadataAllow;  // protected by mLock
        media::Metadata::Filter mMetadataDrop;  // protected by mLock
 
        // Metadata updated. For each MEDIA_INFO_METADATA_UPDATE
        // notification we try to update mMetadataUpdated which is a
        // set: no duplicate.
        // getMetadata clears this set.
        media::Metadata::Filter mMetadataUpdated;  // protected by mLock
 
        std::vector<DeathNotifier> mDeathNotifiers;
        sp<AudioDeviceUpdatedNotifier> mAudioDeviceUpdatedListener;
#if CALLBACK_ANTAGONIZER
                    Antagonizer*                  mAntagonizer;
#endif
    }; // Client

Client继承自BnMediaPlayer,而BnMediaPlayer继承自IMediaPlayer,Client也是MediaPlayerService的一个内部类,所以上述通过MediaPlayerService.create()的得到的player对象再调用setDataSource()时,实际上是执行的MediaPlayerService里面的setDataSource()。

这是BnMediaPlayer的继承关系


setDataSource()先获取到player的类型,MediaPlayer中有三种播放器,StagefrightPlayer、NuPlayer和TestPlayer,StagefrightPlayer因为存在严重的安全漏洞,并被黑客所利用,因此在Android 7.0中被删除了,以后默认使用的是NuPlayer。
获取完播放器类型执行了setDataSource_pre()方法

sp<MediaPlayerBase> MediaPlayerService::Client::setDataSource_pre(
        player_type playerType)
{
    ALOGV("player type = %d", playerType);
 
    // create the right type of player
    sp<MediaPlayerBase> p = createPlayer(playerType);
    if (p == NULL) {
        return p;
    }
 
    std::vector<DeathNotifier> deathNotifiers;
 
    // Listen to death of media.extractor service
    sp<IServiceManager> sm = defaultServiceManager();
    sp<IBinder> binder = sm->getService(String16("media.extractor"));
    if (binder == NULL) {
        ALOGE("extractor service not available");
        return NULL;
    }
    deathNotifiers.emplace_back(
            binder, [l = wp<MediaPlayerBase>(p)]() {
        sp<MediaPlayerBase> listener = l.promote();
        if (listener) {
            ALOGI("media.extractor died. Sending death notification.");
            listener->sendEvent(MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED,
                                MEDIAEXTRACTOR_PROCESS_DEATH);
        } else {
            ALOGW("media.extractor died without a death handler.");
        }
    });
 
    {
        using ::android::hidl::base::V1_0::IBase;
 
        // Listen to death of OMX service
        {
            sp<IBase> base = ::android::hardware::media::omx::V1_0::
                    IOmx::getService();
            if (base == nullptr) {
                ALOGD("OMX service is not available");
            } else {
                deathNotifiers.emplace_back(
                        base, [l = wp<MediaPlayerBase>(p)]() {
                    sp<MediaPlayerBase> listener = l.promote();
                    if (listener) {
                        ALOGI("OMX service died. "
                              "Sending death notification.");
                        listener->sendEvent(
                                MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED,
                                MEDIACODEC_PROCESS_DEATH);
                    } else {
                        ALOGW("OMX service died without a death handler.");
                    }
                });
            }
        }
 
        // Listen to death of Codec2 services
        {
            for (std::shared_ptr<Codec2Client> const& client :
                    Codec2Client::CreateFromAllServices()) {
                sp<IBase> base = client->getBase();
                deathNotifiers.emplace_back(
                        base, [l = wp<MediaPlayerBase>(p),
                               name = std::string(client->getServiceName())]() {
                    sp<MediaPlayerBase> listener = l.promote();
                    if (listener) {
                        ALOGI("Codec2 service \"%s\" died. "
                              "Sending death notification.",
                              name.c_str());
                        listener->sendEvent(
                                MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED,
                                MEDIACODEC_PROCESS_DEATH);
                    } else {
                        ALOGW("Codec2 service \"%s\" died "
                              "without a death handler.",
                              name.c_str());
                    }
                });
            }
        }
    }
 
    Mutex::Autolock lock(mLock);
 
    mDeathNotifiers.clear();
    mDeathNotifiers.swap(deathNotifiers);
    mAudioDeviceUpdatedListener = new AudioDeviceUpdatedNotifier(p);
 
    if (!p->hardwareOutput()) {
        mAudioOutput = new AudioOutput(mAudioSessionId, IPCThreadState::self()->getCallingUid(),
                mPid, mAudioAttributes, mAudioDeviceUpdatedListener);
        static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
    }
 
    return p;
}

首先执行createPlayer()创建NuPlayer对象

里面又通过MediaPlayerFactory的createPlayer()方法创建NuPlayer


这里先创建了NuPlayerDriver对象

NuPlayerDriver的构造方法里通过AVNuFactory::get()→createNuPlayer()最终创建了NuPlayer对象

创建完NuPlayer对象后紧接着执行了它的init()方法,完成一些初始化的动作

在MediaPlayerService.cpp中执行完setDataSource_pre()创建完NuPlayer对象后,紧接着执行setDataSource_post(),该方法第二个参数是NuPlayerDriver.cpp执行setDataSource()返回的结果

里面又执行了NuPlayer.cpp的setDataSourceAsync()

可以看到,应用层传递的视频路径最终会交由NuPlayer处理,具体NuPlayer如何对视频进行解码将在后面的文章详细分析。

在VideoView.java中,当openVideo()执行完mMediaPlayer.setDataSource(),之后执行了mMediaPlayer.setDisplay()用于设置视频显示的对象,此处跟图像显示Surface和Window有关系,后面的文章会详细分析。

执行完mMediaPlayer.setDisplay(),紧接着又执行了mMediaPlayer.prepareAsync(),最终也会执行NuPlayer的prepareAsync(),具体代码流程同setDataSource()

最后应用层VideoView调用start()方法执行播放时,此方法最终也会执行NuPlayer的start(),具体代码流程同setDataSource()

至此,视频播放流程由应用层VideoView到native层NuPlayer的完整流程分析到这儿,当然中间还有很多细节的处理没有具体去分析,但掌握了整体框架后再去分析细节功能就会信手拈来。

有关Android MediaPlayer源码分析的更多相关文章

  1. UE4 源码阅读:从引擎启动到Receive Begin Play - 2

    一、引擎主循环UE版本:4.27一、引擎主循环的位置:Launch.cpp:GuardedMain函数二、、GuardedMain函数执行逻辑:1、EnginePreInit:加载大多数模块int32ErrorLevel=EnginePreInit(CmdLine);PreInit模块加载顺序:模块加载过程:(1)注册模块中定义的UObject,同时为每个类构造一个类默认对象(CDO,记录类的默认状态,作为模板用于子类实例创建)(2)调用模块的StartUpModule方法2、FEngineLoop::Init()1、检查Engine的配置文件找出使用了哪一个GameEngine类(UGame

  2. 建模分析 | 平面2R机器人(二连杆)运动学与动力学建模(附Matlab仿真) - 2

    目录0专栏介绍1平面2R机器人概述2运动学建模2.1正运动学模型2.2逆运动学模型2.3机器人运动学仿真3动力学建模3.1计算动能3.2势能计算与动力学方程3.3动力学仿真0专栏介绍?附C++/Python/Matlab全套代码?课程设计、毕业设计、创新竞赛必备!详细介绍全局规划(图搜索、采样法、智能算法等);局部规划(DWA、APF等);曲线优化(贝塞尔曲线、B样条曲线等)。?详情:图解自动驾驶中的运动规划(MotionPlanning),附几十种规划算法1平面2R机器人概述如图1所示为本文的研究本体——平面2R机器人。对参数进行如下定义:机器人广义坐标

  3. 网站日志分析软件--让网站日志分析工作变得更简单 - 2

    网站的日志分析,是seo优化不可忽视的一门功课,但网站越大,每天产生的日志就越大,大站一天都可以产生几个G的网站日志,如果光靠肉眼去分析,那可能看到猴年马月都看不完,因此借助网站日志分析工具去分析网站日志,那将会使网站日志分析工作变得更简单。下面推荐两款网站日志分析软件。第一款:逆火网站日志分析器逆火网站日志分析器是一款功能全面的网站服务器日志分析软件。通过分析网站的日志文件,不仅能够精准的知道网站的访问量、网站的访问来源,网站的广告点击,访客的地区统计,搜索引擎关键字查询等,还能够一次性分析多个网站的日志文件,让你轻松管理网站。逆火网站日志分析器下载地址:https://pan.baidu.

  4. elasticsearch源码关于TransportSearchAction【阶段三】 - 2

    1.回顾.TransportServicepublicclassTransportServiceextendsAbstractLifecycleComponentTransportService:方法:1publicfinalTextendsTransportResponse>voidsendRequest(finalTransport.Connectionconnection,finalStringaction,finalTransportRequestrequest,finalTransportRequestOptionsoptions,TransportResponseHandlerT>

  5. (附源码)vue3.0+.NET6实现聊天室(实时聊天SignalR) - 2

    参考文章搭建文章gitte源码在线体验可以注册两个号来测试演示图:一.整体介绍  介绍SignalR一种通讯模型Hub(中心模型,或者叫集线器模型),调用这个模型写好的方法,去发送消息。  内容有:    ①:Hub模型的方法介绍    ②:服务器端代码介绍    ③:前端vue3安装并调用后端方法    ④:聊天室样例整体流程:1、进入网站->调用连接SignalR的方法2、与好友发送消息->调用SignalR的自定义方法 前端通过,signalR内置方法.invoke()  去请求接口3、监听接受方法(渲染消息)通过new signalR.HubConnectionBuilder().on

  6. ABB-IRB-1200运动学分析MATLAB RVC工具分析+Simulink-Adams联合仿真 - 2

    一、机器人介绍        此处是基于MATLABRVC工具箱,对ABB-IRB-1200型号的微型机械臂进行正逆向运动学分析,并利Simulink工具实现对机械臂进行具有动力学参数的末端轨迹规划仿真,最后根据机械模型设计Simulink-Adams联合仿真。 图1.ABBIRB 1200尺寸参数示意图ABBIRB 1200提供的两种型号广泛适用于各作业,且两者间零部件通用,两种型号的工作范围分别为700 mm 和 900 mm,大有效负载分别为 7 kg 和5 kg。 IRB 1200 能够在狭小空间内能发挥其工作范围与性能优势,具有全新的设计、小型化的体积、高效的性能、易于集成、便捷的接

  7. 关于Qt程序打包后运行库依赖的常见问题分析及解决方法 - 2

    目录一.大致如下常见问题:(1)找不到程序所依赖的Qt库version`Qt_5'notfound(requiredby(2)CouldnotLoadtheQtplatformplugin"xcb"in""eventhoughitwasfound(3)打包到在不同的linux系统下,或者打包到高版本的相同系统下,运行程序时,直接提示段错误即segmentationfault,或者Illegalinstruction(coredumped)非法指令(4)ldd应用程序或者库,查看运行所依赖的库时,直接报段错误二.问题逐个分析,得出解决方法:(1)找不到程序所依赖的Qt库version`Qt_5'

  8. ruby-on-rails - 如何使用 ruby​​-prof 和 JMeter 分析 Rails - 2

    我想使用ruby​​-prof和JMeter分析Rails应用程序。我对分析特定Controller/操作/或模型方法的建议方法不感兴趣,我想分析完整堆栈,从上到下。所以我运行这样的东西:RAILS_ENV=productionruby-prof-fprof.outscript/server>/dev/null然后我在上面运行我的JMeter测试计划。然而,问题是使用CTRL+C或SIGKILL中断它也会在ruby​​-prof可以写入任何输出之前杀死它。如何在不中断ruby​​-prof的情况下停止mongrel服务器? 最佳答案

  9. 【Unity游戏破解】外挂原理分析 - 2

    文章目录认识unity打包目录结构游戏逆向流程Unity游戏攻击面可被攻击原因mono的打包建议方案锁血飞天无限金币攻击力翻倍以上统称内存挂透视自瞄压枪瞬移内购破解Unity游戏防御开发时注意数据安全接入第三方反作弊系统外挂检测思路狠人自爆实战查看目录结构用il2cppdumper例子2-森林whoishe后记认识unity打包目录结构dll一般很大,因为里面是所有的游戏功能编译成的二进制码游戏逆向流程开发人员代码被编译打包到GameAssembly.dll中使用il2ppDumper工具,并借助游戏名_Data\il2cpp_data\Metadata\global-metadata.dat

  10. Cesium源码解析一(terrain文件的加载、解析与渲染全过程梳理) - 2

    快速导航(持续更新中…)Cesium源码解析一(terrain文件的加载、解析与渲染全过程梳理)Cesium源码解析二(metadataAvailability的含义)Cesium源码解析三(metadata元数据拓展中行列号的分块规则解析)Cesium源码解析四(Quantized-Mesh(.terrain)格式文件在CesiumJS和UE中加载情况的对比)目录1.前言2.本篇的由来3.terrain文件的加载3.1更新环境3.2更新和执行渲染命令3.3数据优化3.4结束当前帧4.总结1.前言  目前市场上三维比较火的实现方案主要有两种,b/s的方案主要是Cesium,c/s的方案主要是u

随机推荐