/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "FastCapture" //#define LOG_NDEBUG 0 #define ATRACE_TAG ATRACE_TAG_AUDIO #include "Configuration.h" #include #include #include #include #include #include "AudioFlinger.h" #include "FastCapture.h" namespace android { /*static*/ const FastCaptureState FastCapture::sInitial; FastCapture::FastCapture() : FastThread(), mInputSource(NULL), mInputSourceGen(0), mPipeSink(NULL), mPipeSinkGen(0), mReadBuffer(NULL), mReadBufferState(-1), mFormat(Format_Invalid), mSampleRate(0), // mDummyDumpState mTotalNativeFramesRead(0) { mPrevious = &sInitial; mCurrent = &sInitial; mDummyDumpState = &mDummyFastCaptureDumpState; } FastCapture::~FastCapture() { } FastCaptureStateQueue* FastCapture::sq() { return &mSQ; } const FastThreadState *FastCapture::poll() { return mSQ.poll(); } void FastCapture::setLog(NBLog::Writer *logWriter __unused) { } void FastCapture::onIdle() { mPreIdle = *(const FastCaptureState *)mCurrent; mCurrent = &mPreIdle; } void FastCapture::onExit() { free(mReadBuffer); } bool FastCapture::isSubClassCommand(FastThreadState::Command command) { switch ((FastCaptureState::Command) command) { case FastCaptureState::READ: case FastCaptureState::WRITE: case FastCaptureState::READ_WRITE: return true; default: return false; } } void FastCapture::onStateChange() { const FastCaptureState * const current = (const FastCaptureState *) mCurrent; const FastCaptureState * const previous = (const FastCaptureState *) mPrevious; FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) mDumpState; const size_t frameCount = current->mFrameCount; bool eitherChanged = false; // check for change in input HAL configuration NBAIO_Format previousFormat = mFormat; if (current->mInputSourceGen != mInputSourceGen) { mInputSource = current->mInputSource; mInputSourceGen = current->mInputSourceGen; if (mInputSource == NULL) { mFormat = Format_Invalid; mSampleRate = 0; } else { mFormat = mInputSource->format(); mSampleRate = Format_sampleRate(mFormat); unsigned channelCount = Format_channelCount(mFormat); ALOG_ASSERT(channelCount >= 1 && channelCount <= 8); } dumpState->mSampleRate = mSampleRate; eitherChanged = true; } // check for change in pipe if (current->mPipeSinkGen != mPipeSinkGen) { mPipeSink = current->mPipeSink; mPipeSinkGen = current->mPipeSinkGen; eitherChanged = true; } // input source and pipe sink must be compatible if (eitherChanged && mInputSource != NULL && mPipeSink != NULL) { ALOG_ASSERT(Format_isEqual(mFormat, mPipeSink->format())); } if ((!Format_isEqual(mFormat, previousFormat)) || (frameCount != previous->mFrameCount)) { // FIXME to avoid priority inversion, don't free here free(mReadBuffer); mReadBuffer = NULL; if (frameCount > 0 && mSampleRate > 0) { // FIXME new may block for unbounded time at internal mutex of the heap // implementation; it would be better to have normal capture thread allocate for // us to avoid blocking here and to prevent possible priority inversion size_t bufferSize = frameCount * Format_frameSize(mFormat); (void)posix_memalign(&mReadBuffer, 32, bufferSize); memset(mReadBuffer, 0, bufferSize); // if posix_memalign fails, will segv here. mPeriodNs = (frameCount * 1000000000LL) / mSampleRate; // 1.00 mUnderrunNs = (frameCount * 1750000000LL) / mSampleRate; // 1.75 mOverrunNs = (frameCount * 500000000LL) / mSampleRate; // 0.50 mForceNs = (frameCount * 950000000LL) / mSampleRate; // 0.95 mWarmupNsMin = (frameCount * 750000000LL) / mSampleRate; // 0.75 mWarmupNsMax = (frameCount * 1250000000LL) / mSampleRate; // 1.25 } else { mPeriodNs = 0; mUnderrunNs = 0; mOverrunNs = 0; mForceNs = 0; mWarmupNsMin = 0; mWarmupNsMax = LONG_MAX; } mReadBufferState = -1; dumpState->mFrameCount = frameCount; } } void FastCapture::onWork() { const FastCaptureState * const current = (const FastCaptureState *) mCurrent; FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) mDumpState; const FastCaptureState::Command command = mCommand; const size_t frameCount = current->mFrameCount; if ((command & FastCaptureState::READ) /*&& isWarm*/) { ALOG_ASSERT(mInputSource != NULL); ALOG_ASSERT(mReadBuffer != NULL); dumpState->mReadSequence++; ATRACE_BEGIN("read"); ssize_t framesRead = mInputSource->read(mReadBuffer, frameCount, AudioBufferProvider::kInvalidPTS); ATRACE_END(); dumpState->mReadSequence++; if (framesRead >= 0) { LOG_ALWAYS_FATAL_IF((size_t) framesRead > frameCount); mTotalNativeFramesRead += framesRead; dumpState->mFramesRead = mTotalNativeFramesRead; mReadBufferState = framesRead; } else { dumpState->mReadErrors++; mReadBufferState = 0; } // FIXME rename to attemptedIO mAttemptedWrite = true; } if (command & FastCaptureState::WRITE) { ALOG_ASSERT(mPipeSink != NULL); ALOG_ASSERT(mReadBuffer != NULL); if (mReadBufferState < 0) { unsigned channelCount = Format_channelCount(mFormat); memset(mReadBuffer, 0, frameCount * Format_frameSize(mFormat)); mReadBufferState = frameCount; } if (mReadBufferState > 0) { ssize_t framesWritten = mPipeSink->write(mReadBuffer, mReadBufferState); // FIXME This supports at most one fast capture client. // To handle multiple clients this could be converted to an array, // or with a lot more work the control block could be shared by all clients. audio_track_cblk_t* cblk = current->mCblk; if (cblk != NULL && framesWritten > 0) { int32_t rear = cblk->u.mStreaming.mRear; android_atomic_release_store(framesWritten + rear, &cblk->u.mStreaming.mRear); cblk->mServer += framesWritten; int32_t old = android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex); if (!(old & CBLK_FUTEX_WAKE)) { // client is never in server process, so don't use FUTEX_WAKE_PRIVATE (void) syscall(__NR_futex, &cblk->mFutex, FUTEX_WAKE, 1); } } } } } } // namespace android