(四)Audio子系統之AudioRecord.read (三)Audio子系統之AudioRecord.startRecording
在上一篇文章《(三)Audio子系統之AudioRecord.startRecording》中已經介紹了AudioRecord如何開始錄製音訊,接下來,繼續分析AudioRecord方法中的read的實現
函式原型:
public int read(byte[] audioData, int offsetInBytes, int sizeInBytes)
作用:
從音訊硬體錄製緩衝區讀取資料,直接複製到指定緩衝區。
引數:
audioData:寫入的音訊錄製資料
offsetInBytes:audioData的起始偏移值,單位byte
sizeInBytes:讀取的最大位元組數
返回值:
讀入緩衝區的總byte數,如果物件屬性沒有初始化,則返回ERROR_INVALID_OPERATION
,如果引數不能解析成有效的資料或索引,則返回ERROR_BAD_VALUE
。 讀取的總byte數不會超過sizeInBytes
接下來進入系統分析具體實現
frameworks\base\media\java\android\media\AudioRecord.java
public int read(byte[] audioData, int offsetInBytes, int sizeInBytes) { if (mState != STATE_INITIALIZED) { return ERROR_INVALID_OPERATION; } if ( (audioData == null) || (offsetInBytes < 0 ) || (sizeInBytes < 0) || (offsetInBytes + sizeInBytes < 0) // detect integer overflow || (offsetInBytes + sizeInBytes > audioData.length)) { return ERROR_BAD_VALUE; } return native_read_in_byte_array(audioData, offsetInBytes, sizeInBytes); }
這裡我們只分析獲取byte[]型別的資料
frameworks\base\core\jni\android_media_AudioRecord.cpp
static jint android_media_AudioRecord_readInByteArray(JNIEnv *env, jobject thiz, jbyteArray javaAudioData, jint offsetInBytes, jint sizeInBytes) { jbyte* recordBuff = NULL; // get the audio recorder from which we'll read new audio samples sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); if (lpRecorder == NULL) { ALOGE("Unable to retrieve AudioRecord object, can't record"); return 0; } if (!javaAudioData) { ALOGE("Invalid Java array to store recorded audio, can't record"); return 0; } recordBuff = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL); if (recordBuff == NULL) { ALOGE("Error retrieving destination for recorded audio data, can't record"); return 0; } // read the new audio data from the native AudioRecord object ssize_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize(); ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes, sizeInBytes > (jint)recorderBuffSize ? (jint)recorderBuffSize : sizeInBytes ); env->ReleaseByteArrayElements(javaAudioData, recordBuff, 0); if (readSize < 0) { readSize = (jint)AUDIO_JAVA_INVALID_OPERATION; } return (jint) readSize; }這裡根據AudioRecord.cpp中的mFrameCount*mFrameSize 重新計算出Audio緩衝區大小,並與傳過來的size比較,選擇較小的那個數,然後再呼叫lpRecorder->read函式,需要注意的是mFrameCount這個值在呼叫openRecord_l函式的時候更新過,我在實際測試的時候發現mFrameCount重新計算過後為3072,而之前是2048,所以這裡傳過去的buffSize依然是4092 frameworks\av\media\libmedia\AudioRecord.cpp
ssize_t AudioRecord::read(void* buffer, size_t userSize) { if (mTransfer != TRANSFER_SYNC) { return INVALID_OPERATION; } if (ssize_t(userSize) < 0 || (buffer == NULL && userSize != 0)) { // sanity-check. user is most-likely passing an error code, and it would // make the return value ambiguous (actualSize vs error). ALOGE("AudioRecord::read(buffer=%p, size=%zu (%zu)", buffer, userSize, userSize); return BAD_VALUE; } ssize_t read = 0; Buffer audioBuffer; while (userSize >= mFrameSize) { audioBuffer.frameCount = userSize / mFrameSize; status_t err = obtainBuffer(&audioBuffer, &ClientProxy::kForever); if (err < 0) { if (read > 0) { break; } return ssize_t(err); } size_t bytesRead = audioBuffer.size; memcpy(buffer, audioBuffer.i8, bytesRead); buffer = ((char *) buffer) + bytesRead; userSize -= bytesRead; read += bytesRead; releaseBuffer(&audioBuffer); } return read; }
這個mFrameSize是通過channelCount*取樣精度所佔位元組計算得出的,所以每次通過obtainBuffer獲取共享記憶體中的資料,然後通過memcpy把資料拷貝到應用層的buffer中,直到把整個userSize都拷貝到buffer中為止。
這裡就詳細分析下obtainBuffer函式
status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, const struct timespec *requested, struct timespec *elapsed, size_t *nonContig) { // previous and new IAudioRecord sequence numbers are used to detect track re-creation uint32_t oldSequence = 0; uint32_t newSequence; Proxy::Buffer buffer; status_t status = NO_ERROR; static const int32_t kMaxTries = 5; int32_t tryCounter = kMaxTries; do { // obtainBuffer() is called with mutex unlocked, so keep extra references to these fields to // keep them from going away if another thread re-creates the track during obtainBuffer() sp<AudioRecordClientProxy> proxy; sp<IMemory> iMem; sp<IMemory> bufferMem; { // start of lock scope AutoMutex lock(mLock); newSequence = mSequence; // did previous obtainBuffer() fail due to media server death or voluntary invalidation? if (status == DEAD_OBJECT) { // re-create track, unless someone else has already done so if (newSequence == oldSequence) { status = restoreRecord_l("obtainBuffer"); if (status != NO_ERROR) { buffer.mFrameCount = 0; buffer.mRaw = NULL; buffer.mNonContig = 0; break; } } } oldSequence = newSequence; // Keep the extra references proxy = mProxy; iMem = mCblkMemory; bufferMem = mBufferMemory; // Non-blocking if track is stopped if (!mActive) { requested = &ClientProxy::kNonBlocking; } } // end of lock scope buffer.mFrameCount = audioBuffer->frameCount; // FIXME starts the requested timeout and elapsed over from scratch status = proxy->obtainBuffer(&buffer, requested, elapsed); } while ((status == DEAD_OBJECT) && (tryCounter-- > 0)); audioBuffer->frameCount = buffer.mFrameCount; audioBuffer->size = buffer.mFrameCount * mFrameSize; audioBuffer->raw = buffer.mRaw; if (nonContig != NULL) { *nonContig = buffer.mNonContig; } return status; }
在這個函式中的主要工作如下:
1.獲取AudioRecordClientProxy代理,mCblkMemory與mBufferMemory,他們是在構建AudioRecord物件的時候,通過AF端獲取的,即RecordThread::RecordTrack->getCblk()與RecordThread::RecordTrack->getBuffers()
2.在start中,AudioRecordThread.resume之前,mActive已經標記為true了,所以requested還是&ClientProxy::kForever;
3.呼叫proxy->obtainBuffer繼續獲取資料;
3.更新audioBuffer的frameCount,size以及pcm資料;
這裡繼續分析第3步:ClientProxy::obtainBuffer
frameworks\av\media\libmedia\AudioTrackShared.cpp
status_t ClientProxy::obtainBuffer(Buffer* buffer, const struct timespec *requested, struct timespec *elapsed) { LOG_ALWAYS_FATAL_IF(buffer == NULL || buffer->mFrameCount == 0); struct timespec total; // total elapsed time spent waiting total.tv_sec = 0; total.tv_nsec = 0; bool measure = elapsed != NULL; // whether to measure total elapsed time spent waiting status_t status; enum { TIMEOUT_ZERO, // requested == NULL || *requested == 0 TIMEOUT_INFINITE, // *requested == infinity TIMEOUT_FINITE, // 0 < *requested < infinity TIMEOUT_CONTINUE, // additional chances after TIMEOUT_FINITE } timeout; if (requested == NULL) { timeout = TIMEOUT_ZERO; } else if (requested->tv_sec == 0 && requested->tv_nsec == 0) { timeout = TIMEOUT_ZERO; } else if (requested->tv_sec == INT_MAX) { timeout = TIMEOUT_INFINITE; } else { timeout = TIMEOUT_FINITE; if (requested->tv_sec > 0 || requested->tv_nsec >= MEASURE_NS) { measure = true; } } struct timespec before; bool beforeIsValid = false; audio_track_cblk_t* cblk = mCblk; bool ignoreInitialPendingInterrupt = true; // check for shared memory corruption if (mIsShutdown) { status = NO_INIT; goto end; } for (;;) { int32_t flags = android_atomic_and(~CBLK_INTERRUPT, &cblk->mFlags); // check for track invalidation by server, or server death detection if (flags & CBLK_INVALID) { ALOGV("Track invalidated"); status = DEAD_OBJECT; goto end; } // check for obtainBuffer interrupted by client if (!ignoreInitialPendingInterrupt && (flags & CBLK_INTERRUPT)) { ALOGV("obtainBuffer() interrupted by client"); status = -EINTR; goto end; } ignoreInitialPendingInterrupt = false; // compute number of frames available to write (AudioTrack) or read (AudioRecord) int32_t front; int32_t rear; if (mIsOut) { // The barrier following the read of mFront is probably redundant. // We're about to perform a conditional branch based on 'filled', // which will force the processor to observe the read of mFront // prior to allowing data writes starting at mRaw. // However, the processor may support speculative execution, // and be unable to undo speculative writes into shared memory. // The barrier will prevent such speculative execution. front = android_atomic_acquire_load(&cblk->u.mStreaming.mFront); rear = cblk->u.mStreaming.mRear; } else { // On the other hand, this barrier is required. rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear); front = cblk->u.mStreaming.mFront; } ssize_t filled = rear - front; // pipe should not be overfull if (!(0 <= filled && (size_t) filled <= mFrameCount)) { if (mIsOut) { ALOGE("Shared memory control block is corrupt (filled=%zd, mFrameCount=%zu); " "shutting down", filled, mFrameCount); mIsShutdown = true; status = NO_INIT; goto end; } // for input, sync up on overrun filled = 0; cblk->u.mStreaming.mFront = rear; (void) android_atomic_or(CBLK_OVERRUN, &cblk->mFlags); } // don't allow filling pipe beyond the nominal size size_t avail = mIsOut ? mFrameCount - filled : filled; if (avail > 0) { // 'avail' may be non-contiguous, so return only the first contiguous chunk size_t part1; if (mIsOut) { rear &= mFrameCountP2 - 1; part1 = mFrameCountP2 - rear; } else { front &= mFrameCountP2 - 1; part1 = mFrameCountP2 - front; } if (part1 > avail) { part1 = avail; } if (part1 > buffer->mFrameCount) { part1 = buffer->mFrameCount; } buffer->mFrameCount = part1; buffer->mRaw = part1 > 0 ? &((char *) mBuffers)[(mIsOut ? rear : front) * mFrameSize] : NULL; buffer->mNonContig = avail - part1; mUnreleased = part1; status = NO_ERROR; break; } struct timespec remaining; const struct timespec *ts; switch (timeout) { case TIMEOUT_ZERO: status = WOULD_BLOCK; goto end; case TIMEOUT_INFINITE: ts = NULL; break; case TIMEOUT_FINITE: timeout = TIMEOUT_CONTINUE; if (MAX_SEC == 0) { ts = requested; break; } // fall through case TIMEOUT_CONTINUE: // FIXME we do not retry if requested < 10ms? needs documentation on this state machine if (!measure || requested->tv_sec < total.tv_sec || (requested->tv_sec == total.tv_sec && requested->tv_nsec <= total.tv_nsec)) { status = TIMED_OUT; goto end; } remaining.tv_sec = requested->tv_sec - total.tv_sec; if ((remaining.tv_nsec = requested->tv_nsec - total.tv_nsec) < 0) { remaining.tv_nsec += 1000000000; remaining.tv_sec++; } if (0 < MAX_SEC && MAX_SEC < remaining.tv_sec) { remaining.tv_sec = MAX_SEC; remaining.tv_nsec = 0; } ts = &remaining; break; default: LOG_ALWAYS_FATAL("obtainBuffer() timeout=%d", timeout); ts = NULL; break; } int32_t old = android_atomic_and(~CBLK_FUTEX_WAKE, &cblk->mFutex); if (!(old & CBLK_FUTEX_WAKE)) { if (measure && !beforeIsValid) { clock_gettime(CLOCK_MONOTONIC, &before); beforeIsValid = true; } errno = 0; (void) syscall(__NR_futex, &cblk->mFutex, mClientInServer ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, old & ~CBLK_FUTEX_WAKE, ts); // update total elapsed time spent waiting if (measure) { struct timespec after; clock_gettime(CLOCK_MONOTONIC, &after); total.tv_sec += after.tv_sec - before.tv_sec; long deltaNs = after.tv_nsec - before.tv_nsec; if (deltaNs < 0) { deltaNs += 1000000000; total.tv_sec--; } if ((total.tv_nsec += deltaNs) >= 1000000000) { total.tv_nsec -= 1000000000; total.tv_sec++; } before = after; beforeIsValid = true; } switch (errno) { case 0: // normal wakeup by server, or by binderDied() case EWOULDBLOCK: // benign race condition with server case EINTR: // wait was interrupted by signal or other spurious wakeup case ETIMEDOUT: // time-out expired // FIXME these error/non-0 status are being dropped break; default: status = errno; ALOGE("%s unexpected error %s", __func__, strerror(status)); goto end; } } } end: if (status != NO_ERROR) { buffer->mFrameCount = 0; buffer->mRaw = NULL; buffer->mNonContig = 0; mUnreleased = 0; } if (elapsed != NULL) { *elapsed = total; } if (requested == NULL) { requested = &kNonBlocking; } if (measure) { ALOGV("requested %ld.%03ld elapsed %ld.%03ld", requested->tv_sec, requested->tv_nsec / 1000000, total.tv_sec, total.tv_nsec / 1000000); } return status; }
這個函式的主要工作如下:
1.從ClientProxy::kForever的定義可知,這個tv_sec是INT_MAX,所以timeout為TIMEOUT_INFINITE;
2.從cblk中獲取到rear以及front,之前分析過了,這兩個指標是在RecordThread執行緒中維護的,他一邊在讀取pcm資料,一直在更新最新資料指標的位置;
3.如果發現獲取到的資料小於mFrameCount,或者沒有獲取到資料,那麼也就是表示應用讀的太快了,其實應該說RecordThread讀的太慢了導致,這時候也需要設定為OVERRUN,則更新cblk的指標,重置filled為0;
4.如果獲取到了資料,則把錄音資料放到mRaw中,把獲取到的資料大小放到mFrameCount中。
總結:
到這裡,整個獲取pcm資料的流程就結束了,到這裡我們應該能理解整個Audio系統中對AudioBuffer的管理策略了,即通過RecordThread執行緒把資料從硬體層讀取到IMemory中,然後應用層在去IMemory中去讀取。
由於作者內功有限,若文章中存在錯誤或不足的地方,還請給位大佬指出,不勝感激!