`
mmdev
  • 浏览: 12921342 次
  • 性别: Icon_minigender_1
  • 来自: 大连
文章分类
社区版块
存档分类
最新评论

Android Audio代码分析5 - 函数getAudioSessionId

 
阅读更多

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

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics