音视频 二 看书的笔记 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;

额 就这吧 瘫了!

相关推荐
赏金术士1 小时前
Kotlin 从入门到进阶 之函数模块(核心基础)(二)
android·开发语言·kotlin
EasyDSS2 小时前
私有化音视频系统/视频直播点播/音视频点播EasyDSS构建智慧校园视频智能服务新体系
音视频
鱼儿也有烦恼2 小时前
8 issues were found when checking AAR metadata:
android
HalvmånEver2 小时前
MySQL的索引
android·linux·数据库·学习·mysql
我是发哥哈3 小时前
跨AI模型生成视频的五大维度对比:选型避坑指南
大数据·人工智能·学习·机器学习·chatgpt·音视频
自小吃多4 小时前
本地部署大模型避坑实录|Ollama+AnythingLLM 一直加载、CPU 爆满、GPU 闲置问题完整解决
笔记
我命由我123456 小时前
Windows 操作系统 - Windows 查看架构类型
运维·windows·笔记·学习·系统架构·运维开发·系统
金蕊泛流霞6 小时前
dify安装教程
笔记
赏金术士7 小时前
Kotlin 从入门到进阶 之作用域函数 & 优雅写法(五)
android·开发语言·kotlin
墨染倾城殇7 小时前
蓝牙 5.3 双模一体,面向车载、智能音箱及多场景的经典音频与LE Audio应用
音视频·智能音箱·le audio·蓝牙双模·蓝牙5.3