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 callbackResult = mBlockAdapter->processVariableBlock(
109 buffer.data(), byteCount);
110 } else {
111 // Call using the AAudio callback interface.
112 callbackResult = callDataCallbackFrames(buffer.data(),
113 buffer.getFrameCount());
114 }
115 if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE) {
116 written = buffer.getFrameCount() * getBytesPerDeviceFrame();
117 } else {
118 if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) {
119 ALOGD("%s() callback returned AAUDIO_CALLBACK_RESULT_STOP", __func__);
120 } else {
121 ALOGW("%s() callback returned invalid result = %d",
122 __func__, callbackResult);
123 }
124 written = 0;
125 systemStopInternal();
126 // Disable the callback just in case the system keeps trying to call us.
127 mCallbackEnabled.store(false);
128 }
129
130 if (processCommands() != AAUDIO_OK) {
131 forceDisconnect();
132 mCallbackEnabled.store(false);
133 }
134 }
135 return written;
136 }
137
138 // TODO (b/216175830) this method is duplicated in order to ease refactoring which will
139 // reconsolidate.
onMoreData(const android::AudioRecord::Buffer & buffer)140 size_t AudioStreamLegacy::onMoreData(const android::AudioRecord::Buffer& buffer) {
141 // This illegal size can be used to tell AudioRecord or AudioTrack to stop calling us.
142 // This takes advantage of them killing the stream when they see a size out of range.
143 // That is an undocumented behavior.
144 // TODO add to API in AudioRecord and AudioTrack
145 const size_t SIZE_STOP_CALLBACKS = SIZE_MAX;
146 aaudio_data_callback_result_t callbackResult;
147 (void) checkForDisconnectRequest(true);
148
149 // Note that this code assumes an AudioTrack::Buffer is the same as
150 // AudioRecord::Buffer
151 // TODO define our own AudioBuffer and pass it from the subclasses.
152 size_t written = buffer.size();
153 if (isDisconnected()) {
154 ALOGW("%s() data, stream disconnected", __func__);
155 // This will kill the stream and prevent it from being restarted.
156 // That is OK because the stream is disconnected.
157 written = SIZE_STOP_CALLBACKS;
158 } else if (!mCallbackEnabled.load()) {
159 ALOGW("%s() no data because callback disabled, set size=0", __func__);
160 // Do NOT use SIZE_STOP_CALLBACKS here because that will kill the stream and
161 // prevent it from being restarted. This can occur because of a race condition
162 // caused by Legacy callbacks running after the track is "stopped".
163 written = 0;
164 } else {
165 if (buffer.getFrameCount() == 0) {
166 ALOGW("%s() data, frameCount is zero", __func__);
167 return written;
168 }
169
170 // If the caller specified an exact size then use a block size adapter.
171 if (mBlockAdapter != nullptr) {
172 int32_t byteCount = buffer.getFrameCount() * getBytesPerDeviceFrame();
173 callbackResult = mBlockAdapter->processVariableBlock(
174 buffer.data(), byteCount);
175 } else {
176 // Call using the AAudio callback interface.
177 callbackResult = callDataCallbackFrames(buffer.data(),
178 buffer.getFrameCount());
179 }
180 if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE) {
181 written = buffer.getFrameCount() * getBytesPerDeviceFrame();
182 } else {
183 if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) {
184 ALOGD("%s() callback returned AAUDIO_CALLBACK_RESULT_STOP", __func__);
185 } else {
186 ALOGW("%s() callback returned invalid result = %d",
187 __func__, callbackResult);
188 }
189 written = 0;
190 systemStopInternal();
191 // Disable the callback just in case the system keeps trying to call us.
192 mCallbackEnabled.store(false);
193 }
194
195 if (processCommands() != AAUDIO_OK) {
196 forceDisconnect();
197 mCallbackEnabled.store(false);
198 }
199 }
200 return written;
201 }
202
checkForDisconnectRequest(bool errorCallbackEnabled)203 aaudio_result_t AudioStreamLegacy::checkForDisconnectRequest(bool errorCallbackEnabled) {
204 if (mRequestDisconnect.isRequested()) {
205 ALOGD("checkForDisconnectRequest() mRequestDisconnect acknowledged");
206 forceDisconnect(errorCallbackEnabled);
207 mRequestDisconnect.acknowledge();
208 mCallbackEnabled.store(false);
209 return AAUDIO_ERROR_DISCONNECTED;
210 } else {
211 return AAUDIO_OK;
212 }
213 }
214
forceDisconnect(bool errorCallbackEnabled)215 void AudioStreamLegacy::forceDisconnect(bool errorCallbackEnabled) {
216 // There is no need to disconnect if already in these states.
217 if (!isDisconnected()
218 && getState() != AAUDIO_STREAM_STATE_CLOSING
219 && getState() != AAUDIO_STREAM_STATE_CLOSED
220 ) {
221 setDisconnected();
222 if (errorCallbackEnabled) {
223 maybeCallErrorCallback(AAUDIO_ERROR_DISCONNECTED);
224 }
225 }
226 }
227
getBestTimestamp(clockid_t clockId,int64_t * framePosition,int64_t * timeNanoseconds,ExtendedTimestamp * extendedTimestamp)228 aaudio_result_t AudioStreamLegacy::getBestTimestamp(clockid_t clockId,
229 int64_t *framePosition,
230 int64_t *timeNanoseconds,
231 ExtendedTimestamp *extendedTimestamp) {
232 int timebase;
233 switch (clockId) {
234 case CLOCK_BOOTTIME:
235 timebase = ExtendedTimestamp::TIMEBASE_BOOTTIME;
236 break;
237 case CLOCK_MONOTONIC:
238 timebase = ExtendedTimestamp::TIMEBASE_MONOTONIC;
239 break;
240 default:
241 ALOGE("getTimestamp() - Unrecognized clock type %d", (int) clockId);
242 return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
243 break;
244 }
245 ExtendedTimestamp::Location location = ExtendedTimestamp::Location::LOCATION_INVALID;
246 int64_t localPosition;
247 status_t status = extendedTimestamp->getBestTimestamp(&localPosition, timeNanoseconds,
248 timebase, &location);
249 if (status == OK) {
250 // use MonotonicCounter to prevent retrograde motion.
251 mTimestampPosition.update32((int32_t) localPosition);
252 *framePosition = mTimestampPosition.get();
253 }
254
255 // ALOGD("getBestTimestamp() fposition: server = %6lld, kernel = %6lld, location = %d",
256 // (long long) extendedTimestamp->mPosition[ExtendedTimestamp::Location::LOCATION_SERVER],
257 // (long long) extendedTimestamp->mPosition[ExtendedTimestamp::Location::LOCATION_KERNEL],
258 // (int)location);
259 return AAudioConvert_androidToAAudioResult(status);
260 }
261
onAudioDeviceUpdate(audio_io_handle_t,audio_port_handle_t deviceId)262 void AudioStreamLegacy::onAudioDeviceUpdate(audio_io_handle_t /* audioIo */,
263 audio_port_handle_t deviceId) {
264 // Device routing is a common source of errors and DISCONNECTS.
265 // Please leave this log in place. If there is a bug then this might
266 // get called after the stream has been deleted so log before we
267 // touch the stream object.
268 ALOGD("%s(deviceId = %d)", __func__, (int)deviceId);
269 if (getDeviceId() != AAUDIO_UNSPECIFIED
270 && getDeviceId() != deviceId
271 && !isDisconnected()
272 ) {
273 // Note that isDataCallbackActive() is affected by state so call it before DISCONNECTING.
274 // If we have a data callback and the stream is active, then ask the data callback
275 // to DISCONNECT and call the error callback.
276 if (isDataCallbackActive()) {
277 ALOGD("%s() request DISCONNECT in data callback, device %d => %d",
278 __func__, (int) getDeviceId(), (int) deviceId);
279 // If the stream is stopped before the data callback has a chance to handle the
280 // request then the requestStop_l() and requestPause() methods will handle it after
281 // the callback has stopped.
282 mRequestDisconnect.request();
283 } else {
284 ALOGD("%s() DISCONNECT the stream now, device %d => %d",
285 __func__, (int) getDeviceId(), (int) deviceId);
286 forceDisconnect();
287 }
288 }
289 setDeviceId(deviceId);
290 }
291