涉及文件目录:
\frameworks\base\media\java\android\media\MediaPlayer.java
\frameworks\base\media\jni\android_media_MediaPlayer.cpp
\frameworks\av\include\media\mediaplayer.h
\frameworks\av\media\libmedia\mediaplayer.cpp
\frameworks\av\media\libmediaplayerservice\MediaPlayerFactory.cpp
\frameworks\av\media\libmediaplayerservice\MediaPlayerService.cpp
\frameworks\av\media\libmediaplayerservice\MediaPlayerService.h

mediaplayer可用于播放音视频文件,简单实现流程如下代码所示:

mp = new MediaPlayer();
AssetFileDescriptor afd = context.getResources().openRawResourceFd(musicId==-1? R.raw.music:musicId);
mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
afd.close();
mp.prepare();//如果是网络数据流,这里建议使用prepareAsync同步方法,但是在得到prepare回调之前,不可以调用start、stop、seek等操作,否则会产生错误
mp.start();
mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {@Overridepublic void onCompletion(MediaPlayer mediaPlayer) {}
});
mp.setOnErrorListener(new MediaPlayer.OnErrorListener() {@Overridepublic boolean onError(MediaPlayer mediaPlayer, int i, int i1) {return false;}
});

也可以直接调用其create方法,将本地资源id或者uri,或者网络资源url传入:

