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