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现在都还只是半成品,台面都上不了,怪自己技术 ...
随机推荐
- 如何通过 kubectl 进入 node shell
概述 假设这样一个场景: 生产环境中,Node 都需要通过堡垒机登录,但是 kubectl 是可以直接在个人电脑上登录的. 这种场景下,我想要通过 kubectl 登录到 K8S 集群里的 Node, ...
- HDC2021技术分论坛:盘点分布式软总线数据传输技术中的黑科技
作者:houweibo,软总线首席技术专家:lidonghua,软总线技术专家 随着万物互联时代的到来,特别是大量媒体资源的涌入和使用,用户对传输的要求不断提高,怎样的传输技术才能满足未来的用户需求呢 ...
- 第十四篇:JavaScript基础
一.CSS内容补充之position 10.position:fixed:固定div在页面的一个位置: top:0; right:0; left:0; position:absolute + rela ...
- VIM YouCompleteMe(ycm) 对于Python3第三方库的自动补全【部分解决】
VIM YouCompleteMe(ycm) 对于Python3第三方库的自动补全[部分解决] Python3 学习笔记 问题:VIM 用YouCompleteMe(ycm)自动补全插件时,只能支持P ...
- SpringBoot学习:文件上传和下载
maven导入依赖 首先创建一个maven项目,然后加入以下配置,就创建好了一个springboot项目 <parent> <groupId>org.springframewo ...
- 如何解决python安装mysqlclient失败问题
在使用Django等框架来操作MySQL时,实际上底层还是通过Python来操作的,首先需要安装一个驱动程序,在Python3中,驱动程序有多种选择,比如有pymysql以及mysqlclient等. ...
- 使用WebApi+Vue3从0到1搭建《权限管理系统》:二、搭建JWT系统鉴权
视频地址:[WebApi+Vue3从0到1搭建<权限管理系统>系列视频:搭建JWT系统鉴权-哔哩哔哩] https://b23.tv/R6cOcDO qq群:801913255 一.在ap ...
- 关于 Data Lake 的概念、架构与应用场景介绍
数据湖(Data Lake)概念介绍 什么是数据湖(Data Lake)? 数据湖的起源,应该追溯到2010年10月,由 Pentaho 的创始人兼 CTO, James Dixon 所提出,他提出的 ...
- OpenYurt 如何 “0 侵入” 攻破云边融合难点
简介: 随着 5G.IoT.直播.CDN 等行业和业务的发展,越来越多的算力和业务开始下沉到距离数据源或者终端用户更近的位置,以期获得很好的响应时间和成本,这是一种明显区别于传统中心模式的计算方式-- ...
- 重度使用Flutter研发模式下的页面性能优化实践
简介: 淘宝特价版是集团内应用Flutter技术场景比较多,且用户量一亿人以上的应用了.目前我们首页.详情.店铺.我的,看看短视频,及评价,设置等二级页面都在用Flutter技术搭建.一旦Flutte ...