在我们学习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对象进行操作了。