text: /*
   * 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 <linux/futex.h>
  #include <sys/syscall.h>
  #include <media/AudioBufferProvider.h>
  #include <utils/Log.h>
  #include <utils/Trace.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);
  #if !LOG_NDEBUG
              unsigned channelCount = Format_channelCount(mFormat);
              ALOG_ASSERT(channelCount >= 1 && channelCount <= FCC_8);
  #endif
          }
          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);
          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) {
              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
  