blob: dfb9a01c25ae6669a5a04143706c6ab8e388e4c7 [file] [log] [blame]
Phil Burke4d7bb42017-03-28 11:32:39 -07001/*
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>
Phil Burk3d786cb2018-04-09 11:58:09 -070022
23#include <aaudio/AAudio.h>
24#include <audio_utils/primitives.h>
Phil Burke4d7bb42017-03-28 11:32:39 -070025#include <media/AudioTrack.h>
Phil Burk7328a802017-08-30 09:29:48 -070026#include <media/AudioTimestamp.h>
Phil Burk3d786cb2018-04-09 11:58:09 -070027#include <utils/String16.h>
Phil Burke4d7bb42017-03-28 11:32:39 -070028
Phil Burka9876702020-04-20 18:16:15 -070029#include "core/AudioGlobal.h"
Phil Burke4d7bb42017-03-28 11:32:39 -070030#include "core/AudioStream.h"
31#include "legacy/AudioStreamLegacy.h"
32
33using namespace android;
34using namespace aaudio;
35
36AudioStreamLegacy::AudioStreamLegacy()
Phil Burk58f5ce12020-08-12 14:29:10 +000037 : AudioStream() {
Phil Burke4d7bb42017-03-28 11:32:39 -070038}
39
Phil Burke4d7bb42017-03-28 11:32:39 -070040
Phil Burk3d786cb2018-04-09 11:58:09 -070041aaudio_data_callback_result_t AudioStreamLegacy::callDataCallbackFrames(uint8_t *buffer,
42 int32_t numFrames) {
43 void *finalAudioData = buffer;
Phil Burk7328a802017-08-30 09:29:48 -070044 if (getDirection() == AAUDIO_DIRECTION_INPUT) {
45 // Increment before because we already got the data from the device.
46 incrementFramesRead(numFrames);
Phil Burk3d786cb2018-04-09 11:58:09 -070047 finalAudioData = (void *) maybeConvertDeviceData(buffer, numFrames);
Phil Burk7328a802017-08-30 09:29:48 -070048 }
49
Phil Burke4d7bb42017-03-28 11:32:39 -070050 // Call using the AAudio callback interface.
Phil Burk3d786cb2018-04-09 11:58:09 -070051 aaudio_data_callback_result_t callbackResult = maybeCallDataCallback(finalAudioData, numFrames);
Phil Burk7328a802017-08-30 09:29:48 -070052
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
62int32_t AudioStreamLegacy::onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) {
Phil Burk4b867492020-02-12 10:58:05 -080063 int32_t numFrames = numBytes / mBlockAdapterBytesPerFrame;
Phil Burk134f1972017-12-08 13:06:11 -080064 return (int32_t) callDataCallbackFrames(buffer, numFrames);
Phil Burke4d7bb42017-03-28 11:32:39 -070065}
66
Atneya Nair7daa4f92021-11-19 14:00:24 -050067
68void AudioStreamLegacy::onNewIAudioTrack() {
69 ALOGD("%s stream disconnected", __func__);
70 forceDisconnect();
71 mCallbackEnabled.store(false);
72}
73
74size_t AudioStreamLegacy::onMoreData(const android::AudioTrack::Buffer& buffer) {
Phil Burk1e83bee2018-12-17 14:15:20 -080075 // 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.
Phil Burk3d786cb2018-04-09 11:58:09 -070078 // TODO add to API in AudioRecord and AudioTrack
Atneya Nair03079272022-01-18 17:03:14 -050079 // TODO(b/216175830) cleanup size re-computation
Phil Burk134f1972017-12-08 13:06:11 -080080 const size_t SIZE_STOP_CALLBACKS = SIZE_MAX;
Atneya Nair7daa4f92021-11-19 14:00:24 -050081 aaudio_data_callback_result_t callbackResult;
82 (void) checkForDisconnectRequest(true);
Eric Laurentfb00fc72017-05-25 18:17:12 -070083
Atneya Nair7daa4f92021-11-19 14:00:24 -050084 // 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.
Atneya Nair03079272022-01-18 17:03:14 -050087 size_t written = buffer.size();
jiabincb212cd2022-08-24 16:50:44 -070088 if (isDisconnected()) {
Atneya Nair7daa4f92021-11-19 14:00:24 -050089 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 {
Atneya Nair03079272022-01-18 17:03:14 -0500100 if (buffer.getFrameCount() == 0) {
Atneya Nair7daa4f92021-11-19 14:00:24 -0500101 ALOGW("%s() data, frameCount is zero", __func__);
102 return written;
Phil Burke4d7bb42017-03-28 11:32:39 -0700103 }
Phil Burke4d7bb42017-03-28 11:32:39 -0700104
Atneya Nair7daa4f92021-11-19 14:00:24 -0500105 // If the caller specified an exact size then use a block size adapter.
106 if (mBlockAdapter != nullptr) {
Atneya Nair03079272022-01-18 17:03:14 -0500107 int32_t byteCount = buffer.getFrameCount() * getBytesPerDeviceFrame();
Atneya Nair7daa4f92021-11-19 14:00:24 -0500108 callbackResult = mBlockAdapter->processVariableBlock(
Atneya Nair03079272022-01-18 17:03:14 -0500109 buffer.data(), byteCount);
Atneya Nair7daa4f92021-11-19 14:00:24 -0500110 } else {
111 // Call using the AAudio callback interface.
Atneya Nair03079272022-01-18 17:03:14 -0500112 callbackResult = callDataCallbackFrames(buffer.data(),
113 buffer.getFrameCount());
Atneya Nair7daa4f92021-11-19 14:00:24 -0500114 }
115 if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE) {
Atneya Nair03079272022-01-18 17:03:14 -0500116 written = buffer.getFrameCount() * getBytesPerDeviceFrame();
Atneya Nair7daa4f92021-11-19 14:00:24 -0500117 } 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
Robert Wu3da128d2022-03-04 23:17:25 +0000130 if (processCommands() != AAUDIO_OK) {
Phil Burk2d5ba532017-09-06 14:36:11 -0700131 forceDisconnect();
Phil Burke4d7bb42017-03-28 11:32:39 -0700132 mCallbackEnabled.store(false);
Atneya Nair7daa4f92021-11-19 14:00:24 -0500133 }
Phil Burke4d7bb42017-03-28 11:32:39 -0700134 }
Atneya Nair7daa4f92021-11-19 14:00:24 -0500135 return written;
Phil Burke4d7bb42017-03-28 11:32:39 -0700136}
Phil Burk5204d312017-05-04 17:16:13 -0700137
Atneya Nair03079272022-01-18 17:03:14 -0500138// TODO (b/216175830) this method is duplicated in order to ease refactoring which will
139// reconsolidate.
140size_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();
jiabincb212cd2022-08-24 16:50:44 -0700153 if (isDisconnected()) {
Atneya Nair03079272022-01-18 17:03:14 -0500154 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
Robert Wu3da128d2022-03-04 23:17:25 +0000195 if (processCommands() != AAUDIO_OK) {
Atneya Nair03079272022-01-18 17:03:14 -0500196 forceDisconnect();
197 mCallbackEnabled.store(false);
198 }
199 }
200 return written;
201}
Atneya Nair7daa4f92021-11-19 14:00:24 -0500202
Phil Burk134f1972017-12-08 13:06:11 -0800203aaudio_result_t AudioStreamLegacy::checkForDisconnectRequest(bool errorCallbackEnabled) {
Phil Burk2d5ba532017-09-06 14:36:11 -0700204 if (mRequestDisconnect.isRequested()) {
205 ALOGD("checkForDisconnectRequest() mRequestDisconnect acknowledged");
Phil Burk134f1972017-12-08 13:06:11 -0800206 forceDisconnect(errorCallbackEnabled);
Phil Burk2d5ba532017-09-06 14:36:11 -0700207 mRequestDisconnect.acknowledge();
208 mCallbackEnabled.store(false);
Phil Burk134f1972017-12-08 13:06:11 -0800209 return AAUDIO_ERROR_DISCONNECTED;
210 } else {
211 return AAUDIO_OK;
Phil Burk2d5ba532017-09-06 14:36:11 -0700212 }
213}
214
Phil Burk134f1972017-12-08 13:06:11 -0800215void AudioStreamLegacy::forceDisconnect(bool errorCallbackEnabled) {
Phil Burk58f5ce12020-08-12 14:29:10 +0000216 // There is no need to disconnect if already in these states.
jiabincb212cd2022-08-24 16:50:44 -0700217 if (!isDisconnected()
Phil Burk58f5ce12020-08-12 14:29:10 +0000218 && getState() != AAUDIO_STREAM_STATE_CLOSING
219 && getState() != AAUDIO_STREAM_STATE_CLOSED
220 ) {
jiabincb212cd2022-08-24 16:50:44 -0700221 setDisconnected();
Phil Burk134f1972017-12-08 13:06:11 -0800222 if (errorCallbackEnabled) {
223 maybeCallErrorCallback(AAUDIO_ERROR_DISCONNECTED);
Phil Burk2d5ba532017-09-06 14:36:11 -0700224 }
225 }
226}
227
Phil Burk5204d312017-05-04 17:16:13 -0700228aaudio_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);
Phil Burk17fff382017-05-16 14:06:45 -0700242 return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
Phil Burk5204d312017-05-04 17:16:13 -0700243 break;
244 }
Phil Burk7328a802017-08-30 09:29:48 -0700245 ExtendedTimestamp::Location location = ExtendedTimestamp::Location::LOCATION_INVALID;
246 int64_t localPosition;
247 status_t status = extendedTimestamp->getBestTimestamp(&localPosition, timeNanoseconds,
248 timebase, &location);
Phil Burkc0959642018-04-05 18:11:01 -0700249 if (status == OK) {
250 // use MonotonicCounter to prevent retrograde motion.
251 mTimestampPosition.update32((int32_t) localPosition);
252 *framePosition = mTimestampPosition.get();
253 }
Phil Burk7328a802017-08-30 09:29:48 -0700254
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);
Phil Burkc0959642018-04-05 18:11:01 -0700259 return AAudioConvert_androidToAAudioResult(status);
Phil Burk5204d312017-05-04 17:16:13 -0700260}
Eric Laurentfb00fc72017-05-25 18:17:12 -0700261
Phil Burk58f5ce12020-08-12 14:29:10 +0000262void AudioStreamLegacy::onAudioDeviceUpdate(audio_io_handle_t /* audioIo */,
Robert Wub7f8edc2024-11-04 19:54:38 +0000263 const android::DeviceIdVector& deviceIds) {
264 // Check for empty deviceIds. Callbacks for duplicating threads returns empty devices.
265 if (deviceIds.empty()) {
266 ALOGW("%s(empty deviceIds", __func__);
Phil Burk1cd9ce62024-06-12 19:13:53 +0000267 return;
268 }
Robert Wub7f8edc2024-11-04 19:54:38 +0000269 android::DeviceIdVector oldDeviceIds = getDeviceIds();
Phil Burk7ba46552019-04-15 08:58:08 -0700270 // Device routing is a common source of errors and DISCONNECTS.
Phil Burk58f5ce12020-08-12 14:29:10 +0000271 // Please leave this log in place. If there is a bug then this might
272 // get called after the stream has been deleted so log before we
273 // touch the stream object.
Robert Wub7f8edc2024-11-04 19:54:38 +0000274 ALOGD("%s() devices %s => %s",
275 __func__, android::toString(oldDeviceIds).c_str(),
276 android::toString(deviceIds).c_str());
277 if (!oldDeviceIds.empty()
278 && !android::areDeviceIdsEqual(oldDeviceIds, deviceIds)
jiabincb212cd2022-08-24 16:50:44 -0700279 && !isDisconnected()
Phil Burk58f5ce12020-08-12 14:29:10 +0000280 ) {
Phil Burk2d5ba532017-09-06 14:36:11 -0700281 // Note that isDataCallbackActive() is affected by state so call it before DISCONNECTING.
282 // If we have a data callback and the stream is active, then ask the data callback
283 // to DISCONNECT and call the error callback.
284 if (isDataCallbackActive()) {
Robert Wub7f8edc2024-11-04 19:54:38 +0000285 ALOGD("%s() request DISCONNECT in data callback, devices %s => %s",
286 __func__, android::toString(oldDeviceIds).c_str(),
287 android::toString(deviceIds).c_str());
Phil Burk2d5ba532017-09-06 14:36:11 -0700288 // If the stream is stopped before the data callback has a chance to handle the
Phil Burkdd582922020-10-15 20:29:51 +0000289 // request then the requestStop_l() and requestPause() methods will handle it after
Phil Burk2d5ba532017-09-06 14:36:11 -0700290 // the callback has stopped.
291 mRequestDisconnect.request();
292 } else {
Robert Wub7f8edc2024-11-04 19:54:38 +0000293 ALOGD("%s() DISCONNECT the stream now, devices %s => %s",
294 __func__, android::toString(oldDeviceIds).c_str(),
295 android::toString(deviceIds).c_str());
Phil Burk2d5ba532017-09-06 14:36:11 -0700296 forceDisconnect();
Eric Laurentfb00fc72017-05-25 18:17:12 -0700297 }
298 }
Robert Wub7f8edc2024-11-04 19:54:38 +0000299 setDeviceIds(deviceIds);
Eric Laurentfb00fc72017-05-25 18:17:12 -0700300}