Android 12(S) MultiMedia Learning(二)MediaPlayer Java
Android提供了MediaPlayer这样一个简单易用的音视频java播放接口,通过几个接口调用即可实现音视频播放。
源码位置 http://aospxref.com/android-12.0.0_r3/xref/frameworJavaks/base/media/java/android/media/MediaPlayer.java ,可以从这里找到我们想要的播放接口,大致浏览一圈,除了没有倍速播放接口,其他还是比较全的。
好,我们要实现一个最简单的播放器要哪些步骤和哪些接口呢?
// 1、创建MediaPlayer对象
new MediaPlayer
// 2、设置数据源
setDataSource
// 3、设置播放窗口
setDisPlay
// 4、准备播放
prepare/prepareAsync
// 5、开始播放以及其他功能
start/pause/seekTo/stop
1、构造函数
public MediaPlayer() {
this(AudioSystem.AUDIO_SESSION_ALLOCATE);
}
private MediaPlayer(int sessionId) {
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>();
AttributionSource attributionSource = AttributionSource.myAttributionSource();
// set the package name to empty if it was null
if (attributionSource.getPackageName() == null) {
attributionSource = attributionSource.withPackageName("");
}
/* Native setup requires a weak reference to our object.
* It's easier to create it here than in C++.
*/
try (ScopedParcelState attributionSourceState = attributionSource.asScopedParcelState()) {
native_setup(new WeakReference<MediaPlayer>(this), attributionSourceState.getParcel());
}
baseRegisterPlayer(sessionId);
}
这里主要做了两件事:
a. 调用baseRegisterPlayer方法获取AudioSevice,并且做一些设定。但是这边有一个疑问,这个sessionId并没有传给native,有什么作用的?
b. 调用JNI方法native_setup
JNI代码位置 http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/media/jni/
static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
jobject jAttributionSource)
{
ALOGV("native_setup"); Parcel* parcel = parcelForJavaObject(env, jAttributionSource);
android::content::AttributionSourceState attributionSource;
attributionSource.readFromParcel(parcel);
sp<MediaPlayer> mp = new MediaPlayer(attributionSource);
if (mp == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
return;
} // create new listener and give it to MediaPlayer
sp<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);
}
JNI代码中创建了一个native的MediaPlayer对象,并且给这个对象注册了一个Listener,这样native MediaPlayer就可以通过这个listener来调用Java层的postEventFromNative方法
// native mediaplayer
void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj)
{
// ......
sp<MediaPlayerListener> listener = mListener;
listener->notify(msg, ext1, ext2, obj);
} // JNI
void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2, const Parcel *obj)
{
// ......
env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
msg, ext1, ext2, NULL);
// ......
} // 在native_init中初始化
fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
"(Ljava/lang/Object;IIILjava/lang/Object;)V"); // Java mediaplayer
private static void postEventFromNative(Object mediaplayer_ref,
int what, int arg1, int arg2, Object obj)
2、setDataSource
这个函数有很多重载版本,用的最多的应该是参数只有uri的版本,最后调用到私有的setDataSource当中,这个方法中会调用nativeSetDataSource方法
public void setDataSource(String path)
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
setDataSource(path, null, null);
} @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void setDataSource(String path, String[] keys, String[] values,
List<HttpCookie> cookies)
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
final Uri uri = Uri.parse(path);
final String scheme = uri.getScheme();
if ("file".equals(scheme)) {
path = uri.getPath();
} else if (scheme != null) {
// handle non-file sources
nativeSetDataSource(
MediaHTTPService.createHttpServiceBinderIfNecessary(path, cookies),
path,
keys,
values);
return;
} final File file = new File(path);
try (FileInputStream is = new FileInputStream(file)) {
setDataSource(is.getFD());
}
}
JNI代码主要调用了native mediaplayer 的 setDataSource方法
static void
android_media_MediaPlayer_setDataSourceAndHeaders(
JNIEnv *env, jobject thiz, jobject httpServiceBinderObj, jstring path,
jobjectArray keys, jobjectArray values) { // ......
status_t opStatus =
mp->setDataSource(
httpService,
pathStr,
headersVector.size() > 0? &headersVector : NULL);
}
3、setDisplay
这里比较简单,直接调用JNI函数 _setVideoSurface
public void setDisplay(SurfaceHolder sh) {
mSurfaceHolder = sh;
Surface surface;
if (sh != null) {
surface = sh.getSurface();
} else {
surface = null;
}
_setVideoSurface(surface);
updateSurfaceScreenOn();
}
JNI中主要是调用native mediaplayer的setVideoSurfaceTexture方法,根据注释内容,这个方法的调用需要在setDataSource之后调用
static void
setVideoSurface(JNIEnv *env, jobject thiz, jobject jsurface, jboolean mediaPlayerMustBeAlive)
{
// .....
sp<IGraphicBufferProducer> new_st;
if (jsurface) {
sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
if (surface != NULL) {
new_st = surface->getIGraphicBufferProducer();
if (new_st == NULL) {
jniThrowException(env, "java/lang/IllegalArgumentException",
"The surface does not have a binding SurfaceTexture!");
return;
}
new_st->incStrong((void*)decVideoSurfaceRef);
} else {
jniThrowException(env, "java/lang/IllegalArgumentException",
"The surface has been released");
return;
}
} env->SetLongField(thiz, fields.surface_texture, (jlong)new_st.get()); // This will fail if the media player has not been initialized yet. This
// can be the case if setDisplay() on MediaPlayer.java has been called
// before setDataSource(). The redundant call to setVideoSurfaceTexture()
// in prepare/prepareAsync covers for this case.
mp->setVideoSurfaceTexture(new_st);
}
4、prepareAsync
这个更简单了,直接调用JNI方法prepareAsync
public native void prepareAsync() throws IllegalStateException;
调用了native mediaplayer的prepareAsync方法
static void
android_media_MediaPlayer_prepareAsync(JNIEnv *env, jobject thiz)
{
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
} // Handle the case where the display surface was set before the mp was
// initialized. We try again to make it stick.
sp<IGraphicBufferProducer> st = getVideoSurfaceTexture(env, thiz);
mp->setVideoSurfaceTexture(st); process_media_player_call( env, thiz, mp->prepareAsync(), "java/io/IOException", "Prepare Async failed." );
}
由于是异步调用,native mediaplayer 的prepare执行结束之后,会发送一个MEDIA_PREPARED,然后执行OnPreparedListener 方法
5、start/pause/stop/seekTo
这几个方法比较类似,都是在接口中去调用JNI方法,start调用 _start方法
public void start() throws IllegalStateException {
//FIXME use lambda to pass startImpl to superclass
final 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(0); // unknown device at this point
stayAwake(true);
tryToEnableNativeRoutingCallback();
_start();
}
private native void _start() throws IllegalStateException;
JNI中调用native mediaplayer的start方法
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 );
}
接下来用这几个接口来写一个最简单的播放器,代码以及效果如下

