Android系统播放器MediaPlayer源码分析

前言

对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java->JNI->C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。

MediaPlayerDemo

public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback {
    private static final String TAG = "MainActivity";
    private SurfaceView surfaceView;
    private MediaPlayer mediaPlayer;
    private SurfaceHolder holder;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init();
    }
    private void init() {
        surfaceView=findViewById(R.id.surfaceView);
        mediaPlayer=new MediaPlayer();
        holder = surfaceView.getHolder();
        holder.addCallback(this);
        try {
            mediaPlayer.setDataSource("/sdcard/m.mp4");
            mediaPlayer.prepare();
            /**
             * 阻塞准备,多用于网络视频流
             */
            mediaPlayer.prepareAsync();

            mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                @Override
                public void onPrepared(MediaPlayer mediaPlayer) {
                    mediaPlayer.start();
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        mediaPlayer.setDisplay(holder);
    }
    @Override
    public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
         mediaPlayer.release();//释放
    }
}

从创建到setDisplay过程时序图

Android系统播放器MediaPlayer源码分析_第1张图片

创建过程

创建MediaPlayer对象有两种,一种是create,一种是直接new,先来看create方法。

public static MediaPlayer create(Context context, Uri uri, SurfaceHolder holder,
            AudioAttributes audioAttributes, int audioSessionId) {
        try {
            //1. new一个MediaPlayer
            MediaPlayer mp = new MediaPlayer(); 
            //2.如果音频相关处理为空,就创建一个音频属性
            final AudioAttributes aa = audioAttributes != null ? audioAttributes :
                new AudioAttributes.Builder().build(); 
            mp.setAudioAttributes(aa); //3.设置音频属性
            mp.setAudioSessionId(audioSessionId); //4.设置声音的会话id,因为声音和视频是分开渲染的,而且在后面根据音视频进行分别解码的时候,就需要根据id来判断流是音频还是视频
            mp.setDataSource(context, uri);//5.设置数据源
            if (holder != null) { // surfaceHolder,控制surface
                mp.setDisplay(holder); 
            }
            mp.prepare(); //准备
            return mp;
        } catch (IOException ex) {
        //省略,这个就是对各种Exception的处理
        }
        return null;
    }

总结,这里可以看出来,使用create()创建播放器的时候,内部会new一个新的mediaplayer,并且设置了数据源,并且做好了prepare准备工作,也就是说在外部再调用一下start()就可以播放音视频资源了。
还有一种方式就是new MediaPlayer也就是我demo里的那种,下面看下源码

 public MediaPlayer() {
        Looper looper; //定义一个looper
        //如果Mylooper不为空,就赋值
        if ((looper = Looper.myLooper()) != null) {
        	  //实例化mEventHandler对象
            mEventHandler = new EventHandler(this, looper);
        } //如果主线程的looper不为空,也可以赋值到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开始创建MediaPlayer了,此处为软引用,为了在C++中更好的创建MediaPlayer
         */
        native_setup(new WeakReference<MediaPlayer>(this));
    }

EventHandler是为MediaPlayer的一个内部类,继承与Handler,用来处理各种消息:MEDIA_PREPARED,MEDIA_PLAYBACK_COMPLETE,MEDIA_STOPPED,MEDIA_SEEK_COMPLETE等等,分别调用不同的接口来进行处理。最后是调用了native层的方法来进行创建,那么肯定需要先加载.so文件,所以在MediaPlayer中有一段静态代码块,用来加载和链接库文件

 static {
        System.loadLibrary("media_jni");//media_jni.so
        native_init();
    }

进入到android_media_MediaPlayer.cpp分析,文件目录在:frameworks/base/media/jni/android_media_MediaPlayer.cpp

