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
30namespace android {
31
32/*static*/ const FastCaptureState FastCapture::sInitial;
33
34FastCapture::FastCapture() : FastThread(),
35    mInputSource(NULL), mInputSourceGen(0), mPipeSink(NULL), mPipeSinkGen(0),
36    mReadBuffer(NULL), mReadBufferState(-1), mFormat(Format_Invalid), mSampleRate(0),
37    // mDummyDumpState
38    mTotalNativeFramesRead(0)
39{
40    mPrevious = &sInitial;
41    mCurrent = &sInitial;
42
43    mDummyDumpState = &mDummyFastCaptureDumpState;
44}
45
46FastCapture::~FastCapture()
47{
48}
49
50FastCaptureStateQueue* FastCapture::sq()
51{
52    return &mSQ;
53}
54
55const FastThreadState *FastCapture::poll()
56{
57    return mSQ.poll();
58}
59
60void FastCapture::setLog(NBLog::Writer *logWriter __unused)
61{
62}
63
64void FastCapture::onIdle()
65{
66    mPreIdle = *(const FastCaptureState *)mCurrent;
67    mCurrent = &mPreIdle;
68}
69
70void FastCapture::onExit()
71{
72    free(mReadBuffer);
73}
74
75bool 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
87void FastCapture::onStateChange()
88{
89    const FastCaptureState * const current = (const FastCaptureState *) mCurrent;
90    const FastCaptureState * const previous = (const FastCaptureState *) mPrevious;
91    FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) mDumpState;
92    const size_t frameCount = current->mFrameCount;
93
94    bool eitherChanged = false;
95
96    // check for change in input HAL configuration
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;
104        } else {
105            mFormat = mInputSource->format();
106            mSampleRate = Format_sampleRate(mFormat);
107#if !LOG_NDEBUG
108            unsigned channelCount = Format_channelCount(mFormat);
109            ALOG_ASSERT(channelCount >= 1 && channelCount <= FCC_8);
110#endif
111        }
112        dumpState->mSampleRate = mSampleRate;
113        eitherChanged = true;
114    }
115
116    // check for change in pipe
117    if (current->mPipeSinkGen != mPipeSinkGen) {
118        mPipeSink = current->mPipeSink;
119        mPipeSinkGen = current->mPipeSinkGen;
120        eitherChanged = true;
121    }
122
123    // input source and pipe sink must be compatible
124    if (eitherChanged && mInputSource != NULL && mPipeSink != NULL) {
125        ALOG_ASSERT(Format_isEqual(mFormat, mPipeSink->format()));
126    }
127
128    if ((!Format_isEqual(mFormat, previousFormat)) || (frameCount != previous->mFrameCount)) {
129        // FIXME to avoid priority inversion, don't free here
130        free(mReadBuffer);
131        mReadBuffer = NULL;
132        if (frameCount > 0 && mSampleRate > 0) {
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
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.
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
145        } else {
146            mPeriodNs = 0;
147            mUnderrunNs = 0;
148            mOverrunNs = 0;
149            mForceNs = 0;
150            mWarmupNsMin = 0;
151            mWarmupNsMax = LONG_MAX;
152        }
153        mReadBufferState = -1;
154        dumpState->mFrameCount = frameCount;
155    }
156
157}
158
159void FastCapture::onWork()
160{
161    const FastCaptureState * const current = (const FastCaptureState *) mCurrent;
162    FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) mDumpState;
163    const FastCaptureState::Command command = mCommand;
164    const size_t frameCount = current->mFrameCount;
165
166    if ((command & FastCaptureState::READ) /*&& isWarm*/) {
167        ALOG_ASSERT(mInputSource != NULL);
168        ALOG_ASSERT(mReadBuffer != NULL);
169        dumpState->mReadSequence++;
170        ATRACE_BEGIN("read");
171        ssize_t framesRead = mInputSource->read(mReadBuffer, frameCount);
172        ATRACE_END();
173        dumpState->mReadSequence++;
174        if (framesRead >= 0) {
175            LOG_ALWAYS_FATAL_IF((size_t) framesRead > frameCount);
176            mTotalNativeFramesRead += framesRead;
177            dumpState->mFramesRead = mTotalNativeFramesRead;
178            mReadBufferState = framesRead;
179        } else {
180            dumpState->mReadErrors++;
181            mReadBufferState = 0;
182        }
183        // FIXME rename to attemptedIO
184        mAttemptedWrite = true;
185    }
186
187    if (command & FastCaptureState::WRITE) {
188        ALOG_ASSERT(mPipeSink != NULL);
189        ALOG_ASSERT(mReadBuffer != NULL);
190        if (mReadBufferState < 0) {
191            memset(mReadBuffer, 0, frameCount * Format_frameSize(mFormat));
192            mReadBufferState = frameCount;
193        }
194        if (mReadBufferState > 0) {
195            ssize_t framesWritten = mPipeSink->write(mReadBuffer, mReadBufferState);
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
214}   // namespace android
215