android 音频切换分析,Android MediaPlayer Playback流程分析

一个最简单的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()开始播发。这两个操作的调用流程类似,最终都是调用到底层播放器的相关接口。这里不分析播放器的具体操作,每个播放器的操作都不同,需要针对播放器做分析。调用流程如下图所示。

bVbybvK

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值