音频流的实现
打开音频流时分配了音频流跑起来所需的大部分资源,在应用程序请求启动音频流时,音频流会真正地跑起来。AAudio 请求启动音频流的接口实现 (位于 frameworks/av/media/libaaudio/src/core/AAudioAudio.cpp) 如下:
AAUDIO_API aaudio_result_t AAudioStream_requestStart(AAudioStream* stream)
{
AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
aaudio_stream_id_t id = audioStream->getId();
ALOGD("%s(s#%u) called --------------", __func__, id);
aaudio_result_t result = audioStream->systemStart();
ALOGD("%s(s#%u) returned %d ---------", __func__, id, result);
return result;
}
这个函数调用 AudioStream::systemStart()
函数,后者的定义 (位于 frameworks/av/media/libaaudio/src/core/AudioStream.cpp) 如下:
aaudio_result_t AudioStream::systemStart() {
if (collidesWithCallback()) {
ALOGE("%s cannot be called from a callback!", __func__);
return AAUDIO_ERROR_INVALID_STATE;
}
std::lock_guard<std::mutex> lock(mStreamLock);
switch (getState()) {
// Is this a good time to start?
case AAUDIO_STREAM_STATE_OPEN:
case AAUDIO_STREAM_STATE_PAUSING:
case AAUDIO_STREAM_STATE_PAUSED:
case AAUDIO_STREAM_STATE_STOPPING:
case AAUDIO_STREAM_STATE_STOPPED:
case AAUDIO_STREAM_STATE_FLUSHING:
case AAUDIO_STREAM_STATE_FLUSHED:
break; // Proceed with starting.
// Already started?
case AAUDIO_STREAM_STATE_STARTING:
case AAUDIO_STREAM_STATE_STARTED:
ALOGW("%s() stream was already started, state = %s", __func__,
AudioGlobal_convertStreamStateToText(getState()));
return AAUDIO_ERROR_INVALID_STATE;
// Don't start when the stream is dead!
case AAUDIO_STREAM_STATE_DISCONNECTED:
case AAUDIO_STREAM_STATE_CLOSING:
case AAUDIO_STREAM_STATE_CLOSED:
default:
ALOGW("%s() stream is dead, state = %s", __func__,
AudioGlobal_convertStreamStateToText(getState()));
return AAUDIO_ERROR_INVALID_STATE;
}
aaudio_result_t result = requestStart_l();
if (result == AAUDIO_OK) {
// We only call this for logging in "dumpsys audio". So ignore return code.
(void) mPlayerBase->startWithStatus(getDeviceId());
}
return result;
}
AudioStream::systemStart()
函数检查音频流的当前状态;当状态允许启动时,则请求具体的音频流执行启动动作,即 requestStart_l()
。AudioStreamInternal::requestStart_l()
函数的定义 (位于 frameworks/av/media/libaaudio/src/client/AudioStreamInternal.cpp) 如下:
aaudio_result_t AudioStreamInternal::requestStart_l()
{
int64_t startTime;
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
ALOGD("requestStart() mServiceStreamHandle invalid");
return AAUDIO_ERROR_INVALID_STATE;
}
if (isActive()) {
ALOGD("requestStart() already active");
return AAUDIO_ERROR_INVALID_STATE;
}
aaudio_stream_state_t originalState = getState();
if (originalState == AAUDIO_STREAM_STATE_DISCONNECTED) {
ALOGD("requestStart() but DISCONNECTED");
return AAUDIO_ERROR_DISCONNECTED;
}
setState(AAUDIO_STREAM_STATE_STARTING);
// Clear any stale timestamps from the previous run.
drainTimestampsFromService();
prepareBuffersForStart(); // tell subclasses to get ready
aaudio_result_t result = mServiceInterface.startStream(mServiceStreamHandle);
if (result == AAUDIO_ERROR_INVALID_HANDLE) {
ALOGD("%s() INVALID_HANDLE, stream was probably stolen", __func__);
// Stealing was added in R. Coerce result to improve backward compatibility.
result = AAUDIO_ERROR_DISCONNECTED;
setState(AAUDIO_STREAM_STATE_DISCONNECTED);
}
startTime = AudioClock::getNanoseconds();
mClockModel.start(startTime);
mNeedCatchUp.request(); // Ask data processing code to catch up when first timestamp received.
// Start data callback thread.
if (result == AAUDIO_OK && isDataCallbackSet()) {
// Launch the callback loop thread.
int64_t periodNanos = mCallbackFrames
* AAUDIO_NANOS_PER_SECOND
/ getSampleRate();
mCallbackEnabled.store(true);
result = createThread_l(periodNanos, aaudio_callback_thread_proc, this);
}
if (result != AAUDIO_OK) {
setState(originalState);
}
return result;
}
. . . . . .
aaudio_result_t AudioStreamInternal::drainTimestampsFromService() {
aaudio_result_t result = AAUDIO_OK;
while (result == AAUDIO_OK) {
AAudioServiceMessage message;
if (!mAudioEndpoint) {
break;
}
if (mAudioEndpoint->readUpCommand(&message) != 1) {
break; // no command this time, no problem
}
switch (message.what) {
// ignore most messages
case AAudioServiceMessage::code::TIMESTAMP_SERVICE:
case AAudioServiceMessage::code::TIMESTAMP_HARDWARE:
break;
case AAudioServiceMessage::code::EVENT:
result = onEventFromServer(&message);
break;
default:
ALOGE("%s - unrecognized message.what = %d", __func__, (int) message.what);
result = AAUDIO_ERROR_INTERNAL;
break;
}
}
return result;
}
这个函数的执行过程如下:
- 检查音频流的当前状态并设置当前状态为
AAUDIO_STREAM_STATE_STARTING
; - 清空服务端发送过来的消息;
- 请求音频流子类为启动做好准备,准备好缓冲区;
- 通过 AAudio 服务接口请求
media.aaudio
服务启动音频流; - 将当前时间设置为同步时钟模型的启动时间,以方便预测给定时间点的音频流位置;
- 音频流的数据传递有推模式和拉模式两种,一种是应用程序通过音频流提供的
read()
/write()
操作同步地将音频数据取出送入音频流;另一种是为音频流设置一个数据回调,此时 AAudio 为音频流起一个线程,先通过回调与应用程序完成数据传递,之后再将数据写入或读出数据流。当设置了数据回调时,计算回调执行的周期,请求启动一个线程。
为音频流启动的线程执行的任务 (位于 frameworks/av/media/libaaudio/src/client/AudioStreamInternal.cpp) 如下:
static void *aaudio_callback_thread_proc(void *context)
{
AudioStreamInternal *stream = (AudioStreamInternal *)context;
//LOGD("oboe_callback_thread, stream = %p", stream);
if (stream != NULL) {
return stream->callbackLoop();
} else {
return NULL;
}
}
播放音频流的回调循环 (位于 frameworks/av/media/libaaudio/src/client/AudioStreamInternalPlay.cpp) 是这样的:
void *AudioStreamInternalPlay::callbackLoop() {
ALOGD("%s() entering >>>>>>>>>>>>>>>", __func__);
aaudio_result_t result = AAUDIO_OK;
aaudio_data_callback_result_t callbackResult = AAUDIO_CALLBACK_RESULT_CONTINUE;
if (!isDataCallbackSet()) return NULL;
int64_t timeoutNanos = calculateReasonableTimeout(mCallbackFrames);
// result might be a frame count
while (mCallbackEnabled.load() && isActive() && (result >= 0)) {
// Call application using the AAudio callback interface.
callbackResult = maybeCallDataCallback(mCallbackBuffer.get(), mCallbackFrames);
if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE) {
// Write audio data to stream. This is a BLOCKING WRITE!
result = write(mCallbackBuffer.get(), mCallbackFrames, timeoutNanos);
if ((result != mCallbackFrames)) {
if (result >= 0) {
// Only wrote some of the frames requested. Must have timed out.
result = AAUDIO_ERROR_TIMEOUT;
}
maybeCallErrorCallback(result);
break;
}
} else if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) {
ALOGD("%s(): callback returned AAUDIO_CALLBACK_RESULT_STOP", __func__);
result = systemStopInternal();
break;
}
}
ALOGD("%s() exiting, result = %d, isActive() = %d <<<<<<<<<<<<<<",
__func__, result, (int) isActive());
return NULL;
}
它不断向应用程序请求音频数据,获得的音频数据被保存在一块缓冲区中,之后被写入音频流。
采集音频流的回调循环 (位于 frameworks/av/media/libaaudio/src/client/AudioStreamInternalCapture.cpp) 是这样的:
void *AudioStreamInternalCapture::callbackLoop() {
aaudio_result_t result = AAUDIO_OK;
aaudio_data_callback_result_t callbackResult = AAUDIO_CALLBACK_RESULT_CONTINUE;
if (!isDataCallbackSet()) return NULL;
// result might be a frame count
while (mCallbackEnabled.load() && isActive() && (result >= 0)) {
// Read audio data from stream.
int64_t timeoutNanos = calculateReasonableTimeout(mCallbackFrames);
// This is a BLOCKING READ!
result = read(mCallbackBuffer.get(), mCallbackFrames, timeoutNanos);
if ((result != mCallbackFrames)) {
ALOGE("callbackLoop: read() returned %d", result);
if (result >= 0) {
// Only read some of the frames requested. Must have timed out.
result = AAUDIO_ERROR_TIMEOUT;
}
maybeCallErrorCallback(result);
break;
}
// Call application using the AAudio callback interface.
callbackResult = maybeCallDataCallback(mCallbackBuffer.get(), mCallbackFrames);
if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) {
ALOGD("%s(): callback returned AAUDIO_CALLBACK_RESULT_STOP", __func__);
result = systemStopInternal();
break;
}
}
ALOGD("callbackLoop() exiting, result = %d, isActive() = %d",
result, (int) isActive());
return NULL;
}
它不断从音频流读取数据,随后通过回调函数传给应用程序。
AudioStream
创建线程的过程 (位于 frameworks/av/media/libaaudio/src/core/AudioStream.cpp) 如下:
void* AudioStream::wrapUserThread() {
void* procResult = nullptr;
mThreadRegistrationResult = registerThread();
if (mThreadRegistrationResult == AAUDIO_OK) {
// Run callback loop. This may take a very long time.
procResult = mThreadProc(mThreadArg);
mThreadRegistrationResult = unregisterThread();
}
return procResult;
}
// This is the entry point for the new thread created by createThread_l().
// It converts the 'C' function call to a C++ method call.
static void* AudioStream_internalThreadProc(void* threadArg) {
AudioStream *audioStream = (AudioStream *) threadArg;
// Prevent the stream from being deleted while being used.
// This is just for extra safety. It is probably not needed because
// this callback should be joined before the stream is closed.
android::sp<AudioStream> protectedStream(audioStream);
// Balance the incStrong() in createThread_l().
protectedStream->decStrong(nullptr);
return protectedStream->wrapUserThread();
}
// This is not exposed in the API.
// But it is still used internally to implement callbacks for MMAP mode.
aaudio_result_t AudioStream::createThread_l(int64_t periodNanoseconds,
aaudio_audio_thread_proc_t threadProc,
void* threadArg)
{
if (mHasThread) {
ALOGD("%s() - previous thread was not joined, join now to be safe", __func__);
joinThread_l(nullptr);
}
if (threadProc == nullptr) {
return AAUDIO_ERROR_NULL;
}
// Pass input parameters to the background thread.
mThreadProc = threadProc;
mThreadArg = threadArg;
setPeriodNanoseconds(periodNanoseconds);
mHasThread = true;
// Prevent this object from getting deleted before the thread has a chance to create
// its strong pointer. Assume the thread will call decStrong().
this->incStrong(nullptr);
int err = pthread_create(&mThread, nullptr, AudioStream_internalThreadProc, this);
if (err != 0) {
android::status_t status = -errno;
ALOGE("%s() - pthread_create() failed, %d", __func__, status);
this->decStrong(nullptr); // Because the thread won't do it.
mHasThread = false;
return AAudioConvert_androidToAAudioResult(status);
} else {
// TODO Use AAudioThread or maybe AndroidThread
// Name the thread with an increasing index, "AAudio_#", for debugging.
static std::atomic<uint32_t> nextThreadIndex{1};
char name[16]; // max length for a pthread_name
uint32_t index = nextThreadIndex++;
// Wrap the index so that we do not hit the 16 char limit
// and to avoid hard-to-read large numbers.
index = index % 100000; // arbitrary
snprintf(name, sizeof(name), "AAudio_%u", index);
err = pthread_setname_np(mThread, name);
ALOGW_IF((err != 0), "Could not set name of AAudio thread. err = %d", err);
return AAUDIO_OK;
}
}
AudioStream
通过 pthread_create()
创建一个线程,执行任务 AudioStream_internalThreadProc
,然后设置线程的名字。新创建的线程跑起来之后,首先注册当前线程,随后运行创建线程时丢进来的任务,也就是 AAudio 音频流的回调循环。注册当前线程会将线程信息发送给 media.aaudio
服务 (位于 frameworks/av/media/libaaudio/src/client/AudioStreamInternal.cpp) :
aaudio_result_t AudioStreamInternal::registerThread() {
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
ALOGW("%s() mServiceStreamHandle invalid", __func__);
return AAUDIO_ERROR_INVALID_STATE;
}
return mServiceInterface.registerAudioThread(mServiceStreamHandle,
gettid(),
getPeriodNanoseconds());
}
尽管请求 AudioStream
创建线程时传入了周期时长,但周期执行不是在 AudioStream
中处理的。
音频流数据传递的同步模式中,应用程序向 AAudio 的音频流写入数据来将音频数据播放出来。向音频流写入音频数据的接口实现 (位于 frameworks/av/media/libaaudio/src/core/AAudioAudio.cpp) 如下:
AAUDIO_API aaudio_result_t AAudioStream_write(AAudioStream* stream,
const void *buffer,
int32_t numFrames,
int64_t timeoutNanoseconds)
{
AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
if (buffer == nullptr) {
return AAUDIO_ERROR_NULL;
}
// Don't allow writes when playing with a callback.
if (audioStream->isDataCallbackActive()) {
// A developer requested this warning because it would have saved lots of debugging.
ALOGW("%s() - Cannot write to a callback stream when running.", __func__);
return AAUDIO_ERROR_INVALID_STATE;
}
if (numFrames < 0) {
return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
} else if (numFrames == 0) {
return 0;
}
aaudio_result_t result = audioStream->write(buffer, numFrames, timeoutNanoseconds);
return result;
}
这个函数检查参数以及音频流是否工作在同步模式下,随后通过音频流对象的 write()
操作写入音频数据,和异步模式的回调循环中使用相同的操作。write()
操作由具体的音频流实现,对于音频播放流,为 AudioStreamInternalPlay::write()
,该函数定义 (位于 frameworks/av/media/libaaudio/src/client/AudioStreamInternalPlay.cpp) 如下:
aaudio_result_t AudioStreamInternalPlay::write(const void *buffer, int32_t numFrames,
int64_t timeoutNanoseconds) {
return processData((void *)buffer, numFrames, timeoutNanoseconds);
}
AudioStreamInternalPlay::write()
通过 AudioStreamInternal::processData()
执行数据处理循环,并执行必要的等待 (位于 frameworks/av/media/libaaudio/src/client/AudioStreamInternal.cpp),直到所有数据都处理完,或最大等待时间到了:
aaudio_result_t AudioStreamInternal::processData(void *buffer, int32_t numFrames,
int64_t timeoutNanoseconds)
{
const char * traceName = "aaProc";
const char * fifoName = "aaRdy";
ATRACE_BEGIN(traceName);
if (ATRACE_ENABLED()) {
int32_t fullFrames = mAudioEndpoint->getFullFramesAvailable();
ATRACE_INT(fifoName, fullFrames);
}
aaudio_result_t result = AAUDIO_OK;
int32_t loopCount = 0;
uint8_t* audioData = (uint8_t*)buffer;
int64_t currentTimeNanos = AudioClock::getNanoseconds();
const int64_t entryTimeNanos = currentTimeNanos;
const int64_t deadlineNanos = currentTimeNanos + timeoutNanoseconds;
int32_t framesLeft = numFrames;
// Loop until all the data has been processed or until a timeout occurs.
while (framesLeft > 0) {
// The call to processDataNow() will not block. It will just process as much as it can.
int64_t wakeTimeNanos = 0;
aaudio_result_t framesProcessed = processDataNow(audioData, framesLeft,
currentTimeNanos, &wakeTimeNanos);
if (framesProcessed < 0) {
result = framesProcessed;
break;
}
framesLeft -= (int32_t) framesProcessed;
audioData += framesProcessed * getBytesPerFrame();
// Should we block?
if (timeoutNanoseconds == 0) {
break; // don't block
} else if (wakeTimeNanos != 0) {
if (!mAudioEndpoint->isFreeRunning()) {
// If there is software on the other end of the FIFO then it may get delayed.
// So wake up just a little after we expect it to be ready.
wakeTimeNanos += mWakeupDelayNanos;
}
currentTimeNanos = AudioClock::getNanoseconds();
int64_t earliestWakeTime = currentTimeNanos + mMinimumSleepNanos;
// Guarantee a minimum sleep time.
if (wakeTimeNanos < earliestWakeTime) {
wakeTimeNanos = earliestWakeTime;
}
if (wakeTimeNanos > deadlineNanos) {
// If we time out, just return the framesWritten so far.
// TODO remove after we fix the deadline bug
ALOGW("processData(): entered at %lld nanos, currently %lld",
(long long) entryTimeNanos, (long long) currentTimeNanos);
ALOGW("processData(): TIMEOUT after %lld nanos",
(long long) timeoutNanoseconds);
ALOGW("processData(): wakeTime = %lld, deadline = %lld nanos",
(long long) wakeTimeNanos, (long long) deadlineNanos);
ALOGW("processData(): past deadline by %d micros",
(int)((wakeTimeNanos - deadlineNanos) / AAUDIO_NANOS_PER_MICROSECOND));
mClockModel.dump();
mAudioEndpoint->dump();
break;
}
if (ATRACE_ENABLED()) {
int32_t fullFrames = mAudioEndpoint->getFullFramesAvailable();
ATRACE_INT(fifoName, fullFrames);
int64_t sleepForNanos = wakeTimeNanos - currentTimeNanos;
ATRACE_INT("aaSlpNs", (int32_t)sleepForNanos);
}
AudioClock::sleepUntilNanoTime(wakeTimeNanos);
currentTimeNanos = AudioClock::getNanoseconds();
}
}
if (ATRACE_ENABLED()) {
int32_t fullFrames = mAudioEndpoint->getFullFramesAvailable();
ATRACE_INT(fifoName, fullFrames);
}
// return error or framesProcessed
(void) loopCount;
ATRACE_END();
return (result < 0) ? result : numFrames - framesLeft;
}
AudioStreamInternal::processData()
的数据处理循环通过其子类的 processDataNow()
处理数据,对于播放音频流,processDataNow()
执行写数据的动作,而对于采集音频流 processDataNow()
执行读数据的动作。processDataNow()
的执行不阻塞,它只处理尽可能多的数据。
播放音频流的 AudioStreamInternalPlay::processDataNow()
函数定义 (位于 frameworks/av/media/libaaudio/src/client/AudioStreamInternalPlay.cpp)如下:
aaudio_result_t AudioStreamInternalPlay::processDataNow(void *buffer, int32_t numFrames,
int64_t currentNanoTime, int64_t *wakeTimePtr) {
aaudio_result_t result = processCommands();
if (result != AAUDIO_OK) {
return result;
}
const char *traceName = "aaWrNow";
ATRACE_BEGIN(traceName);
if (mClockModel.isStarting()) {
// Still haven't got any timestamps from server.
// Keep waiting until we get some valid timestamps then start writing to the
// current buffer position.
ALOGV("%s() wait for valid timestamps", __func__);
// Sleep very briefly and hope we get a timestamp soon.
*wakeTimePtr = currentNanoTime + (2000 * AAUDIO_NANOS_PER_MICROSECOND);
ATRACE_END();
return 0;
}
// If we have gotten this far then we have at least one timestamp from server.
// If a DMA channel or DSP is reading the other end then we have to update the readCounter.
if (mAudioEndpoint->isFreeRunning()) {
// Update data queue based on the timing model.
int64_t estimatedReadCounter = mClockModel.convertTimeToPosition(currentNanoTime);
// ALOGD("AudioStreamInternal::processDataNow() - estimatedReadCounter = %d", (int)estimatedReadCounter);
mAudioEndpoint->setDataReadCounter(estimatedReadCounter);
}
if (mNeedCatchUp.isRequested()) {
// Catch an MMAP pointer that is already advancing.
// This will avoid initial underruns caused by a slow cold start.
// We add a one burst margin in case the DSP advances before we can write the data.
// This can help prevent the beginning of the stream from being skipped.
advanceClientToMatchServerPosition(getFramesPerBurst());
mNeedCatchUp.acknowledge();
}
// If the read index passed the write index then consider it an underrun.
// For shared streams, the xRunCount is passed up from the service.
if (mAudioEndpoint->isFreeRunning() && mAudioEndpoint->getFullFramesAvailable() < 0) {
mXRunCount++;
if (ATRACE_ENABLED()) {
ATRACE_INT("aaUnderRuns", mXRunCount);
}
}
// Write some data to the buffer.
//ALOGD("AudioStreamInternal::processDataNow() - writeNowWithConversion(%d)", numFrames);
int32_t framesWritten = writeNowWithConversion(buffer, numFrames);
//ALOGD("AudioStreamInternal::processDataNow() - tried to write %d frames, wrote %d",
// numFrames, framesWritten);
if (ATRACE_ENABLED()) {
ATRACE_INT("aaWrote", framesWritten);
}
// Sleep if there is too much data in the buffer.
// Calculate an ideal time to wake up.
if (wakeTimePtr != nullptr
&& (mAudioEndpoint->getFullFramesAvailable() >= getBufferSize())) {
// By default wake up a few milliseconds from now. // TODO review
int64_t wakeTime = currentNanoTime + (1 * AAUDIO_NANOS_PER_MILLISECOND);
aaudio_stream_state_t state = getState();
//ALOGD("AudioStreamInternal::processDataNow() - wakeTime based on %s",
// AAudio_convertStreamStateToText(state));
switch (state) {
case AAUDIO_STREAM_STATE_OPEN:
case AAUDIO_STREAM_STATE_STARTING:
if (framesWritten != 0) {
// Don't wait to write more data. Just prime the buffer.
wakeTime = currentNanoTime;
}
break;
case AAUDIO_STREAM_STATE_STARTED:
{
// Sleep until the readCounter catches up and we only have
// the getBufferSize() frames of data sitting in the buffer.
int64_t nextReadPosition = mAudioEndpoint->getDataWriteCounter() - getBufferSize();
wakeTime = mClockModel.convertPositionToTime(nextReadPosition);
}
break;
default:
break;
}
*wakeTimePtr = wakeTime;
}
ATRACE_END();
return framesWritten;
}
这个函数首先调用 processCommands()
处理 media.aaudio
服务发送过来的命令,随后主要执行 writeNowWithConversion()
写入音频数据,随后计算等待时间并返回。AudioStreamInternalPlay::writeNowWithConversion()
函数从我们前面介绍过的 fifo 拿一块内存,处理音频数据,将处理之后的音频数据写入内存区域,并移动写计数器:
aaudio_result_t AudioStreamInternalPlay::writeNowWithConversion(const void *buffer,
int32_t numFrames) {
WrappingBuffer wrappingBuffer;
uint8_t *byteBuffer = (uint8_t *) buffer;
int32_t framesLeft = numFrames;
mAudioEndpoint->getEmptyFramesAvailable(&wrappingBuffer);
// Write data in one or two parts.
int partIndex = 0;
while (framesLeft > 0 && partIndex < WrappingBuffer::SIZE) {
int32_t framesToWrite = framesLeft;
int32_t framesAvailable = wrappingBuffer.numFrames[partIndex];
if (framesAvailable > 0) {
if (framesToWrite > framesAvailable) {
framesToWrite = framesAvailable;
}
int32_t numBytes = getBytesPerFrame() * framesToWrite;
mFlowGraph.process((void *)byteBuffer,
wrappingBuffer.data[partIndex],
framesToWrite);
byteBuffer += numBytes;
framesLeft -= framesToWrite;
} else {
break;
}
partIndex++;
}
int32_t framesWritten = numFrames - framesLeft;
mAudioEndpoint->advanceWriteIndex(framesWritten);
return framesWritten;
}
音频流数据传递的同步模式中,应用程序从 AAudio 的音频流读取数据来获得采集的音频数据。从音频流读取数据的接口实现 (位于 frameworks/av/media/libaaudio/src/core/AAudioAudio.cpp) 如下:
AAUDIO_API aaudio_result_t AAudioStream_read(AAudioStream* stream,
void *buffer,
int32_t numFrames,
int64_t timeoutNanoseconds)
{
AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
if (buffer == nullptr) {
return AAUDIO_ERROR_NULL;
}
if (numFrames < 0) {
return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
} else if (numFrames == 0) {
return 0;
}
aaudio_result_t result = audioStream->read(buffer, numFrames, timeoutNanoseconds);
return result;
}
这个函数检查参数,随后通过音频流对象的 read()
操作读取音频数据,和异步模式的回调循环中使用相同的操作。read()
操作由具体的音频流实现,对于音频采集流,为 AudioStreamInternalCapture::read()
,该函数定义 (位于 frameworks/av/media/libaaudio/src/client/AudioStreamInternalCapture.cpp) 如下:
aaudio_result_t AudioStreamInternalCapture::read(void *buffer, int32_t numFrames,
int64_t timeoutNanoseconds)
{
return processData(buffer, numFrames, timeoutNanoseconds);
}
和 AudioStreamInternalPlay::write()
一样,这里同样通过 AudioStreamInternal::processData()
执行数据处理循环,并执行必要的等待,直到所有数据都处理完,或到达最大等待时间。AudioStreamInternalCapture
的 processDataNow()
定义 (位于 frameworks/av/media/libaaudio/src/client/AudioStreamInternalCapture.cpp) 如下:
// Read as much data as we can without blocking.
aaudio_result_t AudioStreamInternalCapture::processDataNow(void *buffer, int32_t numFrames,
int64_t currentNanoTime, int64_t *wakeTimePtr) {
aaudio_result_t result = processCommands();
if (result != AAUDIO_OK) {
return result;
}
const char *traceName = "aaRdNow";
ATRACE_BEGIN(traceName);
if (mClockModel.isStarting()) {
// Still haven't got any timestamps from server.
// Keep waiting until we get some valid timestamps then start writing to the
// current buffer position.
ALOGD("processDataNow() wait for valid timestamps");
// Sleep very briefly and hope we get a timestamp soon.
*wakeTimePtr = currentNanoTime + (2000 * AAUDIO_NANOS_PER_MICROSECOND);
ATRACE_END();
return 0;
}
// If we have gotten this far then we have at least one timestamp from server.
if (mAudioEndpoint->isFreeRunning()) {
//ALOGD("AudioStreamInternalCapture::processDataNow() - update remote counter");
// Update data queue based on the timing model.
// Jitter in the DSP can cause late writes to the FIFO.
// This might be caused by resampling.
// We want to read the FIFO after the latest possible time
// that the DSP could have written the data.
int64_t estimatedRemoteCounter = mClockModel.convertLatestTimeToPosition(currentNanoTime);
// TODO refactor, maybe use setRemoteCounter()
mAudioEndpoint->setDataWriteCounter(estimatedRemoteCounter);
}
// This code assumes that we have already received valid timestamps.
if (mNeedCatchUp.isRequested()) {
// Catch an MMAP pointer that is already advancing.
// This will avoid initial underruns caused by a slow cold start.
advanceClientToMatchServerPosition();
mNeedCatchUp.acknowledge();
}
// If the capture buffer is full beyond capacity then consider it an overrun.
// For shared streams, the xRunCount is passed up from the service.
if (mAudioEndpoint->isFreeRunning()
&& mAudioEndpoint->getFullFramesAvailable() > mAudioEndpoint->getBufferCapacityInFrames()) {
mXRunCount++;
if (ATRACE_ENABLED()) {
ATRACE_INT("aaOverRuns", mXRunCount);
}
}
// Read some data from the buffer.
//ALOGD("AudioStreamInternalCapture::processDataNow() - readNowWithConversion(%d)", numFrames);
int32_t framesProcessed = readNowWithConversion(buffer, numFrames);
//ALOGD("AudioStreamInternalCapture::processDataNow() - tried to read %d frames, read %d",
// numFrames, framesProcessed);
if (ATRACE_ENABLED()) {
ATRACE_INT("aaRead", framesProcessed);
}
// Calculate an ideal time to wake up.
if (wakeTimePtr != nullptr && framesProcessed >= 0) {
// By default wake up a few milliseconds from now. // TODO review
int64_t wakeTime = currentNanoTime + (1 * AAUDIO_NANOS_PER_MILLISECOND);
aaudio_stream_state_t state = getState();
//ALOGD("AudioStreamInternalCapture::processDataNow() - wakeTime based on %s",
// AAudio_convertStreamStateToText(state));
switch (state) {
case AAUDIO_STREAM_STATE_OPEN:
case AAUDIO_STREAM_STATE_STARTING:
break;
case AAUDIO_STREAM_STATE_STARTED:
{
// When do we expect the next write burst to occur?
// Calculate frame position based off of the readCounter because
// the writeCounter might have just advanced in the background,
// causing us to sleep until a later burst.
int64_t nextPosition = mAudioEndpoint->getDataReadCounter() + getFramesPerBurst();
wakeTime = mClockModel.convertPositionToLatestTime(nextPosition);
}
break;
default:
break;
}
*wakeTimePtr = wakeTime;
}
ATRACE_END();
return framesProcessed;
}
这个函数同样首先调用 processCommands()
处理 media.aaudio
服务发送过来的命令,随后主要执行 readNowWithConversion()
读取音频数据,随后计算等待时间并返回。AudioStreamInternalCapture::readNowWithConversion()
函数从我们前面介绍过的 fifo 拿一块内存,从中将采集的音频数据复制出来,并移动读计数器:
aaudio_result_t AudioStreamInternalCapture::readNowWithConversion(void *buffer,
int32_t numFrames) {
// ALOGD("readNowWithConversion(%p, %d)",
// buffer, numFrames);
WrappingBuffer wrappingBuffer;
uint8_t *destination = (uint8_t *) buffer;
int32_t framesLeft = numFrames;
mAudioEndpoint->getFullFramesAvailable(&wrappingBuffer);
// Read data in one or two parts.
for (int partIndex = 0; framesLeft > 0 && partIndex < WrappingBuffer::SIZE; partIndex++) {
int32_t framesToProcess = framesLeft;
const int32_t framesAvailable = wrappingBuffer.numFrames[partIndex];
if (framesAvailable <= 0) break;
if (framesToProcess > framesAvailable) {
framesToProcess = framesAvailable;
}
const int32_t numBytes = getBytesPerFrame() * framesToProcess;
const int32_t numSamples = framesToProcess * getSamplesPerFrame();
const audio_format_t sourceFormat = getDeviceFormat();
const audio_format_t destinationFormat = getFormat();
memcpy_by_audio_format(destination, destinationFormat,
wrappingBuffer.data[partIndex], sourceFormat, numSamples);
destination += numBytes;
framesLeft -= framesToProcess;
}
int32_t framesProcessed = numFrames - framesLeft;
mAudioEndpoint->advanceReadIndex(framesProcessed);
//ALOGD("readNowWithConversion() returns %d", framesProcessed);
return framesProcessed;
}
音频流的关键操作实现大体如此。
Done.
网友评论