static void
android_media_MediaPlayer_native_init(JNIEnv *env)
{
    jclass clazz;//类的句柄
    //这里是通过Native层调用Java层,获取到MediaPlayer对象
    clazz = env->FindClass("android/media/MediaPlayer");
    if (clazz == NULL) {
        return;
    }
    // 获取Java层的mNativeContext成员变量,实际上对应的是一个内存地址,就是jni层的MediaPlayer对象
    fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
    if (fields.context == NULL) {
        return;
    }
    //获取Java层静态方法,第一个参数是类,第二个参数是方法名,第三个参数是该方法的签名
    fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative","(Ljava/lang/Object;IIILjava/lang/Object;)V");
    if (fields.post_event == NULL) {
        return;
    }

native_init方法是通过JNI调用Java层的MediaPlayer类,然后拿到mNativeContext的指针,接着调用了MediaPlayer.java的静态方法postEventFromNative,这个方法的作用就是把Native事件回调到Java层,然后使用EventHandler post事件回到主线程,使用软引用指向原生的MediaPlayer,是为了保证native层的代码是安全的。

static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
   //在jni层创建了一个MediaPlayer
    sp<MediaPlayer> mp = new MediaPlayer();
    //给MediaPlayer创建了一个一个listener
    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.将这个创建好的jni对象保存到Java中
    setMediaPlayer(env, thiz, mp);
}

来看下这个JNIMediaPlayerListener,也是在这个cpp文件里

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
};

有个notify方法,从下面可以看出,它的作用就是从JNI层向Java层通知

void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2, const Parcel *obj)
{
    JNIEnv *env = AndroidRuntime::getJNIEnv();
    if (obj && obj->dataSize() > 0) {
        jobject jParcel = createJavaParcelObject(env);
        if (jParcel != NULL) {
            Parcel* nativeParcel = parcelForJavaObject(env, jParcel);
            nativeParcel->setData(obj->data(), obj->dataSize());
            //向Java层发送通知事件
            env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
                    msg, ext1, ext2, jParcel);
            env->DeleteLocalRef(jParcel);
        }
    } else {
        env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
                msg, ext1, ext2, NULL);
    }
   
}

fields.post_event也就是Java层的MediaPlayer.java中的postEventFromNative,可以看下它的实现

 private static void postEventFromNative(Object mediaplayer_ref,
                                            int what, int arg1, int arg2, Object obj)
    {
        final MediaPlayer mp = (MediaPlayer)((WeakReference)mediaplayer_ref).get();
        if (mp.mEventHandler != null) {
            Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj);
            mp.mEventHandler.sendMessage(m);
        }
    }
    上面也说过是使用mEventHandler来处理的

来看下setMediaPlayer(env, thiz, mp)这个函数

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);
    }
    //将新的Player对象保存在Java层的mNativeContext中
    env->SetLongField(thiz, fields.context, (jlong)player.get());
    return old;
}

总结一下,在Java使用new或者create()创建MediaPlayer的时候,最后都会调用到native_up函数,这个函数的作用就是创建c++层的MediaPlayer以及去设置一些回调用的listener,然后将其保存在Java层中,到此创建过程就完成了。

setDataSource过程

 private void setDataSource(String path, String[] keys, String[] values,List<HttpCookie> cookies)
            throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
        final Uri uri = Uri.parse(path);
        final String scheme = uri.getScheme();
        if ("file".equals(scheme)) {
            path = uri.getPath();
        } else if (scheme != null) {
            // 1、处理非文件资源
            nativeSetDataSource(
            MediaHTTPService.createHttpServiceBinderIfNecessary(path, cookies),
                path,
                keys,
                values);
            return;
        }
        //2、处理文件类型
        final File file = new File(path);
        if (file.exists()) {
            FileInputStream is = new FileInputStream(file);
            FileDescriptor fd = is.getFD();
            setDataSource(fd);
            is.close();
        } else {
            throw new IOException("setDataSource failed.");
        }
    }

先来看下处理文件类型的,点进去发现最后都是调到了native层,

static void
android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
{
    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
    //省略,一些异常代码
    //获取path变量
    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor)// 对mp->setDataSource(fd, offset, length)函数得到的状态,进行相应的通知
    process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
}

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);
            //通知MEDIA_ERROR,也就是发送了一个通知事件
            if (mp != 0) mp->notify(MEDIA_ERROR, opStatus, 0);
        }
    } else {  // Throw exception! 省略了
    }
}

再来看下非文件类型的:

