此类是用于播放声音和视频的主要 API
对方不想多说向你丢了一个链接 MediaPlayer
- Idle 空闲状态
- Initialized 初始化状态 调用 setDataSource() 时会进入此状态 setDataSource必须在Idle 状态下调用,否则就抛出异常了了了了了。
- Prepared 准备状态 回调监听setOnPreparedListener 进入此状态后 可以设置一些属性 例如音量/循环播放等
- Started
- Paused
- PlaybackCompleted 播放完了状态
- Stopped
- End 调用release() 就结束
- Error 错误状态 播放过程中错误 会 回调到OnErrorListener.onError 此时应嗲用reset方法使MediaPlayer 恢复到Idle状态。
创建 过程
Android MediaPlayer.create(Context context, Uri uri) 的创建过程:
```
public static MediaPlayer create(Context context, Uri uri, SurfaceHolder holder,
AudioAttributes audioAttributes, int audioSessionId) {
try {
MediaPlayer mp = new MediaPlayer(audioSessionId);
final AudioAttributes aa = audioAttributes != null ? audioAttributes :
new AudioAttributes.Builder().build(); //音频属性
mp.setAudioAttributes(aa); //设置音频属性
mp.native_setAudioSessionId(audioSessionId); //音频会话ID
mp.setDataSource(context, uri); //设置资源
if (holder != null) { //控制器 操纵Surface
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;
}
简化过程
val mediaPlayer = MediaPlayer()
mediaPlayer .setDataSource(file.path)
mediaPlayer .prepare()
说明创建 就需要 new MediaPlayer()
接下来我们看看 MediaPlayer的构造方法?
我替大家看了!
定义了个Looper myLooper不为空赋值 MainLooper不为空赋值
创建EventHandler对象
创建了个TimeProvider
创建了个Vector<InputStream>
然后native_setup()
naive方法 都是先加载 native文件
static {
System.loadLibrary("media_jni");
native_init();
}
s0 我们先看 native_init()
env -> FindClass("android/media/MediaPlayer") //调用java层,搞到MediaPlayer
env -> GetFieldID(clazz,"mNativeContext","J") 搞到mNativeContext
env -> GetStaticMethodId(clazz,"postEventFromNative","参数类型省略")
就是下边的
public class MediaPlayer extends PlayerBase
implements SubtitleController.Listener
, VolumeAutomation
, AudioRouting
{
//省略一堆
private long mNativeContext; // accessed by native methods
/*
* Called from native code when an interesting event happens. This method
* just uses the EventHandler system to post the event back to the main app thread.
* We use a weak reference to the original MediaPlayer object so that the native
* code is safe from the object disappearing from underneath it. (This is
* the cookie passed to native_setup().)
*/
private static void postEventFromNative(Object mediaplayer_ref,
int what, int arg1, int arg2, Object obj)
{
//省略一堆
if (mp.mEventHandler != null) {
Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj);
mp.mEventHandler.sendMessage(m);
}
}
//省略一堆
}
postEventFromNative 把Natvie事件回调到Java层,使用EventHandler post事件到主线程中,软引用指向原生MediaPlayer,保证Native代码的安全。
接下来native_setup 干了啥
sp<MediaPlayer> mp = new MediaPlayer();
sp listener = new JNIMediaPlayerListener(env,this,weak_this)
mp->setListener(listener)
setMediaPlayer(env,thiz,mp);
就是创建native MediaPlayer 创建回调
setDataSource 过程
文件非 文件 分开判断处理
文件资源 最终调用 _setDataSource 的映射 setDataSoureceFD
1 获取MediaPlayer对象
2 获取java.io.FileDescriptor
3 检测异常及抛出

非文件资源 nativeSetDataSource 映射 setDataSoureceAndHeaders
1 获取MediaPlayer对象
2 通过Binder机制 最后强制转换获取IMediaHTTPService
3 检测异常及抛出
setDisplay 设置控制器
prepare后的流程
运行时 MediaPlayer 大致可分为 C S 两个部分,在两个进程中运行,通过Binder机制视频IPC通信。
给播放器设置数据源之后。调用prepare 或 prepareAsync。
文件类型,调用prepare 将暂时阻塞,直到回调onPrepared进入Prepared状态。
public void prepare() throws IOException, IllegalStateException {
_prepare();
scanInternalSubtitleTracks();
// DrmInfo, if any, has been resolved by now.
synchronized (mDrmLock) {
mDrmInfoResolved = true;
}
}
调用natvie方法prepare()
简单描述(其实也云里雾里),其他自行脑补领悟百度deep,
获取mediaplayer
getVideoSurfaceTexture
setVideoSurfaceTexture(上边get到的)
检测异常
prepareAsync()
public native void prepareAsync() throws IllegalStateException;
多了点 锁 判断状态 等待之类的
Start()
stayAwake(true); 屏幕操作
/**
* Set the low-level power management behavior for this MediaPlayer. This
* can be used when the MediaPlayer is not playing through a SurfaceHolder
* set with {@link #setDisplay(SurfaceHolder)} and thus can use the
* high-level {@link #setScreenOnWhilePlaying(boolean)} feature.
*
* <p>This function has the MediaPlayer access the low-level power manager
* service to control the device's power usage while playing is occurring.
* The parameter is a combination of {@link android.os.PowerManager} wake flags.
* Use of this method requires {@link android.Manifest.permission#WAKE_LOCK}
* permission.
* By default, no attempt is made to keep the device awake during playback.
*
* @param context the Context to use
* @param mode the power/wake mode to set
* @see android.os.PowerManager
*/
public void setWakeMode(Context context, int mode) {
boolean washeld = false;
/* Disable persistant wakelocks in media player based on property */
if (SystemProperties.getBoolean("audio.offload.ignore_setawake", false) == true) {
Log.w(TAG, "IGNORING setWakeMode " + mode);
return;
}
if (mWakeLock != null) {
if (mWakeLock.isHeld()) {
washeld = true;
mWakeLock.release();
}
mWakeLock = null;
}
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(mode|PowerManager.ON_AFTER_RELEASE, MediaPlayer.class.getName());
mWakeLock.setReferenceCounted(false);
if (washeld) {
mWakeLock.acquire();
}
}
以下是几种模式 对CPS 屏幕 键盘的影响自行搜索
最终调用
private native void _start() throws IllegalStateException;
额 就这吧 瘫了!