1 /*
2 * Copyright 2017 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 "AudioStreamLegacy"
18 //#define LOG_NDEBUG 0
19 #include <utils/Log.h>
20
21 #include <stdint.h>
22
23 #include <aaudio/AAudio.h>
24 #include <audio_utils/primitives.h>
25 #include <media/AudioTrack.h>
26 #include <media/AudioTimestamp.h>
27 #include <utils/String16.h>
28
29 #include "core/AudioGlobal.h"
30 #include "core/AudioStream.h"
31 #include "legacy/AudioStreamLegacy.h"
32
33 using namespace android;
34 using namespace aaudio;
35
AudioStreamLegacy()36 AudioStreamLegacy::AudioStreamLegacy()
37 : AudioStream() {
38 }
39
40
callDataCallbackFrames(uint8_t * buffer,int32_t numFrames)41 aaudio_data_callback_result_t AudioStreamLegacy::callDataCallbackFrames(uint8_t *buffer,
42 int32_t numFrames) {
43 void *finalAudioData = buffer;
44 if (getDirection() == AAUDIO_DIRECTION_INPUT) {
45 // Increment before because we already got the data from the device.
46 incrementFramesRead(numFrames);
47 finalAudioData = (void *) maybeConvertDeviceData(buffer, numFrames);
48 }
49
50 // Call using the AAudio callback interface.
51 aaudio_data_callback_result_t callbackResult = maybeCallDataCallback(finalAudioData, numFrames);
52
53 if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE
54 && getDirection() == AAUDIO_DIRECTION_OUTPUT) {
55 // Increment after because we are going to write the data to the device.
56 incrementFramesWritten(numFrames);
57 }
58 return callbackResult;
59 }
60
61 // Implement FixedBlockProcessor
onProcessFixedBlock(uint8_t * buffer,int32_t numBytes)62 int32_t AudioStreamLegacy::onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) {
63 int32_t numFrames = numBytes / mBlockAdapterBytesPerFrame;
64 return (int32_t) callDataCallbackFrames(buffer, numFrames);
65 }
66
67
onNewIAudioTrack()68 void AudioStreamLegacy::onNewIAudioTrack() {
69 ALOGD("%s stream disconnected", __func__);
70 forceDisconnect();
71 mCallbackEnabled.store(false);
72 }
73
onMoreData(const android::AudioTrack::Buffer & buffer)74 size_t AudioStreamLegacy::onMoreData(const android::AudioTrack::Buffer& buffer) {
75 // This illegal size can be used to tell AudioRecord or AudioTrack to stop calling us.
76 // This takes advantage of them killing the stream when they see a size out of range.
77 // That is an undocumented behavior.
78 // TODO add to API in AudioRecord and AudioTrack
79 // TODO(b/216175830) cleanup size re-computation
80 const size_t SIZE_STOP_CALLBACKS = SIZE_MAX;
81 aaudio_data_callback_result_t callbackResult;
82 (void) checkForDisconnectRequest(true);
83
84 // Note that this code assumes an AudioTrack::Buffer is the same as
85 // AudioRecord::Buffer
86 // TODO define our own AudioBuffer and pass it from the subclasses.
87 size_t written = buffer.size();
88 if (isDisconnected()) {
89 ALOGW("%s() data, stream disconnected", __func__);
90 // This will kill the stream and prevent it from being restarted.
91 // That is OK because the stream is disconnected.
92 written = SIZE_STOP_CALLBACKS;
93 } else if (!mCallbackEnabled.load()) {
94 ALOGW("%s() no data because callback disabled, set size=0", __func__);
95 // Do NOT use SIZE_STOP_CALLBACKS here because that will kill the stream and
96 // prevent it from being restarted. This can occur because of a race condition
97 // caused by Legacy callbacks running after the track is "stopped".
98 written = 0;
99 } else {
100 if (buffer.getFrameCount() == 0) {
101 ALOGW("%s() data, frameCount is zero", __func__);
102 return written;
103 }
104
105 // If the caller specified an exact size then use a block size adapter.
106 if (mBlockAdapter != nullptr) {
107 int32_t byteCount = buffer.getFrameCount() * getBytesPerDeviceFrame();
108 std::tie(callbackResult, written) = mBlockAdapter->processVariableBlock(
109 buffer.data(), byteCount);
110 } else {
111 // Call using the AAudio callback interface.
112 callbackResult = callDataCallbackFrames(buffer.data(),
113 buffer.getFrameCount());
114 written = callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE ?
115 buffer.getFrameCount() * getBytesPerDeviceFrame() : 0;
116 }
117
118 if (callbackResult != AAUDIO_CALLBACK_RESULT_CONTINUE) {
119 if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) {
120 ALOGD("%s() callback returned AAUDIO_CALLBACK_RESULT_STOP", __func__);
121 } else {
122 ALOGW("%s() callback returned invalid result = %d",
123 __func__, callbackResult);
124 }
125 if (callbackResult != AAUDIO_CALLBACK_RESULT_STOP || shouldStopStream()) {
126 // If the callback result is STOP, stop the stream if it should be stopped.
127 // Currently, the framework will not call stop if the client is doing offload
128 // playback and waiting for stream end. The client will already be STOPPING
129 // state when waiting for stream end.
130 systemStopInternal();
131 // Disable the callback just in case the system keeps trying to call us.
132 mCallbackEnabled.store(false);
133 }
134 }
135
136 if (processCommands() != AAUDIO_OK) {
137 forceDisconnect();
138 mCallbackEnabled.store(false);
139 }
140 }
141 return written;
142 }
143
144 // TODO (b/216175830) this method is duplicated in order to ease refactoring which will
145 // reconsolidate.
onMoreData(const android::AudioRecord::Buffer & buffer)146 size_t AudioStreamLegacy::onMoreData(const android::AudioRecord::Buffer& buffer) {
147 // This illegal size can be used to tell AudioRecord or AudioTrack to stop calling us.
148 // This takes advantage of them killing the stream when they see a size out of range.
149 // That is an undocumented behavior.
150 // TODO add to API in AudioRecord and AudioTrack
151 const size_t SIZE_STOP_CALLBACKS = SIZE_MAX;
152 aaudio_data_callback_result_t callbackResult;
153 (void) checkForDisconnectRequest(true);
154
155 // Note that this code assumes an AudioTrack::Buffer is the same as
156 // AudioRecord::Buffer
157 // TODO define our own AudioBuffer and pass it from the subclasses.
158 size_t written = buffer.size();
159 if (isDisconnected()) {
160 ALOGW("%s() data, stream disconnected", __func__);
161 // This will kill the stream and prevent it from being restarted.
162 // That is OK because the stream is disconnected.
163 written = SIZE_STOP_CALLBACKS;
164 } else if (!mCallbackEnabled.load()) {
165 ALOGW("%s() no data because callback disabled, set size=0", __func__);
166 // Do NOT use SIZE_STOP_CALLBACKS here because that will kill the stream and
167 // prevent it from being restarted. This can occur because of a race condition
168 // caused by Legacy callbacks running after the track is "stopped".
169 written = 0;
170 } else {
171 if (buffer.getFrameCount() == 0) {
172 ALOGW("%s() data, frameCount is zero", __func__);
173 return written;
174 }
175
176 // If the caller specified an exact size then use a block size adapter.
177 if (mBlockAdapter != nullptr) {
178 int32_t byteCount = buffer.getFrameCount() * getBytesPerDeviceFrame();
179 std::tie(callbackResult, written) = mBlockAdapter->processVariableBlock(
180 buffer.data(), byteCount);
181 } else {
182 // Call using the AAudio callback interface.
183 callbackResult = callDataCallbackFrames(buffer.data(),
184 buffer.getFrameCount());
185 written = callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE ?
186 buffer.getFrameCount() * getBytesPerDeviceFrame() : 0;
187 }
188 if (callbackResult != AAUDIO_CALLBACK_RESULT_CONTINUE) {
189 if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) {
190 ALOGD("%s() callback returned AAUDIO_CALLBACK_RESULT_STOP", __func__);
191 } else {
192 ALOGW("%s() callback returned invalid result = %d",
193 __func__, callbackResult);
194 }
195 // Always stop the recording case if callback result is not CONTINUE.
196 systemStopInternal();
197 // Disable the callback just in case the system keeps trying to call us.
198 mCallbackEnabled.store(false);
199 }
200
201 if (processCommands() != AAUDIO_OK) {
202 forceDisconnect();
203 mCallbackEnabled.store(false);
204 }
205 }
206 return written;
207 }
208
checkForDisconnectRequest(bool errorCallbackEnabled)209 aaudio_result_t AudioStreamLegacy::checkForDisconnectRequest(bool errorCallbackEnabled) {
210 if (mRequestDisconnect.isRequested()) {
211 ALOGD("checkForDisconnectRequest() mRequestDisconnect acknowledged");
212 forceDisconnect(errorCallbackEnabled);
213 mRequestDisconnect.acknowledge();
214 mCallbackEnabled.store(false);
215 return AAUDIO_ERROR_DISCONNECTED;
216 } else {
217 return AAUDIO_OK;
218 }
219 }
220
forceDisconnect(bool errorCallbackEnabled)221 void AudioStreamLegacy::forceDisconnect(bool errorCallbackEnabled) {
222 // There is no need to disconnect if already in these states.
223 if (!isDisconnected()
224 && getState() != AAUDIO_STREAM_STATE_CLOSING
225 && getState() != AAUDIO_STREAM_STATE_CLOSED
226 ) {
227 setDisconnected();
228 if (errorCallbackEnabled) {
229 maybeCallErrorCallback(AAUDIO_ERROR_DISCONNECTED);
230 }
231 }
232 }
233
getBestTimestamp(clockid_t clockId,int64_t * framePosition,int64_t * timeNanoseconds,ExtendedTimestamp * extendedTimestamp)234 aaudio_result_t AudioStreamLegacy::getBestTimestamp(clockid_t clockId,
235 int64_t *framePosition,
236 int64_t *timeNanoseconds,
237 ExtendedTimestamp *extendedTimestamp) {
238 int timebase;
239 switch (clockId) {
240 case CLOCK_BOOTTIME:
241 timebase = ExtendedTimestamp::TIMEBASE_BOOTTIME;
242 break;
243 case CLOCK_MONOTONIC:
244 timebase = ExtendedTimestamp::TIMEBASE_MONOTONIC;
245 break;
246 default:
247 ALOGE("getTimestamp() - Unrecognized clock type %d", (int) clockId);
248 return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
249 break;
250 }
251 ExtendedTimestamp::Location location = ExtendedTimestamp::Location::LOCATION_INVALID;
252 int64_t localPosition;
253 status_t status = extendedTimestamp->getBestTimestamp(&localPosition, timeNanoseconds,
254 timebase, &location);
255 if (status == OK) {
256 // use MonotonicCounter to prevent retrograde motion.
257 mTimestampPosition.update32((int32_t) localPosition);
258 *framePosition = mTimestampPosition.get();
259 }
260
261 // ALOGD("getBestTimestamp() fposition: server = %6lld, kernel = %6lld, location = %d",
262 // (long long) extendedTimestamp->mPosition[ExtendedTimestamp::Location::LOCATION_SERVER],
263 // (long long) extendedTimestamp->mPosition[ExtendedTimestamp::Location::LOCATION_KERNEL],
264 // (int)location);
265 return AAudioConvert_androidToAAudioResult(status);
266 }
267
onAudioDeviceUpdate(audio_io_handle_t,const android::DeviceIdVector & deviceIds)268 void AudioStreamLegacy::onAudioDeviceUpdate(audio_io_handle_t /* audioIo */,
269 const android::DeviceIdVector& deviceIds) {
270 // Check for empty deviceIds. Callbacks for duplicating threads returns empty devices.
271 if (deviceIds.empty()) {
272 ALOGW("%s(empty deviceIds", __func__);
273 return;
274 }
275 android::DeviceIdVector oldDeviceIds = getDeviceIds();
276 // Device routing is a common source of errors and DISCONNECTS.
277 // Please leave this log in place. If there is a bug then this might
278 // get called after the stream has been deleted so log before we
279 // touch the stream object.
280 ALOGD("%s() devices %s => %s",
281 __func__, android::toString(oldDeviceIds).c_str(),
282 android::toString(deviceIds).c_str());
283 if (!oldDeviceIds.empty()
284 && !android::areDeviceIdsEqual(oldDeviceIds, deviceIds)
285 && !isDisconnected()
286 ) {
287 // Note that isDataCallbackActive() is affected by state so call it before DISCONNECTING.
288 // If we have a data callback and the stream is active, then ask the data callback
289 // to DISCONNECT and call the error callback.
290 if (isDataCallbackActive()) {
291 ALOGD("%s() request DISCONNECT in data callback, devices %s => %s",
292 __func__, android::toString(oldDeviceIds).c_str(),
293 android::toString(deviceIds).c_str());
294 // If the stream is stopped before the data callback has a chance to handle the
295 // request then the requestStop_l() and requestPause() methods will handle it after
296 // the callback has stopped.
297 mRequestDisconnect.request();
298 } else {
299 ALOGD("%s() DISCONNECT the stream now, devices %s => %s",
300 __func__, android::toString(oldDeviceIds).c_str(),
301 android::toString(deviceIds).c_str());
302 forceDisconnect();
303 }
304 }
305 setDeviceIds(deviceIds);
306 }
307