音视频 二 看书的笔记 MediaPlayer

此类是用于播放声音和视频的主要 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;

额 就这吧 瘫了!

相关推荐
恋猫de小郭1 小时前
2026 Flutter VS React Native ,同时在 AI 时代 VS Native 开发,你没见过的版本
android·前端·flutter
冬奇Lab2 小时前
PowerManagerService(上):电源状态与WakeLock管理
android·源码阅读
tingshuo29173 小时前
D006 【模板】并查集
笔记
BoomHe7 小时前
Now in Android 架构模式全面分析
android·android jetpack
二流小码农14 小时前
鸿蒙开发:上传一张参考图片便可实现页面功能
android·ios·harmonyos
鹏程十八少15 小时前
4.Android 30分钟手写一个简单版shadow, 从零理解shadow插件化零反射插件化原理
android·前端·面试
Kapaseker15 小时前
一杯美式搞定 Kotlin 空安全
android·kotlin
三少爷的鞋15 小时前
Android 协程时代,Handler 应该退休了吗?
android
tingshuo29171 天前
S001 【模板】从前缀函数到KMP应用 字符串匹配 字符串周期
笔记
火柴就是我1 天前
让我们实现一个更好看的内部阴影按钮
android·flutter