blob: 5c766497b186515de69d1523d3523818997e5178 [file] [log] [blame]
Glenn Kastenf91df1b2014-03-13 14:59:31 -07001/*
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"
jiabin01c8f562018-07-19 17:47:28 -070023#include <audio_utils/format.h>
Glenn Kastenf91df1b2014-03-13 14:59:31 -070024#include <linux/futex.h>
25#include <sys/syscall.h>
26#include <media/AudioBufferProvider.h>
27#include <utils/Log.h>
28#include <utils/Trace.h>
29#include "FastCapture.h"
30
31namespace android {
32
Glenn Kastene4a7ce22015-03-03 11:23:17 -080033/*static*/ const FastCaptureState FastCapture::sInitial;
Glenn Kastenf91df1b2014-03-13 14:59:31 -070034
Glenn Kastenf9715e42016-07-13 14:02:03 -070035FastCapture::FastCapture() : FastThread("cycleC_ms", "loadC_us"),
Andy Hungf0859f32023-05-25 16:28:04 -070036 mInputSource(nullptr), mInputSourceGen(0), mPipeSink(nullptr), mPipeSinkGen(0),
37 mReadBuffer(nullptr), mReadBufferState(-1), mFormat(Format_Invalid), mSampleRate(0),
Glenn Kastene4a7ce22015-03-03 11:23:17 -080038 // mDummyDumpState
39 mTotalNativeFramesRead(0)
Glenn Kastenf91df1b2014-03-13 14:59:31 -070040{
Glenn Kastene4a7ce22015-03-03 11:23:17 -080041 mPrevious = &sInitial;
42 mCurrent = &sInitial;
Glenn Kastenf91df1b2014-03-13 14:59:31 -070043
Glenn Kastene4a7ce22015-03-03 11:23:17 -080044 mDummyDumpState = &mDummyFastCaptureDumpState;
Glenn Kastenf91df1b2014-03-13 14:59:31 -070045}
46
Glenn Kastenf91df1b2014-03-13 14:59:31 -070047FastCaptureStateQueue* FastCapture::sq()
48{
49 return &mSQ;
50}
51
52const FastThreadState *FastCapture::poll()
53{
54 return mSQ.poll();
55}
56
Glenn Kasten3ab8d662017-04-03 14:35:09 -070057void FastCapture::setNBLogWriter(NBLog::Writer *logWriter __unused)
Glenn Kastenf91df1b2014-03-13 14:59:31 -070058{
59}
60
61void FastCapture::onIdle()
62{
Glenn Kastene4a7ce22015-03-03 11:23:17 -080063 mPreIdle = *(const FastCaptureState *)mCurrent;
64 mCurrent = &mPreIdle;
Glenn Kastenf91df1b2014-03-13 14:59:31 -070065}
66
67void FastCapture::onExit()
68{
Glenn Kasten51157772015-03-03 11:48:45 -080069 free(mReadBuffer);
Glenn Kastenf91df1b2014-03-13 14:59:31 -070070}
71
72bool FastCapture::isSubClassCommand(FastThreadState::Command command)
73{
74 switch ((FastCaptureState::Command) command) {
75 case FastCaptureState::READ:
76 case FastCaptureState::WRITE:
77 case FastCaptureState::READ_WRITE:
78 return true;
79 default:
80 return false;
81 }
82}
83
84void FastCapture::onStateChange()
85{
Glenn Kasten4dd03b52015-03-03 11:32:04 -080086 const FastCaptureState * const current = (const FastCaptureState *) mCurrent;
87 const FastCaptureState * const previous = (const FastCaptureState *) mPrevious;
88 FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) mDumpState;
Glenn Kastenf91df1b2014-03-13 14:59:31 -070089 const size_t frameCount = current->mFrameCount;
90
91 bool eitherChanged = false;
92
93 // check for change in input HAL configuration
Andy Hungf0859f32023-05-25 16:28:04 -070094 const NBAIO_Format previousFormat = mFormat;
Glenn Kastene4a7ce22015-03-03 11:23:17 -080095 if (current->mInputSourceGen != mInputSourceGen) {
96 mInputSource = current->mInputSource;
97 mInputSourceGen = current->mInputSourceGen;
Andy Hungf0859f32023-05-25 16:28:04 -070098 if (mInputSource == nullptr) {
Glenn Kastene4a7ce22015-03-03 11:23:17 -080099 mFormat = Format_Invalid;
100 mSampleRate = 0;
Glenn Kastenf91df1b2014-03-13 14:59:31 -0700101 } else {
Glenn Kastene4a7ce22015-03-03 11:23:17 -0800102 mFormat = mInputSource->format();
103 mSampleRate = Format_sampleRate(mFormat);
Glenn Kasten57c4e6f2016-03-18 14:54:07 -0700104#if !LOG_NDEBUG
Glenn Kastene4a7ce22015-03-03 11:23:17 -0800105 unsigned channelCount = Format_channelCount(mFormat);
Andy Hung936845a2021-06-08 00:09:06 -0700106 ALOG_ASSERT(channelCount >= 1 && channelCount <= FCC_LIMIT);
Glenn Kasten57c4e6f2016-03-18 14:54:07 -0700107#endif
Glenn Kastenf91df1b2014-03-13 14:59:31 -0700108 }
Glenn Kastene4a7ce22015-03-03 11:23:17 -0800109 dumpState->mSampleRate = mSampleRate;
Glenn Kastenf91df1b2014-03-13 14:59:31 -0700110 eitherChanged = true;
111 }
112
113 // check for change in pipe
Glenn Kastene4a7ce22015-03-03 11:23:17 -0800114 if (current->mPipeSinkGen != mPipeSinkGen) {
115 mPipeSink = current->mPipeSink;
116 mPipeSinkGen = current->mPipeSinkGen;
Glenn Kastenf91df1b2014-03-13 14:59:31 -0700117 eitherChanged = true;
118 }
119
120 // input source and pipe sink must be compatible
Andy Hungf0859f32023-05-25 16:28:04 -0700121 if (eitherChanged && mInputSource != nullptr && mPipeSink != nullptr) {
Glenn Kastene4a7ce22015-03-03 11:23:17 -0800122 ALOG_ASSERT(Format_isEqual(mFormat, mPipeSink->format()));
Glenn Kastenf91df1b2014-03-13 14:59:31 -0700123 }
124
Glenn Kastene4a7ce22015-03-03 11:23:17 -0800125 if ((!Format_isEqual(mFormat, previousFormat)) || (frameCount != previous->mFrameCount)) {
Glenn Kasten51157772015-03-03 11:48:45 -0800126 // FIXME to avoid priority inversion, don't free here
127 free(mReadBuffer);
Andy Hungf0859f32023-05-25 16:28:04 -0700128 mReadBuffer = nullptr;
Glenn Kastene4a7ce22015-03-03 11:23:17 -0800129 if (frameCount > 0 && mSampleRate > 0) {
Glenn Kastenf91df1b2014-03-13 14:59:31 -0700130 // FIXME new may block for unbounded time at internal mutex of the heap
131 // implementation; it would be better to have normal capture thread allocate for
132 // us to avoid blocking here and to prevent possible priority inversion
Andy Hungf0859f32023-05-25 16:28:04 -0700133 const size_t bufferSize = frameCount * Format_frameSize(mFormat);
Andy Hung0a01c2f2015-09-21 12:44:54 -0700134 (void)posix_memalign(&mReadBuffer, 32, bufferSize);
135 memset(mReadBuffer, 0, bufferSize); // if posix_memalign fails, will segv here.
Glenn Kastene4a7ce22015-03-03 11:23:17 -0800136 mPeriodNs = (frameCount * 1000000000LL) / mSampleRate; // 1.00
137 mUnderrunNs = (frameCount * 1750000000LL) / mSampleRate; // 1.75
138 mOverrunNs = (frameCount * 500000000LL) / mSampleRate; // 0.50
139 mForceNs = (frameCount * 950000000LL) / mSampleRate; // 0.95
140 mWarmupNsMin = (frameCount * 750000000LL) / mSampleRate; // 0.75
141 mWarmupNsMax = (frameCount * 1250000000LL) / mSampleRate; // 1.25
Glenn Kastenf91df1b2014-03-13 14:59:31 -0700142 } else {
Glenn Kastene4a7ce22015-03-03 11:23:17 -0800143 mPeriodNs = 0;
144 mUnderrunNs = 0;
145 mOverrunNs = 0;
146 mForceNs = 0;
147 mWarmupNsMin = 0;
148 mWarmupNsMax = LONG_MAX;
Glenn Kastenf91df1b2014-03-13 14:59:31 -0700149 }
Glenn Kastene4a7ce22015-03-03 11:23:17 -0800150 mReadBufferState = -1;
Glenn Kastenf91df1b2014-03-13 14:59:31 -0700151 dumpState->mFrameCount = frameCount;
152 }
Eric Laurent33403f02020-05-29 18:35:06 -0700153 dumpState->mSilenced = current->mSilenceCapture;
Glenn Kastenf91df1b2014-03-13 14:59:31 -0700154}
155
156void FastCapture::onWork()
157{
Glenn Kasten4dd03b52015-03-03 11:32:04 -0800158 const FastCaptureState * const current = (const FastCaptureState *) mCurrent;
159 FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) mDumpState;
160 const FastCaptureState::Command command = mCommand;
jiabin01c8f562018-07-19 17:47:28 -0700161 size_t frameCount = current->mFrameCount;
162 AudioBufferProvider* fastPatchRecordBufferProvider = current->mFastPatchRecordBufferProvider;
163 AudioBufferProvider::Buffer patchBuffer;
164
Andy Hungf0859f32023-05-25 16:28:04 -0700165 if (fastPatchRecordBufferProvider != nullptr) {
jiabin01c8f562018-07-19 17:47:28 -0700166 patchBuffer.frameCount = ~0;
Andy Hungf0859f32023-05-25 16:28:04 -0700167 const status_t status = fastPatchRecordBufferProvider->getNextBuffer(&patchBuffer);
jiabin01c8f562018-07-19 17:47:28 -0700168 if (status != NO_ERROR) {
169 frameCount = 0;
170 } else if (patchBuffer.frameCount < frameCount) {
171 // TODO: Make sure that it doesn't cause any issues if we just get a small available
172 // buffer from the buffer provider.
173 frameCount = patchBuffer.frameCount;
174 }
175 }
Glenn Kastenf91df1b2014-03-13 14:59:31 -0700176
177 if ((command & FastCaptureState::READ) /*&& isWarm*/) {
Andy Hungf0859f32023-05-25 16:28:04 -0700178 ALOG_ASSERT(mInputSource != nullptr);
179 ALOG_ASSERT(mReadBuffer != nullptr);
Glenn Kastenf91df1b2014-03-13 14:59:31 -0700180 dumpState->mReadSequence++;
181 ATRACE_BEGIN("read");
Andy Hungf0859f32023-05-25 16:28:04 -0700182 const ssize_t framesRead = mInputSource->read(mReadBuffer, frameCount);
Glenn Kastenf91df1b2014-03-13 14:59:31 -0700183 ATRACE_END();
184 dumpState->mReadSequence++;
185 if (framesRead >= 0) {
186 LOG_ALWAYS_FATAL_IF((size_t) framesRead > frameCount);
Glenn Kastene4a7ce22015-03-03 11:23:17 -0800187 mTotalNativeFramesRead += framesRead;
188 dumpState->mFramesRead = mTotalNativeFramesRead;
189 mReadBufferState = framesRead;
jiabin01c8f562018-07-19 17:47:28 -0700190 patchBuffer.frameCount = framesRead;
Glenn Kastenf91df1b2014-03-13 14:59:31 -0700191 } else {
192 dumpState->mReadErrors++;
Glenn Kastene4a7ce22015-03-03 11:23:17 -0800193 mReadBufferState = 0;
Glenn Kastenf91df1b2014-03-13 14:59:31 -0700194 }
195 // FIXME rename to attemptedIO
Glenn Kastene4a7ce22015-03-03 11:23:17 -0800196 mAttemptedWrite = true;
Glenn Kastenf91df1b2014-03-13 14:59:31 -0700197 }
198
199 if (command & FastCaptureState::WRITE) {
Andy Hungf0859f32023-05-25 16:28:04 -0700200 ALOG_ASSERT(mPipeSink != nullptr);
201 ALOG_ASSERT(mReadBuffer != nullptr);
Glenn Kastene4a7ce22015-03-03 11:23:17 -0800202 if (mReadBufferState < 0) {
Glenn Kasten51157772015-03-03 11:48:45 -0800203 memset(mReadBuffer, 0, frameCount * Format_frameSize(mFormat));
Glenn Kastene4a7ce22015-03-03 11:23:17 -0800204 mReadBufferState = frameCount;
Glenn Kastenf91df1b2014-03-13 14:59:31 -0700205 }
Glenn Kastene4a7ce22015-03-03 11:23:17 -0800206 if (mReadBufferState > 0) {
Eric Laurent33403f02020-05-29 18:35:06 -0700207 if (current->mSilenceCapture) {
208 memset(mReadBuffer, 0, mReadBufferState * Format_frameSize(mFormat));
209 }
Andy Hungf0859f32023-05-25 16:28:04 -0700210 const ssize_t framesWritten = mPipeSink->write(mReadBuffer, mReadBufferState);
Glenn Kastenf91df1b2014-03-13 14:59:31 -0700211 audio_track_cblk_t* cblk = current->mCblk;
Andy Hungf0859f32023-05-25 16:28:04 -0700212 if (fastPatchRecordBufferProvider != nullptr) {
jiabin01c8f562018-07-19 17:47:28 -0700213 // This indicates the fast track is a patch record, update the cblk by
214 // calling releaseBuffer().
215 memcpy_by_audio_format(patchBuffer.raw, current->mFastPatchRecordFormat,
216 mReadBuffer, mFormat.mFormat, framesWritten * mFormat.mChannelCount);
217 patchBuffer.frameCount = framesWritten;
218 fastPatchRecordBufferProvider->releaseBuffer(&patchBuffer);
Andy Hungf0859f32023-05-25 16:28:04 -0700219 } else if (cblk != nullptr && framesWritten > 0) {
jiabin01c8f562018-07-19 17:47:28 -0700220 // FIXME This supports at most one fast capture client.
221 // To handle multiple clients this could be converted to an array,
222 // or with a lot more work the control block could be shared by all clients.
Andy Hungf0859f32023-05-25 16:28:04 -0700223 const int32_t rear = cblk->u.mStreaming.mRear;
Glenn Kastenf91df1b2014-03-13 14:59:31 -0700224 android_atomic_release_store(framesWritten + rear, &cblk->u.mStreaming.mRear);
225 cblk->mServer += framesWritten;
Andy Hungf0859f32023-05-25 16:28:04 -0700226 const int32_t old = android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex);
Glenn Kastenf91df1b2014-03-13 14:59:31 -0700227 if (!(old & CBLK_FUTEX_WAKE)) {
228 // client is never in server process, so don't use FUTEX_WAKE_PRIVATE
229 (void) syscall(__NR_futex, &cblk->mFutex, FUTEX_WAKE, 1);
230 }
231 }
232 }
233 }
234}
235
Glenn Kastenf91df1b2014-03-13 14:59:31 -0700236} // namespace android