static void
android_media_MediaPlayer_setDataSourceAndHeaders(
        JNIEnv *env, jobject thiz, jobject httpServiceBinderObj, jstring path,jobjectArray keys, jobjectArray values) {
    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
  	//省略异常代码
    const char *tmp = env->GetStringUTFChars(path, NULL);
    if (tmp == NULL) {  // Out of memory
        return;
    }
    ALOGV("setDataSource: path %s", tmp);
    String8 pathStr(tmp);
    //释放
    env->ReleaseStringUTFChars(path, tmp);
    tmp = NULL;
    // We build a KeyedVector out of the key and val arrays
    KeyedVector<String8, String8> headersVector;
    if (!ConvertKeyValueArraysToKeyedVector(
            env, keys, values, &headersVector)) {
        return;
    }
    sp<IMediaHTTPService> httpService;
    if (httpServiceBinderObj != NULL) {
    //通过Binder机制将httpServiceBinderObj传给IPC并返回binder
    //后面会具体分析,这里只是JNI层
        sp<IBinder> binder = ibinderForJavaObject(env, httpServiceBinderObj);
        //强制转换成IMediaHTTPService
        httpService = interface_cast<IMediaHTTPService>(binder);
    }
	//这里和上面的文件类型的操作一样
    status_t opStatus =
        mp->setDataSource(
                httpService,
                pathStr,
                headersVector.size() > 0? &headersVector : NULL);
    process_media_player_call(
            env, thiz, opStatus, "java/io/IOException",
            "setDataSource failed." );
}

setDisPlay过程

setDisplay是用来设置播放视频的

 public void displaysetDisplay(SurfaceHolder sh) {
        mSurfaceHolder = sh;//1. surfaceHolder用来控制Surface的
        Surface surface;
        if (sh != null) {
            surface = sh.getSurface();
        } else {
            surface = null;
        }
        _setVideoSurface(surface);//2. 给视频设置Surface,Surface通俗点说就是画画的地方
        updateSurfaceScreenOn();//3.将画面更新到屏幕上
    }

_setVideoSurface对应JNI层的代码是:

static void
setVideoSurface(JNIEnv *env, jobject thiz, jobject jsurface, jboolean mediaPlayerMustBeAlive)
{
    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
    //省略抛出异常代码
    //减少Java层mNativeSurfaceTexture保存的对Jni层之前对ISurfaceTexture的引用
    decVideoSurfaceRef(env, thiz);
    //IGraphicBufferProducer
    sp<IGraphicBufferProducer> new_st;
    if (jsurface) {
    //得到Java层的Surface
        sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
        if (surface != NULL) {//不为空,获取IGraphicBufferProducer
            new_st = surface->getIGraphicBufferProducer();
           	//省略  为空抛出异常
            }
            //incStrong,智能指针的引用计数 +1
            new_st->incStrong((void*)decVideoSurfaceRef);
        } else {
         //异常代码
        }
    }
    //重新设置Java层的mNativeSurfacetexture保存jni层的对象的引用
    env->SetLongField(thiz, fields.surface_texture, (jlong)new_st.get());

    //如果mediaPlayer还没有被初始化,setDataSource就会失败,,
    //但是在setDataSource之前就调用了setDisplay(),
    //在prepare/prepareAsync中调用setVideoSurfaceTexture,就会覆盖这个情况
    mp->setVideoSurfaceTexture(new_st);
}

static void
decVideoSurfaceRef(JNIEnv *env, jobject thiz)
{
    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
    if (mp == NULL) {
        return;
    }
    //得到旧的IGraphicBufferProducer
    sp<IGraphicBufferProducer> old_st = getVideoSurfaceTexture(env, thiz);
    if (old_st != NULL) {//如果不为空,智能引用计数就-1	
        old_st->decStrong((void*)decVideoSurfaceRef);
    }
}

也就是通过JNI返回的Surface,MediaPlayer时时做好更新准备。

本文的分析,看到这里也知道其实只是从Java->JNI层进行了分析,具体的mp->setDataSource方法的具体实现代码,这个并没有具体的来说,下篇会来说说JNI层到C/C++层。可以参照时序图来进行分析。

你可能感兴趣的:(源码分析)