| Glenn Kasten | f91df1b | 2014-03-13 14:59:31 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * Copyright (C) 2014 The Android Open Source Project | 
 | 3 |  * | 
 | 4 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 | 5 |  * you may not use this file except in compliance with the License. | 
 | 6 |  * You may obtain a copy of the License at | 
 | 7 |  * | 
 | 8 |  *      http://www.apache.org/licenses/LICENSE-2.0 | 
 | 9 |  * | 
 | 10 |  * Unless required by applicable law or agreed to in writing, software | 
 | 11 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 | 12 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | 13 |  * See the License for the specific language governing permissions and | 
 | 14 |  * limitations under the License. | 
 | 15 |  */ | 
 | 16 |  | 
 | 17 | #define LOG_TAG "FastCapture" | 
 | 18 | //#define LOG_NDEBUG 0 | 
 | 19 |  | 
 | 20 | #define ATRACE_TAG ATRACE_TAG_AUDIO | 
 | 21 |  | 
 | 22 | #include "Configuration.h" | 
 | 23 | #include <linux/futex.h> | 
 | 24 | #include <sys/syscall.h> | 
 | 25 | #include <media/AudioBufferProvider.h> | 
 | 26 | #include <utils/Log.h> | 
 | 27 | #include <utils/Trace.h> | 
 | 28 | #include "FastCapture.h" | 
 | 29 |  | 
 | 30 | namespace android { | 
 | 31 |  | 
| Glenn Kasten | e4a7ce2 | 2015-03-03 11:23:17 -0800 | [diff] [blame] | 32 | /*static*/ const FastCaptureState FastCapture::sInitial; | 
| Glenn Kasten | f91df1b | 2014-03-13 14:59:31 -0700 | [diff] [blame] | 33 |  | 
| Glenn Kasten | f9715e4 | 2016-07-13 14:02:03 -0700 | [diff] [blame] | 34 | FastCapture::FastCapture() : FastThread("cycleC_ms", "loadC_us"), | 
| Glenn Kasten | e4a7ce2 | 2015-03-03 11:23:17 -0800 | [diff] [blame] | 35 |     mInputSource(NULL), mInputSourceGen(0), mPipeSink(NULL), mPipeSinkGen(0), | 
 | 36 |     mReadBuffer(NULL), mReadBufferState(-1), mFormat(Format_Invalid), mSampleRate(0), | 
 | 37 |     // mDummyDumpState | 
 | 38 |     mTotalNativeFramesRead(0) | 
| Glenn Kasten | f91df1b | 2014-03-13 14:59:31 -0700 | [diff] [blame] | 39 | { | 
| Glenn Kasten | e4a7ce2 | 2015-03-03 11:23:17 -0800 | [diff] [blame] | 40 |     mPrevious = &sInitial; | 
 | 41 |     mCurrent = &sInitial; | 
| Glenn Kasten | f91df1b | 2014-03-13 14:59:31 -0700 | [diff] [blame] | 42 |  | 
| Glenn Kasten | e4a7ce2 | 2015-03-03 11:23:17 -0800 | [diff] [blame] | 43 |     mDummyDumpState = &mDummyFastCaptureDumpState; | 
| Glenn Kasten | f91df1b | 2014-03-13 14:59:31 -0700 | [diff] [blame] | 44 | } | 
 | 45 |  | 
 | 46 | FastCapture::~FastCapture() | 
 | 47 | { | 
 | 48 | } | 
 | 49 |  | 
 | 50 | FastCaptureStateQueue* FastCapture::sq() | 
 | 51 | { | 
 | 52 |     return &mSQ; | 
 | 53 | } | 
 | 54 |  | 
 | 55 | const FastThreadState *FastCapture::poll() | 
 | 56 | { | 
 | 57 |     return mSQ.poll(); | 
 | 58 | } | 
 | 59 |  | 
| Glenn Kasten | 3ab8d66 | 2017-04-03 14:35:09 -0700 | [diff] [blame] | 60 | void FastCapture::setNBLogWriter(NBLog::Writer *logWriter __unused) | 
| Glenn Kasten | f91df1b | 2014-03-13 14:59:31 -0700 | [diff] [blame] | 61 | { | 
 | 62 | } | 
 | 63 |  | 
 | 64 | void FastCapture::onIdle() | 
 | 65 | { | 
| Glenn Kasten | e4a7ce2 | 2015-03-03 11:23:17 -0800 | [diff] [blame] | 66 |     mPreIdle = *(const FastCaptureState *)mCurrent; | 
 | 67 |     mCurrent = &mPreIdle; | 
| Glenn Kasten | f91df1b | 2014-03-13 14:59:31 -0700 | [diff] [blame] | 68 | } | 
 | 69 |  | 
 | 70 | void FastCapture::onExit() | 
 | 71 | { | 
| Glenn Kasten | 5115777 | 2015-03-03 11:48:45 -0800 | [diff] [blame] | 72 |     free(mReadBuffer); | 
| Glenn Kasten | f91df1b | 2014-03-13 14:59:31 -0700 | [diff] [blame] | 73 | } | 
 | 74 |  | 
 | 75 | bool FastCapture::isSubClassCommand(FastThreadState::Command command) | 
 | 76 | { | 
 | 77 |     switch ((FastCaptureState::Command) command) { | 
 | 78 |     case FastCaptureState::READ: | 
 | 79 |     case FastCaptureState::WRITE: | 
 | 80 |     case FastCaptureState::READ_WRITE: | 
 | 81 |         return true; | 
 | 82 |     default: | 
 | 83 |         return false; | 
 | 84 |     } | 
 | 85 | } | 
 | 86 |  | 
 | 87 | void FastCapture::onStateChange() | 
 | 88 | { | 
| Glenn Kasten | 4dd03b5 | 2015-03-03 11:32:04 -0800 | [diff] [blame] | 89 |     const FastCaptureState * const current = (const FastCaptureState *) mCurrent; | 
 | 90 |     const FastCaptureState * const previous = (const FastCaptureState *) mPrevious; | 
 | 91 |     FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) mDumpState; | 
| Glenn Kasten | f91df1b | 2014-03-13 14:59:31 -0700 | [diff] [blame] | 92 |     const size_t frameCount = current->mFrameCount; | 
 | 93 |  | 
 | 94 |     bool eitherChanged = false; | 
 | 95 |  | 
 | 96 |     // check for change in input HAL configuration | 
| Glenn Kasten | e4a7ce2 | 2015-03-03 11:23:17 -0800 | [diff] [blame] | 97 |     NBAIO_Format previousFormat = mFormat; | 
 | 98 |     if (current->mInputSourceGen != mInputSourceGen) { | 
 | 99 |         mInputSource = current->mInputSource; | 
 | 100 |         mInputSourceGen = current->mInputSourceGen; | 
 | 101 |         if (mInputSource == NULL) { | 
 | 102 |             mFormat = Format_Invalid; | 
 | 103 |             mSampleRate = 0; | 
| Glenn Kasten | f91df1b | 2014-03-13 14:59:31 -0700 | [diff] [blame] | 104 |         } else { | 
| Glenn Kasten | e4a7ce2 | 2015-03-03 11:23:17 -0800 | [diff] [blame] | 105 |             mFormat = mInputSource->format(); | 
 | 106 |             mSampleRate = Format_sampleRate(mFormat); | 
| Glenn Kasten | 57c4e6f | 2016-03-18 14:54:07 -0700 | [diff] [blame] | 107 | #if !LOG_NDEBUG | 
| Glenn Kasten | e4a7ce2 | 2015-03-03 11:23:17 -0800 | [diff] [blame] | 108 |             unsigned channelCount = Format_channelCount(mFormat); | 
| Andy Hung | d330ee4 | 2015-04-20 13:23:41 -0700 | [diff] [blame] | 109 |             ALOG_ASSERT(channelCount >= 1 && channelCount <= FCC_8); | 
| Glenn Kasten | 57c4e6f | 2016-03-18 14:54:07 -0700 | [diff] [blame] | 110 | #endif | 
| Glenn Kasten | f91df1b | 2014-03-13 14:59:31 -0700 | [diff] [blame] | 111 |         } | 
| Glenn Kasten | e4a7ce2 | 2015-03-03 11:23:17 -0800 | [diff] [blame] | 112 |         dumpState->mSampleRate = mSampleRate; | 
| Glenn Kasten | f91df1b | 2014-03-13 14:59:31 -0700 | [diff] [blame] | 113 |         eitherChanged = true; | 
 | 114 |     } | 
 | 115 |  | 
 | 116 |     // check for change in pipe | 
| Glenn Kasten | e4a7ce2 | 2015-03-03 11:23:17 -0800 | [diff] [blame] | 117 |     if (current->mPipeSinkGen != mPipeSinkGen) { | 
 | 118 |         mPipeSink = current->mPipeSink; | 
 | 119 |         mPipeSinkGen = current->mPipeSinkGen; | 
| Glenn Kasten | f91df1b | 2014-03-13 14:59:31 -0700 | [diff] [blame] | 120 |         eitherChanged = true; | 
 | 121 |     } | 
 | 122 |  | 
 | 123 |     // input source and pipe sink must be compatible | 
| Glenn Kasten | e4a7ce2 | 2015-03-03 11:23:17 -0800 | [diff] [blame] | 124 |     if (eitherChanged && mInputSource != NULL && mPipeSink != NULL) { | 
 | 125 |         ALOG_ASSERT(Format_isEqual(mFormat, mPipeSink->format())); | 
| Glenn Kasten | f91df1b | 2014-03-13 14:59:31 -0700 | [diff] [blame] | 126 |     } | 
 | 127 |  | 
| Glenn Kasten | e4a7ce2 | 2015-03-03 11:23:17 -0800 | [diff] [blame] | 128 |     if ((!Format_isEqual(mFormat, previousFormat)) || (frameCount != previous->mFrameCount)) { | 
| Glenn Kasten | 5115777 | 2015-03-03 11:48:45 -0800 | [diff] [blame] | 129 |         // FIXME to avoid priority inversion, don't free here | 
 | 130 |         free(mReadBuffer); | 
| Glenn Kasten | e4a7ce2 | 2015-03-03 11:23:17 -0800 | [diff] [blame] | 131 |         mReadBuffer = NULL; | 
 | 132 |         if (frameCount > 0 && mSampleRate > 0) { | 
| Glenn Kasten | f91df1b | 2014-03-13 14:59:31 -0700 | [diff] [blame] | 133 |             // FIXME new may block for unbounded time at internal mutex of the heap | 
 | 134 |             //       implementation; it would be better to have normal capture thread allocate for | 
 | 135 |             //       us to avoid blocking here and to prevent possible priority inversion | 
| Andy Hung | 0a01c2f | 2015-09-21 12:44:54 -0700 | [diff] [blame] | 136 |             size_t bufferSize = frameCount * Format_frameSize(mFormat); | 
 | 137 |             (void)posix_memalign(&mReadBuffer, 32, bufferSize); | 
 | 138 |             memset(mReadBuffer, 0, bufferSize); // if posix_memalign fails, will segv here. | 
| Glenn Kasten | e4a7ce2 | 2015-03-03 11:23:17 -0800 | [diff] [blame] | 139 |             mPeriodNs = (frameCount * 1000000000LL) / mSampleRate;      // 1.00 | 
 | 140 |             mUnderrunNs = (frameCount * 1750000000LL) / mSampleRate;    // 1.75 | 
 | 141 |             mOverrunNs = (frameCount * 500000000LL) / mSampleRate;      // 0.50 | 
 | 142 |             mForceNs = (frameCount * 950000000LL) / mSampleRate;        // 0.95 | 
 | 143 |             mWarmupNsMin = (frameCount * 750000000LL) / mSampleRate;    // 0.75 | 
 | 144 |             mWarmupNsMax = (frameCount * 1250000000LL) / mSampleRate;   // 1.25 | 
| Glenn Kasten | f91df1b | 2014-03-13 14:59:31 -0700 | [diff] [blame] | 145 |         } else { | 
| Glenn Kasten | e4a7ce2 | 2015-03-03 11:23:17 -0800 | [diff] [blame] | 146 |             mPeriodNs = 0; | 
 | 147 |             mUnderrunNs = 0; | 
 | 148 |             mOverrunNs = 0; | 
 | 149 |             mForceNs = 0; | 
 | 150 |             mWarmupNsMin = 0; | 
 | 151 |             mWarmupNsMax = LONG_MAX; | 
| Glenn Kasten | f91df1b | 2014-03-13 14:59:31 -0700 | [diff] [blame] | 152 |         } | 
| Glenn Kasten | e4a7ce2 | 2015-03-03 11:23:17 -0800 | [diff] [blame] | 153 |         mReadBufferState = -1; | 
| Glenn Kasten | f91df1b | 2014-03-13 14:59:31 -0700 | [diff] [blame] | 154 |         dumpState->mFrameCount = frameCount; | 
 | 155 |     } | 
 | 156 |  | 
 | 157 | } | 
 | 158 |  | 
 | 159 | void FastCapture::onWork() | 
 | 160 | { | 
| Glenn Kasten | 4dd03b5 | 2015-03-03 11:32:04 -0800 | [diff] [blame] | 161 |     const FastCaptureState * const current = (const FastCaptureState *) mCurrent; | 
 | 162 |     FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) mDumpState; | 
 | 163 |     const FastCaptureState::Command command = mCommand; | 
