一个最简单的Media Playback过程如下,仅通过五个步骤就可以完成播放。
String url = "http://........"; // your URL here
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(url);
mediaPlayer.prepare(); // might take long! (for buffering, etc)
mediaPlayer.start();
初始化MediaPlayer
创建MediaPlayer对象,并完成Java层和Native层的初始化。
frameworks/base/media/java/android/media/MediaPlayer.java
public class MediaPlayer implements SubtitleController.Listener
{
......
static {
System.loadLibrary("media_jni");
native_init(); // native 初始化
}
......
public 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);
mOutOfBandSubtitleTracks = new Vector();
mOpenSubtitleSources = new Vector();
mInbandSubtitleTracks = new SubtitleTrack[0];
IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
mAppOps = IAppOpsService.Stub.asInterface(b);
/* Native setup requires a weak reference to our object.
* It's easier to create it here than in C++.
*/
native_setup(new WeakReference(this)); // native层setup
}
.......
可以看到,MediaPlayer通过静态初始化的方式对native层进行初始化。MediaPlayer对媒体控制的动作都是在底层实现的,所以必须在MediaPlayer创建时优先初始化底层。
frameworks/base/media/jni/android_media_MediaPlayer.cpp
static void
android_media_MediaPlayer_native_init(JNIEnv *env)
{
jclass clazz;
clazz = env->FindClass("android/media/MediaPlayer");
if (clazz == NULL) {
return;
}
fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
if (fields.context == NULL) {
return;
}
fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
"(Ljava/lang/Object;IIILjava/lang/Object;)V");
if (fields.post_event == NULL) {
return;
}
fields.surface_texture = env->GetFieldID(clazz, "mNativeSurfaceTexture", "J");
if (fields.surface_texture == NULL) {
return;
}
clazz = env->FindClass("android/net/ProxyInfo");
if (clazz == NULL) {
return;
}
fields.proxyConfigGetHost =
env->GetMethodID(clazz, "getHost", "()Ljava/lang/String;");
fields.proxyConfigGetPort =
env->GetMethodID(clazz, "getPort", "()I");
fields.proxyConfigGetExclusionList =
env->GetMethodID(clazz, "getExclusionListAsString", "()Ljava/lang/String;");
}
native_init()获取Java类MediaPlayer和ProxyInfo中的一些方法和变量。
将native MediaPlayer的对象保存到Java MediaPlayer的mNativeContext中。
获取Java MediaPlayer的postEventFromNative()方法,用于发送事件。
将native video surface保存到Java MediaPlayer的mNativeSurfaceTexture中。
获取Java ProxyInfo的getHost()、getPort()、getExclusionListAsString()方法。
之后在MediaPlayer的构造函数中初始化一些媒体播放的必要元素,像消息、时间、字幕、权限等。最后调用native_setup()对native层进行设置。
frameworks/base/media/jni/android_media_MediaPlayer.cpp
static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
ALOGV("native_setup");
sp mp = new MediaPlayer();
if (mp == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
return;
}
// create new listener and give it to MediaPlayer
sp 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);
}
Setup过程完成一下工作。
创建Native MeidaPlayer对象。
创建Listener上报Native事件,这里调用postEventFromNative()。
存储Native MediaPlayer对象到mNativeContext中,以便Java层使用。
设置声音系统
Android系统将定义了不同的Audio Stream类型,用来适用不同的应用场景。像STREAM_VOICE_CALL用于打电话场景,STREAM_SYSTEM用于系统声音,STREAM_RING用于电话铃声,STREAM_MUSIC用于播放音乐等。每种Stream类型都是独立的,有自己的状态和音量。在使用Player前需要选择一个Stream类型,音乐播放和视频播放时基本上采用的都是STREAM_MUSIC。
Java层的setAudioStreamType()直接调用的Native的setAudioStreamType(),而Native的这个方法也很简单,就是赋值给mStreamType。
frameworks/av/media/libmedia/mediaplayer.cpp
status_t MediaPlayer::setAudioStreamType(audio_stream_type_t type)
{
ALOGV("MediaPlayer::setAudioStreamType");
Mutex::Autolock _l(mLock);
if (mStreamType == type) return NO_ERROR;
if (mCurrentState & ( MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_STARTED |
MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_PLAYBACK_COMPLETE ) ) {
// Can't change the stream type after prepare
ALOGE("setAudioStream called in state %d", mCurrentState);
return INVALID_OPERATION;
}
// cache
mStreamType = type;
return OK;
}
mStreamType会在Player Prepare过程中被设置生效,这也就是为什么必须在Player开始前设置Stream类型。
frameworks/av/media/libmedia/mediaplayer.cpp
status_t MediaPlayer::prepareAsync_l()
{
if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_INITIALIZED | MEDIA_PLAYER_STOPPED) ) ) {
mPlayer->setAudioStreamType(mStreamType);
if (mAudioAttributesParcel != NULL) {
mPlayer->setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, *mAudioAttributesParcel);
}
mCurrentState = MEDIA_PLAYER_PREPARING;
return mPlayer->prepareAsync();
}
ALOGE("prepareAsync called in state %d", mCurrentState);
return INVALID_OPERATION;
}
上面的过程又远程调用了MediaPlayerService的setAudioStreamType(),真正的Audio系统设置就发生在这里。Audio系统比较复杂,这里不再展开。
frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp
status_t MediaPlayerService::Client::setAudioStreamType(audio_stream_type_t type)
{
ALOGV("[%d] setAudioStreamType(%d)", mConnId, type);
// TODO: for hardware output, call player instead
Mutex::Autolock l(mLock);
if (mAudioOutput != 0) mAudioOutput->setAudioStreamType(type);
return NO_ERROR;
}
初始化底层播放器
Android MediaPlayer使用”工厂模式“来管理底层播放器。只要按MediaPlayerFactory
定义接口进行实现,并注册到sFactoryMap中,就可以调用自己的Player。原生Android提供了四个可用的播放器:NuPlayer、StagefrightPlayer、SonivoxPlayer和TestPlayer,厂商也可以增加客制化的播放器。在播放前需要根据评分来选择使用播放器,这个选择过程就是在setDataSource()中完成的。
Java层MediaPlayer实现了多个setDataSource(),但最终都是通过两个JNI接口调用到native中。
nativeSetDataSource:处理网络流媒体。
_setDataSource:处理本地的媒体文件。
frameworks/base/media/jni/android_media_MediaPlayer.cpp
static void
android_media_MediaPlayer_setDataSourceAndHeaders(
JNIEnv *env, jobject thiz, jobject httpServiceBinderObj, jstring path,
jobjectArray keys, jobjectArray values) {
sp mp = getMediaPlayer(env, thiz);
......
sp httpService;
if (httpServiceBinderObj != NULL) {
sp binder = ibinderForJavaObject(env, httpServiceBinderObj);
httpService = interface_cast(binder);
}
status_t opStatus =
mp->setDataSource(
httpService,
pathStr,
headersVector.size() > 0? &headersVector : NULL);
......
}
static void
android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
{
sp mp = getMediaPlayer(env, thiz);
......
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." );
}
JNI中又调用Native层MediaPlayer的setDataSource()方法,最终的远程调用到MediaPlayerService的setDataSource()。
frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp
status_t MediaPlayerService::Client::setDataSource(
const sp &httpService,
const char *url,
const KeyedVector *headers)
{
......
if (strncmp(url, "content://", 10) == 0) {
// get a filedescriptor for the content Uri and
// pass it to the setDataSource(fd) method
String16 url16(url);
int fd = android::openContentProviderFile(url16);
if (fd < 0)
{
ALOGE("Couldn't open fd for %s", url);
return UNKNOWN_ERROR;
}
setDataSource(fd, 0, 0x7fffffffffLL); // this sets mStatus
close(fd);
return mStatus;
} else {
player_type playerType = MediaPlayerFactory::getPlayerType(this, url);
.....
sp p = setDataSource_pre(playerType);
if (p == NULL) {
return NO_INIT;
}
setDataSource_post(p, p->setDataSource(httpService, url, headers));
return mStatus;
}
}
status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64_t length)
{
......
player_type playerType = MediaPlayerFactory::getPlayerType(this,
fd,
offset,
length);
sp p = setDataSource_pre(playerType);
if (p == NULL) {
return NO_INIT;
}
// now set data source
setDataSource_post(p, p->setDataSource(fd, offset, length));
return mStatus;
}
status_t MediaPlayerService::Client::setDataSource(
const sp &source) {
// create the right type of player
player_type playerType = MediaPlayerFactory::getPlayerType(this, source);
sp p = setDataSource_pre(playerType);
if (p == NULL) {
return NO_INIT;
}
// now set data source
setDataSource_post(p, p->setDataSource(source));
return mStatus;
}
setDataSource()虽然具体实现不同,但最主要的步骤都是相同的。
getPlayerType:选择合适的底层播放器。
setDataSource_pre:创建底层播放器。
setDataSource_post:调用底层播放器的setDataSource(),设置数据源。
播放器的选择过程也比较简单,就是根据参数寻找评分最高的播放器。每个播放器都会实现scoreFactory()方法,为自己打分。分值最高的播放器有最高优先级。
frameworks/av/media/libmediaplayerservice/MediaPlayerFactory.cpp
#define GET_PLAYER_TYPE_IMPL(a...) \
Mutex::Autolock lock_(&sLock); \
\
player_type ret = STAGEFRIGHT_PLAYER; \
float bestScore = 0.0; \
\
for (size_t i = 0; i < sFactoryMap.size(); ++i) { \
\
IFactory* v = sFactoryMap.valueAt(i); \
float thisScore; \
CHECK(v != NULL); \
thisScore = v->scoreFactory(a, bestScore); \
if (thisScore > bestScore) { \
ret = sFactoryMap.keyAt(i); \
bestScore = thisScore; \
} \
} \
\
if (0.0 == bestScore) { \
ret = getDefaultPlayerType(); \
} \
\
return ret;
player_type MediaPlayerFactory::getPlayerType(const sp& client,
const char* url) {
GET_PLAYER_TYPE_IMPL(client, url);
}
player_type MediaPlayerFactory::getPlayerType(const sp& client,
int fd,
int64_t offset,
int64_t length) {
GET_PLAYER_TYPE_IMPL(client, fd, offset, length);
}
player_type MediaPlayerFactory::getPlayerType(const sp& client,
const sp &source) {
GET_PLAYER_TYPE_IMPL(client, source);
}
以StagefrightPlayer为例,看一下scoreFactory()的实现。
frameworks/av/media/libmediaplayerservice/MediaPlayerFactory.cpp
class StagefrightPlayerFactory :
public MediaPlayerFactory::IFactory {
public:
virtual float scoreFactory(const sp& /*client*/,
int fd,
int64_t offset,
int64_t length,
float /*curScore*/) {
if (legacyDrm()) {
sp source = new FileSource(dup(fd), offset, length);
String8 mimeType;
float confidence;
if (SniffWVM(source, &mimeType, &confidence, NULL /* format */)) {
return 1.0;
}
}
if (getDefaultPlayerType() == STAGEFRIGHT_PLAYER) {
char buf[20];
lseek(fd, offset, SEEK_SET);
read(fd, buf, sizeof(buf));
lseek(fd, offset, SEEK_SET);
uint32_t ident = *((uint32_t*)buf);
// Ogg vorbis?
if (ident == 0x5367674f) // 'OggS'
return 1.0;
}
return 0.0;
}
virtual float scoreFactory(const sp& /*client*/,
const char* url,
float /*curScore*/) {
if (legacyDrm() && !strncasecmp("widevine://", url, 11)) {
return 1.0;
}
return 0.0;
}
......
可以看到,如果媒体为Widevine DRM格式,StagefrightPlayer会给出更高的打分。Ogg vorbis也会返回高分。
准备Player和播放
选择好底层播放器后,使用prepare()做播放前的准备,之后使用start()开始播发。这两个操作的调用流程类似,最终都是调用到底层播放器的相关接口。这里不分析播放器的具体操作,每个播放器的操作都不同,需要针对播放器做分析。调用流程如下图所示。