• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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