| Glenn Kasten | f91df1b | 2014-03-13 14:59:31 -0700 | [diff] [blame] | 164 |     const size_t frameCount = current->mFrameCount; | 
 | 165 |  | 
 | 166 |     if ((command & FastCaptureState::READ) /*&& isWarm*/) { | 
| Glenn Kasten | e4a7ce2 | 2015-03-03 11:23:17 -0800 | [diff] [blame] | 167 |         ALOG_ASSERT(mInputSource != NULL); | 
 | 168 |         ALOG_ASSERT(mReadBuffer != NULL); | 
| Glenn Kasten | f91df1b | 2014-03-13 14:59:31 -0700 | [diff] [blame] | 169 |         dumpState->mReadSequence++; | 
 | 170 |         ATRACE_BEGIN("read"); | 
| Glenn Kasten | d79072e | 2016-01-06 08:41:20 -0800 | [diff] [blame] | 171 |         ssize_t framesRead = mInputSource->read(mReadBuffer, frameCount); | 
| Glenn Kasten | f91df1b | 2014-03-13 14:59:31 -0700 | [diff] [blame] | 172 |         ATRACE_END(); | 
 | 173 |         dumpState->mReadSequence++; | 
 | 174 |         if (framesRead >= 0) { | 
 | 175 |             LOG_ALWAYS_FATAL_IF((size_t) framesRead > frameCount); | 
| Glenn Kasten | e4a7ce2 | 2015-03-03 11:23:17 -0800 | [diff] [blame] | 176 |             mTotalNativeFramesRead += framesRead; | 
 | 177 |             dumpState->mFramesRead = mTotalNativeFramesRead; | 
 | 178 |             mReadBufferState = framesRead; | 
| Glenn Kasten | f91df1b | 2014-03-13 14:59:31 -0700 | [diff] [blame] | 179 |         } else { | 
 | 180 |             dumpState->mReadErrors++; | 
| Glenn Kasten | e4a7ce2 | 2015-03-03 11:23:17 -0800 | [diff] [blame] | 181 |             mReadBufferState = 0; | 
| Glenn Kasten | f91df1b | 2014-03-13 14:59:31 -0700 | [diff] [blame] | 182 |         } | 
 | 183 |         // FIXME rename to attemptedIO | 
| Glenn Kasten | e4a7ce2 | 2015-03-03 11:23:17 -0800 | [diff] [blame] | 184 |         mAttemptedWrite = true; | 
| Glenn Kasten | f91df1b | 2014-03-13 14:59:31 -0700 | [diff] [blame] | 185 |     } | 
 | 186 |  | 
 | 187 |     if (command & FastCaptureState::WRITE) { | 
| Glenn Kasten | e4a7ce2 | 2015-03-03 11:23:17 -0800 | [diff] [blame] | 188 |         ALOG_ASSERT(mPipeSink != NULL); | 
 | 189 |         ALOG_ASSERT(mReadBuffer != NULL); | 
 | 190 |         if (mReadBufferState < 0) { | 
| Glenn Kasten | 5115777 | 2015-03-03 11:48:45 -0800 | [diff] [blame] | 191 |             memset(mReadBuffer, 0, frameCount * Format_frameSize(mFormat)); | 
| Glenn Kasten | e4a7ce2 | 2015-03-03 11:23:17 -0800 | [diff] [blame] | 192 |             mReadBufferState = frameCount; | 
| Glenn Kasten | f91df1b | 2014-03-13 14:59:31 -0700 | [diff] [blame] | 193 |         } | 
| Glenn Kasten | e4a7ce2 | 2015-03-03 11:23:17 -0800 | [diff] [blame] | 194 |         if (mReadBufferState > 0) { | 
 | 195 |             ssize_t framesWritten = mPipeSink->write(mReadBuffer, mReadBufferState); | 
| Glenn Kasten | f91df1b | 2014-03-13 14:59:31 -0700 | [diff] [blame] | 196 |             // FIXME This supports at most one fast capture client. | 
 | 197 |             //       To handle multiple clients this could be converted to an array, | 
 | 198 |             //       or with a lot more work the control block could be shared by all clients. | 
 | 199 |             audio_track_cblk_t* cblk = current->mCblk; | 
 | 200 |             if (cblk != NULL && framesWritten > 0) { | 
 | 201 |                 int32_t rear = cblk->u.mStreaming.mRear; | 
 | 202 |                 android_atomic_release_store(framesWritten + rear, &cblk->u.mStreaming.mRear); | 
 | 203 |                 cblk->mServer += framesWritten; | 
 | 204 |                 int32_t old = android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex); | 
 | 205 |                 if (!(old & CBLK_FUTEX_WAKE)) { | 
 | 206 |                     // client is never in server process, so don't use FUTEX_WAKE_PRIVATE | 
 | 207 |                     (void) syscall(__NR_futex, &cblk->mFutex, FUTEX_WAKE, 1); | 
 | 208 |                 } | 
 | 209 |             } | 
 | 210 |         } | 
 | 211 |     } | 
 | 212 | } | 
 | 213 |  | 
| Glenn Kasten | f91df1b | 2014-03-13 14:59:31 -0700 | [diff] [blame] | 214 | }   // namespace android |