此类是用于播放声音和视频的主要 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) { //控制器 操纵Surfacemp.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 PlayerBaseimplements 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;
额 就这吧 瘫了!