Android源代码分析之Framework的MediaPlayer
在Android中MediaPlayer用来播放音频和视频文件,在这里分析下在Framework层中MediaPlayer是怎样调用的。MediaPlayer的代码位于:./frameworks/base/media/java/android/media/MediaPlayer.java 以下用到的代码是基于Android 4.4
打开后有一个静态代码块是载入库文件的,仅仅要这个类被创建就会载入库。
static { System.loadLibrary("media_jni"); native_init(); }libmedia_jni.so的源码位于:./frameworks/base/media/jni
在jni这个目录中有个makefile文件 Android.mk
LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ android_media_ImageReader.cpp \ android_media_MediaCrypto.cpp \ android_media_MediaCodec.cpp \ android_media_MediaCodecList.cpp \ android_media_MediaDrm.cpp \ android_media_MediaExtractor.cpp \ android_media_MediaMuxer.cpp \ android_media_MediaPlayer.cpp \ android_media_MediaRecorder.cpp \ android_media_MediaScanner.cpp \ android_media_MediaMetadataRetriever.cpp \ android_media_ResampleInputStream.cpp \ android_media_MediaProfiles.cpp \ android_media_AmrInputStream.cpp \ android_media_Utils.cpp \ android_mtp_MtpDatabase.cpp \ android_mtp_MtpDevice.cpp \ android_mtp_MtpServer.cpp \ LOCAL_SHARED_LIBRARIES := \ libandroid_runtime \ libnativehelper \ libutils \ libbinder \ libmedia \ libskia \ libui \ liblog \ libcutils \ libgui \ libstagefright \ libstagefright_foundation \ libcamera_client \ libmtp \ libusbhost \ libexif \ libstagefright_amrnb_common \ LOCAL_REQUIRED_MODULES := \ libjhead_jni LOCAL_STATIC_LIBRARIES := \ libstagefright_amrnbenc LOCAL_C_INCLUDES += \ external/libexif/ \ external/tremor/Tremor \ frameworks/base/core/jni \ frameworks/av/media/libmedia \ frameworks/av/media/libstagefright \ frameworks/av/media/libstagefright/codecs/amrnb/enc/src \ frameworks/av/media/libstagefright/codecs/amrnb/common \ frameworks/av/media/libstagefright/codecs/amrnb/common/include \ $(TOP)/mediatek/external/amr \ frameworks/av/media/mtp \ frameworks/native/include/media/openmax \ $(call include-path-for, libhardware)/hardware \ system/media/camera/include \ $(PV_INCLUDES) \ $(JNI_H_INCLUDE) \ $(call include-path-for, corecg graphics) ifeq ($(strip $(MTK_TB_DEBUG_SUPPORT)),yes) LOCAL_C_INCLUDES += \ $(MTK_PATH_SOURCE)/frameworks/base/include endif ifeq ($(strip $(MTK_HIGH_QUALITY_THUMBNAIL)),yes) LOCAL_CFLAGS += -DMTK_HIGH_QUALITY_THUMBNAIL endif ifeq ($(strip $(MTK_USE_ANDROID_MM_DEFAULT_CODE)),yes) LOCAL_CFLAGS += -DANDROID_DEFAULT_CODE endif LOCAL_CFLAGS += LOCAL_LDLIBS := -lpthread LOCAL_MODULE:= libmedia_jni include $(BUILD_SHARED_LIBRARY) # build libsoundpool.so # build libaudioeffect_jni.so include $(call all-makefiles-under,$(LOCAL_PATH))LOCAL_MODULE:= libmedia_jni 编译后会生成libmedia_jni库
打开文件有四个重载函数
setDataSource (String path)
setDataSource (FileDescriptor fd)
setDataSource (Context context, Uri uri)
setDataSource (FileDescriptor fd, long offset, long length)
在APP中。如原生的Music
/** * M: add async prepare to aviod ANR, add information and duration update listener * * @param player The mediaplayer * @param path The data source path * @param async M: use async prepare if it's set true . * @return If set data source success, return true, otherwise false. */ private boolean setDataSourceImpl(MediaPlayer player, String path, boolean async) { MusicLogUtils.d(TAG, "setDataSourceImpl(" + path + ");async = " + async); try { player.reset(); if (async) { player.setOnPreparedListener(preparedlistener); } else { player.setOnPreparedListener(null); } if (path.startsWith("content://")) { player.setDataSource(MediaPlaybackService.this, Uri.parse(path)); } else { // / M: add add for DRM secure flag @{ MediaPlayerEx.setContextForSecureFlag(player, MediaPlaybackService.this.getApplicationContext()); // / @} player.setDataSource(path); } /// M: Attach auxiliary audio effect only with valid effect id if (mAuxEffectId > 0) { player.attachAuxEffect(mAuxEffectId); player.setAuxEffectSendLevel(1.0f); mWhetherAttachWhenPause = false; MusicLogUtils.d(TAG, "setDataSourceImpl: attachAuxEffect mAuxEffectId = " + mAuxEffectId); } player.setAudioStreamType(AudioManager.STREAM_MUSIC); if (async) { player.prepareAsync(); } else { player.prepare(); } } catch (IOException ex) { // TODO: notify the user why the file couldn't be opened MusicLogUtils.e(TAG, "setDataSourceImpl: " + ex); return false; } catch (IllegalArgumentException ex) { // TODO: notify the user why the file couldn't be opened MusicLogUtils.e(TAG, "setDataSourceImpl: " + ex); return false; } catch (IllegalStateException ex) { MusicLogUtils.e(TAG, "setDataSourceImpl: " + ex); return false; } player.setOnCompletionListener(listener); player.setOnErrorListener(errorListener); player.setOnInfoListener(infoListener); player.setOnDurationUpdateListener(durationListener); sendSessionIdToAudioEffect(false); return true; }无论在App中调用的是哪个函数,最后在MediaPlayer.java中都是调用
/** * Sets the data source (FileDescriptor) to use. The FileDescriptor must be * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility * to close the file descriptor. It is safe to do so as soon as this call returns. * * @param fd the FileDescriptor for the file you want to play * @param offset the offset into the file where the data to be played starts, in bytes * @param length the length in bytes of the data to be played * @throws IllegalStateException if it is called in an invalid state */ public void setDataSource(FileDescriptor fd, long offset, long length) throws IOException, IllegalArgumentException, IllegalStateException { disableProxyListener(); _setDataSource(fd, offset, length); } private native void _setDataSource(FileDescriptor fd, long offset, long length) throws IOException, IllegalArgumentException, IllegalStateException;_setDataSource有native修饰。是libmedia_jni.so中的方法。找到 ./frameworks/base/media/jni/android_media_MediaPlayer.cpp
// ---------------------------------------------------------------------------- static JNINativeMethod gMethods[] = { { "_setDataSource", "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V", (void *)android_media_MediaPlayer_setDataSourceAndHeaders }, {"_setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD}, {"_setVideoSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaPlayer_setVideoSurface}, {"prepare", "()V", (void *)android_media_MediaPlayer_prepare}, {"prepareAsync", "()V", (void *)android_media_MediaPlayer_prepareAsync}, {"_start", "()V", (void *)android_media_MediaPlayer_start}, {"_stop", "()V", (void *)android_media_MediaPlayer_stop}, {"getVideoWidth", "()I", (void *)android_media_MediaPlayer_getVideoWidth}, {"getVideoHeight", "()I", (void *)android_media_MediaPlayer_getVideoHeight}, {"seekTo", "(I)V", (void *)android_media_MediaPlayer_seekTo}, {"_pause", "()V", (void *)android_media_MediaPlayer_pause}, {"isPlaying", "()Z", (void *)android_media_MediaPlayer_isPlaying}, {"getCurrentPosition", "()I", (void *)android_media_MediaPlayer_getCurrentPosition}, {"getDuration", "()I", (void *)android_media_MediaPlayer_getDuration}, {"_release", "()V", (void *)android_media_MediaPlayer_release}, {"_reset", "()V", (void *)android_media_MediaPlayer_reset}, {"setAudioStreamType", "(I)V", (void *)android_media_MediaPlayer_setAudioStreamType}, {"setLooping", "(Z)V", (void *)android_media_MediaPlayer_setLooping}, {"isLooping", "()Z", (void *)android_media_MediaPlayer_isLooping}, {"setVolume", "(FF)V", (void *)android_media_MediaPlayer_setVolume}, {"native_invoke", "(Landroid/os/Parcel;Landroid/os/Parcel;)I",(void *)android_media_MediaPlayer_invoke}, {"native_setMetadataFilter", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_setMetadataFilter}, {"native_getMetadata", "(ZZLandroid/os/Parcel;)Z", (void *)android_media_MediaPlayer_getMetadata}, {"native_init", "()V", (void *)android_media_MediaPlayer_native_init}, {"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_MediaPlayer_native_setup}, {"native_finalize", "()V", (void *)android_media_MediaPlayer_native_finalize}, {"getAudioSessionId", "()I", (void *)android_media_MediaPlayer_get_audio_session_id}, {"setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer_set_audio_session_id}, {"setAuxEffectSendLevel", "(F)V", (void *)android_media_MediaPlayer_setAuxEffectSendLevel}, {"attachAuxEffect", "(I)V", (void *)android_media_MediaPlayer_attachAuxEffect}, {"native_pullBatteryData", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_pullBatteryData}, {"setParameter", "(ILandroid/os/Parcel;)Z", (void *)android_media_MediaPlayer_setParameter}, {"getParameter", "(ILandroid/os/Parcel;)V", (void *)android_media_MediaPlayer_getParameter}, {"native_setRetransmitEndpoint", "(Ljava/lang/String;I)I", (void *)android_media_MediaPlayer_setRetransmitEndpoint}, {"setNextMediaPlayer", "(Landroid/media/MediaPlayer;)V", (void *)android_media_MediaPlayer_setNextMediaPlayer}, {"updateProxyConfig", "(Landroid/net/ProxyProperties;)V", (void *)android_media_MediaPlayer_updateProxyConfig}, };会进入例如以下函数
static void android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length) { sp<MediaPlayer> mp = getMediaPlayer(env, thiz); if (mp == NULL ) { jniThrowException(env, "java/lang/IllegalStateException", NULL); return; } if (fileDescriptor == NULL) { jniThrowException(env, "java/lang/IllegalArgumentException", NULL); return; } int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); ALOGV("setDataSourceFD: fd %d", fd); process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." ); }mp->setDataSource(fd, offset, length) //调用打开文件方法
打开 ./frameworks/av/media/libmedia/mediaplayer.cpp setDataSource有三个重载函数,依据參数
status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length) { ALOGV("setDataSource(%d, %lld, %lld)", fd, offset, length); status_t err = UNKNOWN_ERROR; const sp<IMediaPlayerService>& service(getMediaPlayerService()); if (service != 0) { sp<IMediaPlayer> player(service->create(this, mAudioSessionId)); if ((NO_ERROR != doSetRetransmitEndpoint(player)) || (NO_ERROR != player->setDataSource(fd, offset, length))) { player.clear(); } err = attachNewPlayer(player); } return err; }打开 ./frameworks/av/media/libmedia/IMediaPlayer.cpp setDataSource也有三个重载函数,依据參数
status_t setDataSource(int fd, int64_t offset, int64_t length) { Parcel data, reply; data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); data.writeFileDescriptor(fd); data.writeInt64(offset); data.writeInt64(length); remote()->transact(SET_DATA_SOURCE_FD, data, &reply); return reply.readInt32(); }这个打开文件流程仅仅要是针对本地媒体文件。
process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." ) //依据返回的结果处理
// If exception is NULL and opStatus is not OK, this method sends an error // event to the client application; otherwise, if exception is not NULL and // opStatus is not OK, this method throws the given exception to the client // application. 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); if (mp != 0) mp->notify(MEDIA_ERROR, opStatus, 0); } } else { // Throw exception! if ( opStatus == (status_t) INVALID_OPERATION ) { jniThrowException(env, "java/lang/IllegalStateException", NULL); } else if ( opStatus == (status_t) PERMISSION_DENIED ) { jniThrowException(env, "java/lang/SecurityException", NULL); } else if ( opStatus != (status_t) OK ) { if (strlen(message) > 230) { // if the message is too long, don't bother displaying the status code jniThrowException( env, exception, message); } else { char msg[256]; // append the status code to the message sprintf(msg, "%s: status=0x%X", message, opStatus); jniThrowException( env, exception, msg); } } } } 相关文件 Environment.java MediaScannerReceiver.java MediaScannerService.java其他功能(start‘stop‘play‘seek_to)的流程也差点儿相同,难点就是JNI技术和APP中的AIDL。当然以上仅仅是一个简单的调用过程。要更深入了解还是要花些时间的。