一、引子:
先贴一段log:
08-18 21:57:20.110 11775 11812 V MediaPlayer: resetDrmState: mDrmInfo=null mDrmProvisioningThread=null mPrepareDrmInProgress=false mActiveDrmScheme=false
08-18 21:57:20.110 11775 11775 E MediaPlayerNative: error (-38, 0)
08-18 21:57:20.110 3206 3206 D MTK_KL : 4,60541,9379664038,-;[MI_AUDIO_Stop:8901][3079] u32ErrCode:0x0
08-18 21:57:20.110 3206 3206 D MTK_KL : 4,60542,9379664153,-;[_MI_AOUT_NotifyDisconnectInput:7948][3079] hAoutImpl:0x17000002, hInputImpl:0x19000000
08-18 21:57:20.110 3206 3206 D MTK_KL : 4,60543,9379664175,-;[_MI_AOUT_SetMultiMute:2794][3079] ePath:2, pszMuteName:_MI_AOUT_NotifyDisconnectInput, bMute:1, u32AutoUnmuteTimer:246, u32AoutMuteFlag:0x3
08-18 21:57:20.110 11775 11812 V MediaPlayer: cleanDrmObj: mDrmObj=null mDrmSessionId=null
08-18 21:57:20.110 11775 11775 E MediaPlayer: Error (-38,0)
08-18 21:57:20.110 11775 11812 V MediaPlayer: resetDrmState: mDrmInfo=null mDrmProvisioningThread=null mPrepareDrmInProgress=false mActiveDrmScheme=false
08-18 21:57:20.110 11775 11812 V MediaPlayer: cleanDrmObj: mDrmObj=null mDrmSessionId=null
经常处理媒体问题的朋友肯定遇到过上述log中的打印,实际上,这是Android原生MediaPlayer的消息回调机制。将底层的error/info/waring回调至应用层,而往往出现问题的时候,我们想要确认是哪个消息出现的问题却无从下手,这就需要真正理解这个消息机制才行,实际上,我们只需要关注三个点:listener、post event 和 notify。这三者的作用是:注册监听器给下层,下层调用监听器发送回调事件,上层处理该消息,而伴随着binder机制,这三者并不是在framework层代码中成对存在的,下面就对这三者依次进行分析。
Error (-38,0)对应的错误查找:
frameworks/base/services/core/jni/BroadcastRadio/types.h
二、Java(framework)->JNI:
先看看Java层是如何接收到消息的:JNI层利用Java层反射机制调用至Java层。
notify@ frameworks\base\media\jni\android_media_MediaPlayer.cpp
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());
/* 通过反射机制将底层msg传至java层 */
env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
msg, ext1, ext2, jParcel);
env->DeleteLocalRef(jParcel);
}
} else {
/* 通过反射机制将底层msg传至java层 */
env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
msg, ext1, ext2, NULL);
}
if (env->ExceptionCheck()) {
ALOGW("An exception occurred while notifying an event.");
LOGW_EX(env);
env->ExceptionClear();
}
}
看一下fields.post_event是什么:
fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
"(Ljava/lang/Object;IIILjava/lang/Object;)V");
本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓
对应的是MediaPlayer.java中的postEventFromNative方法:
postEventFromNative@ frameworks\base\media\java\android\media\MediaPlayer.java
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);
/* 发送消息给handler处理 */
mp.mEventHandler.sendMessage(m);
}
}
继续跟进:
public void handleMessage(Message msg) {
...
switch(msg.what) {
case MEDIA_PREPARED:
...
OnPreparedListener onPreparedListener = mOnPreparedListener;
if (onPreparedListener != null)
/* 将onPrepared消息发送至apk中 */
onPreparedListener.onPrepared(mMediaPlayer);
return;
}
}
可以看到,mediaplayer.java中可以注册非常多的listener,mediaplayer.java会调用这些listener的回调函数至apk,完成应用需要的操作。
三、JNI->mediaplayer.cpp:
先看listener的注册:
static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
ALOGV("native_setup");
/* 1.实例化MediaPlayer */
sp<MediaPlayer> mp = new MediaPlayer();
if (mp == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
return;
}
/* 2.创建JNI层的listener并设置下去 */
// 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层自己创建了一个listener并设置到下层中。下面看一下mediaplayer.cpp的处理:
void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj)
{
...
sp<MediaPlayerListener> listener = mListener;
if (locked) mLock.unlock();
// this prevents re-entrant calls into client code
if ((listener != 0) && send) {
Mutex::Autolock _l(mNotifyLock);
ALOGV("callback application");
/* 通过listener将消息回调至jni层 */
listener->notify(msg, ext1, ext2, obj);
ALOGV("back from callback");
}
}
四、mediaplayer.cpp(Bp)->mediaplayerservice(Bn):
这里就告诉了我们上面的notify是何时调用的,答案而是直接从Bn端的notify中回调上来的:
client::notify@ frameworks\av\media\libmediaplayerservice\MediaPlayerService.cpp
void MediaPlayerService::Client::notify(
int msg, int ext1, int ext2, const Parcel *obj)
{
...
/* 这里就是调用到mediaplayer.cpp中去的地方 */
if (c != NULL) {
ALOGV("[%d] notify (%d, %d, %d)", mConnId, msg, ext1, ext2);
c->notify(msg, ext1, ext2, obj);
}
}
五、mediaplayerservice(Bn)->底层播放器:
首先Bn端需要有一个listener,在MediaPlayerService::Client::Client构造函数中:
MediaPlayerService::Client::Client(
const sp<MediaPlayerService>& service, pid_t pid,
int32_t connId, const sp<IMediaPlayerClient>& client,
audio_session_t audioSessionId, uid_t uid)
{
...
mListener = new Listener(this);
...
}
这里会构建一个listener,这个listener拿来是注册到底层播放器中的,什么时候注册的?来看
MediaPlayerService::Client::createPlayer:
sp<MediaPlayerBase> MediaPlayerService::Client::createPlayer(player_type playerType)
{
// determine if we have the right player type
sp<MediaPlayerBase> p = getPlayer();
if ((p != NULL) && (p->playerType() != playerType)) {
ALOGV("delete player");
p.clear();
}
if (p == NULL) {
/* 将已构造的listener传进去 */
p = MediaPlayerFactory::createPlayer(playerType, mListener, mPid);
}
if (p != NULL) {
p->setUID(mUid);
}
return p;
}
跟进下createPlayer:
createPlayer@ frameworks\av\media\libmediaplayerservice\MediaPlayerFactory.cpp:
sp<MediaPlayerBase> MediaPlayerFactory::createPlayer(
player_type playerType,
const sp<MediaPlayerBase::Listener> &listener,
pid_t pid) {
...
/* 1.创建底层播放器 */
p = factory->createPlayer(pid);
...
init_result = p->initCheck();
if (init_result == NO_ERROR) {
/* 2.将listener注册到播放器的回调函数中 */
p->setNotifyCallback(listener);
} else {
ALOGE("Failed to create player object of type %d, initCheck failed"
" (res = %d)", playerType, init_result);
p.clear();
...
}
}
继续往下跟进,MediaPlayerBase在MediaPlayerInterface.h中:
setNotifyCallback@frameworks\av\media\libmediaplayerservice\include\MediaPlayerInterface.h:
void setNotifyCallback(
const sp<Listener> &listener) {
Mutex::Autolock autoLock(mNotifyLock);
mListener = listener;
}
这里需要注意一下继承关系,mstplayer: MediaPlayerInterface: MediaPlayerBase,所以Bn端的这个listener会一路注册到底层的播放器中,之后,底层播放器就会调用sendevent将消息返回上来了:
void sendEvent(int msg, int ext1=0, int ext2=0,
const Parcel *obj=NULL) {
sp<Listener> listener;
{
Mutex::Autolock autoLock(mNotifyLock);
listener = mListener;
}
if (listener != NULL) {
/* 底层的播放器都会调用这个接口通知至上层 */
listener->notify(msg, ext1, ext2, obj);
}
}
六、总结:
如果你对音视频开发感兴趣,觉得文章对您有帮助,别忘了点赞、收藏哦!或者对本文的一些阐述有自己的看法,有任何问题,欢迎在下方评论区讨论!
本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