e9ed2721f4
Added getRenderPosition() API to IAudioFlinger to retreive number of audio frames written by AudioFlinger to audio HAL and by DSP to DAC. Added getRenderPosition() API to AudioHardwareInterface to retreive number of audio frames written by DSP to DAC. Exposed AudioTrack::getPosition() to AudioSink() to make it available to media player. Removed excessive log in AudioHardwareGeneric.
467 lines
12 KiB
C++
467 lines
12 KiB
C++
/*
|
|
* Copyright (C) 2008 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.
|
|
*/
|
|
|
|
#include <math.h>
|
|
|
|
//#define LOG_NDEBUG 0
|
|
#define LOG_TAG "A2dpAudioInterface"
|
|
#include <utils/Log.h>
|
|
#include <utils/String8.h>
|
|
|
|
#include "A2dpAudioInterface.h"
|
|
#include "audio/liba2dp.h"
|
|
|
|
|
|
namespace android {
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
//AudioHardwareInterface* A2dpAudioInterface::createA2dpInterface()
|
|
//{
|
|
// AudioHardwareInterface* hw = 0;
|
|
//
|
|
// hw = AudioHardwareInterface::create();
|
|
// LOGD("new A2dpAudioInterface(hw: %p)", hw);
|
|
// hw = new A2dpAudioInterface(hw);
|
|
// return hw;
|
|
//}
|
|
|
|
A2dpAudioInterface::A2dpAudioInterface(AudioHardwareInterface* hw) :
|
|
mOutput(0), mHardwareInterface(hw), mBluetoothEnabled(true), mSuspended(false)
|
|
{
|
|
}
|
|
|
|
A2dpAudioInterface::~A2dpAudioInterface()
|
|
{
|
|
closeOutputStream((AudioStreamOut *)mOutput);
|
|
delete mHardwareInterface;
|
|
}
|
|
|
|
status_t A2dpAudioInterface::initCheck()
|
|
{
|
|
if (mHardwareInterface == 0) return NO_INIT;
|
|
return mHardwareInterface->initCheck();
|
|
}
|
|
|
|
AudioStreamOut* A2dpAudioInterface::openOutputStream(
|
|
uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status)
|
|
{
|
|
if (!AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices)) {
|
|
LOGV("A2dpAudioInterface::openOutputStream() open HW device: %x", devices);
|
|
return mHardwareInterface->openOutputStream(devices, format, channels, sampleRate, status);
|
|
}
|
|
|
|
status_t err = 0;
|
|
|
|
// only one output stream allowed
|
|
if (mOutput) {
|
|
if (status)
|
|
*status = -1;
|
|
return NULL;
|
|
}
|
|
|
|
// create new output stream
|
|
A2dpAudioStreamOut* out = new A2dpAudioStreamOut();
|
|
if ((err = out->set(devices, format, channels, sampleRate)) == NO_ERROR) {
|
|
mOutput = out;
|
|
mOutput->setBluetoothEnabled(mBluetoothEnabled);
|
|
mOutput->setSuspended(mSuspended);
|
|
} else {
|
|
delete out;
|
|
}
|
|
|
|
if (status)
|
|
*status = err;
|
|
return mOutput;
|
|
}
|
|
|
|
void A2dpAudioInterface::closeOutputStream(AudioStreamOut* out) {
|
|
if (mOutput == 0 || mOutput != out) {
|
|
mHardwareInterface->closeOutputStream(out);
|
|
}
|
|
else {
|
|
delete mOutput;
|
|
mOutput = 0;
|
|
}
|
|
}
|
|
|
|
|
|
AudioStreamIn* A2dpAudioInterface::openInputStream(
|
|
uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status,
|
|
AudioSystem::audio_in_acoustics acoustics)
|
|
{
|
|
return mHardwareInterface->openInputStream(devices, format, channels, sampleRate, status, acoustics);
|
|
}
|
|
|
|
void A2dpAudioInterface::closeInputStream(AudioStreamIn* in)
|
|
{
|
|
return mHardwareInterface->closeInputStream(in);
|
|
}
|
|
|
|
status_t A2dpAudioInterface::setMode(int mode)
|
|
{
|
|
return mHardwareInterface->setMode(mode);
|
|
}
|
|
|
|
status_t A2dpAudioInterface::setMicMute(bool state)
|
|
{
|
|
return mHardwareInterface->setMicMute(state);
|
|
}
|
|
|
|
status_t A2dpAudioInterface::getMicMute(bool* state)
|
|
{
|
|
return mHardwareInterface->getMicMute(state);
|
|
}
|
|
|
|
status_t A2dpAudioInterface::setParameters(const String8& keyValuePairs)
|
|
{
|
|
AudioParameter param = AudioParameter(keyValuePairs);
|
|
String8 value;
|
|
String8 key;
|
|
status_t status = NO_ERROR;
|
|
|
|
LOGV("setParameters() %s", keyValuePairs.string());
|
|
|
|
key = "bluetooth_enabled";
|
|
if (param.get(key, value) == NO_ERROR) {
|
|
mBluetoothEnabled = (value == "true");
|
|
if (mOutput) {
|
|
mOutput->setBluetoothEnabled(mBluetoothEnabled);
|
|
}
|
|
param.remove(key);
|
|
}
|
|
key = String8("A2dpSuspended");
|
|
if (param.get(key, value) == NO_ERROR) {
|
|
mSuspended = (value == "true");
|
|
if (mOutput) {
|
|
mOutput->setSuspended(mSuspended);
|
|
}
|
|
param.remove(key);
|
|
}
|
|
|
|
if (param.size()) {
|
|
status_t hwStatus = mHardwareInterface->setParameters(param.toString());
|
|
if (status == NO_ERROR) {
|
|
status = hwStatus;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
String8 A2dpAudioInterface::getParameters(const String8& keys)
|
|
{
|
|
AudioParameter param = AudioParameter(keys);
|
|
AudioParameter a2dpParam = AudioParameter();
|
|
String8 value;
|
|
String8 key;
|
|
|
|
key = "bluetooth_enabled";
|
|
if (param.get(key, value) == NO_ERROR) {
|
|
value = mBluetoothEnabled ? "true" : "false";
|
|
a2dpParam.add(key, value);
|
|
param.remove(key);
|
|
}
|
|
key = "A2dpSuspended";
|
|
if (param.get(key, value) == NO_ERROR) {
|
|
value = mSuspended ? "true" : "false";
|
|
a2dpParam.add(key, value);
|
|
param.remove(key);
|
|
}
|
|
|
|
String8 keyValuePairs = a2dpParam.toString();
|
|
|
|
if (param.size()) {
|
|
if (keyValuePairs != "") {
|
|
keyValuePairs += ";";
|
|
}
|
|
keyValuePairs += mHardwareInterface->getParameters(param.toString());
|
|
}
|
|
|
|
LOGV("getParameters() %s", keyValuePairs.string());
|
|
return keyValuePairs;
|
|
}
|
|
|
|
size_t A2dpAudioInterface::getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
|
|
{
|
|
return mHardwareInterface->getInputBufferSize(sampleRate, format, channelCount);
|
|
}
|
|
|
|
status_t A2dpAudioInterface::setVoiceVolume(float v)
|
|
{
|
|
return mHardwareInterface->setVoiceVolume(v);
|
|
}
|
|
|
|
status_t A2dpAudioInterface::setMasterVolume(float v)
|
|
{
|
|
return mHardwareInterface->setMasterVolume(v);
|
|
}
|
|
|
|
status_t A2dpAudioInterface::dump(int fd, const Vector<String16>& args)
|
|
{
|
|
return mHardwareInterface->dumpState(fd, args);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
A2dpAudioInterface::A2dpAudioStreamOut::A2dpAudioStreamOut() :
|
|
mFd(-1), mStandby(true), mStartCount(0), mRetryCount(0), mData(NULL),
|
|
// assume BT enabled to start, this is safe because its only the
|
|
// enabled->disabled transition we are worried about
|
|
mBluetoothEnabled(true), mDevice(0), mClosing(false), mSuspended(false)
|
|
{
|
|
// use any address by default
|
|
strcpy(mA2dpAddress, "00:00:00:00:00:00");
|
|
init();
|
|
}
|
|
|
|
status_t A2dpAudioInterface::A2dpAudioStreamOut::set(
|
|
uint32_t device, int *pFormat, uint32_t *pChannels, uint32_t *pRate)
|
|
{
|
|
int lFormat = pFormat ? *pFormat : 0;
|
|
uint32_t lChannels = pChannels ? *pChannels : 0;
|
|
uint32_t lRate = pRate ? *pRate : 0;
|
|
|
|
LOGD("A2dpAudioStreamOut::set %x, %d, %d, %d\n", device, lFormat, lChannels, lRate);
|
|
|
|
// fix up defaults
|
|
if (lFormat == 0) lFormat = format();
|
|
if (lChannels == 0) lChannels = channels();
|
|
if (lRate == 0) lRate = sampleRate();
|
|
|
|
// check values
|
|
if ((lFormat != format()) ||
|
|
(lChannels != channels()) ||
|
|
(lRate != sampleRate())){
|
|
if (pFormat) *pFormat = format();
|
|
if (pChannels) *pChannels = channels();
|
|
if (pRate) *pRate = sampleRate();
|
|
return BAD_VALUE;
|
|
}
|
|
|
|
if (pFormat) *pFormat = lFormat;
|
|
if (pChannels) *pChannels = lChannels;
|
|
if (pRate) *pRate = lRate;
|
|
|
|
mDevice = device;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
A2dpAudioInterface::A2dpAudioStreamOut::~A2dpAudioStreamOut()
|
|
{
|
|
LOGV("A2dpAudioStreamOut destructor");
|
|
standby();
|
|
close();
|
|
LOGV("A2dpAudioStreamOut destructor returning from close()");
|
|
}
|
|
|
|
ssize_t A2dpAudioInterface::A2dpAudioStreamOut::write(const void* buffer, size_t bytes)
|
|
{
|
|
Mutex::Autolock lock(mLock);
|
|
|
|
size_t remaining = bytes;
|
|
status_t status = -1;
|
|
|
|
if (!mBluetoothEnabled || mClosing || mSuspended) {
|
|
LOGV("A2dpAudioStreamOut::write(), but bluetooth disabled \
|
|
mBluetoothEnabled %d, mClosing %d, mSuspended %d",
|
|
mBluetoothEnabled, mClosing, mSuspended);
|
|
goto Error;
|
|
}
|
|
|
|
status = init();
|
|
if (status < 0)
|
|
goto Error;
|
|
|
|
while (remaining > 0) {
|
|
status = a2dp_write(mData, buffer, remaining);
|
|
if (status <= 0) {
|
|
LOGE("a2dp_write failed err: %d\n", status);
|
|
goto Error;
|
|
}
|
|
remaining -= status;
|
|
buffer = ((char *)buffer) + status;
|
|
}
|
|
|
|
mStandby = false;
|
|
|
|
return bytes;
|
|
|
|
Error:
|
|
// Simulate audio output timing in case of error
|
|
usleep(bytes * 1000000 / frameSize() / sampleRate());
|
|
|
|
return status;
|
|
}
|
|
|
|
status_t A2dpAudioInterface::A2dpAudioStreamOut::init()
|
|
{
|
|
if (!mData) {
|
|
status_t status = a2dp_init(44100, 2, &mData);
|
|
if (status < 0) {
|
|
LOGE("a2dp_init failed err: %d\n", status);
|
|
mData = NULL;
|
|
return status;
|
|
}
|
|
a2dp_set_sink(mData, mA2dpAddress);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
status_t A2dpAudioInterface::A2dpAudioStreamOut::standby()
|
|
{
|
|
int result = 0;
|
|
|
|
if (mClosing) {
|
|
LOGV("Ignore standby, closing");
|
|
return result;
|
|
}
|
|
|
|
Mutex::Autolock lock(mLock);
|
|
|
|
if (!mStandby) {
|
|
result = a2dp_stop(mData);
|
|
if (result == 0)
|
|
mStandby = true;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
status_t A2dpAudioInterface::A2dpAudioStreamOut::setParameters(const String8& keyValuePairs)
|
|
{
|
|
AudioParameter param = AudioParameter(keyValuePairs);
|
|
String8 value;
|
|
String8 key = String8("a2dp_sink_address");
|
|
status_t status = NO_ERROR;
|
|
int device;
|
|
LOGV("A2dpAudioStreamOut::setParameters() %s", keyValuePairs.string());
|
|
|
|
if (param.get(key, value) == NO_ERROR) {
|
|
if (value.length() != strlen("00:00:00:00:00:00")) {
|
|
status = BAD_VALUE;
|
|
} else {
|
|
setAddress(value.string());
|
|
}
|
|
param.remove(key);
|
|
}
|
|
key = String8("closing");
|
|
if (param.get(key, value) == NO_ERROR) {
|
|
mClosing = (value == "true");
|
|
param.remove(key);
|
|
}
|
|
key = AudioParameter::keyRouting;
|
|
if (param.getInt(key, device) == NO_ERROR) {
|
|
if (AudioSystem::isA2dpDevice((AudioSystem::audio_devices)device)) {
|
|
mDevice = device;
|
|
status = NO_ERROR;
|
|
} else {
|
|
status = BAD_VALUE;
|
|
}
|
|
param.remove(key);
|
|
}
|
|
|
|
if (param.size()) {
|
|
status = BAD_VALUE;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
String8 A2dpAudioInterface::A2dpAudioStreamOut::getParameters(const String8& keys)
|
|
{
|
|
AudioParameter param = AudioParameter(keys);
|
|
String8 value;
|
|
String8 key = String8("a2dp_sink_address");
|
|
|
|
if (param.get(key, value) == NO_ERROR) {
|
|
value = mA2dpAddress;
|
|
param.add(key, value);
|
|
}
|
|
key = AudioParameter::keyRouting;
|
|
if (param.get(key, value) == NO_ERROR) {
|
|
param.addInt(key, (int)mDevice);
|
|
}
|
|
|
|
LOGV("A2dpAudioStreamOut::getParameters() %s", param.toString().string());
|
|
return param.toString();
|
|
}
|
|
|
|
status_t A2dpAudioInterface::A2dpAudioStreamOut::setAddress(const char* address)
|
|
{
|
|
Mutex::Autolock lock(mLock);
|
|
|
|
if (strlen(address) != strlen("00:00:00:00:00:00"))
|
|
return -EINVAL;
|
|
|
|
strcpy(mA2dpAddress, address);
|
|
if (mData)
|
|
a2dp_set_sink(mData, mA2dpAddress);
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
status_t A2dpAudioInterface::A2dpAudioStreamOut::setBluetoothEnabled(bool enabled)
|
|
{
|
|
LOGD("setBluetoothEnabled %d", enabled);
|
|
|
|
Mutex::Autolock lock(mLock);
|
|
|
|
mBluetoothEnabled = enabled;
|
|
if (!enabled) {
|
|
return close_l();
|
|
}
|
|
return NO_ERROR;
|
|
}
|
|
|
|
status_t A2dpAudioInterface::A2dpAudioStreamOut::setSuspended(bool onOff)
|
|
{
|
|
LOGV("setSuspended %d", onOff);
|
|
mSuspended = onOff;
|
|
standby();
|
|
return NO_ERROR;
|
|
}
|
|
|
|
status_t A2dpAudioInterface::A2dpAudioStreamOut::close()
|
|
{
|
|
Mutex::Autolock lock(mLock);
|
|
LOGV("A2dpAudioStreamOut::close() calling close_l()");
|
|
return close_l();
|
|
}
|
|
|
|
status_t A2dpAudioInterface::A2dpAudioStreamOut::close_l()
|
|
{
|
|
if (mData) {
|
|
LOGV("A2dpAudioStreamOut::close_l() calling a2dp_cleanup(mData)");
|
|
a2dp_cleanup(mData);
|
|
mData = NULL;
|
|
}
|
|
return NO_ERROR;
|
|
}
|
|
|
|
status_t A2dpAudioInterface::A2dpAudioStreamOut::dump(int fd, const Vector<String16>& args)
|
|
{
|
|
return NO_ERROR;
|
|
}
|
|
|
|
status_t A2dpAudioInterface::A2dpAudioStreamOut::getRenderPosition(uint32_t *driverFrames)
|
|
{
|
|
//TODO: enable when supported by driver
|
|
return INVALID_OPERATION;
|
|
}
|
|
|
|
}; // namespace android
|