package com.example.mediaplayertest; import androidx.appcompat.app.AppCompatActivity; import android.media.MediaPlayer;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import android.widget.TextView; import java.io.IOException; public class MainActivity extends AppCompatActivity {
public static final String TAG = "MainActivity"; TextView textView = null;
Button start = null;
Button pause = null;
Button resume = null;
Button stop = null;
SurfaceView surfaceView = null;
String uri = null;
MediaPlayer player = null; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); start = findViewById(R.id.btn_play);
pause = findViewById(R.id.btn_pause);
resume = findViewById(R.id.btn_resume);
stop = findViewById(R.id.btn_stop);
textView = findViewById(R.id.textView);
surfaceView = findViewById(R.id.surfaceView); start.setOnClickListener(clickListener);
pause.setOnClickListener(clickListener);
resume.setOnClickListener(clickListener);
stop.setOnClickListener(clickListener); } private View.OnClickListener clickListener = new View.OnClickListener() {
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.btn_play:
Play();
break;
case R.id.btn_pause:
Pause();
break;
case R.id.btn_resume:
Resume();
break;
case R.id.btn_stop:
Stop();
break;
default:
break;
}
}
}; @Override
protected void onDestroy()
{
super.onDestroy();
if (player != null && player.isPlaying())
{
player.stop();
player.release();
player = null;
}
} private void Play()
{
uri = textView.getText().toString();
player = new MediaPlayer();
player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayer.start();
}
});
player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
mediaPlayer.reset();
start.setEnabled(true);
}
});
player.setDisplay(surfaceView.getHolder());
try {
player.setDataSource(uri);
} catch (IOException e) {
e.printStackTrace();
}
player.prepareAsync();
start.setEnabled(false);
} private void Pause()
{
if(player != null && player.isPlaying())
player.pause();
} private void Resume()
{
if (player != null)
player.start();
} private void Stop()
{
if (player != null)
{
player.stop();
player.release();
player = null;
}
start.setEnabled(true);
}
}
Android 12(S) MultiMedia Learning(二)MediaPlayer Java的更多相关文章
- 【朝花夕拾】Android性能篇之(二)Java内存分配
前言 在内存方面,相比于C/C++程序员,咱们java系程序员算是比较幸运的,因为对于内存的分配和回收,都交给了JVM来处理了,而不需要手动在代码中去完成.有了虚拟机内存管理机制,也就不 ...
- android NDK 实用学习(二)-java端对象成员赋值和获取对象成员值
1,关于java端类及接口定义请参考: android NDK 实用学习-获取java端类及其类变量 2,对传过来的参数进行赋值: 对bool类型成员进行赋值 env->SetBooleanF ...
- Android 音频的播放之二MediaPlayer
MediaPlayer类可用于控制音频/视频文件或流的播放.关于怎样使用这个类的方法还能够阅读VideoView类的文档. 1.状态图 对播放音频/视频文件和流的控制是通过一个状态机来管理的. 下图显 ...
- 【朝花夕拾】Android性能篇之(三)Java内存回收
在上一篇日志([朝花夕拾]Android性能篇之(二)Java内存分配)中有讲到,JVM内存由程序计数器.虚拟机栈.本地方法栈.GC堆,方法区五个部分组成.其中GC堆是一块多线程的共享区域,它存在的作 ...
- Android 12(S) 图形显示系统 - Surface 一点补充知识(十二)
必读: Android 12(S) 图形显示系统 - 开篇 一.前言 因为个人工作主要是Android多媒体播放的内容,在工作中查看源码或设计程序经常会遇到调用API: static inline i ...
- Android 基于Android的手机邮件收发(JavaMail)之二( Welcome.java 和 ReceiveAndSend.java )
周末休息,这次我们继上次内容继续.上一篇内容我们讲述的是一些准备工作.下载两个javamail.jar和activation.jar文件,然后再BuildPath~ 言归正传,为了展示效果,在这里我申 ...
- Android 12(S) 图形显示系统 - 示例应用(二)
1 前言 为了更深刻的理解Android图形系统抽象的概念和BufferQueue的工作机制,这篇文章我们将从Native Level入手,基于Android图形系统API写作一个简单的图形处理小程序 ...
- Android反编译(一)之反编译JAVA源码
Android反编译(一) 之反编译JAVA源码 [目录] 1.工具 2.反编译步骤 3.实例 4.装X技巧 1.工具 1).dex反编译JAR工具 dex2jar http://code.go ...
- Android系列之网络(二)----HTTP请求头与响应头
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/ ...
- Android特效专辑(十二)——仿支付宝咻一咻功能实现波纹扩散特效,精细小巧的View
Android特效专辑(十二)--仿支付宝咻一咻功能实现波纹扩散特效,精细小巧的View 先来看看这个效果 这是我的在Only上添加的效果,说实话,Only现在都还只是半成品,台面都上不了,怪自己技术 ...
随机推荐
- 一种提升深度多视角行人检测的泛化性能的方法 Bringing Generalization to Deep Multi-View Pedestrian Detection
一种提升深度多视角行人检测的泛化性能的方法 Bringing Generalization to Deep Multi-View Pedestrian Detection 论文url: https:/ ...
- 一、Unity调用Xcode封装方法(工程引用文件)
1.Xcode新建Static Library 工程 (我起的名字是UnityExtend 可以在接下来的图中看到) 2.打包unity ios工程 unity打包ios 打出Xcode工程 3.打开 ...
- HH的项链—树状数组
题目描述 HH有一串由各种漂亮的贝壳组成的项链.HH相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义. HH不断地收集新的贝壳,因此他的项链变得越来越长.有一天 ...
- vscode使用ES6装饰器器语法
1. react项目 配置 package.json 需要安装 npm i @babel/plugin-proposal-decorators 可能需要重启项目 { "babel" ...
- 【Oracle】力扣简单的练习题
Oracle力扣简单的练习题 请你编写一个 SQL 查询来交换所有的 'f' 和 'm' /* Write your PL/SQL query statement below */ /******** ...
- 力扣287(java&python)-寻找重复数(中等)
题目: 给定一个包含 n + 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1 和 n),可知至少存在一个重复的整数. 假设 nums 只有 一个重复的整数 ,返回 这个重复的 ...
- 业内首款云原生技术中台产品云原生 Stack 来了!
简介: 云原生 Stack 满足了各种典型场景下客户对于线下高集成平台的诉求,让企业数字话转型不受技术约束,专注业务本身,加速企业数字化迭代. 今天,企业数字化转型依然面临很大的挑战,虽然有很多新技术 ...
- 移动云正式发布基于龙蜥 Anolis OS 的 BC-Linux V8.2 通用版操作系统
简介: 2020年12月CentOS项目组宣布CentOS 8将于2021年12月31日结束支持,这意味着从2022年开始,使用CentOS 8的用户,将无法得到来自官方的新硬件支持.bug修复和安全 ...
- [GPT] 序列模型分类及其模型方案选择
序列模型可以分为两大类:线性序列模型和非线性序列模型. 线性序列模型:这类模型基于线性关系对时间序列进行建模和预测.常见的线性序列模型包括自回归模型(AR).移动平均模型(MA)和自回归移动平均模 ...
- [FAQ] golang-migrate/migrate error: migration failed in line 0: (details: Error 1065: Query was empty)
当我们使用 migrate create 创建了迁移文件. 没有及时填写内容,此时运行 migrate 的后续命令比如 up.down 会抛出错误: error: migration failed i ...