blob: f32ef654384eca3d23745f8fad4718b81d58c82e [file] [log] [blame]
/*
* Copyright 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "AudioStreamLegacy"
//#define LOG_NDEBUG 0
#include <utils/Log.h>
#include <stdint.h>
#include <aaudio/AAudio.h>
#include <audio_utils/primitives.h>
#include <media/AudioTrack.h>
#include <media/AudioTimestamp.h>
#include <utils/String16.h>
#include "core/AudioGlobal.h"
#include "core/AudioStream.h"
#include "legacy/AudioStreamLegacy.h"
using namespace android;
using namespace aaudio;
AudioStreamLegacy::AudioStreamLegacy()
: AudioStream() {
}
aaudio_data_callback_result_t AudioStreamLegacy::callDataCallbackFrames(uint8_t *buffer,
int32_t numFrames) {
void *finalAudioData = buffer;
if (getDirection() == AAUDIO_DIRECTION_INPUT) {
// Increment before because we already got the data from the device.
incrementFramesRead(numFrames);
finalAudioData = (void *) maybeConvertDeviceData(buffer, numFrames);
}
// Call using the AAudio callback interface.
aaudio_data_callback_result_t callbackResult = maybeCallDataCallback(finalAudioData, numFrames);
if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE
&& getDirection() == AAUDIO_DIRECTION_OUTPUT) {
// Increment after because we are going to write the data to the device.
incrementFramesWritten(numFrames);
}
return callbackResult;
}
// Implement FixedBlockProcessor
int32_t AudioStreamLegacy::onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) {
int32_t numFrames = numBytes / mBlockAdapterBytesPerFrame;
return (int32_t) callDataCallbackFrames(buffer, numFrames);
}
void AudioStreamLegacy::onNewIAudioTrack() {
ALOGD("%s stream disconnected", __func__);
forceDisconnect();
mCallbackEnabled.store(false);
}
size_t AudioStreamLegacy::onMoreData(const android::AudioTrack::Buffer& buffer) {
// This illegal size can be used to tell AudioRecord or AudioTrack to stop calling us.
// This takes advantage of them killing the stream when they see a size out of range.
// That is an undocumented behavior.
// TODO add to API in AudioRecord and AudioTrack
// TODO(b/216175830) cleanup size re-computation
const size_t SIZE_STOP_CALLBACKS = SIZE_MAX;
aaudio_data_callback_result_t callbackResult;
(void) checkForDisconnectRequest(true);
// Note that this code assumes an AudioTrack::Buffer is the same as
// AudioRecord::Buffer
// TODO define our own AudioBuffer and pass it from the subclasses.
size_t written = buffer.size();
if (getState() == AAUDIO_STREAM_STATE_DISCONNECTED) {
ALOGW("%s() data, stream disconnected", __func__);
// This will kill the stream and prevent it from being restarted.
// That is OK because the stream is disconnected.
written = SIZE_STOP_CALLBACKS;
} else if (!mCallbackEnabled.load()) {
ALOGW("%s() no data because callback disabled, set size=0", __func__);
// Do NOT use SIZE_STOP_CALLBACKS here because that will kill the stream and
// prevent it from being restarted. This can occur because of a race condition
// caused by Legacy callbacks running after the track is "stopped".
written = 0;
} else {
if (buffer.getFrameCount() == 0) {
ALOGW("%s() data, frameCount is zero", __func__);
return written;
}
// If the caller specified an exact size then use a block size adapter.
if (mBlockAdapter != nullptr) {
int32_t byteCount = buffer.getFrameCount() * getBytesPerDeviceFrame();
callbackResult = mBlockAdapter->processVariableBlock(
buffer.data(), byteCount);
} else {
// Call using the AAudio callback interface.
callbackResult = callDataCallbackFrames(buffer.data(),
buffer.getFrameCount());
}
if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE) {
written = buffer.getFrameCount() * getBytesPerDeviceFrame();
} else {
if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) {
ALOGD("%s() callback returned AAUDIO_CALLBACK_RESULT_STOP", __func__);
} else {
ALOGW("%s() callback returned invalid result = %d",
__func__, callbackResult);
}
written = 0;
systemStopInternal();
// Disable the callback just in case the system keeps trying to call us.
mCallbackEnabled.store(false);
}
if (processCommands() != AAUDIO_OK) {
forceDisconnect();
mCallbackEnabled.store(false);
}
}
return written;
}
// TODO (b/216175830) this method is duplicated in order to ease refactoring which will
// reconsolidate.
size_t AudioStreamLegacy::onMoreData(const android::AudioRecord::Buffer& buffer) {
// This illegal size can be used to tell AudioRecord or AudioTrack to stop calling us.
// This takes advantage of them killing the stream when they see a size out of range.
// That is an undocumented behavior.
// TODO add to API in AudioRecord and AudioTrack
const size_t SIZE_STOP_CALLBACKS = SIZE_MAX;
aaudio_data_callback_result_t callbackResult;
(void) checkForDisconnectRequest(true);
// Note that this code assumes an AudioTrack::Buffer is the same as
// AudioRecord::Buffer
// TODO define our own AudioBuffer and pass it from the subclasses.
size_t written = buffer.size();
if (getState() == AAUDIO_STREAM_STATE_DISCONNECTED) {
ALOGW("%s() data, stream disconnected", __func__);
// This will kill the stream and prevent it from being restarted.
// That is OK because the stream is disconnected.
written = SIZE_STOP_CALLBACKS;
} else if (!mCallbackEnabled.load()) {
ALOGW("%s() no data because callback disabled, set size=0", __func__);
// Do NOT use SIZE_STOP_CALLBACKS here because that will kill the stream and
// prevent it from being restarted. This can occur because of a race condition
// caused by Legacy callbacks running after the track is "stopped".
written = 0;
} else {
if (buffer.getFrameCount() == 0) {
ALOGW("%s() data, frameCount is zero", __func__);
return written;
}
// If the caller specified an exact size then use a block size adapter.
if (mBlockAdapter != nullptr) {
int32_t byteCount = buffer.getFrameCount() * getBytesPerDeviceFrame();
callbackResult = mBlockAdapter->processVariableBlock(
buffer.data(), byteCount);
} else {
// Call using the AAudio callback interface.
callbackResult = callDataCallbackFrames(buffer.data(),
buffer.getFrameCount());
}
if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE) {
written = buffer.getFrameCount() * getBytesPerDeviceFrame();
} else {
if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) {
ALOGD("%s() callback returned AAUDIO_CALLBACK_RESULT_STOP", __func__);
} else {
ALOGW("%s() callback returned invalid result = %d",
__func__, callbackResult);
}
written = 0;
systemStopInternal();
// Disable the callback just in case the system keeps trying to call us.
mCallbackEnabled.store(false);
}
if (processCommands() != AAUDIO_OK) {
forceDisconnect();
mCallbackEnabled.store(false);
}
}
return written;
}
aaudio_result_t AudioStreamLegacy::checkForDisconnectRequest(bool errorCallbackEnabled) {
if (mRequestDisconnect.isRequested()) {
ALOGD("checkForDisconnectRequest() mRequestDisconnect acknowledged");
forceDisconnect(errorCallbackEnabled);
mRequestDisconnect.acknowledge();
mCallbackEnabled.store(false);
return AAUDIO_ERROR_DISCONNECTED;
} else {
return AAUDIO_OK;
}
}
void AudioStreamLegacy::forceDisconnect(bool errorCallbackEnabled) {
// There is no need to disconnect if already in these states.
if (getState() != AAUDIO_STREAM_STATE_DISCONNECTED
&& getState() != AAUDIO_STREAM_STATE_CLOSING
&& getState() != AAUDIO_STREAM_STATE_CLOSED
) {
setState(AAUDIO_STREAM_STATE_DISCONNECTED);
if (errorCallbackEnabled) {
maybeCallErrorCallback(AAUDIO_ERROR_DISCONNECTED);
}
}
}
aaudio_result_t AudioStreamLegacy::getBestTimestamp(clockid_t clockId,
int64_t *framePosition,
int64_t *timeNanoseconds,
ExtendedTimestamp *extendedTimestamp) {
int timebase;
switch (clockId) {
case CLOCK_BOOTTIME:
timebase = ExtendedTimestamp::TIMEBASE_BOOTTIME;
break;
case CLOCK_MONOTONIC:
timebase = ExtendedTimestamp::TIMEBASE_MONOTONIC;
break;
default:
ALOGE("getTimestamp() - Unrecognized clock type %d", (int) clockId);
return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
break;
}
ExtendedTimestamp::Location location = ExtendedTimestamp::Location::LOCATION_INVALID;
int64_t localPosition;
status_t status = extendedTimestamp->getBestTimestamp(&localPosition, timeNanoseconds,
timebase, &location);
if (status == OK) {
// use MonotonicCounter to prevent retrograde motion.
mTimestampPosition.update32((int32_t) localPosition);
*framePosition = mTimestampPosition.get();
}
// ALOGD("getBestTimestamp() fposition: server = %6lld, kernel = %6lld, location = %d",
// (long long) extendedTimestamp->mPosition[ExtendedTimestamp::Location::LOCATION_SERVER],
// (long long) extendedTimestamp->mPosition[ExtendedTimestamp::Location::LOCATION_KERNEL],
// (int)location);
return AAudioConvert_androidToAAudioResult(status);
}
void AudioStreamLegacy::onAudioDeviceUpdate(audio_io_handle_t /* audioIo */,
audio_port_handle_t deviceId) {
// Device routing is a common source of errors and DISCONNECTS.
// Please leave this log in place. If there is a bug then this might
// get called after the stream has been deleted so log before we
// touch the stream object.
ALOGD("%s(deviceId = %d)", __func__, (int)deviceId);
if (getDeviceId() != AAUDIO_UNSPECIFIED
&& getDeviceId() != deviceId
&& getState() != AAUDIO_STREAM_STATE_DISCONNECTED
) {
// Note that isDataCallbackActive() is affected by state so call it before DISCONNECTING.
// If we have a data callback and the stream is active, then ask the data callback
// to DISCONNECT and call the error callback.
if (isDataCallbackActive()) {
ALOGD("%s() request DISCONNECT in data callback, device %d => %d",
__func__, (int) getDeviceId(), (int) deviceId);
// If the stream is stopped before the data callback has a chance to handle the
// request then the requestStop_l() and requestPause() methods will handle it after
// the callback has stopped.
mRequestDisconnect.request();
} else {
ALOGD("%s() DISCONNECT the stream now, device %d => %d",
__func__, (int) getDeviceId(), (int) deviceId);
forceDisconnect();
}
}
setDeviceId(deviceId);
}