在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。当然以上仅仅是一个简单的调用过程。要更深入了解还是要花些时间的。

Android源代码分析之Framework的MediaPlayer的更多相关文章

  1. Android源代码分析之拍照、图片、录音、视频和音频功能

    Android源代码分析之拍照.图片.录音.视频和音频功能   //选择图片 requestCode 返回的标识 Intent innerIntent = new Intent(Intent.ACTI ...

  2. Cordova Android源代码分析系列一(项目总览和CordovaActivity分析)

    版权声明:本文为博主offbye西涛原创文章.未经博主同意不得转载. https://blog.csdn.net/offbye/article/details/31776833 PhoneGap/Co ...

  3. Android源代码分析-资源载入机制

    转载请注明出处:http://blog.csdn.net/singwhatiwanna/article/details/23387079 (来自singwhatiwanna的csdn博客) 前言 我们 ...

  4. android源代码分析 android toast使用具体解释 toast自己定义

    在安卓开发过程中.toast使我们常常使用的一个类.当我们须要向用户传达一些信息,可是不须要和用户交互时,该方式就是一种十分恰当的途径. 我们习惯了这样使用toast:Toast.makeText(C ...

  5. 1、android源代码下载及目录分析,和eclipser的跟踪

    1.在eclipse中跟踪源代码:假如对mainactivity.java里面的activity按Ctrl+鼠标左键(前提已经导入android源代码:方法1:在项目点击右键,然后找到properti ...

  6. Android应用程序安装过程源代码分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6766010 Android系统在启动的过程中, ...

  7. Android系统进程间通信(IPC)机制Binder中的Client获得Server远程接口过程源代码分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6633311 在上一篇文章中,我 们分析了And ...

  8. android(cm11)状态栏源代码分析(一)

    (一):写在前面 近期因为工作须要,须要了解CM11中的有关于StatusBar相关的内容.总的来说,刚開始阅读其源代码的时候,是有点困难,只是通过构建相关代码的脑图和流程图,几天下来.我已经对其源代 ...

  9. Android 中View的绘制机制源代码分析 三

    到眼下为止,measure过程已经解说完了,今天開始我们就来学习layout过程.只是在学习layout过程之前.大家有没有发现我换了编辑器,哈哈.最终下定决心从Html编辑器切换为markdown编 ...

随机推荐

  1. 计算机天才Aaron Swartz 名作 《如何提高效率》——纪念真正的“hacker"!

    如何提高效率 <HOWTO: Be more productive>(如何提高效率)作者:Aaron Swartz 肯定有人跟你说过这样的话,“你有看电视的那么长时间,都可以用来写一本书了 ...

  2. 微信JS-SDK实际分享功能

    为了净化网络,整顿诱导分享及诱导关注行为,微信于2014年12月30日发布了<微信公众平台关于整顿诱导分享及诱导关注行为的公告>,微信平台开发者发现,原有的微信分享功能不能用了,在ipho ...

  3. UVa1605 - Building for UN(构造法)

    UVA - 1605 Time Limit: 3000MS   Memory Limit: Unknown   64bit IO Format: %lld & %llu Description ...

  4. “layout_”下划线开头的属性

    观看幕课网的视频的收获 “layout_”下划线开头的属性都是交给父容器去处理的属性,如: android:layout_width="match_parent" android: ...

  5. 利用HTML5分片上传超大文件

    在网页中直接上传大文件一直是个比较头疼的问题,主要面临的问题一般包括两类:一是上传时间长中途一旦出错会导致前功尽弃:二是服务端配置复杂,要考虑接收超大表单和超时问题,如果是托管主机没准还改不了配置,默 ...

  6. Plan-9效应:为什么东西不坏就不要去修它

    http://www.aqee.net/the-plan-9-effect-or-why-you-should-not-fix-it-if-it-aint-broken/ Plan-9是一个很棒的.很 ...

  7. UIPickerView 地区解析 -- 全国省、市、区 plist 解析 -- 读取UIPickerView 当前显示内容

    一个简单的plist 解析过程,借助UIPickerView 实现了手选全国的 省市区 方法, 源码中有详细注释:长句自己可以拆开看,最好的方法是,拆开,并打印,查看每一步打印的结果,结合Plist文 ...

  8. org.springframework.web.context.ContextLoaderListener

    ContextLoaderListener的作用就是启动Web容器时,自动装配ApplicationContext的配置信息.因为它实现了ServletContextListener这个接口,在web ...

  9. Android 中使用MediaRecorder进行录像详解(视频录制)

    在这里给出自己的一个测试DEMO,里面注释很详细.简单的视频录制功能. package com.video; import java.io.IOException; import android.ap ...

  10. PlatformTransactionManager

    Spring Boot 使用事务非常简单,首先使用注解 @EnableTransactionManagement 开启事务支持后,然后在访问数据库的Service方法上添加注解 @Transactio ...