对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java->JNI->C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。
public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback {
private static final String TAG = "MainActivity";
private SurfaceView surfaceView;
private MediaPlayer mediaPlayer;
private SurfaceHolder holder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init() {
surfaceView=findViewById(R.id.surfaceView);
mediaPlayer=new MediaPlayer();
holder = surfaceView.getHolder();
holder.addCallback(this);
try {
mediaPlayer.setDataSource("/sdcard/m.mp4");
mediaPlayer.prepare();
/**
* 阻塞准备,多用于网络视频流
*/
mediaPlayer.prepareAsync();
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayer.start();
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
mediaPlayer.setDisplay(holder);
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
mediaPlayer.release();//释放
}
}
创建MediaPlayer对象有两种,一种是create,一种是直接new,先来看create方法。
public static MediaPlayer create(Context context, Uri uri, SurfaceHolder holder,
AudioAttributes audioAttributes, int audioSessionId) {
try {
//1. new一个MediaPlayer
MediaPlayer mp = new MediaPlayer();
//2.如果音频相关处理为空,就创建一个音频属性
final AudioAttributes aa = audioAttributes != null ? audioAttributes :
new AudioAttributes.Builder().build();
mp.setAudioAttributes(aa); //3.设置音频属性
mp.setAudioSessionId(audioSessionId); //4.设置声音的会话id,因为声音和视频是分开渲染的,而且在后面根据音视频进行分别解码的时候,就需要根据id来判断流是音频还是视频
mp.setDataSource(context, uri);//5.设置数据源
if (holder != null) { // surfaceHolder,控制surface
mp.setDisplay(holder);
}
mp.prepare(); //准备
return mp;
} catch (IOException ex) {
//省略,这个就是对各种Exception的处理
}
return null;
}
总结,这里可以看出来,使用create()创建播放器的时候,内部会new一个新的mediaplayer,并且设置了数据源,并且做好了prepare准备工作,也就是说在外部再调用一下start()就可以播放音视频资源了。
还有一种方式就是new MediaPlayer也就是我demo里的那种,下面看下源码
public MediaPlayer() {
Looper looper; //定义一个looper
//如果Mylooper不为空,就赋值
if ((looper = Looper.myLooper()) != null) {
//实例化mEventHandler对象
mEventHandler = new EventHandler(this, looper);
} //如果主线程的looper不为空,也可以赋值到looper中
else if ((looper = Looper.getMainLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
} else {
mEventHandler = null;
}
//时间数据容器
mTimeProvider = new TimeProvider(this);
mOpenSubtitleSources = new Vector<InputStream>();
/* Native setup requires a weak reference to our object.
* It's easier to create it here than in C++.
* 使用native_setup开始创建MediaPlayer了,此处为软引用,为了在C++中更好的创建MediaPlayer
*/
native_setup(new WeakReference<MediaPlayer>(this));
}
EventHandler是为MediaPlayer的一个内部类,继承与Handler,用来处理各种消息:MEDIA_PREPARED,MEDIA_PLAYBACK_COMPLETE,MEDIA_STOPPED,MEDIA_SEEK_COMPLETE等等,分别调用不同的接口来进行处理。最后是调用了native层的方法来进行创建,那么肯定需要先加载.so文件,所以在MediaPlayer中有一段静态代码块,用来加载和链接库文件
static {
System.loadLibrary("media_jni");//media_jni.so
native_init();
}
进入到android_media_MediaPlayer.cpp分析,文件目录在:frameworks/base/media/jni/android_media_MediaPlayer.cpp
static void
android_media_MediaPlayer_native_init(JNIEnv *env)
{
jclass clazz;//类的句柄
//这里是通过Native层调用Java层,获取到MediaPlayer对象
clazz = env->FindClass("android/media/MediaPlayer");
if (clazz == NULL) {
return;
}
// 获取Java层的mNativeContext成员变量,实际上对应的是一个内存地址,就是jni层的MediaPlayer对象
fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
if (fields.context == NULL) {
return;
}
//获取Java层静态方法,第一个参数是类,第二个参数是方法名,第三个参数是该方法的签名
fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative","(Ljava/lang/Object;IIILjava/lang/Object;)V");
if (fields.post_event == NULL) {
return;
}
native_init方法是通过JNI调用Java层的MediaPlayer类,然后拿到mNativeContext的指针,接着调用了MediaPlayer.java的静态方法postEventFromNative,这个方法的作用就是把Native事件回调到Java层,然后使用EventHandler post事件回到主线程,使用软引用指向原生的MediaPlayer,是为了保证native层的代码是安全的。
static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
//在jni层创建了一个MediaPlayer
sp<MediaPlayer> mp = new MediaPlayer();
//给MediaPlayer创建了一个一个listener
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.将这个创建好的jni对象保存到Java中
setMediaPlayer(env, thiz, mp);
}
来看下这个JNIMediaPlayerListener,也是在这个cpp文件里
class JNIMediaPlayerListener: public MediaPlayerListener
{
public:
JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz);
~JNIMediaPlayerListener();
virtual void notify(int msg, int ext1, int ext2, const Parcel *obj = NULL);
private:
JNIMediaPlayerListener();
jclass mClass; // Reference to MediaPlayer class
jobject mObject; // Weak ref to MediaPlayer Java object to call on
};
有个notify方法,从下面可以看出,它的作用就是从JNI层向Java层通知
void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2, const Parcel *obj)
{
JNIEnv *env = AndroidRuntime::getJNIEnv();
if (obj && obj->dataSize() > 0) {
jobject jParcel = createJavaParcelObject(env);
if (jParcel != NULL) {
Parcel* nativeParcel = parcelForJavaObject(env, jParcel);
nativeParcel->setData(obj->data(), obj->dataSize());
//向Java层发送通知事件
env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
msg, ext1, ext2, jParcel);
env->DeleteLocalRef(jParcel);
}
} else {
env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
msg, ext1, ext2, NULL);
}
}
fields.post_event也就是Java层的MediaPlayer.java中的postEventFromNative,可以看下它的实现
private static void postEventFromNative(Object mediaplayer_ref,
int what, int arg1, int arg2, Object obj)
{
final MediaPlayer mp = (MediaPlayer)((WeakReference)mediaplayer_ref).get();
if (mp.mEventHandler != null) {
Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj);
mp.mEventHandler.sendMessage(m);
}
}
上面也说过是使用mEventHandler来处理的
来看下setMediaPlayer(env, thiz, mp)这个函数
static sp<MediaPlayer> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer>& player)
{
Mutex::Autolock l(sLock);//锁
sp<MediaPlayer> old = (MediaPlayer*)env->GetLongField(thiz, fields.context);
if (player.get()) {
player->incStrong((void*)setMediaPlayer);
}
if (old != 0) {
old->decStrong((void*)setMediaPlayer);
}
//将新的Player对象保存在Java层的mNativeContext中
env->SetLongField(thiz, fields.context, (jlong)player.get());
return old;
}
总结一下,在Java使用new或者create()创建MediaPlayer的时候,最后都会调用到native_up函数,这个函数的作用就是创建c++层的MediaPlayer以及去设置一些回调用的listener,然后将其保存在Java层中,到此创建过程就完成了。
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) {
// 1、处理非文件资源
nativeSetDataSource(
MediaHTTPService.createHttpServiceBinderIfNecessary(path, cookies),
path,
keys,
values);
return;
}
//2、处理文件类型
final File file = new File(path);
if (file.exists()) {
FileInputStream is = new FileInputStream(file);
FileDescriptor fd = is.getFD();
setDataSource(fd);
is.close();
} else {
throw new IOException("setDataSource failed.");
}
}
先来看下处理文件类型的,点进去发现最后都是调到了native层,
static void
android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
{
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
//省略,一些异常代码
//获取path变量
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
// 对mp->setDataSource(fd, offset, length)函数得到的状态,进行相应的通知
process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
}
static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message)
{
if (exception == NULL) { // Don't throw exception. Instead, send an event.
if (opStatus != (status_t) OK) { //不抛出异常,发送一个事件来替代异常
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
//通知MEDIA_ERROR,也就是发送了一个通知事件
if (mp != 0) mp->notify(MEDIA_ERROR, opStatus, 0);
}
} else { // Throw exception! 省略了
}
}
再来看下非文件类型的:
static void
android_media_MediaPlayer_setDataSourceAndHeaders(
JNIEnv *env, jobject thiz, jobject httpServiceBinderObj, jstring path,jobjectArray keys, jobjectArray values) {
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
//省略异常代码
const char *tmp = env->GetStringUTFChars(path, NULL);
if (tmp == NULL) { // Out of memory
return;
}
ALOGV("setDataSource: path %s", tmp);
String8 pathStr(tmp);
//释放
env->ReleaseStringUTFChars(path, tmp);
tmp = NULL;
// We build a KeyedVector out of the key and val arrays
KeyedVector<String8, String8> headersVector;
if (!ConvertKeyValueArraysToKeyedVector(
env, keys, values, &headersVector)) {
return;
}
sp<IMediaHTTPService> httpService;
if (httpServiceBinderObj != NULL) {
//通过Binder机制将httpServiceBinderObj传给IPC并返回binder
//后面会具体分析,这里只是JNI层
sp<IBinder> binder = ibinderForJavaObject(env, httpServiceBinderObj);
//强制转换成IMediaHTTPService
httpService = interface_cast<IMediaHTTPService>(binder);
}
//这里和上面的文件类型的操作一样
status_t opStatus =
mp->setDataSource(
httpService,
pathStr,
headersVector.size() > 0? &headersVector : NULL);
process_media_player_call(
env, thiz, opStatus, "java/io/IOException",
"setDataSource failed." );
}
setDisplay是用来设置播放视频的
public void displaysetDisplay(SurfaceHolder sh) {
mSurfaceHolder = sh;//1. surfaceHolder用来控制Surface的
Surface surface;
if (sh != null) {
surface = sh.getSurface();
} else {
surface = null;
}
_setVideoSurface(surface);//2. 给视频设置Surface,Surface通俗点说就是画画的地方
updateSurfaceScreenOn();//3.将画面更新到屏幕上
}
_setVideoSurface对应JNI层的代码是:
static void
setVideoSurface(JNIEnv *env, jobject thiz, jobject jsurface, jboolean mediaPlayerMustBeAlive)
{
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
//省略抛出异常代码
//减少Java层mNativeSurfaceTexture保存的对Jni层之前对ISurfaceTexture的引用
decVideoSurfaceRef(env, thiz);
//IGraphicBufferProducer
sp<IGraphicBufferProducer> new_st;
if (jsurface) {
//得到Java层的Surface
sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
if (surface != NULL) {//不为空,获取IGraphicBufferProducer
new_st = surface->getIGraphicBufferProducer();
//省略 为空抛出异常
}
//incStrong,智能指针的引用计数 +1
new_st->incStrong((void*)decVideoSurfaceRef);
} else {
//异常代码
}
}
//重新设置Java层的mNativeSurfacetexture保存jni层的对象的引用
env->SetLongField(thiz, fields.surface_texture, (jlong)new_st.get());
//如果mediaPlayer还没有被初始化,setDataSource就会失败,,
//但是在setDataSource之前就调用了setDisplay(),
//在prepare/prepareAsync中调用setVideoSurfaceTexture,就会覆盖这个情况
mp->setVideoSurfaceTexture(new_st);
}
static void
decVideoSurfaceRef(JNIEnv *env, jobject thiz)
{
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL) {
return;
}
//得到旧的IGraphicBufferProducer
sp<IGraphicBufferProducer> old_st = getVideoSurfaceTexture(env, thiz);
if (old_st != NULL) {//如果不为空,智能引用计数就-1
old_st->decStrong((void*)decVideoSurfaceRef);
}
}
也就是通过JNI返回的Surface,MediaPlayer时时做好更新准备。
本文的分析,看到这里也知道其实只是从Java->JNI层进行了分析,具体的mp->setDataSource方法的具体实现代码,这个并没有具体的来说,下篇会来说说JNI层到C/C++层。可以参照时序图来进行分析。