在我们学习AudioTrack对象的创建过程时,了解到,创建一个AudioTrack对象,必须指定一个SessionId,并与其他使用该SessionId的AudioTrack和MediaPlayer共享AudioEffect。
如果不指定SessionId,将会自动生成一个SessionId,AudioEffect会将该SessionId与新创建的AudioTrack对象关联起来。
别人可以通过getAudioSessionId函数取得该SessionId。
今天我们就看看getAudioSessionId函数。
在frameworks的代码中搜了一下,发现有两个地方实现了该函数,分别来说一下。
先说说在类MediaPlayer中的实现。
*****************************************源码*************************************************
int MediaPlayer::getAudioSessionId()
{
Mutex::Autolock _l(mLock);
return mAudioSessionId;
}
**********************************************************************************************
源码路径:
frameworks\base\media\libmedia\mediaplayer.cpp
###########################################说明################################################
代码很简单,只是将成员变量返回,没啥好说的。
那我们看看该成员变量是何时被赋值的。
找了下代码,发现有两个地方对该成员变量进行了赋值。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
一个在构造函数中。
MediaPlayer::MediaPlayer()
{
LOGV("constructor");
mListener = NULL;
mCookie = NULL;
mDuration = -1;
mStreamType = AudioSystem::MUSIC;
mCurrentPosition = -1;
mSeekPosition = -1;
mCurrentState = MEDIA_PLAYER_IDLE;
mPrepareSync = false;
mPrepareStatus = NO_ERROR;
mLoop = false;
mLeftVolume = mRightVolume = 1.0;
mVideoWidth = mVideoHeight = 0;
mLockThreadId = 0;
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
int AudioSystem::newAudioSessionId() {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return 0;
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
int AudioFlinger::newAudioSessionId()
{
// 看到了函数nextUniqueId,我们感觉到有点熟悉。
// 不错,在我们看函数AudioSystem::getOutputSamplingRate的时候,看到,在函数AudioFlinger::openOutput中有调过函数nextUniqueId
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
int AudioFlinger::nextUniqueId()
{
// 这是一个自增的操作
// 可见,SessionId最终是在AudioFlinger中维护的。
// 关于函数android_atomic_inc,可参考以下链接:
// http://hi.baidu.com/obiwong/blog/item/5317a7d4c6e481cf50da4b1e.html
return android_atomic_inc(&mNextUniqueId);
}
----------------------------------------------------------------
return nextUniqueId();
}
----------------------------------------------------------------
return af->newAudioSessionId();
}
----------------------------------------------------------------
mAudioSessionId = AudioSystem::newAudioSessionId();
mSendLevel = 0;
}
----------------------------------------------------------------
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
另一个给成员变量mAudioSessionId赋值的地方是在函数MediaPlayer::setAudioSessionId中。
status_t MediaPlayer::setAudioSessionId(int sessionId)
{
LOGV("MediaPlayer::setAudioSessionId(%d)", sessionId);
Mutex::Autolock _l(mLock);
if (!(mCurrentState & MEDIA_PLAYER_IDLE)) {
LOGE("setAudioSessionId called in state %d", mCurrentState);
return INVALID_OPERATION;
}
if (sessionId < 0) {
return BAD_VALUE;
}
mAudioSessionId = sessionId;
return NO_ERROR;
}
函数MediaPlayer::setAudioSessionId在函数android_media_MediaPlayer_set_audio_session_id中被调用。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static void android_media_MediaPlayer_set_audio_session_id(JNIEnv *env, jobject thiz, jint sessionId) {
LOGV("set_session_id(): %d", sessionId);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static sp<MediaPlayer> getMediaPlayer(JNIEnv* env, jobject thiz)
{
Mutex::Autolock l(sLock);
MediaPlayer* const p = (MediaPlayer*)env->GetIntField(thiz, fields.context);
return sp<MediaPlayer>(p);
}
// 以前见到过类似的函数,肯定有某个地方把它set进去。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static sp<MediaPlayer> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer>& player)
{
Mutex::Autolock l(sLock);
sp<MediaPlayer> old = (MediaPlayer*)env->GetIntField(thiz, fields.context);
if (player.get()) {
player->incStrong(thiz);
}
if (old != 0) {
old->decStrong(thiz);
}
env->SetIntField(thiz, fields.context, (int)player.get());
return old;
}
函数setMediaPlayer在函数android_media_MediaPlayer_native_setup中被调用。
函数android_media_MediaPlayer_native_setup也是供java调用的。
对应的接口,在下面的一个列表中可以看到。
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
LOGV("native_setup");
// 在上面的代码中,SessionId的一个赋值的地方就是MediaPlayer的构造函数中。
// MediaPlayer对象就是在这儿被构造的。
sp<MediaPlayer> mp = new MediaPlayer();
if (mp == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
return;
}
// create new listener and give it to MediaPlayer
// JNIMediaPlayerListener是一个代码量不多的类,其声明和实现如下:
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// ref-counted object for callbacks
class JNIMediaPlayerListener: public MediaPlayerListener
{
public:
JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz);
~JNIMediaPlayerListener();
void notify(int msg, int ext1, int ext2);
private:
JNIMediaPlayerListener();
jclass mClass; // Reference to MediaPlayer class
jobject mObject; // Weak ref to MediaPlayer Java object to call on
};
JNIMediaPlayerListener::JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz)
{
// Hold onto the MediaPlayer class for use in calling the static method
// that posts events to the application thread.
jclass clazz = env->GetObjectClass(thiz);
if (clazz == NULL) {
LOGE("Can't find android/media/MediaPlayer");
jniThrowException(env, "java/lang/Exception", NULL);
return;
}
mClass = (jclass)env->NewGlobalRef(clazz);
// We use a weak reference so the MediaPlayer object can be garbage collected.
// The reference is only used as a proxy for callbacks.
mObject = env->NewGlobalRef(weak_thiz);
}
JNIMediaPlayerListener::~JNIMediaPlayerListener()
{
// remove global references
JNIEnv *env = AndroidRuntime::getJNIEnv();
env->DeleteGlobalRef(mObject);
env->DeleteGlobalRef(mClass);
}
void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2)
{
JNIEnv *env = AndroidRuntime::getJNIEnv();
env->CallStaticVoidMethod(mClass, fields.post_event, mObject, msg, ext1, ext2, 0);
}
//----------------------------------------------------------------
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);
}
//----------------------------------------------------------------
//----------------------------------------------------------------
//----------------------------------------------------------------
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 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) 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);
}
}
}
}
//----------------------------------------------------------------
process_media_player_call( env, thiz, mp->setAudioSessionId(sessionId), NULL, NULL );
}
根据以往的经验,函数android_media_MediaPlayer_set_audio_session_id应该是供java代码调用的。
果然。。。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static JNINativeMethod gMethods[] = {
{"setDataSource", "(Ljava/lang/String;)V", (void *)android_media_MediaPlayer_setDataSource},
{"setDataSource", "(Ljava/lang/String;Ljava/util/Map;)V",(void *)android_media_MediaPlayer_setDataSourceAndHeaders},
{"setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD},
{"_setVideoSurface", "()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},
{"getFrameAt", "(I)Landroid/graphics/Bitmap;", (void *)android_media_MediaPlayer_getFrameAt},
{"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},
{"native_suspend_resume", "(Z)I", (void *)android_media_MediaPlayer_native_suspend_resume},
{"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},
{"setAudioEffect", "(III)V", (void *)android_media_MediaPlayer_setAudioEffect},
{"setAudioEqualizer", "(Z)V", (void *)android_media_MediaPlayer_setAudioEqualizer},
{"captureCurrentFrame", "()Landroid/graphics/Bitmap;", (void *)android_media_MediaPlayer_captureCurrentFrame},
{"setVideoCrop", "(IIII)V", (void *)android_media_MediaPlayer_setVideoCrop},
{"getTrackCount", "()I", (void *)android_media_MediaPlayer_getTrackCount},
{"getTrackName", "(I)Ljava/lang/String;", (void *)android_media_MediaPlayer_getTrackName},
{"getDefaultTrack", "()I", (void *)android_media_MediaPlayer_getDefaultTrack},
{"selectTrack", "(I)V", (void *)android_media_MediaPlayer_selectTrack},
};
这些函数,应该都是java中的MediaPlayer类,通过JNI接口调用过来的。
----------------------------------------------------------------
----------------------------------------------------------------
----------------------------------------------------------------
###############################################################################################
文章开头有说到,函数getAudioSessionId函数的实现有两个地方。
另外一个是在类Client(class Client : public BnMediaPlayer),
类MediaPlayerService(class MediaPlayerService : public BnMediaPlayerService)的一个内部类
*****************************************源码*************************************************
int getAudioSessionId() { return mAudioSessionId; }
**********************************************************************************************
源码路径:
frameworks\base\media\libmediaplayerservice\MediaPlayerService.h
###########################################说明################################################
只有类Client的构造函数中,有对成员变量mAudioSessionId赋值
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MediaPlayerService::Client::Client(const sp<MediaPlayerService>& service, pid_t pid,
int32_t connId, const sp<IMediaPlayerClient>& client, int audioSessionId)
{
LOGV("Client(%d) constructor", connId);
mPid = pid;
mConnId = connId;
mService = service;
mClient = client;
mLoop = false;
mStatus = NO_INIT;
mAudioSessionId = audioSessionId;
#if CALLBACK_ANTAGONIZER
LOGD("create Antagonizer");
mAntagonizer = new Antagonizer(notify, this);
#endif
}
// Client对象在函数MediaPlayerService::create中被创建
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sp<IMediaPlayer> MediaPlayerService::create(
pid_t pid, const sp<IMediaPlayerClient>& client, const char* url,
const KeyedVector<String8, String8> *headers, int audioSessionId)
{
int32_t connId = android_atomic_inc(&mNextConnId);
sp<Client> c = new Client(this, pid, connId, client, audioSessionId);
LOGV("Create new client(%d) from pid %d, url=%s, connId=%d, audioSessionId=%d",
connId, pid, url, connId, audioSessionId);
if (NO_ERROR != c->setDataSource(url, headers))
{
c.clear();
return c;
}
wp<Client> w = c;
Mutex::Autolock lock(mLock);
mClients.add(w);
return c;
}
//----------------------------------------------------------------
//----------------------------------------------------------------
###############################################################################################
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&总结&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
1、关于SessionId
一个Session就是一个会话。每个会话都有一个独一无二的Id来标识。该Id的最终管理在AudioFlinger中。
一个会话可以被多个AudioTrack对象和MediaPlayer共用。
共用一个Session的AudioTrack和MediaPlayer共享相同的AudioEffect。
2、关于java中调用native中的函数
调用的实现当然是通过JNI。此处要说的是,通过JNI调用过来之后,如何使用native中的对象。
首先会通过native_setup之类的函数创建一个native对象,并通过函数SetIntField保存到java侧。
需要使用的时候调用函数GetIntField便可获取native对象,之后就可以对native对象进行操作了。
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&