繼續啃AudioTrack的測試代碼。
*****************************************源碼*************************************************
//Test case 2: getPlaybackHeadPosition() increases after play()
@LargeTest
public void testPlaybackHeadPositionIncrease() throws Exception {
// constants for test
final String TEST_NAME = "testPlaybackHeadPositionIncrease";
final int TEST_SR = 22050;
final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
//——– initialization ————–
int minBuffSize = AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT);
AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT,
minBuffSize, TEST_MODE);
byte data[] = new byte[minBuffSize/2];
//——– test ————–
assumeTrue(TEST_NAME, track.getState() == AudioTrack.STATE_INITIALIZED);
track.write(data, 0, data.length);
track.write(data, 0, data.length);
track.play();
Thread.sleep(100);
log(TEST_NAME, "position ="+ track.getPlaybackHeadPosition());
assertTrue(TEST_NAME, track.getPlaybackHeadPosition() > 0);
//——– tear down ————–
track.release();
}
**********************************************************************************************
源碼路徑:
frameworks\base\media\tests\mediaframeworktest\src\com\android\mediaframeworktest\functional\MediaAudioTrackTest.java
#######################說明################################
//Test case 2: getPlaybackHeadPosition() increases after play()
// 在上一篇關於getPlaybackHeadPosition的分析中,沒有調用play函數,就調用瞭getPlaybackHeadPosition,所以獲取的position應該為0.
// 而本例子中,在write之後就調用瞭play,然後sleep瞭100ms,才調用的getPlaybackHeadPosition。
// 也就是說,播放瞭100ms才去獲取的位置,此時的位置當然應該大於0
@LargeTest
public void testPlaybackHeadPositionIncrease() throws Exception {
// constants for test
final String TEST_NAME = "testPlaybackHeadPositionIncrease";
final int TEST_SR = 22050;
final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
//——– initialization ————–
int minBuffSize = AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT);
AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT,
minBuffSize, TEST_MODE);
byte data[] = new byte[minBuffSize/2];
//——– test ————–
assumeTrue(TEST_NAME, track.getState() == AudioTrack.STATE_INITIALIZED);
track.write(data, 0, data.length);
track.write(data, 0, data.length);
// 這次的引子是position如何被改變的
// 既然播放之後,獲取的position為正,那麼play函數的調用就應該會導致position的改變。
// 一起來看看play函數如何導致position改變的。
track.play();
++++++++++++++++++++++++++++++play++++++++++++++++++++++++++++++++++
/**
* Starts playing an AudioTrack.
* @throws IllegalStateException
*/
public void play()
throws IllegalStateException {
if (mState != STATE_INITIALIZED) {
throw(new IllegalStateException("play() called on uninitialized AudioTrack."));
}
synchronized(mPlayStateLock) {
native_start();
++++++++++++++++++++++++++++++android_media_AudioTrack_start++++++++++++++++++++++++++++++++++
static void
android_media_AudioTrack_start(JNIEnv *env, jobject thiz)
{
AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
thiz, javaAudioTrackFields.nativeTrackInJavaObj);
if (lpTrack == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioTrack pointer for start()");
return;
}
lpTrack->start();
++++++++++++++++++++++++++++++AudioTrack::start++++++++++++++++++++++++++++++++++
void AudioTrack::start()
{
// mAudioTrackThread在函數AudioTrack::set中被賦值。
sp<AudioTrackThread> t = mAudioTrackThread;
status_t status;
LOGV("start %p", this);
if (t != 0) {
// 函數exitPending返回成員變量mExitPending
if (t->exitPending()) {
if (t->requestExitAndWait() == WOULD_BLOCK) {
LOGE("AudioTrack::start called from thread");
return;
}
}
t->mLock.lock();
}
if (android_atomic_or(1, &mActive) == 0) {
mNewPosition = mCblk->server + mUpdatePeriod;
mCblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;
mCblk->waitTimeMs = 0;
mCblk->flags &= ~CBLK_DISABLED_ON;
if (t != 0) {
// 調用的是函數Thread::run
t->run("AudioTrackThread", THREAD_PRIORITY_AUDIO_CLIENT);
+++++++++++++++++++++++++++++++Thread::run+++++++++++++++++++++++++++++++++
status_t Thread::run(const char* name, int32_t priority, size_t stack)
{
Mutex::Autolock _l(mLock);
if (mRunning) {
// thread already started
return INVALID_OPERATION;
}
// reset status and exitPending to their default value, so we can
// try again after an error happened (either below, or in readyToRun())
mStatus = NO_ERROR;
// 在函數exitPending中會返回該成員變量
mExitPending = false;
mThread = thread_id_t(-1);
// hold a strong reference on ourself
mHoldSelf = this;
mRunning = true;
bool res;
// 我們調用函數AudioTrack::set的時候,將threadCanCallJava指定為瞭false
if (mCanCallJava) {
res = createThreadEtc(_threadLoop,
this, name, priority, stack, &mThread);
+++++++++++++++++++++++++++++++createThreadEtc+++++++++++++++++++++++++++++++++
// Create thread with lots of parameters
inline bool createThreadEtc(thread_func_t entryFunction,
void *userData,
const char* threadName = "android:unnamed_thread",
int32_t threadPriority = PRIORITY_DEFAULT,
size_t threadStackSize = 0,
thread_id_t *threadId = 0)
{
return androidCreateThreadEtc(entryFunction, userData, threadName,
threadPriority, threadStackSize, threadId) ? true : false;
+++++++++++++++++++++++++++++++++androidCreateThreadEtc+++++++++++++++++++++++++++++++
int androidCreateThreadEtc(android_thread_func_t entryFunction,
void *userData,
const char* threadName,
int32_t threadPriority,
size_t threadStackSize,
android_thread_id_t *threadId)
{
// gCreateThreadFn被初始化為androidCreateRawThreadEtc。
// 函數androidSetCreateThreadFunc會對其賦值
return gCreateThreadFn(entryFunction, userData, threadName,
threadPriority, threadStackSize, threadId);
++++++++++++++++++++++++++++++androidSetCreateThreadFunc++++++++++++++++++++++++++++++++++
void androidSetCreateThreadFunc(android_create_thread_fn func)
{
gCreateThreadFn = func;
}
函數AndroidRuntime::startReg調用瞭函數androidSetCreateThreadFunc
+++++++++++++++++++++++++++++AndroidRuntime::startReg+++++++++++++++++++++++++++++++++++
/*
* Register android native functions with the VM.
*/
/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{
/*
* This hook causes all future threads created in this process to be
* attached to the JavaVM. (This needs to go away in favor of JNI
* Attach calls.)
*/
androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);
+++++++++++++++++++++++++++javaCreateThreadEtc+++++++++++++++++++++++++++++++++++++
/*
* This is invoked from androidCreateThreadEtc() via the callback
* set with androidSetCreateThreadFunc().
*
* We need to create the new thread in such a way that it gets hooked
* into the VM before it really starts executing.
*/
/*static*/ int AndroidRuntime::javaCreateThreadEtc(
android_thread_func_t entryFunction,
void* userData,
const char* threadName,
int32_t threadPriority,
size_t threadStackSize,
android_thread_id_t* threadId)
{
void** args = (void**) malloc(3 * sizeof(void*)); // javaThreadShell must free
int result;
assert(threadName != NULL);
args[0] = (void*) entryFunction;
args[1] = userData;
args[2] = (void*) strdup(threadName); // javaThreadShell must free
// 如果可以從java層調用的話,就在thread外包瞭層殼
result = androidCreateRawThreadEtc(AndroidRuntime::javaThreadShell, args,
threadName, threadPriority, threadStackSize, threadId);
return result;
}
—————————javaCreateThreadEtc————————————-
LOGV("— registering native functions —\n");
/*
* Every "register" function calls one or more things that return
* a local reference (e.g. FindClass). Because we haven't really
* started the VM yet, they're all getting stored in the base frame
* and never released. Use Push/Pop to manage the storage.
*/
env->PushLocalFrame(200);
if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
env->PopLocalFrame(NULL);
return -1;
}
env->PopLocalFrame(NULL);
//createJavaThread("fubar", quickTest, (void*) "hello");
return 0;
}
函數AndroidRuntime::start調用瞭函數AndroidRuntime::startReg。
++++++++++++++++++++++++++++++AndroidRuntime::start++++++++++++++++++++++++++++++++++
/*
* Start the Android runtime. This involves starting the virtual machine
* and calling the "static void main(String[] args)" method in the class
* named by "className".
*/
void AndroidRuntime::start(const char* className, const bool startSystemServer)
{
LOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n",
className != NULL ? className : "(unknown)");
char* slashClassName = NULL;
char* cp;
JNIEnv* env;
blockSigpipe();
/*
* 'startSystemServer == true' means runtime is obslete and not run from
* init.rc anymore, so we print out the boot start event here.
*/
if (startSystemServer) {
/* track our progress through the boot sequence */
const int LOG_BOOT_PROGRESS_START = 3000;
LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,
ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
}
const char* rootDir = getenv("ANDROID_ROOT");
if (rootDir == NULL) {
rootDir = "/system";
if (!hasDir("/system")) {
LOG_FATAL("No root directory specified, and /android does not exist.");
goto bail;
}
setenv("ANDROID_ROOT", rootDir, 1);
}
//const char* kernelHack = getenv("LD_ASSUME_KERNEL");
//LOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);
/* start the virtual machine */
if (startVm(&mJavaVM, &env) != 0)
goto bail;
/*
* Register android functions.
*/
if (startReg(env) < 0) {
LOGE("Unable to register all android natives\n");
goto bail;
}
/*
* We want to call main() with a String array with arguments in it.
* At present we only have one argument, the class name. Create an
* array to hold it.
*/
jclass stringClass;
jobjectArray strArray;
jstring classNameStr;
jstring startSystemServerStr;
stringClass = env->FindClass("java/lang/String");
assert(stringClass != NULL);
strArray = env->NewObjectArray(2, stringClass, NULL);
assert(strArray != NULL);
classNameStr = env->NewStringUTF(className);
assert(classNameStr != NULL);
env->SetObjectArrayElement(strArray, 0, classNameStr);
startSystemServerStr = env->NewStringUTF(startSystemServer ?
"true" : "false");
env->SetObjectArrayElement(strArray, 1, startSystemServerStr);
/*
* Start VM. This thread becomes the main thread of the VM, and will
* not return until the VM exits.
*/
jclass startClass;
jmethodID startMeth;
slashClassName = strdup(className);
for (cp = slashClassName; *cp != '\0'; cp++)
if (*cp == '.')
*cp = '/';
startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
LOGE("JavaVM unable to locate class '%s'\n", slashClassName);
/* keep going */
} else {
startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL) {
LOGE("JavaVM unable to find main() in '%s'\n", className);
/* keep going */
} else {
env->CallStaticVoidMethod(startClass, startMeth, strArray);
#if 0
if (env->ExceptionCheck())
threadExitUncaughtException(env);
#endif
}
}
LOGD("Shutting down VM\n");
if (mJavaVM->DetachCurrentThread() != JNI_OK)
LOGW("Warning: unable to detach main thread\n");
if (mJavaVM->DestroyJavaVM() != 0)
LOGW("Warning: VM did not shut down cleanly\n");
bail:
free(slashClassName);
}
——————————AndroidRuntime::start———————————-
—————————–AndroidRuntime::startReg———————————–
——————————androidSetCreateThreadFunc———————————-
}
———————————androidCreateThreadEtc——————————-
}
——————————-createThreadEtc———————————
} else {
res = androidCreateRawThreadEtc(_threadLoop,
this, name, priority, stack, &mThread);
+++++++++++++++++++++++++++++++androidCreateRawThreadEtc+++++++++++++++++++++++++++++++++
函數androidCreateRawThreadEtc有兩種實現。
#if defined(HAVE_PTHREADS)
int androidCreateRawThreadEtc(android_thread_func_t entryFunction,
void *userData,
const char* threadName,
int32_t threadPriority,
size_t threadStackSize,
android_thread_id_t *threadId)
{
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
#ifdef HAVE_ANDROID_OS /* valgrind is rejecting RT-priority create reqs */
if (threadPriority != PRIORITY_DEFAULT || threadName != NULL) {
// We could avoid the trampoline if there was a way to get to the
// android_thread_id_t (pid) from pthread_t
thread_data_t* t = new thread_data_t;
t->priority = threadPriority;
t->threadName = threadName ? strdup(threadName) : NULL;
t->entryFunction = entryFunction;
t->userData = userData;
entryFunction = (android_thread_func_t)&thread_data_t::trampoline;
userData = t;
}
#endif
if (threadStackSize) {
pthread_attr_setstacksize(&attr, threadStackSize);
}
errno = 0;
pthread_t thread;
int result = pthread_create(&thread, &attr,
(android_pthread_entry)entryFunction, userData);
if (result != 0) {
LOGE("androidCreateRawThreadEtc failed (entry=%p, res=%d, errno=%d)\n"
"(android threadPriority=%d)",
entryFunction, result, errno, threadPriority);
return 0;
}
if (threadId != NULL) {
*threadId = (android_thread_id_t)thread; // XXX: this is not portable
}
return 1;
}
#elif defined(HAVE_WIN32_THREADS)
int androidCreateRawThreadEtc(android_thread_func_t fn,
void *userData,
const char* threadName,
int32_t threadPriority,
size_t threadStackSize,
android_thread_id_t *threadId)
{
return doCreateThread( fn, userData, threadId);
++++++++++++++++++++++++++++++doCreateThread++++++++++++++++++++++++++++++++++
/*
* Create and run a new thread.
*/
static bool doCreateThread(android_thread_func_t fn, void* arg, android_thread_id_t *id)
{
HANDLE hThread;
struct threadDetails* pDetails = new threadDetails; // must be on heap
unsigned int thrdaddr;
pDetails->func = fn;
pDetails->arg = arg;
#if defined(HAVE__BEGINTHREADEX)
hThread = (HANDLE) _beginthreadex(NULL, 0, threadIntermediary, pDetails, 0,
&thrdaddr);
if (hThread == 0)
#elif defined(HAVE_CREATETHREAD)
hThread = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE) threadIntermediary,
(void*) pDetails, 0, (DWORD*) &thrdaddr);
if (hThread == NULL)
#endif
{
LOG(LOG_WARN, "thread", "WARNING: thread create failed\n");
return false;
}
#if defined(HAVE_CREATETHREAD)
/* close the management handle */
CloseHandle(hThread);
#endif
if (id != NULL) {
*id = (android_thread_id_t)thrdaddr;
}
return true;
}
——————————doCreateThread———————————-
}
#endif
——————————-androidCreateRawThreadEtc———————————
}
if (res == false) {
mStatus = UNKNOWN_ERROR; // something happened!
mRunning = false;
mThread = thread_id_t(-1);
mHoldSelf.clear(); // "this" may have gone away after this.
return UNKNOWN_ERROR;
}
// Do not refer to mStatus here: The thread is already running (may, in fact
// already have exited with a valid mStatus result). The NO_ERROR indication
// here merely indicates successfully starting the thread and does not
// imply successful termination/execution.
return NO_ERROR;
}
// 在上面創建線程的時候,我們傳入的是函數_threadLoop。
++++++++++++++++++++++++++++Thread::_threadLoop++++++++++++++++++++++++++++++++++++
int Thread::_threadLoop(void* user)
{
Thread* const self = static_cast<Thread*>(user);
sp<Thread> strong(self->mHoldSelf);
wp<Thread> weak(strong);
self->mHoldSelf.clear();
#if HAVE_ANDROID_OS
// this is very useful for debugging with gdb
self->mTid = gettid();
#endif
bool first = true;
do {
bool result;
if (first) {
first = false;
self->mStatus = self->readyToRun();
result = (self->mStatus == NO_ERROR);
if (result && !self->mExitPending) {
// Binder threads (and maybe others) rely on threadLoop
// running at least once after a successful ::readyToRun()
// (unless, of course, the thread has already been asked to exit
// at that point).
// This is because threads are essentially used like this:
// (new ThreadSubclass())->run();
// The caller therefore does not retain a strong reference to
// the thread and the thread would simply disappear after the
// successful ::readyToRun() call instead of entering the
// threadLoop at least once.
// 可見,最終調用的還是各thread的threadLoop函數
result = self->threadLoop();
+++++++++++++++++++++++++++AudioTrack::AudioTrackThread::threadLoop+++++++++++++++++++++++++++++++++++++
bool AudioTrack::AudioTrackThread::threadLoop()
{
// 調用的其實是函數AudioTrack::processAudioBuffer
return mReceiver.processAudioBuffer(this);
++++++++++++++++++++++++++++AudioTrack::processAudioBuffer++++++++++++++++++++++++++++++++++++
bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
{
Buffer audioBuffer;
uint32_t frames;
size_t writtenSize;
// Manage underrun callback
if (mActive && (mCblk->framesReady() == 0)) {
LOGV("Underrun user: %x, server: %x, flags %04x", mCblk->user, mCblk->server, mCblk->flags);
if ((mCblk->flags & CBLK_UNDERRUN_MSK) == CBLK_UNDERRUN_OFF) {
mCbf(EVENT_UNDERRUN, mUserData, 0);
if (mCblk->server == mCblk->frameCount) {
// 函數指針mCbf指向的是函數audioCallback
mCbf(EVENT_BUFFER_END, mUserData, 0);
++++++++++++++++++++++++++++audioCallback++++++++++++++++++++++++++++++++++++
static void audioCallback(int event, void* user, void *info) {
if (event == AudioTrack::EVENT_MORE_DATA) {
// set size to 0 to signal we're not using the callback to write more data
AudioTrack::Buffer* pBuff = (AudioTrack::Buffer*)info;
pBuff->size = 0;
} else if (event == AudioTrack::EVENT_MARKER) {
audiotrack_callback_cookie *callbackInfo = (audiotrack_callback_cookie *)user;
JNIEnv *env = AndroidRuntime::getJNIEnv();
if (user && env) {
env->CallStaticVoidMethod(
callbackInfo->audioTrack_class,
javaAudioTrackFields.postNativeEventInJava,
callbackInfo->audioTrack_ref, event, 0,0, NULL);
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
}
}
} else if (event == AudioTrack::EVENT_NEW_POS) {
audiotrack_callback_cookie *callbackInfo = (audiotrack_callback_cookie *)user;
JNIEnv *env = AndroidRuntime::getJNIEnv();
if (user && env) {
env->CallStaticVoidMethod(
callbackInfo->audioTrack_class,
javaAudioTrackFields.postNativeEventInJava,
callbackInfo->audioTrack_ref, event, 0,0, NULL);
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
}
}
}
}
—————————-audioCallback————————————
}
mCblk->flags |= CBLK_UNDERRUN_ON;
if (mSharedBuffer != 0) return false;
}
}
// Manage loop end callback
while (mLoopCount > mCblk->loopCount) {
int loopCount = -1;
mLoopCount–;
if (mLoopCount >= 0) loopCount = mLoopCount;
mCbf(EVENT_LOOP_END, mUserData, (void *)&loopCount);
}
// Manage marker callback
if (!mMarkerReached && (mMarkerPosition > 0)) {
if (mCblk->server >= mMarkerPosition) {
mCbf(EVENT_MARKER, mUserData, (void *)&mMarkerPosition);
mMarkerReached = true;
}
}
// Manage new position callback
if (mUpdatePeriod > 0) {
while (mCblk->server >= mNewPosition) {
mCbf(EVENT_NEW_POS, mUserData, (void *)&mNewPosition);
mNewPosition += mUpdatePeriod;
}
}
// If Shared buffer is used, no data is requested from client.
if (mSharedBuffer != 0) {
frames = 0;
} else {
frames = mRemainingFrames;
}
do {
audioBuffer.frameCount = frames;
// Calling obtainBuffer() with a wait count of 1
// limits wait time to WAIT_PERIOD_MS. This prevents from being
// stuck here not being able to handle timed events (position, markers, loops).
status_t err = obtainBuffer(&audioBuffer, 1);
if (err < NO_ERROR) {
if (err != TIMED_OUT) {
LOGE_IF(err != status_t(NO_MORE_BUFFERS), "Error obtaining an audio buffer, giving up.");
return false;
}
break;
}
if (err == status_t(STOPPED)) return false;
// Divide buffer size by 2 to take into account the expansion
// due to 8 to 16 bit conversion: the callback must fill only half
// of the destination buffer
if (mFormat == AudioSystem::PCM_8_BIT && !(mFlags & AudioSystem::OUTPUT_FLAG_DIRECT)) {
audioBuffer.size >>= 1;
}
size_t reqSize = audioBuffer.size;
mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer);
writtenSize = audioBuffer.size;
// Sanity check on returned size
if (ssize_t(writtenSize) <= 0) {
// The callback is done filling buffers
// Keep this thread going to handle timed events and
// still try to get more data in intervals of WAIT_PERIOD_MS
// but don't just loop and block the CPU, so wait
usleep(WAIT_PERIOD_MS*1000);
break;
}
if (writtenSize > reqSize) writtenSize = reqSize;
if (mFormat == AudioSystem::PCM_8_BIT && !(mFlags & AudioSystem::OUTPUT_FLAG_DIRECT)) {
// 8 to 16 bit conversion
const int8_t *src = audioBuffer.i8 + writtenSize-1;
int count = writtenSize;
int16_t *dst = audioBuffer.i16 + writtenSize-1;
while(count–) {
*dst– = (int16_t)(*src–^0x80) << 8;
}
writtenSize <<= 1;
}
audioBuffer.size = writtenSize;
// NOTE: mCblk->frameSize is not equal to AudioTrack::frameSize() for
// 8 bit PCM data: in this case, mCblk->frameSize is based on a sampel size of
// 16 bit.
audioBuffer.frameCount = writtenSize/mCblk->frameSize;
frames -= audioBuffer.frameCount;
releaseBuffer(&audioBuffer);
}
while (frames);
if (frames == 0) {
mRemainingFrames = mNotificationFramesAct;
} else {
mRemainingFrames = frames;
}
return true;
}
—————————-AudioTrack::processAudioBuffer————————————
}
—————————AudioTrack::AudioTrackThread::threadLoop————————————-
}
} else {
result = self->threadLoop();
}
if (result == false || self->mExitPending) {
self->mExitPending = true;
self->mLock.lock();
self->mRunning = false;
// clear thread ID so that requestExitAndWait() does not exit if
// called by a new thread using the same thread ID as this one.
self->mThread = thread_id_t(-1);
self->mThreadExitedCondition.broadcast();
self->mThread = thread_id_t(-1); // thread id could be reused
self->mLock.unlock();
break;
}
// Release our strong reference, to let a chance to the thread
// to die a peaceful death.
strong.clear();
// And immediately, re-acquire a strong reference for the next loop
strong = weak.promote();
} while(strong != 0);
return 0;
}
—————————-Thread::_threadLoop————————————
——————————-Thread::run———————————
} else {
setpriority(PRIO_PROCESS, 0, THREAD_PRIORITY_AUDIO_CLIENT);
}
if (mCblk->flags & CBLK_INVALID_MSK) {
LOGW("start() track %p invalidated, creating a new one", this);
// no need to clear the invalid flag as this cblk will not be used anymore
// force new track creation
status = DEAD_OBJECT;
} else {
// mAudioTrack在函數AudioTrack::createTrack中被賦值,其最終指向的其實是一個TrackHandle對象
status = mAudioTrack->start();
+++++++++++++++++++++++++++AudioFlinger::TrackHandle::start+++++++++++++++++++++++++++++++++++++
status_t AudioFlinger::TrackHandle::start() {
return mTrack->start();
+++++++++++++++++++++++++++++++AudioFlinger::PlaybackThread::Track::start+++++++++++++++++++++++++++++++++
status_t AudioFlinger::PlaybackThread::Track::start()
{
status_t status = NO_ERROR;
LOGV("start(%d), calling thread %d session %d",
mName, IPCThreadState::self()->getCallingPid(), mSessionId);
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
Mutex::Autolock _l(thread->mLock);
int state = mState;
// here the track could be either new, or restarted
// in both cases "unstop" the track
if (mState == PAUSED) {
mState = TrackBase::RESUMING;
LOGV("PAUSED => RESUMING (%d) on thread %p", mName, this);
} else {
mState = TrackBase::ACTIVE;
LOGV("? => ACTIVE (%d) on thread %p", mName, this);
}
if (!isOutputTrack() && state != ACTIVE && state != RESUMING) {
thread->mLock.unlock();
status = AudioSystem::startOutput(thread->id(),
(AudioSystem::stream_type)mStreamType,
mSessionId);
+++++++++++++++++++++++AudioSystem::startOutput+++++++++++++++++++++++++++++++++++++++++
status_t AudioSystem::startOutput(audio_io_handle_t output,
AudioSystem::stream_type stream,
int session)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
return aps->startOutput(output, stream, session);
++++++++++++++++++++++++AudioPolicyService::startOutput++++++++++++++++++++++++++++++++++++++++
status_t AudioPolicyService::startOutput(audio_io_handle_t output,
AudioSystem::stream_type stream,
int session)
{
if (mpPolicyManager == NULL) {
return NO_INIT;
}
LOGV("startOutput() tid %d", gettid());
Mutex::Autolock _l(mLock);
return mpPolicyManager->startOutput(output, stream, session);
+++++++++++++++++++++++++++AudioPolicyManagerBase::startOutput+++++++++++++++++++++++++++++++++++++
status_t AudioPolicyManagerBase::startOutput(audio_io_handle_t output,
AudioSystem::stream_type stream,
int session)
{
LOGV("startOutput() output %d, stream %d, session %d", output, stream, session);
// 函數AudioPolicyManagerBase::addOutput調用瞭函數mOutputs.add往mOutputs添加成員
ssize_t index = mOutputs.indexOfKey(output);
+++++++++++++++++++++++++++++AudioPolicyManagerBase::addOutput+++++++++++++++++++++++++++++++++++
void AudioPolicyManagerBase::addOutput(audio_io_handle_t id, AudioOutputDescriptor *outputDesc)
{
outputDesc->mId = id;
mOutputs.add(id, outputDesc);
}
類AudioPolicyManagerBase中有幾處調用瞭函數AudioPolicyManagerBase::addOutput。
一般是調用mpClientInterface->openOutput函數後,會調用函數AudioPolicyManagerBase::addOutput將output添加到mOutputs。
(mpClientInterface其實是一個AudioPolicyService對象)
例如:
函數AudioPolicyManagerBase::getOutput和AudioPolicyManagerBase的構造函數。
創建AudioTrack對象的時候,在函數AudioTrack::set中會調用函數AudioPolicyManagerBase::getOutput。
AudioPolicyManagerBase對象是在AudioPolicyService的構造函數中被創建。
—————————–AudioPolicyManagerBase::addOutput———————————–
if (index < 0) {
LOGW("startOutput() unknow output %d", output);
return BAD_VALUE;
}
// 在mOutputs中,output作為key,outputDesc作為value。
AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index);
// 函數getStrategy就是根據stream type返回特定的strategy。
routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream);
#ifdef WITH_A2DP
if (mA2dpOutput != 0 && !a2dpUsedForSonification() && strategy == STRATEGY_SONIFICATION) {
setStrategyMute(STRATEGY_MEDIA, true, mA2dpOutput);
++++++++++++++++++++++++++++AudioPolicyManagerBase::setStrategyMute++++++++++++++++++++++++++++++++++++
void AudioPolicyManagerBase::setStrategyMute(routing_strategy strategy, bool on, audio_io_handle_t output, int delayMs)
{
LOGV("setStrategyMute() strategy %d, mute %d, output %d", strategy, on, output);
// 將strategy對應的stream全部mute
for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
if (getStrategy((AudioSystem::stream_type)stream) == strategy) {
setStreamMute(stream, on, output, delayMs);
+++++++++++++++++++++++++++++AudioPolicyManagerBase::setStreamMute+++++++++++++++++++++++++++++++++++
void AudioPolicyManagerBase::setStreamMute(int stream, bool on, audio_io_handle_t output, int delayMs)
{
StreamDescriptor &streamDesc = mStreams[stream];
++++++++++++++++++++++++++++++StreamDescriptor++++++++++++++++++++++++++++++++++
// stream descriptor used for volume control
class StreamDescriptor
{
public:
StreamDescriptor()
: mIndexMin(0), mIndexMax(1), mIndexCur(1), mCanBeMuted(true) {}
void dump(char* buffer, size_t size);
int mIndexMin; // min volume index
int mIndexMax; // max volume index
int mIndexCur; // current volume index
bool mCanBeMuted; // true is the stream can be muted
};
——————————StreamDescriptor———————————-
AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
++++++++++++++++++++++++++++++AudioOutputDescriptor++++++++++++++++++++++++++++++++++
// descriptor for audio outputs. Used to maintain current configuration of each opened audio output
// and keep track of the usage of this output by each audio stream type.
class AudioOutputDescriptor
{
public:
AudioOutputDescriptor();
status_t dump(int fd);
uint32_t device();
void changeRefCount(AudioSystem::stream_type, int delta);
uint32_t refCount();
uint32_t strategyRefCount(routing_strategy strategy);
bool isUsedByStrategy(routing_strategy strategy) { return (strategyRefCount(strategy) != 0);}
bool isDuplicated() { return (mOutput1 != NULL && mOutput2 != NULL); }
audio_io_handle_t mId; // output handle
uint32_t mSamplingRate; //
uint32_t mFormat; //
uint32_t mChannels; // output configuration
uint32_t mLatency; //
AudioSystem::output_flags mFlags; //
uint32_t mDevice; // current device this output is routed to
uint32_t mRefCount[AudioSystem::NUM_STREAM_TYPES]; // number of streams of each type using this output
AudioOutputDescriptor *mOutput1; // used by duplicated outputs: first output
AudioOutputDescriptor *mOutput2; // used by duplicated outputs: second output
float mCurVolume[AudioSystem::NUM_STREAM_TYPES]; // current stream volume
int mMuteCount[AudioSystem::NUM_STREAM_TYPES]; // mute request counter
};
——————————AudioOutputDescriptor———————————-
LOGV("setStreamMute() stream %d, mute %d, output %d, mMuteCount %d", stream, on, output, outputDesc->mMuteCount[stream]);
if (on) {
if (outputDesc->mMuteCount[stream] == 0) {
if (streamDesc.mCanBeMuted) {
checkAndSetVolume(stream, 0, output, outputDesc->device(), delayMs);
++++++++++++++++++++++++++++AudioPolicyManagerBase::checkAndSetVolume++++++++++++++++++++++++++++++++++++
status_t AudioPolicyManagerBase::checkAndSetVolume(int stream, int index, audio_io_handle_t output, uint32_t device, int delayMs, bool force)
{
// do not change actual stream volume if the stream is muted
if (mOutputs.valueFor(output)->mMuteCount[stream] != 0) {
LOGV("checkAndSetVolume() stream %d muted count %d", stream, mOutputs.valueFor(output)->mMuteCount[stream]);
return NO_ERROR;
}
// do not change in call volume if bluetooth is connected and vice versa
if ((stream == AudioSystem::VOICE_CALL && mForceUse[AudioSystem::FOR_COMMUNICATION] == AudioSystem::FORCE_BT_SCO) ||
(stream == AudioSystem::BLUETOOTH_SCO && mForceUse[AudioSystem::FOR_COMMUNICATION] != AudioSystem::FORCE_BT_SCO)) {
LOGV("checkAndSetVolume() cannot set stream %d volume with force use = %d for comm",
stream, mForceUse[AudioSystem::FOR_COMMUNICATION]);
return INVALID_OPERATION;
}
float volume = computeVolume(stream, index, output, device);
+++++++++++++++++++++++++++++++AudioPolicyManagerBase::computeVolume+++++++++++++++++++++++++++++++++
float AudioPolicyManagerBase::computeVolume(int stream, int index, audio_io_handle_t output, uint32_t device)
{
float volume = 1.0;
AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
StreamDescriptor &streamDesc = mStreams[stream];
if (device == 0) {
device = outputDesc->device();
}
int volInt = (100 * (index – streamDesc.mIndexMin)) / (streamDesc.mIndexMax – streamDesc.mIndexMin);
volume = AudioSystem::linearToLog(volInt);
+++++++++++++++++++++++++++++++AudioSystem::linearToLog+++++++++++++++++++++++++++++++++
float AudioSystem::linearToLog(int volume)
{
// float v = volume ? exp(float(100 – volume) * dBConvert) : 0;
// LOGD("linearToLog(%d)=%f", volume, v);
// return v;
return volume ? exp(float(100 – volume) * dBConvert) : 0;
}
——————————-AudioSystem::linearToLog———————————
// if a headset is connected, apply the following rules to ring tones and notifications
// to avoid sound level bursts in user's ears:
// – always attenuate ring tones and notifications volume by 6dB
// – if music is playing, always limit the volume to current music volume,
// with a minimum threshold at -36dB so that notification is always perceived.
if ((device &
(AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP |
AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
AudioSystem::DEVICE_OUT_WIRED_HEADSET |
AudioSystem::DEVICE_OUT_WIRED_HEADPHONE)) &&
((getStrategy((AudioSystem::stream_type)stream) == STRATEGY_SONIFICATION) ||
(stream == AudioSystem::SYSTEM)) &&
streamDesc.mCanBeMuted) {
// Attenuation applied to STRATEGY_SONIFICATION streams when a headset is connected: 6dB
// #define SONIFICATION_HEADSET_VOLUME_FACTOR 0.5
volume *= SONIFICATION_HEADSET_VOLUME_FACTOR;
// when the phone is ringing we must consider that music could have been paused just before
// by the music application and behave as if music was active if the last music track was
// just stopped
// 函數AudioPolicyManagerBase::setPhoneState中有對mLimitRingtoneVolume賦值
++++++++++++++++++++++++++++++mLimitRingtoneVolume++++++++++++++++++++++++++++++++++
// Flag that ringtone volume must be limited to music volume until we exit MODE_RINGTONE
if (state == AudioSystem::MODE_RINGTONE &&
(hwOutputDesc->mRefCount[AudioSystem::MUSIC] ||
(systemTime() – mMusicStopTime) < seconds(SONIFICATION_HEADSET_MUSIC_DELAY))) {
mLimitRingtoneVolume = true;
} else {
mLimitRingtoneVolume = false;
}
——————————mLimitRingtoneVolume———————————-
if (outputDesc->mRefCount[AudioSystem::MUSIC] || mLimitRingtoneVolume) {
float musicVol = computeVolume(AudioSystem::MUSIC, mStreams[AudioSystem::MUSIC].mIndexCur, output, device);
float minVol = (musicVol > SONIFICATION_HEADSET_VOLUME_MIN) ? musicVol : SONIFICATION_HEADSET_VOLUME_MIN;
if (volume > minVol) {
volume = minVol;
LOGV("computeVolume limiting volume to %f musicVol %f", minVol, musicVol);
}
}
}
return volume;
}
——————————-AudioPolicyManagerBase::computeVolume———————————
// We actually change the volume if:
// – the float value returned by computeVolume() changed
// – the force flag is set
if (volume != mOutputs.valueFor(output)->mCurVolume[stream] ||
force) {
mOutputs.valueFor(output)->mCurVolume[stream] = volume;
LOGV("setStreamVolume() for output %d stream %d, volume %f, delay %d", output, stream, volume, delayMs);
if (stream == AudioSystem::VOICE_CALL ||
stream == AudioSystem::DTMF ||
stream == AudioSystem::BLUETOOTH_SCO) {
// offset value to reflect actual hardware volume that never reaches 0
// 1% corresponds roughly to first step in VOICE_CALL stream volume setting (see AudioService.java)
volume = 0.01 + 0.99 * volume;
}
mpClientInterface->setStreamVolume((AudioSystem::stream_type)stream, volume, output, delayMs);
+++++++++++++++++++++++++++++AudioPolicyService::setStreamVolume+++++++++++++++++++++++++++++++++++
status_t AudioPolicyService::setStreamVolume(AudioSystem::stream_type stream,
float volume,
audio_io_handle_t output,
int delayMs)
{
// mAudioCommandThread在AudioPolicyService的構造函數中被創建
return mAudioCommandThread->volumeCommand((int)stream, volume, (int)output, delayMs);
++++++++++++++++++++++++++++++AudioPolicyService::AudioCommandThread::volumeCommand++++++++++++++++++++++++++++++++++
status_t AudioPolicyService::AudioCommandThread::volumeCommand(int stream,
float volume,
int output,
int delayMs)
{
status_t status = NO_ERROR;
AudioCommand *command = new AudioCommand();
command->mCommand = SET_VOLUME;
VolumeData *data = new VolumeData();
data->mStream = stream;
data->mVolume = volume;
data->mIO = output;
command->mParam = data;
if (delayMs == 0) {
command->mWaitStatus = true;
} else {
command->mWaitStatus = false;
}
Mutex::Autolock _l(mLock);
insertCommand_l(command, delayMs);
+++++++++++++++++++++++++++++++++AudioPolicyService::AudioCommandThread::insertCommand_l+++++++++++++++++++++++++++++++
// insertCommand_l() must be called with mLock held
void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *command, int delayMs)
{
ssize_t i;
Vector <AudioCommand *> removedCommands;
command->mTime = systemTime() + milliseconds(delayMs);
// acquire wake lock to make sure delayed commands are processed
if (mName != "" && mAudioCommands.isEmpty()) {
acquire_wake_lock(PARTIAL_WAKE_LOCK, mName.string());
}
// check same pending commands with later time stamps and eliminate them
// 如果command隊列中,存在正在等待的相同的command,如果其延遲比新加入的還要晚,則刪除這些command
// 從後往前遍歷
for (i = mAudioCommands.size()-1; i >= 0; i–) {
AudioCommand *command2 = mAudioCommands[i];
// commands are sorted by increasing time stamp: no need to scan the rest of mAudioCommands
// command在隊列中是按照時間戳排序的,開頭的時間戳最短
// 從後往前遍歷,碰到比自己小的時間戳,說明前面所有command 的時間戳都比新command的時間戳要小,也就沒必要再比較瞭
if (command2->mTime <= command->mTime) break;
// 隻比較相同的command類型
if (command2->mCommand != command->mCommand) continue;
switch (command->mCommand) {
// 如果是設置參數,因為要設置的參數比較多,隻把要設置的相同類型的參數刪掉
// 如果隊列中command設置的所有的參數類型與新command的所有參數類型完全相同,則將原來的command刪除
case SET_PARAMETERS: {
ParametersData *data = (ParametersData *)command->mParam;
ParametersData *data2 = (ParametersData *)command2->mParam;
if (data->mIO != data2->mIO) break;
LOGV("Comparing parameter command %s to new command %s",
data2->mKeyValuePairs.string(), data->mKeyValuePairs.string());
AudioParameter param = AudioParameter(data->mKeyValuePairs);
AudioParameter param2 = AudioParameter(data2->mKeyValuePairs);
for (size_t j = 0; j < param.size(); j++) {
String8 key;
String8 value;
param.getAt(j, key, value);
for (size_t k = 0; k < param2.size(); k++) {
String8 key2;
String8 value2;
param2.getAt(k, key2, value2);
if (key2 == key) {
param2.remove(key2);
LOGV("Filtering out parameter %s", key2.string());
break;
}
}
}
// if all keys have been filtered out, remove the command.
// otherwise, update the key value pairs
if (param2.size() == 0) {
removedCommands.add(command2);
} else {
data2->mKeyValuePairs = param2.toString();
}
} break;
// 如果是設置音量,比較它們是否是要設置同一個output上的相同的stream的音量
// 如果是,則刪除原command
case SET_VOLUME: {
VolumeData *data = (VolumeData *)command->mParam;
VolumeData *data2 = (VolumeData *)command2->mParam;
if (data->mIO != data2->mIO) break;
if (data->mStream != data2->mStream) break;
LOGV("Filtering out volume command on output %d for stream %d",
data->mIO, data->mStream);
removedCommands.add(command2);
} break;
case START_TONE:
case STOP_TONE:
default:
break;
}
}
// remove filtered commands
for (size_t j = 0; j < removedCommands.size(); j++) {
// removed commands always have time stamps greater than current command
for (size_t k = i + 1; k < mAudioCommands.size(); k++) {
if (mAudioCommands[k] == removedCommands[j]) {
LOGV("suppressing command: %d", mAudioCommands[k]->mCommand);
mAudioCommands.removeAt(k);
break;
}
}
}
removedCommands.clear();
// insert command at the right place according to its time stamp
LOGV("inserting command: %d at index %d, num commands %d",
command->mCommand, (int)i+1, mAudioCommands.size());
// 將command加入到隊列mAudioCommands中,位置i是根據時間戳算出來的
// 函數AudioPolicyService::AudioCommandThread::threadLoop中會處理mAudioCommands中的command
mAudioCommands.insertAt(command, i + 1);
+++++++++++++++++++++++++++++++++AudioPolicyService::AudioCommandThread::threadLoop+++++++++++++++++++++++++++++++
bool AudioPolicyService::AudioCommandThread::threadLoop()
{
nsecs_t waitTime = INT64_MAX;
mLock.lock();
while (!exitPending())
{
while(!mAudioCommands.isEmpty()) {
nsecs_t curTime = systemTime();
// commands are sorted by increasing time stamp: execute them from index 0 and up
if (mAudioCommands[0]->mTime <= curTime) {
// 每次總是取第一個command進行處理,因為第一個command 的時間戳永遠是最早的
AudioCommand *command = mAudioCommands[0];
mAudioCommands.removeAt(0);
mLastCommand = *command;
switch (command->mCommand) {
case START_TONE: {
mLock.unlock();
ToneData *data = (ToneData *)command->mParam;
LOGV("AudioCommandThread() processing start tone %d on stream %d",
data->mType, data->mStream);
if (mpToneGenerator != NULL)
delete mpToneGenerator;
mpToneGenerator = new ToneGenerator(data->mStream, 1.0);
mpToneGenerator->startTone(data->mType);
delete data;
mLock.lock();
}break;
case STOP_TONE: {
mLock.unlock();
LOGV("AudioCommandThread() processing stop tone");
if (mpToneGenerator != NULL) {
mpToneGenerator->stopTone();
delete mpToneGenerator;
mpToneGenerator = NULL;
}
mLock.lock();
}break;
case SET_VOLUME: {
VolumeData *data = (VolumeData *)command->mParam;
LOGV("AudioCommandThread() processing set volume stream %d, \
volume %f, output %d", data->mStream, data->mVolume, data->mIO);
command->mStatus = AudioSystem::setStreamVolume(data->mStream,
data->mVolume,
data->mIO);
++++++++++++++++++++++++++++++AudioSystem::setStreamVolume++++++++++++++++++++++++++++++++++
status_t AudioSystem::setStreamVolume(int stream, float value, int output)
{
if (uint32_t(stream) >= NUM_STREAM_TYPES) return BAD_VALUE;
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
af->setStreamVolume(stream, value, output);
++++++++++++++++++++++++++++++AudioFlinger::setStreamVolume++++++++++++++++++++++++++++++++++
status_t AudioFlinger::setStreamVolume(int stream, float value, int output)
{
// check calling permissions
if (!settingsAllowed()) {
return PERMISSION_DENIED;
}
if (stream < 0 || uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) {
return BAD_VALUE;
}
AutoMutex lock(mLock);
PlaybackThread *thread = NULL;
if (output) {
thread = checkPlaybackThread_l(output);
if (thread == NULL) {
return BAD_VALUE;
}
}
mStreamTypes[stream].volume = value;
// 如果沒有找到output對應的playback thread,則將所有的playback thread中的對應stream的音量全部改變
// 否則隻改變對應的playback thread中對應stream的音量
if (thread == NULL) {
for (uint32_t i = 0; i < mPlaybackThreads.size(); i++) {
mPlaybackThreads.valueAt(i)->setStreamVolume(stream, value);
}
} else {
thread->setStreamVolume(stream, value);
++++++++++++++++++++++++++++++AudioFlinger::PlaybackThread::setStreamVolume++++++++++++++++++++++++++++++++++
status_t AudioFlinger::PlaybackThread::setStreamVolume(int stream, float value)
{
#ifdef LVMX
int audioOutputType = LifeVibes::getMixerType(mId, mType);
if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) {
LifeVibes::setStreamVolume(audioOutputType, stream, value);
}
#endif
// 隻是改變瞭軟件音量
// mStreamTypes中保存的音量,在函數AudioFlinger::MixerThread::prepareTracks_l中有被使用
mStreamTypes[stream].volume = value;
+++++++++++++++++++++++++++++mStreamTypes+++++++++++++++++++++++++++++++++++
// read original volumes with volume control
float typeVolume = mStreamTypes[track->type()].volume;
#ifdef LVMX
bool streamMute=false;
// read the volume from the LivesVibes audio engine.
if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType))
{
LifeVibes::getStreamVolumes(audioOutputType, track->type(), &typeVolume, &streamMute);
if (streamMute) {
typeVolume = 0;
}
}
#endif
float v = masterVolume * typeVolume;
vl = (uint32_t)(v * cblk->volume[0]) << 12;
vr = (uint32_t)(v * cblk->volume[1]) << 12;
va = (uint32_t)(v * cblk->sendLevel);
}
// Delegate volume control to effect in track effect chain if needed
// 轉換後的音量在此處有被使用
// chain->setVolume_l函數中不僅使用瞭音量,也有去改變音量
if (chain != 0 && chain->setVolume_l(&vl, &vr)) {
++++++++++++++++++++++++++++++AudioFlinger::EffectChain::setVolume_l++++++++++++++++++++++++++++++++++
// setVolume_l() must be called with PlaybackThread::mLock held
bool AudioFlinger::EffectChain::setVolume_l(uint32_t *left, uint32_t *right)
{
uint32_t newLeft = *left;
uint32_t newRight = *right;
bool hasControl = false;
int ctrlIdx = -1;
size_t size = mEffects.size();
// first update volume controller
for (size_t i = size; i > 0; i–) {
if (mEffects[i – 1]->isProcessEnabled() &&
(mEffects[i – 1]->desc().flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_CTRL) {
ctrlIdx = i – 1;
hasControl = true;
break;
}
}
if (ctrlIdx == mVolumeCtrlIdx && *left == mLeftVolume && *right == mRightVolume) {
if (hasControl) {
*left = mNewLeftVolume;
*right = mNewRightVolume;
}
return hasControl;
}
mVolumeCtrlIdx = ctrlIdx;
mLeftVolume = newLeft;
mRightVolume = newRight;
// second get volume update from volume controller
if (ctrlIdx >= 0) {
mEffects[ctrlIdx]->setVolume(&newLeft, &newRight, true);
+++++++++++++++++++++++++++++AudioFlinger::EffectModule::setVolume+++++++++++++++++++++++++++++++++++
status_t AudioFlinger::EffectModule::setVolume(uint32_t *left, uint32_t *right, bool controller)
{
Mutex::Autolock _l(mLock);
status_t status = NO_ERROR;
// Send volume indication if EFFECT_FLAG_VOLUME_IND is set and read back altered volume
// if controller flag is set (Note that controller == TRUE => EFFECT_FLAG_VOLUME_CTRL set)
if (isProcessEnabled() &&
((mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_CTRL ||
(mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_IND)) {
status_t cmdStatus;
uint32_t volume[2];
uint32_t *pVolume = NULL;
uint32_t size = sizeof(volume);
volume[0] = *left;
volume[1] = *right;
if (controller) {
pVolume = volume;
}
status = (*mEffectInterface)->command(mEffectInterface,
EFFECT_CMD_SET_VOLUME,
size,
volume,
&size,
pVolume);
if (controller && status == NO_ERROR && size == sizeof(volume)) {
*left = volume[0];
*right = volume[1];
}
}
return status;
}
—————————–AudioFlinger::EffectModule::setVolume———————————–
mNewLeftVolume = newLeft;
mNewRightVolume = newRight;
}
// then indicate volume to all other effects in chain.
// Pass altered volume to effects before volume controller
// and requested volume to effects after controller
uint32_t lVol = newLeft;
uint32_t rVol = newRight;
for (size_t i = 0; i < size; i++) {
if ((int)i == ctrlIdx) continue;
// this also works for ctrlIdx == -1 when there is no volume controller
if ((int)i > ctrlIdx) {
lVol = *left;
rVol = *right;
}
mEffects[i]->setVolume(&lVol, &rVol, false);
}
*left = newLeft;
*right = newRight;
return hasControl;
}
——————————AudioFlinger::EffectChain::setVolume_l———————————-
// Do not ramp volume if volume is controlled by effect
param = AudioMixer::VOLUME;
track->mHasVolumeController = true;
} else {
// force no volume ramp when volume controller was just disabled or removed
// from effect chain to avoid volume spike
if (track->mHasVolumeController) {
param = AudioMixer::VOLUME;
}
track->mHasVolumeController = false;
}
// Convert volumes from 8.24 to 4.12 format
int16_t left, right, aux;
uint32_t v_clamped = (vl + (1 << 11)) >> 12;
if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
left = int16_t(v_clamped);
v_clamped = (vr + (1 << 11)) >> 12;
if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
right = int16_t(v_clamped);
if (va > MAX_GAIN_INT) va = MAX_GAIN_INT;
aux = int16_t(va);
#ifdef LVMX
if ( tracksConnectedChanged || stateChanged )
{
// only do the ramp when the volume is changed by the user / application
param = AudioMixer::VOLUME;
}
#endif
// XXX: these things DON'T need to be done each time
mAudioMixer->setBufferProvider(track);
mAudioMixer->enable(AudioMixer::MIXING);
// setParameter函數將volume信息設置到track對象中
// 函數process__OneTrack16BitsStereoNoResampling中會根據音量對數據進行處理
mAudioMixer->setParameter(param, AudioMixer::VOLUME0, (void *)left);
mAudioMixer->setParameter(param, AudioMixer::VOLUME1, (void *)right);
mAudioMixer->setParameter(param, AudioMixer::AUXLEVEL, (void *)aux);
—————————–mStreamTypes———————————–
return NO_ERROR;
}
——————————AudioFlinger::PlaybackThread::setStreamVolume———————————-
}
return NO_ERROR;
}
——————————AudioFlinger::setStreamVolume———————————-
return NO_ERROR;
}
——————————AudioSystem::setStreamVolume———————————-
if (command->mWaitStatus) {
command->mCond.signal();
mWaitWorkCV.wait(mLock);
}
delete data;
}break;
case SET_PARAMETERS: {
ParametersData *data = (ParametersData *)command->mParam;
LOGV("AudioCommandThread() processing set parameters string %s, io %d",
data->mKeyValuePairs.string(), data->mIO);
command->mStatus = AudioSystem::setParameters(data->mIO, data->mKeyValuePairs);
if (command->mWaitStatus) {
command->mCond.signal();
mWaitWorkCV.wait(mLock);
}
delete data;
}break;
case SET_VOICE_VOLUME: {
VoiceVolumeData *data = (VoiceVolumeData *)command->mParam;
LOGV("AudioCommandThread() processing set voice volume volume %f",
data->mVolume);
// 這個命令會因函數AudioPolicyManagerBase::checkAndSetVolume中調用mpClientInterface->setVoiceVolume函數引起
// 這兒也展開看看
command->mStatus = AudioSystem::setVoiceVolume(data->mVolume);
++++++++++++++++++++++++++++++AudioSystem::setVoiceVolume++++++++++++++++++++++++++++++++++
status_t AudioSystem::setVoiceVolume(float value)
{
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
return af->setVoiceVolume(value);
++++++++++++++++++++++++++++++AudioFlinger::setVoiceVolume++++++++++++++++++++++++++++++++++
status_t AudioFlinger::setVoiceVolume(float value)
{
// check calling permissions
if (!settingsAllowed()) {
return PERMISSION_DENIED;
}
AutoMutex lock(mHardwareLock);
mHardwareStatus = AUDIO_SET_VOICE_VOLUME;
status_t ret = mAudioHardware->setVoiceVolume(value);
+++++++++++++++++++++++++++++AudioHardwareALSA::setVoiceVolume+++++++++++++++++++++++++++++++++++
status_t AudioHardwareALSA::setVoiceVolume(float volume)
{
/* could not set this params on SPDIF card, will return directly */
if (!strcmp(mCurCard,SPDIF))
return INVALID_OPERATION;
// The voice volume is used by the VOICE_CALL audio stream.
if (mMixer)
return mMixer->setVolume(AudioSystem::DEVICE_OUT_EARPIECE, volume, volume);
+++++++++++++++++++++++++++++AudioFlinger::setVoiceVolume+++++++++++++++++++++++++++++++++++
status_t ALSAMixer::setVolume(uint32_t device, float left, float right)
{
for (int j = 0; mixerProp[j][SND_PCM_STREAM_PLAYBACK].device; j++)
if (mixerProp[j][SND_PCM_STREAM_PLAYBACK].device & device) {
mixer_info_t *info = mixerProp[j][SND_PCM_STREAM_PLAYBACK].mInfo;
if (!info || !info->elem) return INVALID_OPERATION;
long minVol = info->min;
long maxVol = info->max;
// Make sure volume is between bounds.
long vol = minVol + left * (maxVol – minVol);
if (vol > maxVol) vol = maxVol;
if (vol < minVol) vol = minVol;
info->volume = vol;
snd_mixer_selem_set_playback_volume_all (info->elem, vol);
+++++++++++++++++++++++++++++snd_mixer_selem_set_playback_volume_all+++++++++++++++++++++++++++++++++++
/**
* \brief Set value of playback volume control for all channels of a mixer simple element
* \param elem Mixer simple element handle
* \param value control value
* \return 0 on success otherwise a negative error code
*/
int snd_mixer_selem_set_playback_volume_all(snd_mixer_elem_t *elem, long value)
{
snd_mixer_selem_channel_id_t chn;
int err;
for (chn = 0; chn < 32; chn++) {
if (!snd_mixer_selem_has_playback_channel(elem, chn))
+++++++++++++++++++++++++++++++snd_mixer_selem_has_playback_channel+++++++++++++++++++++++++++++++++
/**
* \brief Get info about channels of playback stream of a mixer simple element
* \param elem Mixer simple element handle
* \param channel Mixer simple element channel identifier
* \return 0 if channel is not present, 1 if present
*/
int snd_mixer_selem_has_playback_channel(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel)
{
CHECK_BASIC(elem);
return sm_selem_ops(elem)->is(elem, SM_PLAY, SM_OPS_IS_CHANNEL, (int)channel);
}
——————————-snd_mixer_selem_has_playback_channel———————————
continue;
err = snd_mixer_selem_set_playback_volume(elem, chn, value);
+++++++++++++++++++++++++++++snd_mixer_selem_set_playback_volume+++++++++++++++++++++++++++++++++++
/**
* \brief Set value of playback volume control of a mixer simple element
* \param elem Mixer simple element handle
* \param channel mixer simple element channel identifier
* \param value control value
* \return 0 on success otherwise a negative error code
*/
int snd_mixer_selem_set_playback_volume(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long value)
{
CHECK_BASIC(elem);
CHECK_DIR_CHN(elem, SM_CAP_PVOLUME, SM_CAP_PVOLUME_JOIN, channel);
// #define sm_selem_ops(x)((sm_selem_t *)((x)->private_data))->ops
return sm_selem_ops(elem)->set_volume(elem, SM_PLAY, channel, value);
+++++++++++++++++++++++++++++sm_selem_t+++++++++++++++++++++++++++++++++++
typedef struct _sm_selem {
snd_mixer_selem_id_t *id;
struct sm_elem_ops *ops;
unsigned int caps;
unsigned int capture_group;
} sm_selem_t;
—————————–sm_selem_t———————————–
}
—————————–snd_mixer_selem_set_playback_volume———————————–
if (err < 0)
return err;
if (chn == 0 && snd_mixer_selem_has_playback_volume_joined(elem))
+++++++++++++++++++++++++++++++++snd_mixer_selem_has_playback_volume_joined+++++++++++++++++++++++++++++++
/**
* \brief Return info about playback volume control of a mixer simple element
* \param elem Mixer simple element handle
* \return 0 if control is separated per channel, 1 if control acts on all channels together
*/
int snd_mixer_selem_has_playback_volume_joined(snd_mixer_elem_t *elem)
{
CHECK_BASIC(elem);
return COND_CAPS(elem, SM_CAP_PVOLUME_JOIN);
}
———————————snd_mixer_selem_has_playback_volume_joined——————————-
return 0;
}
return 0;
}
—————————–snd_mixer_selem_set_playback_volume_all———————————–
}
return NO_ERROR;
}
—————————–AudioFlinger::setVoiceVolume———————————–
else
return INVALID_OPERATION;
}
—————————–AudioHardwareALSA::setVoiceVolume———————————–
mHardwareStatus = AUDIO_HW_IDLE;
return ret;
}
——————————AudioFlinger::setVoiceVolume———————————-
}
——————————AudioSystem::setVoiceVolume———————————-
if (command->mWaitStatus) {
command->mCond.signal();
mWaitWorkCV.wait(mLock);
}
delete data;
}break;
default:
LOGW("AudioCommandThread() unknown command %d", command->mCommand);
}
delete command;
waitTime = INT64_MAX;
} else {
waitTime = mAudioCommands[0]->mTime – curTime;
break;
}
}
// release delayed commands wake lock
if (mName != "" && mAudioCommands.isEmpty()) {
release_wake_lock(mName.string());
}
LOGV("AudioCommandThread() going to sleep");
mWaitWorkCV.waitRelative(mLock, waitTime);
LOGV("AudioCommandThread() waking up");
}
mLock.unlock();
return false;
}
———————————AudioPolicyService::AudioCommandThread::threadLoop——————————-
}
———————————AudioPolicyService::AudioCommandThread::insertCommand_l——————————-
LOGV("AudioCommandThread() adding set volume stream %d, volume %f, output %d",
stream, volume, output);
mWaitWorkCV.signal();
if (command->mWaitStatus) {
command->mCond.wait(mLock);
status = command->mStatus;
mWaitWorkCV.signal();
}
return status;
}
——————————AudioPolicyService::AudioCommandThread::volumeCommand———————————-
}
—————————–AudioPolicyService::setStreamVolume———————————–
}
if (stream == AudioSystem::VOICE_CALL ||
stream == AudioSystem::BLUETOOTH_SCO) {
float voiceVolume;
// Force voice volume to max for bluetooth SCO as volume is managed by the headset
if (stream == AudioSystem::VOICE_CALL) {
voiceVolume = (float)index/(float)mStreams[stream].mIndexMax;
} else {
voiceVolume = 1.0;
}
if (voiceVolume != mLastVoiceVolume && output == mHardwareOutput) {
mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
mLastVoiceVolume = voiceVolume;
}
}
return NO_ERROR;
}
—————————-AudioPolicyManagerBase::checkAndSetVolume————————————
}
}
// increment mMuteCount after calling checkAndSetVolume() so that volume change is not ignored
outputDesc->mMuteCount[stream]++;
} else {
if (outputDesc->mMuteCount[stream] == 0) {
LOGW("setStreamMute() unmuting non muted stream!");
return;
}
if (–outputDesc->mMuteCount[stream] == 0) {
checkAndSetVolume(stream, streamDesc.mIndexCur, output, outputDesc->device(), delayMs);
}
}
}
—————————–AudioPolicyManagerBase::setStreamMute———————————–
}
}
}
—————————-AudioPolicyManagerBase::setStrategyMute————————————
}
#endif
// incremenent usage count for this stream on the requested output:
// NOTE that the usage count is the same for duplicated output and hardware output which is
// necassary for a correct control of hardware output routing by startOutput() and stopOutput()
outputDesc->changeRefCount(stream, 1);
setOutputDevice(output, getNewDevice(output));
++++++++++++++++++++++++++++AudioPolicyManagerBase::setOutputDevice++++++++++++++++++++++++++++++++++++
void AudioPolicyManagerBase::setOutputDevice(audio_io_handle_t output, uint32_t device, bool force, int delayMs)
{
LOGV("setOutputDevice() output %d device %x delayMs %d", output, device, delayMs);
AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
if (outputDesc->isDuplicated()) {
setOutputDevice(outputDesc->mOutput1->mId, device, force, delayMs);
setOutputDevice(outputDesc->mOutput2->mId, device, force, delayMs);
return;
}
#ifdef WITH_A2DP
// filter devices according to output selected
if (output == mA2dpOutput) {
device &= AudioSystem::DEVICE_OUT_ALL_A2DP;
} else {
device &= ~AudioSystem::DEVICE_OUT_ALL_A2DP;
}
#endif
uint32_t prevDevice = (uint32_t)outputDesc->device();
// Do not change the routing if:
// – the requestede device is 0
// – the requested device is the same as current device and force is not specified.
// Doing this check here allows the caller to call setOutputDevice() without conditions
if ((device == 0 || device == prevDevice) && !force) {
LOGV("setOutputDevice() setting same device %x or null device for output %d", device, output);
return;
}
outputDesc->mDevice = device;
// mute media streams if both speaker and headset are selected
if (output == mHardwareOutput && AudioSystem::popCount(device) == 2) {
setStrategyMute(STRATEGY_MEDIA, true, output);
// wait for the PCM output buffers to empty before proceeding with the rest of the command
usleep(outputDesc->mLatency*2*1000);
}
// do the routing
AudioParameter param = AudioParameter();
param.addInt(String8(AudioParameter::keyRouting), (int)device);
mpClientInterface->setParameters(mHardwareOutput, param.toString(), delayMs);
+++++++++++++++++++++++++++++AudioPolicyService::setParameters+++++++++++++++++++++++++++++++++++
void AudioPolicyService::setParameters(audio_io_handle_t ioHandle,
const String8& keyValuePairs,
int delayMs)
{
mAudioCommandThread->parametersCommand((int)ioHandle, keyValuePairs, delayMs);
+++++++++++++++++++++++++++++AudioPolicyService::AudioCommandThread::parametersCommand+++++++++++++++++++++++++++++++++++
status_t AudioPolicyService::AudioCommandThread::parametersCommand(int ioHandle,
const String8& keyValuePairs,
int delayMs)
{
status_t status = NO_ERROR;
AudioCommand *command = new AudioCommand();
command->mCommand = SET_PARAMETERS;
ParametersData *data = new ParametersData();
data->mIO = ioHandle;
data->mKeyValuePairs = keyValuePairs;
command->mParam = data;
if (delayMs == 0) {
command->mWaitStatus = true;
} else {
command->mWaitStatus = false;
}
Mutex::Autolock _l(mLock);
// 將command添加到隊列
// command的處理還是在函數AudioPolicyService::AudioCommandThread::threadLoop中
// 這兒隻摘取瞭set parameter的片段
insertCommand_l(command, delayMs);
++++++++++++++++++++++++++++++++case SET_PARAMETERS++++++++++++++++++++++++++++++++
case SET_PARAMETERS: {
ParametersData *data = (ParametersData *)command->mParam;
LOGV("AudioCommandThread() processing set parameters string %s, io %d",
data->mKeyValuePairs.string(), data->mIO);
command->mStatus = AudioSystem::setParameters(data->mIO, data->mKeyValuePairs);
++++++++++++++++++++++++++++++++AudioSystem::setParameters++++++++++++++++++++++++++++++++
status_t AudioSystem::setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs) {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
return af->setParameters(ioHandle, keyValuePairs);
++++++++++++++++++++++++++++++++AudioFlinger::setParameters++++++++++++++++++++++++++++++++
status_t AudioFlinger::setParameters(int ioHandle, const String8& keyValuePairs)
{
status_t result;
LOGV("setParameters(): io %d, keyvalue %s, tid %d, calling tid %d",
ioHandle, keyValuePairs.string(), gettid(), IPCThreadState::self()->getCallingPid());
// check calling permissions
if (!settingsAllowed()) {
return PERMISSION_DENIED;
}
#ifdef LVMX
AudioParameter param = AudioParameter(keyValuePairs);
LifeVibes::setParameters(ioHandle,keyValuePairs);
String8 key = String8(AudioParameter::keyRouting);
int device;
if (NO_ERROR != param.getInt(key, device)) {
device = -1;
}
key = String8(LifevibesTag);
String8 value;
int musicEnabled = -1;
if (NO_ERROR == param.get(key, value)) {
if (value == LifevibesEnable) {
mLifeVibesClientPid = IPCThreadState::self()->getCallingPid();
musicEnabled = 1;
} else if (value == LifevibesDisable) {
mLifeVibesClientPid = -1;
musicEnabled = 0;
}
}
#endif
// ioHandle == 0 means the parameters are global to the audio hardware interface
if (ioHandle == 0) {
AutoMutex lock(mHardwareLock);
mHardwareStatus = AUDIO_SET_PARAMETER;
// 函數setParameters其實是個空的,並沒有實現。
// 類AudioHardwareALSA中沒有實現,基類AudioHardwareBase中是個空的
result = mAudioHardware->setParameters(keyValuePairs);
#ifdef LVMX
if (musicEnabled != -1) {
LifeVibes::enableMusic((bool) musicEnabled);
}
#endif
mHardwareStatus = AUDIO_HW_IDLE;
return result;
}
// hold a strong ref on thread in case closeOutput() or closeInput() is called
// and the thread is exited once the lock is released
sp<ThreadBase> thread;
{
Mutex::Autolock _l(mLock);
thread = checkPlaybackThread_l(ioHandle);
if (thread == NULL) {
thread = checkRecordThread_l(ioHandle);
}
}
if (thread != NULL) {
result = thread->setParameters(keyValuePairs);
+++++++++++++++++++++++++++++++AudioFlinger::ThreadBase::setParameters+++++++++++++++++++++++++++++++++
status_t AudioFlinger::ThreadBase::setParameters(const String8& keyValuePairs)
{
status_t status;
LOGV("ThreadBase::setParameters() %s", keyValuePairs.string());
Mutex::Autolock _l(mLock);
// checkForNewParameters_l函數會使用mNewParameters,如函數AudioFlinger::MixerThread::checkForNewParameters_l
mNewParameters.add(keyValuePairs);
+++++++++++++++++++++++++++++++AudioFlinger::MixerThread::checkForNewParameters_l+++++++++++++++++++++++++++++++++
// checkForNewParameters_l() must be called with ThreadBase::mLock held
bool AudioFlinger::MixerThread::checkForNewParameters_l()
{
bool reconfig = false;
while (!mNewParameters.isEmpty()) {
status_t status = NO_ERROR;
String8 keyValuePair = mNewParameters[0];
AudioParameter param = AudioParameter(keyValuePair);
int value;
if (param.getInt(String8(AudioParameter::keySamplingRate), value) == NO_ERROR) {
reconfig = true;
}
if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) {
if (value != AudioSystem::PCM_16_BIT) {
status = BAD_VALUE;
} else {
reconfig = true;
}
}
if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) {
if (value != AudioSystem::CHANNEL_OUT_STEREO) {
status = BAD_VALUE;
} else {
reconfig = true;
}
}
if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) {
// do not accept frame count changes if tracks are open as the track buffer
// size depends on frame count and correct behavior would not be garantied
// if frame count is changed after track creation
if (!mTracks.isEmpty()) {
status = INVALID_OPERATION;
} else {
reconfig = true;
}
}
if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) {
// forward device change to effects that have requested to be
// aware of attached audio device.
mDevice = (uint32_t)value;
for (size_t i = 0; i < mEffectChains.size(); i++) {
mEffectChains[i]->setDevice_l(mDevice);
}
}
if (status == NO_ERROR) {
// mOutput在父類PlaybackThread的構造函數中被賦值,其值是通過構造函數參數傳過去的
// MixerThread對象是在函數AudioFlinger::openOutput中創建的
// 其中的output是調用函數AudioHardwareALSA::openOutputStream得到的
// 其中setParameters函數的實現是調用父類ALSAStreamOps的setParameters函數。
status = mOutput->setParameters(keyValuePair);
++++++++++++++++++++++++++++++AudioStreamOutALSA::setParameters++++++++++++++++++++++++++++++++++
virtual status_t setParameters(const String8& keyValuePairs) {
return ALSAStreamOps::setParameters(keyValuePairs);
++++++++++++++++++++++++++++++ALSAStreamOps::setParameters++++++++++++++++++++++++++++++++++
status_t ALSAStreamOps::setParameters(const String8& keyValuePairs)
{
AudioParameter param = AudioParameter(keyValuePairs);
String8 key = String8(AudioParameter::keyRouting);
status_t status = NO_ERROR;
int device;
LOGV("setParameters() %s", keyValuePairs.string());
if (param.getInt(key, device) == NO_ERROR) {
// mParent其實是AudioHardwareALSA對象
// mALSADevice的賦值在AudioHardwareALSA的構造函數中
// 通過調用函數module->methods->open得到,真正調用的函數是s_device_open
// mParent->mALSADevice->route其實是函數s_route
mParent->mALSADevice->route(mHandle, (uint32_t)device, mParent->mode());
++++++++++++++++++++++++++++++s_route++++++++++++++++++++++++++++++++++
static status_t s_route(alsa_handle_t *handle, uint32_t devices, int mode)
{
status_t status = NO_ERROR;
LOGD("route called for devices %08x in mode %d…", devices, mode);
// below Always noting to do, so we open device every time.
#if 0
if (handle->handle && handle->curDev == devices && handle->curMode == mode)
; // Nothing to do
else if (handle->handle && (handle->devices & devices))
setAlsaControls(handle, devices, mode);
else {
LOGE("Why are we routing to a device that isn't supported by this object?!?!?!?!");
status = s_open(handle, devices, mode);
}
#else
/* fix me if I was wrong here to judge the current sound card. In facat, in s_open, it will reconfig the controls */
setAlsaControls(handle, devices, mode);
+++++++++++++++++++++++++++++++++++setAlsaControls+++++++++++++++++++++++++++++
void setAlsaControls(alsa_handle_t *handle, uint32_t devices, int mode)
{
AlsaControlSet set = (AlsaControlSet) handle->modPrivate;
const char *card = deviceName(handle, devices, mode, 0);
set(devices, mode, card);
}
———————————–setAlsaControls—————————–
status = s_open(handle, devices, mode);
#endif
return status;
}
——————————s_route———————————-
param.remove(key);
}
if (param.size()) {
status = BAD_VALUE;
}
return status;
}
——————————ALSAStreamOps::setParameters———————————-
}
——————————AudioStreamOutALSA::setParameters———————————-
if (!mStandby && status == INVALID_OPERATION) {
mOutput->standby();
mStandby = true;
mBytesWritten = 0;
status = mOutput->setParameters(keyValuePair);
}
if (status == NO_ERROR && reconfig) {
delete mAudioMixer;
readOutputParameters();
+++++++++++++++++++++++++++++++AudioFlinger::PlaybackThread::readOutputParameters+++++++++++++++++++++++++++++++++
void AudioFlinger::PlaybackThread::readOutputParameters()
{
mSampleRate = mOutput->sampleRate();
mChannels = mOutput->channels();
mChannelCount = (uint16_t)AudioSystem::popCount(mChannels);
mFormat = mOutput->format();
mFrameSize = (uint16_t)mOutput->frameSize();
mFrameCount = mOutput->bufferSize() / mFrameSize;
// FIXME – Current mixer implementation only supports stereo output: Always
// Allocate a stereo buffer even if HW output is mono.
if (mMixBuffer != NULL) delete[] mMixBuffer;
mMixBuffer = new int16_t[mFrameCount * 2];
memset(mMixBuffer, 0, mFrameCount * 2 * sizeof(int16_t));
// force reconfiguration of effect chains and engines to take new buffer size and audio
// parameters into account
// Note that mLock is not held when readOutputParameters() is called from the constructor
// but in this case nothing is done below as no audio sessions have effect yet so it doesn't
// matter.
// create a copy of mEffectChains as calling moveEffectChain_l() can reorder some effect chains
Vector< sp<EffectChain> > effectChains = mEffectChains;
for (size_t i = 0; i < effectChains.size(); i ++) {
mAudioFlinger->moveEffectChain_l(effectChains[i]->sessionId(), this, this, false);
}
}
——————————-AudioFlinger::PlaybackThread::readOutputParameters———————————
mAudioMixer = new AudioMixer(mFrameCount, mSampleRate);
for (size_t i = 0; i < mTracks.size() ; i++) {
int name = getTrackName_l();
if (name < 0) break;
mTracks[i]->mName = name;
// limit track sample rate to 2 x new output sample rate
if (mTracks[i]->mCblk->sampleRate > 2 * sampleRate()) {
mTracks[i]->mCblk->sampleRate = 2 * sampleRate();
}
}
sendConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED);
++++++++++++++++++++++++++++++++++AudioFlinger::ThreadBase::sendConfigEvent_l++++++++++++++++++++++++++++++
// sendConfigEvent_l() must be called with ThreadBase::mLock held
void AudioFlinger::ThreadBase::sendConfigEvent_l(int event, int param)
{
ConfigEvent *configEvent = new ConfigEvent();
configEvent->mEvent = event;
configEvent->mParam = param;
// 函數AudioFlinger::ThreadBase::processConfigEvents會處理mConfigEvents中的event
mConfigEvents.add(configEvent);
++++++++++++++++++++++++++++++++++AudioFlinger::ThreadBase::processConfigEvents++++++++++++++++++++++++++++++
void AudioFlinger::ThreadBase::processConfigEvents()
{
mLock.lock();
while(!mConfigEvents.isEmpty()) {
LOGV("processConfigEvents() remaining events %d", mConfigEvents.size());
ConfigEvent *configEvent = mConfigEvents[0];
mConfigEvents.removeAt(0);
// release mLock before locking AudioFlinger mLock: lock order is always
// AudioFlinger then ThreadBase to avoid cross deadlock
mLock.unlock();
mAudioFlinger->mLock.lock();
audioConfigChanged_l(configEvent->mEvent, configEvent->mParam);
+++++++++++++++++++++++++++++++++++AudioFlinger::audioConfigChanged_l+++++++++++++++++++++++++++++
// audioConfigChanged_l() must be called with AudioFlinger::mLock held
void AudioFlinger::audioConfigChanged_l(int event, int ioHandle, void *param2)
{
size_t size = mNotificationClients.size();
for (size_t i = 0; i < size; i++) {
// 函數AudioFlinger::registerClient會往mNotificationClients中add 對象
mNotificationClients.valueAt(i)->client()->ioConfigChanged(event, ioHandle, param2);
++++++++++++++++++++++++++++++++++AudioFlinger::registerClient++++++++++++++++++++++++++++++
void AudioFlinger::registerClient(const sp<IAudioFlingerClient>& client)
{
Mutex::Autolock _l(mLock);
int pid = IPCThreadState::self()->getCallingPid();
if (mNotificationClients.indexOfKey(pid) < 0) {
sp<NotificationClient> notificationClient = new NotificationClient(this,
client,
pid);
LOGV("registerClient() client %p, pid %d", notificationClient.get(), pid);
mNotificationClients.add(pid, notificationClient);
sp<IBinder> binder = client->asBinder();
binder->linkToDeath(notificationClient);
// the config change is always sent from playback or record threads to avoid deadlock
// with AudioSystem::gLock
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
mPlaybackThreads.valueAt(i)->sendConfigEvent(AudioSystem::OUTPUT_OPENED);
}
for (size_t i = 0; i < mRecordThreads.size(); i++) {
mRecordThreads.valueAt(i)->sendConfigEvent(AudioSystem::INPUT_OPENED);
}
}
}
函數AudioSystem::get_audio_flinger中調用瞭函數AudioFlinger::registerClient。
+++++++++++++++++++++++++++++++AudioSystem::get_audio_flinger+++++++++++++++++++++++++++++++++
// establish binder interface to AudioFlinger service
const sp<IAudioFlinger>& AudioSystem::get_audio_flinger()
{
Mutex::Autolock _l(gLock);
if (gAudioFlinger.get() == 0) {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
do {
binder = sm->getService(String16("media.audio_flinger"));
if (binder != 0)
break;
LOGW("AudioFlinger not published, waiting…");
usleep(500000); // 0.5 s
} while(true);
if (gAudioFlingerClient == NULL) {
// 上面調用的函數ioConfigChanged其實是函數AudioSystem::AudioFlingerClient::ioConfigChanged
gAudioFlingerClient = new AudioFlingerClient();
++++++++++++++++++++++++++++++AudioSystem::AudioFlingerClient::ioConfigChanged++++++++++++++++++++++++++++++++++
void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, int ioHandle, void *param2) {
LOGV("ioConfigChanged() event %d", event);
OutputDescriptor *desc;
uint32_t stream;
if (ioHandle == 0) return;
Mutex::Autolock _l(AudioSystem::gLock);
switch (event) {
case STREAM_CONFIG_CHANGED:
if (param2 == 0) break;
stream = *(uint32_t *)param2;
LOGV("ioConfigChanged() STREAM_CONFIG_CHANGED stream %d, output %d", stream, ioHandle);
if (gStreamOutputMap.indexOfKey(stream) >= 0) {
gStreamOutputMap.replaceValueFor(stream, ioHandle);
}
break;
case OUTPUT_OPENED: {
if (gOutputs.indexOfKey(ioHandle) >= 0) {
LOGV("ioConfigChanged() opening already existing output! %d", ioHandle);
break;
}
if (param2 == 0) break;
desc = (OutputDescriptor *)param2;
OutputDescriptor *outputDesc = new OutputDescriptor(*desc);
gOutputs.add(ioHandle, outputDesc);
LOGV("ioConfigChanged() new output samplingRate %d, format %d channels %d frameCount %d latency %d",
outputDesc->samplingRate, outputDesc->format, outputDesc->channels, outputDesc->frameCount, outputDesc->latency);
} break;
case OUTPUT_CLOSED: {
if (gOutputs.indexOfKey(ioHandle) < 0) {
LOGW("ioConfigChanged() closing unknow output! %d", ioHandle);
break;
}
LOGV("ioConfigChanged() output %d closed", ioHandle);
gOutputs.removeItem(ioHandle);
for (int i = gStreamOutputMap.size() – 1; i >= 0 ; i–) {
if (gStreamOutputMap.valueAt(i) == ioHandle) {
gStreamOutputMap.removeItemsAt(i);
}
}
} break;
// 我們上面添加的是這個event
case OUTPUT_CONFIG_CHANGED: {
int index = gOutputs.indexOfKey(ioHandle);
if (index < 0) {
LOGW("ioConfigChanged() modifying unknow output! %d", ioHandle);
break;
}
if (param2 == 0) break;
desc = (OutputDescriptor *)param2;
LOGV("ioConfigChanged() new config for output %d samplingRate %d, format %d channels %d frameCount %d latency %d",
ioHandle, desc->samplingRate, desc->format,
desc->channels, desc->frameCount, desc->latency);
OutputDescriptor *outputDesc = gOutputs.valueAt(index);
delete outputDesc;
outputDesc = new OutputDescriptor(*desc);
gOutputs.replaceValueFor(ioHandle, outputDesc);
} break;
case INPUT_OPENED:
case INPUT_CLOSED:
case INPUT_CONFIG_CHANGED:
break;
}
}
——————————-AudioSystem::AudioFlingerClient::ioConfigChanged———————————
} else {
if (gAudioErrorCallback) {
gAudioErrorCallback(NO_ERROR);
}
}
binder->linkToDeath(gAudioFlingerClient);
gAudioFlinger = interface_cast<IAudioFlinger>(binder);
gAudioFlinger->registerClient(gAudioFlingerClient);
}
LOGE_IF(gAudioFlinger==0, "no AudioFlinger!?");
return gAudioFlinger;
}
——————————-AudioSystem::get_audio_flinger———————————
———————————-AudioFlinger::registerClient——————————
}
}
———————————–AudioFlinger::audioConfigChanged_l—————————–
mAudioFlinger->mLock.unlock();
delete configEvent;
mLock.lock();
}
mLock.unlock();
}
———————————-AudioFlinger::ThreadBase::processConfigEvents——————————
LOGV("sendConfigEvent() num events %d event %d, param %d", mConfigEvents.size(), event, param);
mWaitWorkCV.signal();
}
———————————-AudioFlinger::ThreadBase::sendConfigEvent_l——————————
}
}
mNewParameters.removeAt(0);
mParamStatus = status;
mParamCond.signal();
mWaitWorkCV.wait(mLock);
}
return reconfig;
}
——————————-AudioFlinger::MixerThread::checkForNewParameters_l———————————
mWaitWorkCV.signal();
// wait condition with timeout in case the thread loop has exited
// before the request could be processed
if (mParamCond.waitRelative(mLock, seconds(2)) == NO_ERROR) {
status = mParamStatus;
mWaitWorkCV.signal();
} else {
status = TIMED_OUT;
}
return status;
}
——————————-AudioFlinger::ThreadBase::setParameters———————————
#ifdef LVMX
if ((NO_ERROR == result) && (device != -1)) {
LifeVibes::setDevice(LifeVibes::threadIdToAudioOutputType(thread->id()), device);
}
#endif
return result;
}
return BAD_VALUE;
}
——————————–AudioFlinger::setParameters——————————–
}
——————————–AudioSystem::setParameters——————————–
if (command->mWaitStatus) {
command->mCond.signal();
mWaitWorkCV.wait(mLock);
}
delete data;
}break;
——————————–case SET_PARAMETERS——————————–
LOGV("AudioCommandThread() adding set parameter string %s, io %d ,delay %d",
keyValuePairs.string(), ioHandle, delayMs);
mWaitWorkCV.signal();
if (command->mWaitStatus) {
command->mCond.wait(mLock);
status = command->mStatus;
mWaitWorkCV.signal();
}
return status;
}
—————————–AudioPolicyService::AudioCommandThread::parametersCommand———————————–
}
—————————–AudioPolicyService::setParameters———————————–
// update stream volumes according to new device
applyStreamVolumes(output, device, delayMs);
+++++++++++++++++++++++++++++AudioPolicyManagerBase::applyStreamVolumes+++++++++++++++++++++++++++++++++++
void AudioPolicyManagerBase::applyStreamVolumes(audio_io_handle_t output, uint32_t device, int delayMs)
{
LOGV("applyStreamVolumes() for output %d and device %x", output, device);
for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
checkAndSetVolume(stream, mStreams[stream].mIndexCur, output, device, delayMs);
}
}
—————————–AudioPolicyManagerBase::applyStreamVolumes———————————–
// if changing from a combined headset + speaker route, unmute media streams
if (output == mHardwareOutput && AudioSystem::popCount(prevDevice) == 2) {
setStrategyMute(STRATEGY_MEDIA, false, output, delayMs);
}
}
—————————-AudioPolicyManagerBase::setOutputDevice————————————
// handle special case for sonification while in call
if (isInCall()) {
handleIncallSonification(stream, true, false);
}
// apply volume rules for current stream and device if necessary
checkAndSetVolume(stream, mStreams[stream].mIndexCur, output, outputDesc->device());
return NO_ERROR;
}
—————————AudioPolicyManagerBase::startOutput————————————-
}
———————–AudioPolicyService::startOutput—————————————–
}
———————–AudioSystem::startOutput—————————————–
thread->mLock.lock();
}
if (status == NO_ERROR) {
PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
playbackThread->addTrack_l(this);
} else {
mState = state;
}
} else {
status = BAD_VALUE;
}
return status;
}
—————————-AudioFlinger::PlaybackThread::Track::start————————————
}
—————————AudioFlinger::TrackHandle::start————————————-
}
// 如果狀態為DEAD_OBJECT,則創建一個AudioTrack對象,並調用其start函數
if (status == DEAD_OBJECT) {
LOGV("start() dead IAudioTrack: creating a new one");
status = createTrack(mStreamType, mCblk->sampleRate, mFormat, mChannelCount,
mFrameCount, mFlags, mSharedBuffer, getOutput(), false);
if (status == NO_ERROR) {
status = mAudioTrack->start();
if (status == NO_ERROR) {
mNewPosition = mCblk->server + mUpdatePeriod;
}
}
}
if (status != NO_ERROR) {
LOGV("start() failed");
android_atomic_and(~1, &mActive);
if (t != 0) {
t->requestExit();
} else {
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_NORMAL);
}
}
}
if (t != 0) {
t->mLock.unlock();
}
}
——————————AudioTrack::start———————————-
}
——————————android_media_AudioTrack_start———————————-
mPlayState = PLAYSTATE_PLAYING;
}
}
——————————play———————————-
Thread.sleep(100);
log(TEST_NAME, "position ="+ track.getPlaybackHeadPosition());
assertTrue(TEST_NAME, track.getPlaybackHeadPosition() > 0);
//——– tear down ————–
track.release();
}
###########################################################
摘自:江風的專欄