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
~AudioStreamLegacy()40 AudioStreamLegacy::~AudioStreamLegacy() {
41 }
42
43 // Called from AudioTrack.cpp or AudioRecord.cpp
AudioStreamLegacy_callback(int event,void * userData,void * info)44 static void AudioStreamLegacy_callback(int event, void* userData, void *info) {
45 AudioStreamLegacy *streamLegacy = (AudioStreamLegacy *) userData;
46 streamLegacy->processCallback(event, info);
47 }
48
getLegacyCallback()49 aaudio_legacy_callback_t AudioStreamLegacy::getLegacyCallback() {
50 return AudioStreamLegacy_callback;
51 }
52
callDataCallbackFrames(uint8_t * buffer,int32_t numFrames)53 aaudio_data_callback_result_t AudioStreamLegacy::callDataCallbackFrames(uint8_t *buffer,
54 int32_t numFrames) {
55 void *finalAudioData = buffer;
56 if (getDirection() == AAUDIO_DIRECTION_INPUT) {
57 // Increment before because we already got the data from the device.
58 incrementFramesRead(numFrames);
59 finalAudioData = (void *) maybeConvertDeviceData(buffer, numFrames);
60 }
61
62 // Call using the AAudio callback interface.
63 aaudio_data_callback_result_t callbackResult = maybeCallDataCallback(finalAudioData, numFrames);
64
65 if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE
66 && getDirection() == AAUDIO_DIRECTION_OUTPUT) {
67 // Increment after because we are going to write the data to the device.
68 incrementFramesWritten(numFrames);
69 }
70 return callbackResult;
71 }
72
73 // Implement FixedBlockProcessor
onProcessFixedBlock(uint8_t * buffer,int32_t numBytes)74 int32_t AudioStreamLegacy::onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) {
75 int32_t numFrames = numBytes / mBlockAdapterBytesPerFrame;
76 return (int32_t) callDataCallbackFrames(buffer, numFrames);
77 }
78
processCallbackCommon(aaudio_callback_operation_t opcode,void * info)79 void AudioStreamLegacy::processCallbackCommon(aaudio_callback_operation_t opcode, void *info) {
80 aaudio_data_callback_result_t callbackResult;
81 // This illegal size can be used to tell AudioRecord or AudioTrack to stop calling us.
82 // This takes advantage of them killing the stream when they see a size out of range.
83 // That is an undocumented behavior.
84 // TODO add to API in AudioRecord and AudioTrack
85 const size_t SIZE_STOP_CALLBACKS = SIZE_MAX;
86
87 switch (opcode) {
88 case AAUDIO_CALLBACK_OPERATION_PROCESS_DATA: {
89 (void) checkForDisconnectRequest(true);
90
91 // Note that this code assumes an AudioTrack::Buffer is the same as
92 // AudioRecord::Buffer
93 // TODO define our own AudioBuffer and pass it from the subclasses.
94 AudioTrack::Buffer *audioBuffer = static_cast<AudioTrack::Buffer *>(info);
95 if (getState() == AAUDIO_STREAM_STATE_DISCONNECTED) {
96 ALOGW("processCallbackCommon() data, stream disconnected");
97 // This will kill the stream and prevent it from being restarted.
98 // That is OK because the stream is disconnected.
99 audioBuffer->size = SIZE_STOP_CALLBACKS;
100 } else if (!mCallbackEnabled.load()) {
101 ALOGW("processCallbackCommon() no data because callback disabled, set size=0");
102 // Do NOT use SIZE_STOP_CALLBACKS here because that will kill the stream and
103 // prevent it from being restarted. This can occur because of a race condition
104 // caused by Legacy callbacks running after the track is "stopped".
105 audioBuffer->size = 0;
106 } else {
107 if (audioBuffer->frameCount == 0) {
108 ALOGW("processCallbackCommon() data, frameCount is zero");
109 return;
110 }
111
112 // If the caller specified an exact size then use a block size adapter.
113 if (mBlockAdapter != nullptr) {
114 int32_t byteCount = audioBuffer->frameCount * getBytesPerDeviceFrame();
115 callbackResult = mBlockAdapter->processVariableBlock(
116 (uint8_t *) audioBuffer->raw, byteCount);
117 } else {
118 // Call using the AAudio callback interface.
119 callbackResult = callDataCallbackFrames((uint8_t *)audioBuffer->raw,
120 audioBuffer->frameCount);
121 }
122 if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE) {
123 audioBuffer->size = audioBuffer->frameCount * getBytesPerDeviceFrame();
124 } else {
125 if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) {
126 ALOGD("%s() callback returned AAUDIO_CALLBACK_RESULT_STOP", __func__);
127 } else {
128 ALOGW("%s() callback returned invalid result = %d",
129 __func__, callbackResult);
130 }
131 audioBuffer->size = 0;
132 systemStopInternal();
133 // Disable the callback just in case the system keeps trying to call us.
134 mCallbackEnabled.store(false);
135 }
136
137 if (updateStateMachine() != AAUDIO_OK) {
138 forceDisconnect();
139 mCallbackEnabled.store(false);
140 }
141 }
142 }
143 break;
144
145 // Stream got rerouted so we disconnect.
146 case AAUDIO_CALLBACK_OPERATION_DISCONNECTED:
147 ALOGD("processCallbackCommon() stream disconnected");
148 forceDisconnect();
149 mCallbackEnabled.store(false);
150 break;
151
152 default:
153 break;
154 }
155 }
156
checkForDisconnectRequest(bool errorCallbackEnabled)157 aaudio_result_t AudioStreamLegacy::checkForDisconnectRequest(bool errorCallbackEnabled) {
158 if (mRequestDisconnect.isRequested()) {
159 ALOGD("checkForDisconnectRequest() mRequestDisconnect acknowledged");
160 forceDisconnect(errorCallbackEnabled);
161 mRequestDisconnect.acknowledge();
162 mCallbackEnabled.store(false);
163 return AAUDIO_ERROR_DISCONNECTED;
164 } else {
165 return AAUDIO_OK;
166 }
167 }
168
forceDisconnect(bool errorCallbackEnabled)169 void AudioStreamLegacy::forceDisconnect(bool errorCallbackEnabled) {
170 // There is no need to disconnect if already in these states.
171 if (getState() != AAUDIO_STREAM_STATE_DISCONNECTED
172 && getState() != AAUDIO_STREAM_STATE_CLOSING
173 && getState() != AAUDIO_STREAM_STATE_CLOSED
174 ) {
175 setState(AAUDIO_STREAM_STATE_DISCONNECTED);
176 if (errorCallbackEnabled) {
177 maybeCallErrorCallback(AAUDIO_ERROR_DISCONNECTED);
178 }
179 }
180 }
181
getBestTimestamp(clockid_t clockId,int64_t * framePosition,int64_t * timeNanoseconds,ExtendedTimestamp * extendedTimestamp)182 aaudio_result_t AudioStreamLegacy::getBestTimestamp(clockid_t clockId,
183 int64_t *framePosition,
184 int64_t *timeNanoseconds,
185 ExtendedTimestamp *extendedTimestamp) {
186 int timebase;
187 switch (clockId) {
188 case CLOCK_BOOTTIME:
189 timebase = ExtendedTimestamp::TIMEBASE_BOOTTIME;
190 break;
191 case CLOCK_MONOTONIC:
192 timebase = ExtendedTimestamp::TIMEBASE_MONOTONIC;
193 break;
194 default:
195 ALOGE("getTimestamp() - Unrecognized clock type %d", (int) clockId);
196 return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
197 break;
198 }
199 ExtendedTimestamp::Location location = ExtendedTimestamp::Location::LOCATION_INVALID;
200 int64_t localPosition;
201 status_t status = extendedTimestamp->getBestTimestamp(&localPosition, timeNanoseconds,
202 timebase, &location);
203 if (status == OK) {
204 // use MonotonicCounter to prevent retrograde motion.
205 mTimestampPosition.update32((int32_t) localPosition);
206 *framePosition = mTimestampPosition.get();
207 }
208
209 // ALOGD("getBestTimestamp() fposition: server = %6lld, kernel = %6lld, location = %d",
210 // (long long) extendedTimestamp->mPosition[ExtendedTimestamp::Location::LOCATION_SERVER],
211 // (long long) extendedTimestamp->mPosition[ExtendedTimestamp::Location::LOCATION_KERNEL],
212 // (int)location);
213 return AAudioConvert_androidToAAudioResult(status);
214 }
215
onAudioDeviceUpdate(audio_io_handle_t,audio_port_handle_t deviceId)216 void AudioStreamLegacy::onAudioDeviceUpdate(audio_io_handle_t /* audioIo */,
217 audio_port_handle_t deviceId) {
218 // Device routing is a common source of errors and DISCONNECTS.
219 // Please leave this log in place. If there is a bug then this might
220 // get called after the stream has been deleted so log before we
221 // touch the stream object.
222 ALOGD("%s(deviceId = %d)", __func__, (int)deviceId);
223 if (getDeviceId() != AAUDIO_UNSPECIFIED
224 && getDeviceId() != deviceId
225 && getState() != AAUDIO_STREAM_STATE_DISCONNECTED
226 ) {
227 // Note that isDataCallbackActive() is affected by state so call it before DISCONNECTING.
228 // If we have a data callback and the stream is active, then ask the data callback
229 // to DISCONNECT and call the error callback.
230 if (isDataCallbackActive()) {
231 ALOGD("%s() request DISCONNECT in data callback, device %d => %d",
232 __func__, (int) getDeviceId(), (int) deviceId);
233 // If the stream is stopped before the data callback has a chance to handle the
234 // request then the requestStop_l() and requestPause() methods will handle it after
235 // the callback has stopped.
236 mRequestDisconnect.request();
237 } else {
238 ALOGD("%s() DISCONNECT the stream now, device %d => %d",
239 __func__, (int) getDeviceId(), (int) deviceId);
240 forceDisconnect();
241 }
242 }
243 setDeviceId(deviceId);
244 }
245