public static MediaPlayer create(Context context, int resid,AudioAttributes audioAttributes, int audioSessionId) {try {AssetFileDescriptor afd = context.getResources().openRawResourceFd(resid);if (afd == null) return null;MediaPlayer mp = new MediaPlayer();final AudioAttributes aa = audioAttributes != null ? audioAttributes :new AudioAttributes.Builder().build();mp.setAudioAttributes(aa);mp.setAudioSessionId(audioSessionId);mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());afd.close();mp.prepare();return mp;} catch (IOException ex) {Log.d(TAG, "create failed:", ex);// fall through} catch (IllegalArgumentException ex) {Log.d(TAG, "create failed:", ex);// fall through} catch (SecurityException ex) {Log.d(TAG, "create failed:", ex);// fall through}return null;}public static MediaPlayer create(Context context, Uri uri, SurfaceHolder holder,AudioAttributes audioAttributes, int audioSessionId) {try {MediaPlayer mp = new MediaPlayer();final AudioAttributes aa = audioAttributes != null ? audioAttributes :new AudioAttributes.Builder().build();mp.setAudioAttributes(aa);mp.setAudioSessionId(audioSessionId);mp.setDataSource(context, uri);if (holder != null) {mp.setDisplay(holder);}mp.prepare();return mp;} catch (IOException ex) {Log.d(TAG, "create failed:", ex);// fall through} catch (IllegalArgumentException ex) {Log.d(TAG, "create failed:", ex);// fall through} catch (SecurityException ex) {Log.d(TAG, "create failed:", ex);// fall through}return null;}

mediaplayer运行流程图如下,在java端调用mediaplay接口实现播放音视频的开始、暂停、结束之类操作,都是通过JNI调用在native由native中的mediaplyer类来实现接口的调用,在mediaplayer中通过binder实现跨进程调用,最终实现是在MediaPlayerService中管理的client去具体实现播放暂停功能。

mediaplayer中static块如下:

static {System.loadLibrary("media_jni");native_init();}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;}//后续通过postEventFromNativ将native层消息传送至java层fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative","(Ljava/lang/Object;IIILjava/lang/Object;)V");if (fields.post_event == NULL) {return;}//......省略
}

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 == null) {return;}switch (what) {case MEDIA_INFO:if (arg1 == MEDIA_INFO_STARTED_AS_NEXT) {new Thread(new Runnable() {@Overridepublic void run() {// this acquires the wakelock if needed, and sets the client side statemp.start();}}).start();Thread.yield();}break;case MEDIA_DRM_INFO:if (obj instanceof Parcel) {Parcel parcel = (Parcel)obj;DrmInfo drmInfo = new DrmInfo(parcel);synchronized (mp.mDrmLock) {mp.mDrmInfo = drmInfo;}} else {Log.w(TAG, "MEDIA_DRM_INFO msg.obj of unexpected type " + obj);}break;case MEDIA_PREPARED:synchronized (mp.mDrmLock) {mp.mDrmInfoResolved = true;}break;}if (mp.mEventHandler != null) {Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj);mp.mEventHandler.sendMessage(m);}}

mediaplayer构造函数如下:主要工作为初始化内部handler消息处理器,和调用JNI初始化,

public MediaPlayer() {super(new AudioAttributes.Builder().build(),AudioPlaybackConfiguration.PLAYER_TYPE_JAM_MEDIAPLAYER);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(new WeakReference<MediaPlayer>(this));baseRegisterPlayer();}

native_setup在android_media_MediaPlayer.cpp实现,新建c++层的MediaPlayer,将java层的Mediaplayer的引用传入,以便及时的到C++层运行状况的通知

static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{ALOGV("native_setup");sp<MediaPlayer> mp = new MediaPlayer();if (mp == NULL) {jniThrowException(env, "java/lang/RuntimeException", "Out of memory");return;}// create new listener and give it to MediaPlayersp<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);
}

单个流程分析如下,首先分析setDataSource,在java层最终进入流程_setDataSource,

private native void _setDataSource(FileDescriptor fd, long offset, long length)throws IOException, IllegalArgumentException, IllegalStateException;

在JNI中如下:

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

\frameworks\av\include\media\mediaplayer.h中定义了MediaPlayer类,其中包含了java层MediaPlayer类所包含的所有针对音视频播放的具体操作,如下:

class MediaPlayer : public BnMediaPlayerClient,public virtual IMediaDeathNotifier
{
public:MediaPlayer();~MediaPlayer();status_t        setDataSource(const sp<IMediaHTTPService> &httpService,const char *url,const KeyedVector<String8, String8> *headers);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        prepare();status_t        prepareAsync();status_t        start();status_t        stop();status_t        pause();bool            isPlaying();//......省略部分method以及filed,包括复位控制、声音控制、位置控制、监听控制、播放控制等};

MediaPlayer中setDataSource实现如下:通过获取MediaPlayerService服务,来从MediaPlayerService中获取IMediaPlayer实体,通过该实体完成setDataSource操作

status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length)
{ALOGV("setDataSource(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length);status_t err = UNKNOWN_ERROR;//得到mediaplayerservice服务const sp<IMediaPlayerService> service(getMediaPlayerService());if (service != 0) {//得到具体播放实体类sp<IMediaPlayer> player(service->create(this, mAudioSessionId));if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||//使用具体实现完成setDataSource操作(NO_ERROR != player->setDataSource(fd, offset, length))) {player.clear();}err = attachNewPlayer(player);}return err;
}

frameworks\av\media\libmedia\IMediaDeathNotifier.cpp中:

const sp<IMediaPlayerService> IMediaDeathNotifier::getMediaPlayerService()
{ALOGV("getMediaPlayerService");Mutex::Autolock _l(sServiceLock);if (sMediaPlayerService == 0) {sp<IServiceManager> sm = defaultServiceManager();sp<IBinder> binder;do {binder = sm->getService(String16("media.player"));if (binder != 0) {break;}ALOGW("Media player service not published, waiting...");usleep(500000); // 0.5 s} while (true);if (sDeathNotifier == NULL) {sDeathNotifier = new DeathNotifier();}binder->linkToDeath(sDeathNotifier);sMediaPlayerService = interface_cast<IMediaPlayerService>(binder);}ALOGE_IF(sMediaPlayerService == 0, "no media player service!?");return sMediaPlayerService;
}

\frameworks\av\media\libmediaplayerservice\MediaPlayerService.h中client类包含了所有MediaPlayer类中对播放进行的具体操作,只是全部都是虚函数,可能这个是跟具体硬件有差异,所有此处只是给出接口规范,具体实现方法因具体芯片而有差异。

class Client : public BnMediaPlayer {// IMediaPlayer interfacevirtual 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);

MediaPlayerService中create如下:

sp<IMediaPlayer> MediaPlayerService::create(const sp<IMediaPlayerClient>& client,audio_session_t audioSessionId)
{pid_t pid = IPCThreadState::self()->getCallingPid();int32_t connId = android_atomic_inc(&mNextConnId);sp<Client> c = new Client(this, pid, connId, client, audioSessionId,IPCThreadState::self()->getCallingUid());ALOGV("Create new client(%d) from pid %d, uid %d, ", connId, pid,IPCThreadState::self()->getCallingUid());wp<Client> w = c;{Mutex::Autolock lock(mLock);mClients.add(w);}return c;
}

返回了IMediaPlayer实体之后,就使用IMediaPlayer完成具体的操作了,比如:setDataSource、start、pause等操作。
start流程如下:

public void start() throws IllegalStateException {//FIXME use lambda to pass startImpl to superclassfinal int delay = getStartDelayMs();if (delay == 0) {startImpl();   //没有延迟要求则直接开始播放} else {new Thread() {public void run() {try {Thread.sleep(delay);} catch (InterruptedException e) {e.printStackTrace();}baseSetStartDelayMs(0);try {startImpl();     //否则就完成延时后再播放} catch (IllegalStateException e) {// fail silently for a state exception when it is happening after// a delayed start, as the player state could have changed between the// call to start() and the execution of startImpl()}}}.start();}}
private void startImpl() {baseStart(); //调用MediaPlayer父类方法设置当前状态之类stayAwake(true); //是否需要保持屏幕常量,如果需要则需要持有必要的唤醒锁_start();}private native void _start() throws IllegalStateException;
static void
android_media_MediaPlayer_start(JNIEnv *env, jobject thiz)
{ALOGV("start");sp<MediaPlayer> mp = getMediaPlayer(env, thiz);if (mp == NULL ) {jniThrowException(env, "java/lang/IllegalStateException", NULL);return;}process_media_player_call( env, thiz, mp->start(), NULL, NULL );
}
status_t MediaPlayer::start()
{ALOGV("start");status_t ret = NO_ERROR;Mutex::Autolock _l(mLock);mLockThreadId = getThreadId();if (mCurrentState & MEDIA_PLAYER_STARTED) {ret = NO_ERROR;   //mPlayer实体在setdatasource的时候就实现了初始化,start流程在其后面,这里就可以直接开始播放了。} else if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_PREPARED |MEDIA_PLAYER_PLAYBACK_COMPLETE | MEDIA_PLAYER_PAUSED ) ) ) {mPlayer->setLooping(mLoop);mPlayer->setVolume(mLeftVolume, mRightVolume);mPlayer->setAuxEffectSendLevel(mSendLevel);mCurrentState = MEDIA_PLAYER_STARTED;ret = mPlayer->start();if (ret != NO_ERROR) {mCurrentState = MEDIA_PLAYER_STATE_ERROR;} else {if (mCurrentState == MEDIA_PLAYER_PLAYBACK_COMPLETE) {ALOGV("playback completed immediately following start()");}}} else {ALOGE("start called in state %d, mPlayer(%p)", mCurrentState, mPlayer.get());ret = INVALID_OPERATION;}mLockThreadId = 0;return ret;
}

至此,MediaPlayer源码流程简要分析完成,MediaPlayer.java只是C++用于方便android应用调用的一个接口,该类并不负责具体的音视频的播放等相关操作,
具体的操作都是通过JNI调用Nativie层的MediaPlayer类来实现,Native层的MediaPlayer通过binder调用从MediaPlayerService中得到具体实施播放等操作的
真实实体client,Native层的MediaPlayer与MediaPlayerService通信,指挥client完成相应操作指令,并且接收client返回的状态值,继续通过JNI将其上传至java
层中的MediaPlayer类。

MediaPlayer源码流程简要分析相关推荐

  1. android 虚拟按键源码流程分析

    android 虚拟按键流程分析 今天来说说android 的虚拟按键的源码流程.大家都知道,android 系统的状态栏,虚拟按键,下拉菜单,以及通知显示,keyguard 锁屏都是在framewo ...

  2. Dalvik解释器源码到VMP分析

    前言 学习这块的主要目的还是想知道vmp是如何实现的,如何与系统本身的虚拟机配合工作,所以简单的学习了Dalvik的源码并对比分析了数字公司的解释器.笔记结构如下: dalvik解释器分析 dalvi ...

  3. JAVA源码优化、分析工具

    JAVA源码优化.分析工具 一.11款用于优化.分析源代码的Java工具 1. PMD from http://pmd.sourceforge.net/ PMD能够扫描Java 源代码,查找类似以下的 ...

  4. SpringSecurity详细介绍RememberMe源码流程

      本文我们来详细看看rememberMe的源码流程 rememberMe源码分析   首先我们要搞清楚rememberMe功能应该是在认证成功后才能具有的,所以我们应该从Usernamepasswo ...

  5. java sofa rpc_sofa-rpc服务端源码的详细分析(附流程图)

    本篇文章给大家带来的内容是关于sofa-rpc服务端源码的详细分析(附流程图),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. sofa-rpc是阿里开源的一款高性能的rpc框架,这篇 ...

  6. Vue 合同模板_vue源码逐行注释分析+40多m的vue源码程序流程图思维导图

    vue源码业余时间差不多看了一年,以前在网上找帖子,发现很多帖子很零散,都是一部分一部分说,断章的很多,所以自己下定决定一行行看,经过自己坚持与努力,现在基本看完了,差ddf那部分,因为考虑到自己要换 ...

  7. ReviewForJob——最小生成树(prim + kruskal)源码实现和分析

    [0]README 1)本文旨在给出 ReviewForJob--最小生成树(prim + kruskal)源码实现和分析, 还会对其用到的 技术 做介绍: 2)最小生成树是对无向图而言的:一个无向图 ...

  8. django-admin的源码流程

    一.admin的源码流程 首先可以确定的是:路由关系一定对应一个视图函数 a.当点击运行的时候,会先找到每一个app中的admin.py文件,并执行 settings---->INSTALLED ...

  9. strings.Builder 源码阅读与分析

    strings.Builder源码阅读与分析 背景之字符串拼接 在 Go 语言中,对于字符串的拼接处理有很多种方法,那么那种方法才是效率最高的呢? str := []string{"aa&q ...

最新文章

  1. 请求接口时params和data的区别
  2. [转载]《博客园精华集》Winform筛选结果(共105篇)
  3. View及ViewGroup的事件分发及传递(一)
  4. 如何在基于Bytom开发过程中集成IPFS
  5. 《实战突击:PHP项目开发案例整合(第2版)(含DVD光盘1张)》
  6. transformers库的使用【二】tokenizer的使用,模型的保存自定义
  7. Java Web Start入门基础教程
  8. Codeforces Round #133 (Div. 2) C. Hiring Staff 想法题目
  9. c++ 连接两个字符串实现代码 实现类似strcat功能(转)
  10. oauth2.0 php简化模式,OAuth2.0学习(1-5)授权方式2-简化模式(implicit grant type)
  11. java成员变量的初始化_Java成员变量初始化过程
  12. 大约HR升级版的设计为组汇总
  13. js进阶 11-8 jquery如何获取元素相对于父元素的位置
  14. cmd命令行开启windows远程桌面服务
  15. 使用MATLAB的trainNetwork设计一个简单的LSTM神经网络
  16. sysadmin.php,骆驼IPTV源码及搭建教程(只提供源码和安装视频不提供任何技术支持)-396资源...
  17. PLC指令系统的介绍
  18. 处暑(Limit of Heat )节到了,应了解的生活常识
  19. ContestHunter #26 B 玩骰子
  20. 当我们聊策略的时候,我们在聊什么?策略 Strategy。

热门文章

  1. 宁以pass-by-reference-to-const替换pass-by-value——effective c++学习笔记
  2. mapboxgl加载google地图、高德地图的在线切片地图
  3. idea 导入 vue项目 improt全都报红
  4. 手机APP开发(安卓、IOS)logo图标在线生成工具上线啦。
  5. TA游戏推荐:黑暗画风3D动作游戏《恐惧之魂》
  6. 计算机表演赛noc竞赛,【转载】NoC大赛教师竞赛项目介绍
  7. 微服务框架之微软Service Fabric
  8. JS 客户端ip归属地查询
  9. 移动构造函数和拷贝构造函数的区别
  10. u3d游戏开发视频潭州_Unity MMORPG游戏开发教程(一)——初识Unity