AudioTrack中write函數size疑問
最近在看《深入理解Android》中Audio相關部分時,看到AudioTrack::write函數的實現時,對其中操作的size有些疑問。
函數完整代碼如下:
ssize_t AudioTrack::write(const void* buffer, size_t userSize)
{
if (mSharedBuffer != 0) return INVALID_OPERATION;
if (mIsTimed) return INVALID_OPERATION;
if (ssize_t(userSize) < 0) {
// Sanity-check: user is most-likely passing an error code, and it would
// make the return value ambiguous (actualSize vs error).
ALOGE("AudioTrack::write(buffer=%p, size=%u (%d)",
buffer, userSize, userSize);
return BAD_VALUE;
}
ALOGV("write %p: %d bytes, mActive=%d", this, userSize, mActive);
if (userSize == 0) {
return 0;
}
// acquire a strong reference on the IMemory and IAudioTrack so that they cannot be destroyed
// while we are accessing the cblk
mLock.lock();
sp audioTrack = mAudioTrack;
sp iMem = mCblkMemory;
mLock.unlock();
ssize_t written = 0;
const int8_t *src = (const int8_t *)buffer;
Buffer audioBuffer;
size_t frameSz = frameSize();
do {
audioBuffer.frameCount = userSize/frameSz;
status_t err = obtainBuffer(&audioBuffer, -1);
if (err < 0) {
// out of buffers, return #bytes written
if (err == status_t(NO_MORE_BUFFERS))
break;
return ssize_t(err);
}
size_t toWrite;
if (mFormat == AUDIO_FORMAT_PCM_8_BIT && !(mFlags & AUDIO_OUTPUT_FLAG_DIRECT)) {
// Divide capacity by 2 to take expansion into account
toWrite = audioBuffer.size>>1;
memcpy_to_i16_from_u8(audioBuffer.i16, (const uint8_t *) src, toWrite);
} else {
// !!!疑問點!!!
toWrite = audioBuffer.size;
memcpy(audioBuffer.i8, src, toWrite);
src += toWrite;
}
userSize -= toWrite;
written += toWrite;
releaseBuffer(&audioBuffer);
} while (userSize >= frameSz);
return written;
}
疑問點就是上面代碼中標識出的疑問點。
因為audioBuffer是調用obtainBuffer擷取的,此處copy資料時只考慮到了audioBuffer的size,而沒考慮來源資料src的size,如果audioBuffer的size大於src的size,豈不是會copy到無效資料?
除非audioBuffer的size與src的size有一定關係。
看看obtainBuffer的實現(只列出相關部分):
status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
{
AutoMutex lock(mLock);
...
audio_track_cblk_t* cblk = mCblk;
// 關注點 1
uint32_t framesReq = audioBuffer->frameCount;
...
audioBuffer->frameCount = 0;
audioBuffer->size = 0;
uint32_t framesAvail = cblk->framesAvailable();
...
cblk->lock.unlock();
if (framesAvail == 0) {
cblk->lock.lock();
goto start_loop_here;
while (framesAvail == 0) {
// 迴圈嘗試擷取可寫的空間
...
// read the server count again
start_loop_here:
framesAvail = cblk->framesAvailable_l();
}
cblk->lock.unlock();
}
cblk->waitTimeMs = 0;
// 關注點 2
if (framesReq > framesAvail) {
framesReq = framesAvail;
}
uint32_t u = cblk->user;
uint32_t bufferEnd = cblk->userBase + cblk->frameCount;
// 關注點 3
if (framesReq > bufferEnd - u) {
framesReq = bufferEnd - u;
}
audioBuffer->flags = mMuted ? Buffer::MUTE : 0;
audioBuffer->channelCount = mChannelCount;
audioBuffer->frameCount = framesReq;
// 關注點 4
audioBuffer->size = framesReq * cblk->frameSize;
if (audio_is_linear_pcm(mFormat)) {
audioBuffer->format = AUDIO_FORMAT_PCM_16_BIT;
} else {
audioBuffer->format = mFormat;
}
audioBuffer->raw = (int8_t *)cblk->buffer(u);
active = mActive;
return active ? status_t(NO_ERROR) : status_t(STOPPED);
}
從上面的4個關注點可知,audioBuffer的size來源於framesReq,即audioBuffer->frameCount,當然中間設計到比較適配處理。
從函數AudioTrack::write的實現可知,audioBuffer->frameCount是根據src的size計算得來:
audioBuffer.frameCount = userSize/frameSz;
也就是說,audioBuffer的size最終來源於src的size。
並且根據上述關注點2、3的處理可知,audioBuffer的size小於或等於src的size。
因此之前的擔心點也就不用擔心了。