1 /*
2 * Copyright 2015 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 #include <sys/types.h>
18 #include <pthread.h>
19 #include <thread>
20
21 #include <oboe/AudioStream.h>
22 #include "OboeDebug.h"
23 #include "AudioClock.h"
24 #include <oboe/Utilities.h>
25
26 namespace oboe {
27
28 /*
29 * AudioStream
30 */
AudioStream(const AudioStreamBuilder & builder)31 AudioStream::AudioStream(const AudioStreamBuilder &builder)
32 : AudioStreamBase(builder) {
33 }
34
close()35 Result AudioStream::close() {
36 // Update local counters so they can be read after the close.
37 updateFramesWritten();
38 updateFramesRead();
39 return Result::OK;
40 }
41
42 // Call this from fireDataCallback() if you want to monitor CPU scheduler.
checkScheduler()43 void AudioStream::checkScheduler() {
44 int scheduler = sched_getscheduler(0) & ~SCHED_RESET_ON_FORK; // for current thread
45 if (scheduler != mPreviousScheduler) {
46 LOGD("AudioStream::%s() scheduler = %s", __func__,
47 ((scheduler == SCHED_FIFO) ? "SCHED_FIFO" :
48 ((scheduler == SCHED_OTHER) ? "SCHED_OTHER" :
49 ((scheduler == SCHED_RR) ? "SCHED_RR" : "UNKNOWN")))
50 );
51 mPreviousScheduler = scheduler;
52 }
53 }
54
fireDataCallback(void * audioData,int32_t numFrames)55 DataCallbackResult AudioStream::fireDataCallback(void *audioData, int32_t numFrames) {
56 if (!isDataCallbackEnabled()) {
57 LOGW("AudioStream::%s() called with data callback disabled!", __func__);
58 return DataCallbackResult::Stop; // We should not be getting called any more.
59 }
60
61 DataCallbackResult result;
62 if (mStreamCallback == nullptr) {
63 result = onDefaultCallback(audioData, numFrames);
64 } else {
65 result = mStreamCallback->onAudioReady(this, audioData, numFrames);
66 }
67 // On Oreo, we might get called after returning stop.
68 // So block that here.
69 setDataCallbackEnabled(result == DataCallbackResult::Continue);
70
71 return result;
72 }
73
waitForStateTransition(StreamState startingState,StreamState endingState,int64_t timeoutNanoseconds)74 Result AudioStream::waitForStateTransition(StreamState startingState,
75 StreamState endingState,
76 int64_t timeoutNanoseconds)
77 {
78 StreamState state;
79 {
80 std::lock_guard<std::mutex> lock(mLock);
81 state = getState();
82 if (state == StreamState::Closed) {
83 return Result::ErrorClosed;
84 } else if (state == StreamState::Disconnected) {
85 return Result::ErrorDisconnected;
86 }
87 }
88
89 StreamState nextState = state;
90 // TODO Should this be a while()?!
91 if (state == startingState && state != endingState) {
92 Result result = waitForStateChange(state, &nextState, timeoutNanoseconds);
93 if (result != Result::OK) {
94 return result;
95 }
96 }
97
98 if (nextState != endingState) {
99 return Result::ErrorInvalidState;
100 } else {
101 return Result::OK;
102 }
103 }
104
start(int64_t timeoutNanoseconds)105 Result AudioStream::start(int64_t timeoutNanoseconds)
106 {
107 Result result = requestStart();
108 if (result != Result::OK) return result;
109 if (timeoutNanoseconds <= 0) return result;
110 return waitForStateTransition(StreamState::Starting,
111 StreamState::Started, timeoutNanoseconds);
112 }
113
pause(int64_t timeoutNanoseconds)114 Result AudioStream::pause(int64_t timeoutNanoseconds)
115 {
116 Result result = requestPause();
117 if (result != Result::OK) return result;
118 if (timeoutNanoseconds <= 0) return result;
119 return waitForStateTransition(StreamState::Pausing,
120 StreamState::Paused, timeoutNanoseconds);
121 }
122
flush(int64_t timeoutNanoseconds)123 Result AudioStream::flush(int64_t timeoutNanoseconds)
124 {
125 Result result = requestFlush();
126 if (result != Result::OK) return result;
127 if (timeoutNanoseconds <= 0) return result;
128 return waitForStateTransition(StreamState::Flushing,
129 StreamState::Flushed, timeoutNanoseconds);
130 }
131
stop(int64_t timeoutNanoseconds)132 Result AudioStream::stop(int64_t timeoutNanoseconds)
133 {
134 Result result = requestStop();
135 if (result != Result::OK) return result;
136 if (timeoutNanoseconds <= 0) return result;
137 return waitForStateTransition(StreamState::Stopping,
138 StreamState::Stopped, timeoutNanoseconds);
139 }
140
getBytesPerSample() const141 int32_t AudioStream::getBytesPerSample() const {
142 return convertFormatToSizeInBytes(mFormat);
143 }
144
getFramesRead()145 int64_t AudioStream::getFramesRead() {
146 updateFramesRead();
147 return mFramesRead;
148 }
149
getFramesWritten()150 int64_t AudioStream::getFramesWritten() {
151 updateFramesWritten();
152 return mFramesWritten;
153 }
154
getAvailableFrames()155 ResultWithValue<int32_t> AudioStream::getAvailableFrames() {
156 int64_t readCounter = getFramesRead();
157 if (readCounter < 0) return ResultWithValue<int32_t>::createBasedOnSign(readCounter);
158 int64_t writeCounter = getFramesWritten();
159 if (writeCounter < 0) return ResultWithValue<int32_t>::createBasedOnSign(writeCounter);
160 int32_t framesAvailable = writeCounter - readCounter;
161 return ResultWithValue<int32_t>(framesAvailable);
162 }
163
waitForAvailableFrames(int32_t numFrames,int64_t timeoutNanoseconds)164 ResultWithValue<int32_t> AudioStream::waitForAvailableFrames(int32_t numFrames,
165 int64_t timeoutNanoseconds) {
166 if (numFrames == 0) return Result::OK;
167 if (numFrames < 0) return Result::ErrorOutOfRange;
168
169 int64_t framesAvailable = 0;
170 int64_t burstInNanos = getFramesPerBurst() * kNanosPerSecond / getSampleRate();
171 bool ready = false;
172 int64_t deadline = AudioClock::getNanoseconds() + timeoutNanoseconds;
173 do {
174 ResultWithValue<int32_t> result = getAvailableFrames();
175 if (!result) return result;
176 framesAvailable = result.value();
177 ready = (framesAvailable >= numFrames);
178 if (!ready) {
179 int64_t now = AudioClock::getNanoseconds();
180 if (now > deadline) break;
181 AudioClock::sleepForNanos(burstInNanos);
182 }
183 } while (!ready);
184 return (!ready)
185 ? ResultWithValue<int32_t>(Result::ErrorTimeout)
186 : ResultWithValue<int32_t>(framesAvailable);
187 }
188
getTimestamp(clockid_t clockId)189 ResultWithValue<FrameTimestamp> AudioStream::getTimestamp(clockid_t clockId) {
190 FrameTimestamp frame;
191 Result result = getTimestamp(clockId, &frame.position, &frame.timestamp);
192 if (result == Result::OK){
193 return ResultWithValue<FrameTimestamp>(frame);
194 } else {
195 return ResultWithValue<FrameTimestamp>(static_cast<Result>(result));
196 }
197 }
198
oboe_stop_thread_proc(AudioStream * oboeStream)199 static void oboe_stop_thread_proc(AudioStream *oboeStream) {
200 if (oboeStream != nullptr) {
201 oboeStream->requestStop();
202 }
203 }
204
launchStopThread()205 void AudioStream::launchStopThread() {
206 // Stop this stream on a separate thread
207 std::thread t(oboe_stop_thread_proc, this);
208 t.detach();
209 }
210
211 } // namespace oboe
212