zoukankan      html  css  js  c++  java
  • Android MediaPlayer架构 -- MediaPlayer的创建过程

    本文系作者自己学习之所用,文章内容仅出自作者拙劣之思考,问题之处烦请不吝指教。

      MediaPlayer 能被用来控制音/视频文件或流媒体的回放。Android中以MediaPlayer类作为音视频播放的基础类,围绕着他开展了一系列的处理。学习一个新的模块,最简单的步骤就是找到一个典型的应用程序,通过它的实现,来分析整个模块的数据流和控制流。典型的MediaPlayer在Java处的接口包括视频播放类VideoView以及音频专用MediaPlayer类。

      一、 一个简单的视频播放demo app

      Android中实现视频的播放可以采用MediaPlayer+SurfaceView配合的方式,其实Android还为开发人员提供了另外一种更简单的播放视频媒体的方式,那就是VideoView。VideoView类,其实质是用MediaPlayer类来实现的,只是由于其是视频播放,不得不和Surfaceview挂上够,才将其独立出来。使得其有如下的结构:

    1 public class VideoView extends SurfaceView
    2         implements MediaPlayerControl, SubtitleController.Anchor {
    3     private static final String TAG = "VideoView";
    4     ......
    5 }

      在Android中,提供了VideoView组件用于播放视频文件。想要使用VideoView组件播放视频,首先需要在布局文件中创建该组件,然后在Activity中获取该组件,并应用其setVideoPath()方法或setVideoURI()方法加载要播放的视频,最后调用start()方法来播放视频。另外,VideoView组件还提供了stop()和pause()方法,用于停止或暂停视频的播放。

      在APP中,VideoView的典型简单使用如下:

    1     mMediaController =new MediaController(this);
    2     mVideoView = (VideoView) findViewById(R.id.videoView); 
    3     mVideoView.setVideoPath("/sdcard/1080P24FPS.mp4"); // 设置档案路径
    4     mVideoView.setMediaController(mMediaController); // 设置播放器的控制器
    5     mVideoView.start(); // 开始播放

      先看看效果就是下面这个样子,短短几行代码一个播放器就做好了,还可以进行暂停,快进,快退,进度条控制。

      PS:VideoView还提供许多其他播放控制API,在此不做重点介绍,以上代码也仅仅是个人demo,难免有误,谨慎参考使用。

    二、 VideoView中setVideoPath的处理

      任何华丽的语言都不如source code来的简单直接,上代码:

     1     /**
     2      * Sets video path.
     3      *
     4      * @param path the path of the video.
     5      */
     6     public void setVideoPath(String path) {
     7         setVideoURI(Uri.parse(path));
     8     }
     9 
    10     /**
    11      * Sets video URI.
    12      *
    13      * @param uri the URI of the video.
    14      */
    15     public void setVideoURI(Uri uri) {
    16         setVideoURI(uri, null);
    17     }
    18 
    19     /**
    20      * Sets video URI using specific headers.
    21      *
    22      * @param uri     the URI of the video.
    23      * @param headers the headers for the URI request.
    24      *                Note that the cross domain redirection is allowed by default, but that can be
    25      *                changed with key/value pairs through the headers parameter with
    26      *                "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value
    27      *                to disallow or allow cross domain redirection.
    28      */
    29     public void setVideoURI(Uri uri, Map<String, String> headers) {
    30         mUri = uri;
    31         mHeaders = headers;
    32         mSeekWhenPrepared = 0;
    33         openVideo(); // openVideo的处理,让最终的处理权交给了MediaPlayer
    34         requestLayout();
    35         invalidate();
    36     }

      经过setVideoPath(String path) --> setVideoURI(Uri uri) --> setVideoURI(Uri uri, Map<String, String> headers) 的调用流程,程序最终来到了openVideo()这一函数中:

     1     private void openVideo() {
     2             ......
     3             mMediaPlayer = new MediaPlayer();
     4 
     5             mMediaPlayer.setDataSource(mContext, mUri, mHeaders);
     6             mMediaPlayer.setDisplay(mSurfaceHolder);
     7             
     8             mMediaPlayer.prepareAsync();
     9             .......
    10     }

      在上面的代码中可以清楚的看到,我们首先new 了一个MediaPlayer类的对象,然后去setDataSource,到这里VideoView::openVideo的处理让最终的处理权交给了MediaPlayer。接下来我们就进入MediaPlayer的世界.

    三、 MediaPlayer的世界

      3.1 new MediaPlayer对象过程

      首先关注MediaPlayer对象的创建过程,这也是分析android源码的一个基本要求。依次通过Java --> JNI(libmedia_jni.so) -- > Frameworks(libmedia.so)的处理流程。

      MediaPlayer.java 构造函数,这一部分在 Android MediaPlayer架构 -- 前言小知识点(一)也有分析

        public MediaPlayer() {
            super(new AudioAttributes.Builder().build());
    
            Looper looper;
            if ((looper = Looper.myLooper()) != null) {
                mEventHandler = new EventHandler(this, looper);
            } else if ((looper = Looper.getMainLooper()) != null) {
                mEventHandler = new EventHandler(this, looper);
            } else {
                mEventHandler = null;
            }
    
            mTimeProvider = new TimeProvider(this);
            mOpenSubtitleSources = new Vector<InputStream>();
    
            /* Native setup requires a weak reference to our object.
             * It's easier to create it here than in C++.
             */
            native_setup(new WeakReference<MediaPlayer>(this));
        }

      可以看到,在使用VideoView中到创建MediaPlayer会经过:new VideoView——> new MediaPlayer ——>native_setup 这样一个典型的对象建立过程,并传递到JNI。

      native_setup主要用于本地C++层的对象的建立,在JNI代码(frameworksasemediajniandroid_media_MediaPlayer.cpp)中可以找到对应的native函数:

     1 static void
     2 android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
     3 {
     4     ALOGV("native_setup");
     5     sp<MediaPlayer> mp = new MediaPlayer(); // 实例化一个native MediaPlayer(frameworksavmedialibmediamediaplayer.cpp)
     6     if (mp == NULL) {
     7         jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
     8         return;
     9     }
    10 
    11     // create new listener and give it to MediaPlayer
    12     sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
    13     mp->setListener(listener);
    14 
    15     // Stow our new C++ MediaPlayer in an opaque field in the Java object.
    16     setMediaPlayer(env, thiz, mp);
    17 }

      进入JNI做android_media_MediaPlayer_native_setup处理:sp<MediaPlayer> mp = new MediaPlayer() 这个native MediaPlayer会去和media service进行交互实现真正的播放功能,使得最终处理进入C++的世界。   

      3.2 setDataSource过程

      MediaPlayer java class中提供了多种setDataSource方法来设置不同的URI播放流,在此我们以播放本地档案为例来介绍处理流程:

      VideoView::setVideoURI() --> MediaPlayer::setDataSource(mContext, mUri, mHeaders); --> MediaPlayer::setDataSource(uri.toString()) --> MediaPlayer::setDataSource(path, null, null) --> MediaPlayer::setDataSource(fd) --> setDataSource(fd, 0, 0x7ffffffffffffffL) --> _setDataSource(fd, offset, length)

      最后会调到 _setDataSource(fd, offset, length),看这个方法被声明为 native method

    1     private native void _setDataSource(MediaDataSource dataSource)
    2           throws IllegalArgumentException, IllegalStateException;

      在JNI层我们找到该方法对应的JNI method实现:

    1     {"_setDataSource",      "(Ljava/io/FileDescriptor;JJ)V",    (void *)android_media_MediaPlayer_setDataSourceFD},

        android_media_MediaPlayer_setDataSourceFD()方法定义如下:

    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." );
    }

      上面这段代码可以看到最终调用了status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length)

    //*********************************************************************************************************************************************************************

      MediaPlayer的C++代码位于/frameworks/av/media/libmedia/mediaplayer.cpp, 编译后形成一个libmedia.so。

      下面来看这个API的处理,接下去都只分析framework层的C++的处理流

     1 status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length)
     2 {
     3     ALOGV("setDataSource(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length);
     4     status_t err = UNKNOWN_ERROR;
     5     const sp<IMediaPlayerService>& service(getMediaPlayerService());
     6     if (service != 0) {
     7         sp<IMediaPlayer> player(service->create(this, mAudioSessionId));
     8         if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
     9             (NO_ERROR != player->setDataSource(fd, offset, length))) {
    10             player.clear();
    11         }
    12         err = attachNewPlayer(player);
    13     }
    14     return err;
    15 }

      典型的Binder C/S架构,获取MediaPlayerService的proxy,通过MediaPlayerService来创建一个player,然后对这个player调用setDataSource

      3.3 MediaPlayerService的工作

      启动与获取

      MediaPlayerService同其他的Binder Service一样,作为一个server对外提供服务,它是在mediaserver中启动的:

      /frameworks/av/media/mediaserver/main_mediaserver.cpp

     1 int main(int argc __unused, char **argv __unused)
     2 {
     3     signal(SIGPIPE, SIG_IGN);
     4 
     5     sp<ProcessState> proc(ProcessState::self());
     6     sp<IServiceManager> sm(defaultServiceManager());
     7     ALOGI("ServiceManager: %p", sm.get());
     8     InitializeIcuOrDie();
     9     MediaPlayerService::instantiate(); //启动MediaPlayerService
    10     ResourceManagerService::instantiate();
    11     registerExtensions();
    12     ProcessState::self()->startThreadPool();
    13     IPCThreadState::self()->joinThreadPool();
    14 }

       在/frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp中对instantiate()方法的定义:

    1 void MediaPlayerService::instantiate() {
    2     defaultServiceManager()->addService(
    3             String16("media.player"), new MediaPlayerService());
    4 }

       在上面这段代码中我们注册了一个名为“media.player"的Binder Service,也就是MediaPlayerService,之后就可以通过 binder = sm->getService(String16("media.player"));来请求这个服务了

       Player的创建

      获取MediaPlayerService后就要去create player: sp<IMediaPlayer> player(service->create(this, mAudioSessionId));

      create请求处理:

      

  • 相关阅读:
    poj 2528 Mayor's posters (线段树+离散化)
    poj 1201 Intervals (差分约束)
    hdu 4109 Instrction Arrangement (差分约束)
    poj 1195 Mobile phones (二维 树状数组)
    poj 2983 Is the Information Reliable? (差分约束)
    树状数组 讲解
    poj 2828 Buy Tickets (线段树)
    hdu 1166 敌兵布阵 (树状数组)
    Ubuntu网络配置
    Button控制窗体变量(开关控制灯的状态)
  • 原文地址:https://www.cnblogs.com/roger-yu/p/8144536.html
Copyright © 2011-2022 走看看