• 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             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