summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoryucliu <yucliu@google.com>2020-05-15 14:13:39 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2020-05-15 14:13:39 +0000
commit8ef72a433f74c03cef1574ddec3e79033ebb151d (patch)
tree2f7825610428d120693baab97755b211c24b346a
parent6c605adb5a129611b67d2a6d8ade943625300e16 (diff)
parent77f096abc28402ea6dd1ac3dd1e925135d78eaa5 (diff)
downloadatv-8ef72a433f74c03cef1574ddec3e79033ebb151d.tar.gz
[AudioProxy] client library -- HAL interfaces am: 77f096abc2
Change-Id: I022432692d97e9b54314acb1739b165c2e792110
-rw-r--r--audio_proxy/Android.bp3
-rw-r--r--audio_proxy/AudioProxyDevice.cpp61
-rw-r--r--audio_proxy/AudioProxyDevice.h16
-rw-r--r--audio_proxy/AudioProxyManager.cpp3
-rw-r--r--audio_proxy/AudioProxyStreamOut.cpp248
-rw-r--r--audio_proxy/AudioProxyStreamOut.h90
-rw-r--r--audio_proxy/BusDeviceImpl.cpp13
-rw-r--r--audio_proxy/HidlTypeUtil.cpp57
-rw-r--r--audio_proxy/HidlTypeUtil.h38
-rw-r--r--audio_proxy/StreamOutImpl.cpp460
-rw-r--r--audio_proxy/StreamOutImpl.h128
-rw-r--r--audio_proxy/public/audio_proxy.h172
12 files changed, 1288 insertions, 1 deletions
diff --git a/audio_proxy/Android.bp b/audio_proxy/Android.bp
index ed82228..3c926bd 100644
--- a/audio_proxy/Android.bp
+++ b/audio_proxy/Android.bp
@@ -34,7 +34,10 @@ cc_defaults {
srcs: [
"AudioProxyDevice.cpp",
"AudioProxyManager.cpp",
+ "AudioProxyStreamOut.cpp",
"BusDeviceImpl.cpp",
+ "HidlTypeUtil.cpp",
+ "StreamOutImpl.cpp",
],
header_libs: [
diff --git a/audio_proxy/AudioProxyDevice.cpp b/audio_proxy/AudioProxyDevice.cpp
index 657073e..16cdcf4 100644
--- a/audio_proxy/AudioProxyDevice.cpp
+++ b/audio_proxy/AudioProxyDevice.cpp
@@ -18,8 +18,42 @@
#include <utils/Log.h>
+#include "AudioProxyStreamOut.h"
+#include "HidlTypeUtil.h"
+
+#define CHECK_API(func) \
+ do { \
+ if (!stream->func) { \
+ ALOGD("Undefined API %s", #func); \
+ return false; \
+ } \
+ } while (0)
+
namespace audio_proxy {
namespace CPP_VERSION {
+namespace {
+bool isValidStreamOut(const audio_proxy_stream_out_t* stream) {
+ CHECK_API(get_buffer_size);
+ CHECK_API(get_frame_count);
+ CHECK_API(get_supported_sample_rates);
+ CHECK_API(get_sample_rate);
+ CHECK_API(get_supported_channel_masks);
+ CHECK_API(get_channel_mask);
+ CHECK_API(get_supported_formats);
+ CHECK_API(get_format);
+ CHECK_API(get_latency);
+ CHECK_API(standby);
+ CHECK_API(pause);
+ CHECK_API(resume);
+ CHECK_API(flush);
+ CHECK_API(write);
+ CHECK_API(get_presentation_position);
+ CHECK_API(set_parameters);
+ CHECK_API(get_parameters);
+
+ return true;
+}
+} // namespace
AudioProxyDevice::AudioProxyDevice(audio_proxy_device_t* device)
: mDevice(device) {}
@@ -30,5 +64,32 @@ const char* AudioProxyDevice::getAddress() {
return mDevice->get_address(mDevice);
}
+Result AudioProxyDevice::openOutputStream(
+ hidl_bitfield<AudioOutputFlag> flags, const AudioConfig& hidlConfig,
+ std::unique_ptr<AudioProxyStreamOut>* streamOut,
+ AudioConfig* hidlConfigOut) {
+ audio_proxy_config_t config = toAudioProxyConfig(hidlConfig);
+
+ audio_proxy_stream_out_t* stream = nullptr;
+ int ret = mDevice->open_output_stream(
+ mDevice, static_cast<audio_proxy_output_flags_t>(flags), &config,
+ &stream);
+
+ if (stream) {
+ if (!isValidStreamOut(stream)) {
+ mDevice->close_output_stream(mDevice, stream);
+ return Result::NOT_SUPPORTED;
+ }
+
+ *streamOut = std::make_unique<AudioProxyStreamOut>(stream, mDevice);
+ }
+
+ // Pass the config out even if open_output_stream returns error, as the
+ // suggested config, so that audio service can re-open the stream with
+ // suggested config.
+ *hidlConfigOut = toHidlAudioConfig(config);
+ return toResult(ret);
+}
+
} // namespace CPP_VERSION
} // namespace audio_proxy
diff --git a/audio_proxy/AudioProxyDevice.h b/audio_proxy/AudioProxyDevice.h
index 010335b..d801fc9 100644
--- a/audio_proxy/AudioProxyDevice.h
+++ b/audio_proxy/AudioProxyDevice.h
@@ -18,9 +18,20 @@
#include "public/audio_proxy.h"
+// clang-format off
+#include PATH(android/hardware/audio/FILE_VERSION/types.h)
+#include PATH(android/hardware/audio/common/FILE_VERSION/types.h)
+// clang-format on
+
namespace audio_proxy {
namespace CPP_VERSION {
+using ::android::hardware::hidl_bitfield;
+using namespace ::android::hardware::audio::CPP_VERSION;
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+
+class AudioProxyStreamOut;
+
// C++ friendly wrapper of audio_proxy_device.
class AudioProxyDevice final {
public:
@@ -29,6 +40,11 @@ class AudioProxyDevice final {
const char* getAddress();
+ Result openOutputStream(hidl_bitfield<AudioOutputFlag> flags,
+ const AudioConfig& config,
+ std::unique_ptr<AudioProxyStreamOut>* streamOut,
+ AudioConfig* configOut);
+
private:
audio_proxy_device_t* const mDevice;
};
diff --git a/audio_proxy/AudioProxyManager.cpp b/audio_proxy/AudioProxyManager.cpp
index b507046..b84a206 100644
--- a/audio_proxy/AudioProxyManager.cpp
+++ b/audio_proxy/AudioProxyManager.cpp
@@ -43,7 +43,8 @@ namespace CPP_VERSION {
namespace {
bool checkDevice(audio_proxy_device_t* device) {
- return device && device->get_address;
+ return device && device->get_address && device->open_output_stream &&
+ device->close_output_stream;
}
class DeathRecipient;
diff --git a/audio_proxy/AudioProxyStreamOut.cpp b/audio_proxy/AudioProxyStreamOut.cpp
new file mode 100644
index 0000000..97a40dd
--- /dev/null
+++ b/audio_proxy/AudioProxyStreamOut.cpp
@@ -0,0 +1,248 @@
+// Copyright (C) 2020 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 "AudioProxyStreamOut.h"
+
+#include "HidlTypeUtil.h"
+
+#define CHECK_OPT_API(func) \
+ do { \
+ if (!mStream->func) return Result::NOT_SUPPORTED; \
+ } while (0)
+
+namespace audio_proxy {
+namespace CPP_VERSION {
+namespace {
+
+template <typename T>
+size_t getArraySize(const T* const arr, T terminator) {
+ if (!arr) {
+ return 0;
+ }
+
+ const T* ptr = arr;
+ while (*ptr != terminator) {
+ ptr++;
+ }
+
+ return ptr - arr;
+}
+
+template <typename T, typename H>
+hidl_vec<H> convertToHidlVec(const T* const arr, T terminator) {
+ const size_t size = getArraySize(arr, terminator);
+ hidl_vec<H> vec(size);
+
+ for (size_t i = 0; i < size; i++) {
+ vec[i] = H(arr[i]);
+ }
+
+ return vec;
+}
+
+std::vector<audio_proxy_key_val_t> buildKeyValVec(
+ const hidl_vec<ParameterValue>& parameters) {
+ std::vector<audio_proxy_key_val_t> kvVec(parameters.size() + 1);
+
+ for (size_t i = 0; i < parameters.size(); i++) {
+ kvVec[i] = {parameters[i].key.c_str(), parameters[i].value.c_str()};
+ }
+
+ // Terminator.
+ kvVec.back() = {};
+ return kvVec;
+}
+
+std::vector<const char*> buildKeyVec(const hidl_vec<hidl_string>& keys) {
+ std::vector<const char*> keyVec(keys.size() + 1);
+ for (size_t i = 0; i < keys.size(); i++) {
+ keyVec[i] = keys[i].c_str();
+ }
+
+ // Terminator.
+ keyVec.back() = nullptr;
+ return keyVec;
+}
+
+void onParametersAvailable(void* obj, const audio_proxy_key_val_t* params) {
+ std::vector<ParameterValue>* results =
+ static_cast<std::vector<ParameterValue>*>(obj);
+ while (params->key != nullptr) {
+ ParameterValue result;
+ result.key = params->key;
+ result.value = params->val;
+ results->push_back(result);
+ params++;
+ }
+}
+} // namespace
+
+AudioProxyStreamOut::AudioProxyStreamOut(audio_proxy_stream_out_t* stream,
+ audio_proxy_device_t* device)
+ : mStream(stream), mDevice(device) {}
+
+AudioProxyStreamOut::~AudioProxyStreamOut() {
+ mDevice->close_output_stream(mDevice, mStream);
+}
+
+uint64_t AudioProxyStreamOut::getFrameCount() const {
+ return mStream->get_frame_count(mStream);
+}
+
+uint32_t AudioProxyStreamOut::getSampleRate() const {
+ return mStream->get_sample_rate(mStream);
+}
+
+Result AudioProxyStreamOut::setSampleRate(uint32_t rate) {
+ CHECK_OPT_API(set_sample_rate);
+ return toResult(mStream->set_sample_rate(mStream, rate));
+}
+
+hidl_vec<uint32_t> AudioProxyStreamOut::getSupportedSampleRates(
+ AudioFormat format) const {
+ return convertToHidlVec<uint32_t, uint32_t>(
+ mStream->get_supported_sample_rates(
+ mStream, static_cast<audio_proxy_format_t>(format)),
+ 0);
+}
+
+size_t AudioProxyStreamOut::getBufferSize() const {
+ return mStream->get_buffer_size(mStream);
+}
+
+hidl_bitfield<AudioChannelMask> AudioProxyStreamOut::getChannelMask() const {
+ return hidl_bitfield<AudioChannelMask>(mStream->get_channel_mask(mStream));
+}
+
+Result AudioProxyStreamOut::setChannelMask(
+ hidl_bitfield<AudioChannelMask> mask) {
+ CHECK_OPT_API(set_channel_mask);
+ return toResult(mStream->set_channel_mask(
+ mStream, static_cast<audio_proxy_channel_mask_t>(mask)));
+}
+
+hidl_vec<hidl_bitfield<AudioChannelMask>>
+AudioProxyStreamOut::getSupportedChannelMasks(AudioFormat format) const {
+ const audio_proxy_channel_mask_t* channelMasks =
+ mStream->get_supported_channel_masks(
+ mStream, static_cast<audio_proxy_format_t>(format));
+
+ return convertToHidlVec<audio_proxy_channel_mask_t,
+ hidl_bitfield<AudioChannelMask>>(
+ channelMasks, AUDIO_PROXY_CHANNEL_INVALID);
+}
+
+AudioFormat AudioProxyStreamOut::getFormat() const {
+ return AudioFormat(mStream->get_format(mStream));
+}
+
+hidl_vec<AudioFormat> AudioProxyStreamOut::getSupportedFormats() const {
+ return convertToHidlVec<audio_proxy_format_t, AudioFormat>(
+ mStream->get_supported_formats(mStream), AUDIO_PROXY_FORMAT_INVALID);
+}
+
+Result AudioProxyStreamOut::setFormat(AudioFormat format) {
+ CHECK_OPT_API(set_format);
+ return toResult(
+ mStream->set_format(mStream, static_cast<audio_proxy_format_t>(format)));
+}
+
+Result AudioProxyStreamOut::standby() {
+ return toResult(mStream->standby(mStream));
+}
+
+Result AudioProxyStreamOut::setParameters(
+ const hidl_vec<ParameterValue>& context,
+ const hidl_vec<ParameterValue>& parameters) {
+ std::vector<audio_proxy_key_val_t> contextKvVec = buildKeyValVec(context);
+ std::vector<audio_proxy_key_val_t> parameterKvVec =
+ buildKeyValVec(parameters);
+ return toResult(mStream->set_parameters(mStream, contextKvVec.data(),
+ parameterKvVec.data()));
+}
+
+hidl_vec<ParameterValue> AudioProxyStreamOut::getParameters(
+ const hidl_vec<ParameterValue>& context,
+ const hidl_vec<hidl_string>& keys) const {
+ std::vector<audio_proxy_key_val_t> contextKvVec = buildKeyValVec(context);
+ std::vector<const char*> keyVec = buildKeyVec(keys);
+
+ std::vector<ParameterValue> results;
+ results.reserve(keys.size());
+
+ mStream->get_parameters(mStream, contextKvVec.data(), keyVec.data(),
+ onParametersAvailable, &results);
+
+ return hidl_vec<ParameterValue>(results);
+}
+
+ssize_t AudioProxyStreamOut::write(const void* buffer, size_t bytes) {
+ return mStream->write(mStream, buffer, bytes);
+}
+
+uint32_t AudioProxyStreamOut::getLatency() const {
+ return mStream->get_latency(mStream);
+}
+
+Result AudioProxyStreamOut::getRenderPosition(uint32_t* dsp_frames) const {
+ CHECK_OPT_API(get_render_position);
+ return toResult(mStream->get_render_position(mStream, dsp_frames));
+}
+
+Result AudioProxyStreamOut::getNextWriteTimestamp(int64_t* timestamp) const {
+ CHECK_OPT_API(get_next_write_timestamp);
+ return toResult(mStream->get_next_write_timestamp(mStream, timestamp));
+}
+
+Result AudioProxyStreamOut::getPresentationPosition(uint64_t* frames,
+ TimeSpec* timestamp) const {
+ struct timespec ts;
+ int ret = mStream->get_presentation_position(mStream, frames, &ts);
+ if (ret != 0) {
+ return toResult(ret);
+ }
+
+ timestamp->tvSec = ts.tv_sec;
+ timestamp->tvNSec = ts.tv_nsec;
+ return Result::OK;
+}
+
+Result AudioProxyStreamOut::pause() {
+ return toResult(mStream->pause(mStream));
+}
+
+Result AudioProxyStreamOut::resume() {
+ return toResult(mStream->resume(mStream));
+}
+
+bool AudioProxyStreamOut::supportsDrain() const {
+ return mStream->drain != nullptr;
+}
+
+Result AudioProxyStreamOut::drain(AudioDrain type) {
+ return toResult(
+ mStream->drain(mStream, static_cast<audio_proxy_drain_type_t>(type)));
+}
+
+Result AudioProxyStreamOut::flush() {
+ return toResult(mStream->flush(mStream));
+}
+
+Result AudioProxyStreamOut::setVolume(float left, float right) {
+ CHECK_OPT_API(set_volume);
+ return toResult(mStream->set_volume(mStream, left, right));
+}
+
+} // namespace CPP_VERSION
+} // namespace audio_proxy
diff --git a/audio_proxy/AudioProxyStreamOut.h b/audio_proxy/AudioProxyStreamOut.h
new file mode 100644
index 0000000..56ad6a2
--- /dev/null
+++ b/audio_proxy/AudioProxyStreamOut.h
@@ -0,0 +1,90 @@
+// Copyright (C) 2020 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.
+
+#pragma once
+
+#include <memory>
+
+// clang-format off
+#include PATH(android/hardware/audio/FILE_VERSION/types.h)
+#include PATH(android/hardware/audio/common/FILE_VERSION/types.h)
+// clang-format on
+
+#include "public/audio_proxy.h"
+
+namespace audio_proxy {
+namespace CPP_VERSION {
+
+using ::android::hardware::hidl_bitfield;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+using namespace ::android::hardware::audio::CPP_VERSION;
+
+// C++ friendly wrapper of audio_proxy_stream_out. It handles type conversion
+// between C type and hidl type.
+class AudioProxyStreamOut final {
+ public:
+ AudioProxyStreamOut(audio_proxy_stream_out_t* stream,
+ audio_proxy_device_t* device);
+ ~AudioProxyStreamOut();
+
+ size_t getBufferSize() const;
+ uint64_t getFrameCount() const;
+
+ hidl_vec<uint32_t> getSupportedSampleRates(AudioFormat format) const;
+ uint32_t getSampleRate() const;
+ Result setSampleRate(uint32_t rate);
+
+ hidl_vec<hidl_bitfield<AudioChannelMask>> getSupportedChannelMasks(
+ AudioFormat format) const;
+ hidl_bitfield<AudioChannelMask> getChannelMask() const;
+ Result setChannelMask(hidl_bitfield<AudioChannelMask> mask);
+
+ hidl_vec<AudioFormat> getSupportedFormats() const;
+ AudioFormat getFormat() const;
+ Result setFormat(AudioFormat format);
+
+ Result standby();
+
+ Result setParameters(const hidl_vec<ParameterValue>& context,
+ const hidl_vec<ParameterValue>& parameters);
+ hidl_vec<ParameterValue> getParameters(
+ const hidl_vec<ParameterValue>& context,
+ const hidl_vec<hidl_string>& keys) const;
+
+ uint32_t getLatency() const;
+ ssize_t write(const void* buffer, size_t bytes);
+ Result getRenderPosition(uint32_t* dsp_frames) const;
+ Result getNextWriteTimestamp(int64_t* timestamp) const;
+ Result getPresentationPosition(uint64_t* frames, TimeSpec* timestamp) const;
+
+ Result pause();
+ Result resume();
+
+ bool supportsDrain() const;
+ Result drain(AudioDrain type);
+
+ Result flush();
+
+ Result setVolume(float left, float right);
+
+ private:
+ audio_proxy_stream_out_t* const mStream;
+ audio_proxy_device_t* const mDevice;
+};
+
+} // namespace CPP_VERSION
+} // namespace audio_proxy
diff --git a/audio_proxy/BusDeviceImpl.cpp b/audio_proxy/BusDeviceImpl.cpp
index a3b13a9..ee53baf 100644
--- a/audio_proxy/BusDeviceImpl.cpp
+++ b/audio_proxy/BusDeviceImpl.cpp
@@ -19,6 +19,8 @@
#include <utils/Log.h>
#include "AudioProxyDevice.h"
+#include "AudioProxyStreamOut.h"
+#include "StreamOutImpl.h"
using ::android::hardware::Void;
@@ -32,6 +34,17 @@ Return<void> BusDeviceImpl::openOutputStream(
int32_t ioHandle, const DeviceAddress& device, const AudioConfig& config,
hidl_bitfield<AudioOutputFlag> flags, const SourceMetadata& sourceMetadata,
openOutputStream_cb _hidl_cb) {
+ std::unique_ptr<AudioProxyStreamOut> stream;
+ AudioConfig suggestedConfig;
+
+ Result res =
+ mDevice->openOutputStream(flags, config, &stream, &suggestedConfig);
+ ALOGE_IF(res != Result::OK, "Open output stream error.");
+
+ // Still pass `suggestedConfig` back when `openOutputStream` returns error,
+ // so that audio service can re-open the stream with a new config.
+ _hidl_cb(res, stream ? new StreamOutImpl(std::move(stream)) : nullptr,
+ suggestedConfig);
return Void();
}
diff --git a/audio_proxy/HidlTypeUtil.cpp b/audio_proxy/HidlTypeUtil.cpp
new file mode 100644
index 0000000..3eb3dcb
--- /dev/null
+++ b/audio_proxy/HidlTypeUtil.cpp
@@ -0,0 +1,57 @@
+// Copyright (C) 2020 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 "HidlTypeUtil.h"
+
+#include <error.h>
+
+using ::android::hardware::hidl_bitfield;
+
+namespace audio_proxy {
+namespace CPP_VERSION {
+
+Result toResult(int res) {
+ switch (res) {
+ case 0:
+ return Result::OK;
+ case EINVAL:
+ return Result::INVALID_ARGUMENTS;
+ case ENOSYS:
+ return Result::NOT_SUPPORTED;
+ default:
+ return Result::INVALID_STATE;
+ }
+}
+
+AudioConfig toHidlAudioConfig(const audio_proxy_config_t& config) {
+ AudioConfig hidlConfig;
+ hidlConfig.sampleRateHz = config.sample_rate;
+ hidlConfig.channelMask = hidl_bitfield<AudioChannelMask>(config.channel_mask);
+ hidlConfig.format = AudioFormat(config.format);
+ hidlConfig.frameCount = config.frame_count;
+ return hidlConfig;
+}
+
+audio_proxy_config_t toAudioProxyConfig(const AudioConfig& hidlConfig) {
+ audio_proxy_config_t config = {};
+ config.sample_rate = hidlConfig.sampleRateHz;
+ config.channel_mask =
+ static_cast<audio_proxy_channel_mask_t>(hidlConfig.channelMask);
+ config.format = static_cast<audio_proxy_format_t>(hidlConfig.format);
+ config.frame_count = hidlConfig.frameCount;
+ return config;
+}
+
+} // namespace CPP_VERSION
+} // namespace audio_proxy \ No newline at end of file
diff --git a/audio_proxy/HidlTypeUtil.h b/audio_proxy/HidlTypeUtil.h
new file mode 100644
index 0000000..138fd5d
--- /dev/null
+++ b/audio_proxy/HidlTypeUtil.h
@@ -0,0 +1,38 @@
+// Copyright (C) 2020 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.
+
+#pragma once
+
+#include "public/audio_proxy.h"
+
+// clang-format off
+#include PATH(android/hardware/audio/FILE_VERSION/types.h)
+#include PATH(android/hardware/audio/common/FILE_VERSION/types.h)
+// clang-format on
+
+namespace audio_proxy {
+namespace CPP_VERSION {
+
+using namespace ::android::hardware::audio::CPP_VERSION;
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+
+// Convert from C type to HIDL type.
+Result toResult(int res);
+AudioConfig toHidlAudioConfig(const audio_proxy_config_t& config);
+
+// Convert from HIDL type to C type.
+audio_proxy_config_t toAudioProxyConfig(const AudioConfig& hidlConfig);
+
+} // namespace CPP_VERSION
+} // namespace audio_proxy \ No newline at end of file
diff --git a/audio_proxy/StreamOutImpl.cpp b/audio_proxy/StreamOutImpl.cpp
new file mode 100644
index 0000000..eabc1e3
--- /dev/null
+++ b/audio_proxy/StreamOutImpl.cpp
@@ -0,0 +1,460 @@
+// Copyright (C) 2020 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 "audio_proxy_client"
+
+#include "StreamOutImpl.h"
+
+#include <system/audio.h>
+#include <time.h>
+#include <utils/Log.h>
+
+#include <cstring>
+
+#include "AudioProxyStreamOut.h"
+
+using ::android::status_t;
+
+namespace audio_proxy {
+namespace CPP_VERSION {
+namespace {
+// 1GB
+constexpr uint32_t kMaxBufferSize = 1 << 30;
+
+void deleteEventFlag(EventFlag* obj) {
+ if (!obj) {
+ return;
+ }
+
+ status_t status = EventFlag::deleteEventFlag(&obj);
+ ALOGE_IF(status, "write MQ event flag deletion error: %s", strerror(-status));
+}
+
+class WriteThread : public Thread {
+ public:
+ // WriteThread's lifespan never exceeds StreamOut's lifespan.
+ WriteThread(std::atomic<bool>* stop, AudioProxyStreamOut* stream,
+ StreamOutImpl::CommandMQ* commandMQ,
+ StreamOutImpl::DataMQ* dataMQ, StreamOutImpl::StatusMQ* statusMQ,
+ EventFlag* eventFlag);
+
+ ~WriteThread() override;
+
+ private:
+ bool threadLoop() override;
+
+ IStreamOut::WriteStatus doGetLatency();
+ IStreamOut::WriteStatus doGetPresentationPosition();
+ IStreamOut::WriteStatus doWrite();
+
+ std::atomic<bool>* const mStop;
+ AudioProxyStreamOut* mStream;
+ StreamOutImpl::CommandMQ* const mCommandMQ;
+ StreamOutImpl::DataMQ* const mDataMQ;
+ StreamOutImpl::StatusMQ* const mStatusMQ;
+ EventFlag* const mEventFlag;
+ const std::unique_ptr<uint8_t[]> mBuffer;
+};
+
+WriteThread::WriteThread(std::atomic<bool>* stop, AudioProxyStreamOut* stream,
+ StreamOutImpl::CommandMQ* commandMQ,
+ StreamOutImpl::DataMQ* dataMQ,
+ StreamOutImpl::StatusMQ* statusMQ,
+ EventFlag* eventFlag)
+ : Thread(false /*canCallJava*/),
+ mStop(stop),
+ mStream(stream),
+ mCommandMQ(commandMQ),
+ mDataMQ(dataMQ),
+ mStatusMQ(statusMQ),
+ mEventFlag(eventFlag),
+ mBuffer(new uint8_t[mDataMQ->getQuantumCount()]) {}
+
+WriteThread::~WriteThread() = default;
+
+IStreamOut::WriteStatus WriteThread::doWrite() {
+ const size_t availToRead = mDataMQ->availableToRead();
+ IStreamOut::WriteStatus status;
+ status.replyTo = IStreamOut::WriteCommand::WRITE;
+ status.retval = Result::OK;
+ status.reply.written = 0;
+ if (mDataMQ->read(&mBuffer[0], availToRead)) {
+ status.reply.written = availToRead;
+ ssize_t writeResult = mStream->write(&mBuffer[0], availToRead);
+ if (writeResult >= 0) {
+ status.reply.written = writeResult;
+ ALOGW_IF(writeResult < availToRead,
+ "Stream doesn't write all the bytes. Drop the unwritten bytes.");
+ } else {
+ status.retval = Result::INVALID_STATE;
+ }
+ }
+
+ return status;
+}
+
+IStreamOut::WriteStatus WriteThread::doGetPresentationPosition() {
+ IStreamOut::WriteStatus status;
+ status.replyTo = IStreamOut::WriteCommand::GET_PRESENTATION_POSITION;
+ status.retval = mStream->getPresentationPosition(
+ &status.reply.presentationPosition.frames,
+ &status.reply.presentationPosition.timeStamp);
+ return status;
+}
+
+IStreamOut::WriteStatus WriteThread::doGetLatency() {
+ IStreamOut::WriteStatus status;
+ status.replyTo = IStreamOut::WriteCommand::GET_LATENCY;
+ status.retval = Result::OK;
+ status.reply.latencyMs = mStream->getLatency();
+ return status;
+}
+
+bool WriteThread::threadLoop() {
+ // This implementation doesn't return control back to the Thread until the
+ // parent thread decides to stop, as the Thread uses mutexes, and this can
+ // lead to priority inversion.
+ while (!std::atomic_load_explicit(mStop, std::memory_order_acquire)) {
+ uint32_t efState = 0;
+ mEventFlag->wait(static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY),
+ &efState);
+ if (!(efState & static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY))) {
+ continue; // Nothing to do.
+ }
+
+ IStreamOut::WriteCommand replyTo;
+ if (!mCommandMQ->read(&replyTo)) {
+ continue; // Nothing to do.
+ }
+
+ IStreamOut::WriteStatus status;
+ switch (replyTo) {
+ case IStreamOut::WriteCommand::WRITE:
+ status = doWrite();
+ break;
+ case IStreamOut::WriteCommand::GET_PRESENTATION_POSITION:
+ status = doGetPresentationPosition();
+ break;
+ case IStreamOut::WriteCommand::GET_LATENCY:
+ status = doGetLatency();
+ break;
+ default:
+ ALOGE("Unknown write thread command code %d", replyTo);
+ status.retval = Result::NOT_SUPPORTED;
+ break;
+ }
+ if (!mStatusMQ->write(&status)) {
+ ALOGE("status message queue write failed");
+ }
+ mEventFlag->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL));
+ }
+
+ return false;
+}
+
+} // namespace
+
+StreamOutImpl::StreamOutImpl(std::unique_ptr<AudioProxyStreamOut> stream)
+ : mStream(std::move(stream)), mEventFlag(nullptr, deleteEventFlag) {}
+
+StreamOutImpl::~StreamOutImpl() {
+ closeImpl();
+
+ if (mWriteThread) {
+ status_t status = mWriteThread->join();
+ ALOGE_IF(status, "write thread exit error: %s", strerror(-status));
+ }
+
+ mEventFlag.reset();
+}
+
+Return<uint64_t> StreamOutImpl::getFrameSize() {
+ audio_format_t format = static_cast<audio_format_t>(mStream->getFormat());
+
+ if (!audio_has_proportional_frames(format)) {
+ return sizeof(int8_t);
+ }
+
+ size_t channel_sample_size = audio_bytes_per_sample(format);
+ return audio_channel_count_from_out_mask(
+ static_cast<audio_channel_mask_t>(mStream->getChannelMask())) *
+ channel_sample_size;
+}
+
+Return<uint64_t> StreamOutImpl::getFrameCount() {
+ return mStream->getFrameCount();
+}
+
+Return<uint64_t> StreamOutImpl::getBufferSize() {
+ return mStream->getBufferSize();
+}
+
+Return<uint32_t> StreamOutImpl::getSampleRate() {
+ return mStream->getSampleRate();
+}
+
+Return<void> StreamOutImpl::getSupportedSampleRates(
+ AudioFormat format, getSupportedSampleRates_cb _hidl_cb) {
+ _hidl_cb(Result::OK, mStream->getSupportedSampleRates(format));
+ return Void();
+}
+
+Return<void> StreamOutImpl::getSupportedChannelMasks(
+ AudioFormat format, getSupportedChannelMasks_cb _hidl_cb) {
+ _hidl_cb(Result::OK, mStream->getSupportedChannelMasks(format));
+ return Void();
+}
+
+Return<Result> StreamOutImpl::setSampleRate(uint32_t sampleRateHz) {
+ return mStream->setSampleRate(sampleRateHz);
+}
+
+Return<hidl_bitfield<AudioChannelMask>> StreamOutImpl::getChannelMask() {
+ return hidl_bitfield<AudioChannelMask>(mStream->getChannelMask());
+}
+
+Return<Result> StreamOutImpl::setChannelMask(
+ hidl_bitfield<AudioChannelMask> mask) {
+ return mStream->setChannelMask(mask);
+}
+
+Return<AudioFormat> StreamOutImpl::getFormat() { return mStream->getFormat(); }
+
+Return<void> StreamOutImpl::getSupportedFormats(
+ getSupportedFormats_cb _hidl_cb) {
+ _hidl_cb(mStream->getSupportedFormats());
+ return Void();
+}
+
+Return<Result> StreamOutImpl::setFormat(AudioFormat format) {
+ return mStream->setFormat(format);
+}
+
+Return<void> StreamOutImpl::getAudioProperties(getAudioProperties_cb _hidl_cb) {
+ _hidl_cb(mStream->getSampleRate(), mStream->getChannelMask(),
+ mStream->getFormat());
+ return Void();
+}
+
+Return<Result> StreamOutImpl::addEffect(uint64_t effectId) {
+ return Result::NOT_SUPPORTED;
+}
+
+Return<Result> StreamOutImpl::removeEffect(uint64_t effectId) {
+ return Result::NOT_SUPPORTED;
+}
+
+Return<Result> StreamOutImpl::standby() { return mStream->standby(); }
+
+Return<void> StreamOutImpl::getDevices(getDevices_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, {});
+ return Void();
+}
+
+Return<Result> StreamOutImpl::setDevices(
+ const hidl_vec<DeviceAddress>& devices) {
+ return Result::NOT_SUPPORTED;
+}
+
+Return<void> StreamOutImpl::getParameters(
+ const hidl_vec<ParameterValue>& context, const hidl_vec<hidl_string>& keys,
+ getParameters_cb _hidl_cb) {
+ _hidl_cb(Result::OK, mStream->getParameters(context, keys));
+ return Void();
+}
+
+Return<Result> StreamOutImpl::setParameters(
+ const hidl_vec<ParameterValue>& context,
+ const hidl_vec<ParameterValue>& parameters) {
+ return mStream->setParameters(context, parameters);
+}
+
+Return<Result> StreamOutImpl::setHwAvSync(uint32_t hwAvSync) {
+ return Result::NOT_SUPPORTED;
+}
+
+Return<Result> StreamOutImpl::close() { return closeImpl(); }
+
+Result StreamOutImpl::closeImpl() {
+ if (mStopWriteThread.load(
+ std::memory_order_relaxed)) { // only this thread writes
+ return Result::INVALID_STATE;
+ }
+ mStopWriteThread.store(true, std::memory_order_release);
+ if (mEventFlag) {
+ mEventFlag->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY));
+ }
+ return Result::OK;
+}
+
+Return<uint32_t> StreamOutImpl::getLatency() { return mStream->getLatency(); }
+
+Return<Result> StreamOutImpl::setVolume(float left, float right) {
+ return mStream->setVolume(left, right);
+}
+
+Return<void> StreamOutImpl::prepareForWriting(uint32_t frameSize,
+ uint32_t framesCount,
+ prepareForWriting_cb _hidl_cb) {
+ ThreadInfo threadInfo = {0, 0};
+
+ // Wrap the _hidl_cb to return an error
+ auto sendError = [&threadInfo, &_hidl_cb](Result result) -> Return<void> {
+ _hidl_cb(result, CommandMQ::Descriptor(), DataMQ::Descriptor(),
+ StatusMQ::Descriptor(), threadInfo);
+ return Void();
+ };
+
+ if (mDataMQ) {
+ ALOGE("the client attempted to call prepareForWriting twice");
+ return sendError(Result::INVALID_STATE);
+ }
+
+ if (frameSize == 0 || framesCount == 0) {
+ ALOGE("Invalid frameSize (%u) or framesCount (%u)", frameSize, framesCount);
+ return sendError(Result::INVALID_ARGUMENTS);
+ }
+
+ if (frameSize > kMaxBufferSize / framesCount) {
+ ALOGE("Buffer too big: %u*%u bytes > MAX_BUFFER_SIZE (%u)", frameSize,
+ framesCount, kMaxBufferSize);
+ return sendError(Result::INVALID_ARGUMENTS);
+ }
+
+ auto commandMQ = std::make_unique<CommandMQ>(1);
+ if (!commandMQ->isValid()) {
+ ALOGE("command MQ is invalid");
+ return sendError(Result::INVALID_ARGUMENTS);
+ }
+
+ auto dataMQ =
+ std::make_unique<DataMQ>(frameSize * framesCount, true /* EventFlag */);
+ if (!dataMQ->isValid()) {
+ ALOGE("data MQ is invalid");
+ return sendError(Result::INVALID_ARGUMENTS);
+ }
+
+ auto statusMQ = std::make_unique<StatusMQ>(1);
+ if (!statusMQ->isValid()) {
+ ALOGE("status MQ is invalid");
+ return sendError(Result::INVALID_ARGUMENTS);
+ }
+
+ EventFlag* rawEventFlag = nullptr;
+ status_t status =
+ EventFlag::createEventFlag(dataMQ->getEventFlagWord(), &rawEventFlag);
+ std::unique_ptr<EventFlag, EventFlagDeleter> eventFlag(rawEventFlag,
+ deleteEventFlag);
+ if (status != ::android::OK || !eventFlag) {
+ ALOGE("failed creating event flag for data MQ: %s", strerror(-status));
+ return sendError(Result::INVALID_ARGUMENTS);
+ }
+
+ sp<WriteThread> writeThread =
+ new WriteThread(&mStopWriteThread, mStream.get(), commandMQ.get(),
+ dataMQ.get(), statusMQ.get(), eventFlag.get());
+ status = writeThread->run("writer", ::android::PRIORITY_URGENT_AUDIO);
+ if (status != ::android::OK) {
+ ALOGW("failed to start writer thread: %s", strerror(-status));
+ return sendError(Result::INVALID_ARGUMENTS);
+ }
+
+ mCommandMQ = std::move(commandMQ);
+ mDataMQ = std::move(dataMQ);
+ mStatusMQ = std::move(statusMQ);
+ mEventFlag = std::move(eventFlag);
+ mWriteThread = std::move(writeThread);
+ threadInfo.pid = getpid();
+ threadInfo.tid = mWriteThread->getTid();
+ _hidl_cb(Result::OK, *mCommandMQ->getDesc(), *mDataMQ->getDesc(),
+ *mStatusMQ->getDesc(), threadInfo);
+ return Void();
+}
+
+Return<void> StreamOutImpl::getRenderPosition(getRenderPosition_cb _hidl_cb) {
+ uint32_t dspFrames = 0;
+ Result res = mStream->getRenderPosition(&dspFrames);
+ _hidl_cb(res, dspFrames);
+ return Void();
+}
+
+Return<void> StreamOutImpl::getNextWriteTimestamp(
+ getNextWriteTimestamp_cb _hidl_cb) {
+ int64_t timestamp = 0;
+ Result res = mStream->getNextWriteTimestamp(&timestamp);
+ _hidl_cb(res, timestamp);
+ return Void();
+}
+
+Return<Result> StreamOutImpl::setCallback(
+ const sp<IStreamOutCallback>& callback) {
+ return Result::NOT_SUPPORTED;
+}
+
+Return<Result> StreamOutImpl::clearCallback() { return Result::NOT_SUPPORTED; }
+
+Return<void> StreamOutImpl::supportsPauseAndResume(
+ supportsPauseAndResume_cb _hidl_cb) {
+ _hidl_cb(true, true);
+ return Void();
+}
+
+Return<Result> StreamOutImpl::pause() { return mStream->pause(); }
+
+Return<Result> StreamOutImpl::resume() { return mStream->resume(); }
+
+Return<bool> StreamOutImpl::supportsDrain() { return mStream->supportsDrain(); }
+
+Return<Result> StreamOutImpl::drain(AudioDrain type) {
+ return mStream->drain(type);
+}
+
+Return<Result> StreamOutImpl::flush() { return mStream->flush(); }
+
+Return<void> StreamOutImpl::getPresentationPosition(
+ getPresentationPosition_cb _hidl_cb) {
+ uint64_t frames = 0;
+ TimeSpec ts = {0, 0};
+ Result result = mStream->getPresentationPosition(&frames, &ts);
+ _hidl_cb(result, frames, ts);
+ return Void();
+}
+
+Return<Result> StreamOutImpl::start() { return Result::NOT_SUPPORTED; }
+
+Return<Result> StreamOutImpl::stop() { return Result::NOT_SUPPORTED; }
+
+Return<void> StreamOutImpl::createMmapBuffer(int32_t minSizeFrames,
+ createMmapBuffer_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, MmapBufferInfo());
+ return Void();
+}
+
+Return<void> StreamOutImpl::getMmapPosition(getMmapPosition_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, MmapPosition());
+ return Void();
+}
+
+Return<void> StreamOutImpl::updateSourceMetadata(
+ const SourceMetadata& sourceMetadata) {
+ return Void();
+}
+
+Return<Result> StreamOutImpl::selectPresentation(int32_t presentationId,
+ int32_t programId) {
+ return Result::NOT_SUPPORTED;
+}
+
+} // namespace CPP_VERSION
+} // namespace audio_proxy
diff --git a/audio_proxy/StreamOutImpl.h b/audio_proxy/StreamOutImpl.h
new file mode 100644
index 0000000..435e0f8
--- /dev/null
+++ b/audio_proxy/StreamOutImpl.h
@@ -0,0 +1,128 @@
+// Copyright (C) 2020 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.
+
+#pragma once
+
+// clang-format off
+#include PATH(android/hardware/audio/FILE_VERSION/IStreamOut.h)
+// clang-format on
+
+#include <fmq/EventFlag.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <utils/Thread.h>
+
+using ::android::sp;
+using ::android::Thread;
+using ::android::hardware::EventFlag;
+using ::android::hardware::hidl_bitfield;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+using namespace ::android::hardware::audio::CPP_VERSION;
+
+namespace audio_proxy {
+namespace CPP_VERSION {
+class AudioProxyStreamOut;
+
+class StreamOutImpl : public IStreamOut {
+ public:
+ using CommandMQ = MessageQueue<WriteCommand, kSynchronizedReadWrite>;
+ using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+ using StatusMQ = MessageQueue<WriteStatus, kSynchronizedReadWrite>;
+
+ explicit StreamOutImpl(std::unique_ptr<AudioProxyStreamOut> stream);
+ ~StreamOutImpl() override;
+
+ // Methods from ::android::hardware::audio::CPP_VERSION::IStream follow.
+ Return<uint64_t> getFrameSize() override;
+ Return<uint64_t> getFrameCount() override;
+ Return<uint64_t> getBufferSize() override;
+ Return<uint32_t> getSampleRate() override;
+ Return<void> getSupportedSampleRates(
+ AudioFormat format, getSupportedSampleRates_cb _hidl_cb) override;
+ Return<void> getSupportedChannelMasks(
+ AudioFormat format, getSupportedChannelMasks_cb _hidl_cb) override;
+ Return<Result> setSampleRate(uint32_t sampleRateHz) override;
+ Return<hidl_bitfield<AudioChannelMask>> getChannelMask() override;
+ Return<Result> setChannelMask(hidl_bitfield<AudioChannelMask> mask) override;
+ Return<AudioFormat> getFormat() override;
+ Return<void> getSupportedFormats(getSupportedFormats_cb _hidl_cb) override;
+ Return<Result> setFormat(AudioFormat format) override;
+ Return<void> getAudioProperties(getAudioProperties_cb _hidl_cb) override;
+ Return<Result> addEffect(uint64_t effectId) override;
+ Return<Result> removeEffect(uint64_t effectId) override;
+ Return<Result> standby() override;
+ Return<void> getDevices(getDevices_cb _hidl_cb) override;
+ Return<Result> setDevices(const hidl_vec<DeviceAddress>& devices) override;
+ Return<void> getParameters(const hidl_vec<ParameterValue>& context,
+ const hidl_vec<hidl_string>& keys,
+ getParameters_cb _hidl_cb) override;
+ Return<Result> setParameters(
+ const hidl_vec<ParameterValue>& context,
+ const hidl_vec<ParameterValue>& parameters) override;
+ Return<Result> setHwAvSync(uint32_t hwAvSync) override;
+ Return<Result> close() override;
+
+ // Methods from ::android::hardware::audio::CPP_VERSION::IStreamOut follow.
+ Return<uint32_t> getLatency() override;
+ Return<Result> setVolume(float left, float right) override;
+ Return<void> prepareForWriting(uint32_t frameSize, uint32_t framesCount,
+ prepareForWriting_cb _hidl_cb) override;
+ Return<void> getRenderPosition(getRenderPosition_cb _hidl_cb) override;
+ Return<void> getNextWriteTimestamp(
+ getNextWriteTimestamp_cb _hidl_cb) override;
+ Return<Result> setCallback(const sp<IStreamOutCallback>& callback) override;
+ Return<Result> clearCallback() override;
+ Return<void> supportsPauseAndResume(
+ supportsPauseAndResume_cb _hidl_cb) override;
+ Return<Result> pause() override;
+ Return<Result> resume() override;
+ Return<bool> supportsDrain() override;
+ Return<Result> drain(AudioDrain type) override;
+ Return<Result> flush() override;
+ Return<void> getPresentationPosition(
+ getPresentationPosition_cb _hidl_cb) override;
+ Return<Result> start() override;
+ Return<Result> stop() override;
+ Return<void> createMmapBuffer(int32_t minSizeFrames,
+ createMmapBuffer_cb _hidl_cb) override;
+ Return<void> getMmapPosition(getMmapPosition_cb _hidl_cb) override;
+ Return<void> updateSourceMetadata(
+ const SourceMetadata& sourceMetadata) override;
+ Return<Result> selectPresentation(int32_t presentationId,
+ int32_t programId) override;
+
+ private:
+ typedef void (*EventFlagDeleter)(EventFlag*);
+
+ Result closeImpl();
+
+ std::unique_ptr<AudioProxyStreamOut> mStream;
+
+ std::unique_ptr<CommandMQ> mCommandMQ;
+ std::unique_ptr<DataMQ> mDataMQ;
+ std::unique_ptr<StatusMQ> mStatusMQ;
+ std::unique_ptr<EventFlag, EventFlagDeleter> mEventFlag;
+ std::atomic<bool> mStopWriteThread = false;
+ sp<Thread> mWriteThread;
+};
+
+} // namespace CPP_VERSION
+} // namespace audio_proxy
diff --git a/audio_proxy/public/audio_proxy.h b/audio_proxy/public/audio_proxy.h
index ae79381..6ffca0d 100644
--- a/audio_proxy/public/audio_proxy.h
+++ b/audio_proxy/public/audio_proxy.h
@@ -34,11 +34,183 @@ extern "C" {
// Most of the struct/functions just converts the HIDL definitions into C
// definitions.
+// The following enum and typedef are subset of those defined in
+// hardware/interfaces/audio/common/$VERSION/types.hal, or
+// hardware/interfaces/audio/$VERSION/types.hal.
+// The selected subsets are those commonly supported by a normal audio HAL. The
+// library won't check the validation of these enums. In other words, Audio
+// server can still pass value not defined here to the application.
+
+// AudioFormat
+enum {
+ AUDIO_PROXY_FORMAT_INVALID = 0xFFFFFFFFu,
+ AUDIO_PROXY_FORMAT_PCM_16_BIT = 0x1u,
+ AUDIO_PROXY_FORMAT_PCM_8_BIT = 0x2u,
+ AUDIO_PROXY_FORMAT_PCM_FLOAT = 0x5u,
+};
+typedef uint32_t audio_proxy_format_t;
+
+// AudioChannelMask
+enum {
+ AUDIO_PROXY_CHANNEL_INVALID = 0xC0000000u,
+ AUDIO_PROXY_CHANNEL_OUT_MONO = 0x1u,
+ AUDIO_PROXY_CHANNEL_OUT_STEREO = 0x3u,
+};
+typedef uint32_t audio_proxy_channel_mask_t;
+
+// AudioDrain
+enum {
+ AUDIO_PROXY_DRAIN_ALL,
+ AUDIO_PROXY_DRAIN_EARLY_NOTIFY,
+};
+typedef int32_t audio_proxy_drain_type_t;
+
+// AudioOutputFlag
+enum {
+ AUDIO_PROXY_OUTPUT_FLAG_NONE = 0x0,
+ AUDIO_PROXY_OUTPUT_FLAG_DIRECT = 0x1,
+};
+typedef int32_t audio_proxy_output_flags_t;
+
+// AudioConfig
+typedef struct {
+ uint32_t sample_rate;
+ audio_proxy_channel_mask_t channel_mask;
+ audio_proxy_format_t format;
+ uint32_t frame_count;
+
+ // Points to extra fields defined in the future versions.
+ void* extension;
+} audio_proxy_config_t;
+
+// Util structure for key value pair.
+typedef struct {
+ const char* key;
+ const char* val;
+} audio_proxy_key_val_t;
+
+typedef void (*audio_proxy_get_parameters_callback_t)(
+ void*, const audio_proxy_key_val_t*);
+
+// The following struct/functions mirror those definitions in audio HAL. They
+// should have the same requirement as audio HAL interfaces, unless specified.
+
+// IStreamOut.
+struct audio_proxy_stream_out {
+ size_t (*get_buffer_size)(const struct audio_proxy_stream_out* stream);
+ uint64_t (*get_frame_count)(const struct audio_proxy_stream_out* stream);
+
+ // Gets all the sample rate supported by the stream. The list is terminated
+ // by 0. The returned list should have the same life cycle of |stream|.
+ const uint32_t* (*get_supported_sample_rates)(
+ const struct audio_proxy_stream_out* stream, audio_proxy_format_t format);
+ uint32_t (*get_sample_rate)(const struct audio_proxy_stream_out* stream);
+
+ // optional.
+ int (*set_sample_rate)(struct audio_proxy_stream_out* stream, uint32_t rate);
+
+ // Gets all the channel mask supported by the stream. The list is terminated
+ // by AUDIO_PROXY_CHANNEL_INVALID. The returned list should have the same life
+ // cycle of |stream|.
+ const audio_proxy_channel_mask_t* (*get_supported_channel_masks)(
+ const struct audio_proxy_stream_out* stream, audio_proxy_format_t format);
+ audio_proxy_channel_mask_t (*get_channel_mask)(
+ const struct audio_proxy_stream_out* stream);
+
+ // optional.
+ int (*set_channel_mask)(struct audio_proxy_stream_out* stream,
+ audio_proxy_channel_mask_t mask);
+
+ // Gets all the audio formats supported by the stream. The list is terminated
+ // by AUDIO_PROXY_FORMAT_INVALID. The returned list should have the same life
+ // cycle of |stream|.
+ const audio_proxy_format_t* (*get_supported_formats)(
+ const struct audio_proxy_stream_out* stream);
+ audio_proxy_format_t (*get_format)(
+ const struct audio_proxy_stream_out* stream);
+
+ // optional.
+ int (*set_format)(struct audio_proxy_stream_out* stream,
+ audio_proxy_format_t format);
+
+ uint32_t (*get_latency)(const struct audio_proxy_stream_out* stream);
+
+ int (*standby)(struct audio_proxy_stream_out* stream);
+
+ int (*pause)(struct audio_proxy_stream_out* stream);
+ int (*resume)(struct audio_proxy_stream_out* stream);
+
+ // optional.
+ int (*drain)(struct audio_proxy_stream_out* stream,
+ audio_proxy_drain_type_t type);
+
+ int (*flush)(struct audio_proxy_stream_out* stream);
+
+ // Writes |buffer| into |stream|. This is called on an internal thread of this
+ // library.
+ ssize_t (*write)(struct audio_proxy_stream_out* self, const void* buffer,
+ size_t bytes);
+
+ // optional.
+ int (*get_render_position)(const struct audio_proxy_stream_out* stream,
+ uint32_t* dsp_frames);
+
+ // optional.
+ int (*get_next_write_timestamp)(const struct audio_proxy_stream_out* stream,
+ int64_t* timestamp);
+
+ int (*get_presentation_position)(const struct audio_proxy_stream_out* stream,
+ uint64_t* frames,
+ struct timespec* timestamp);
+
+ // opional.
+ int (*set_volume)(struct audio_proxy_stream_out* stream, float left,
+ float right);
+
+ // Sets parameters on |stream|. Both |context| and |param| are terminated
+ // by key_val_t whose key is null. They are only valid during the function
+ // call.
+ int (*set_parameters)(struct audio_proxy_stream_out* stream,
+ const audio_proxy_key_val_t* context,
+ const audio_proxy_key_val_t* param);
+
+ // Gets parameters from |stream|.
+ // |context| is key val pairs array terminated by null key
+ // audio_proxy_key_val_t. |keys| is C string array, terminated by nullptr.
+ // |on_result| is the callback to deliver the result. It must be called before
+ // this function returns, with |obj| as the first argument, and the list of
+ // caller owned list of key value pairs as the second argument.
+ // |obj| opaque object. Implementation should not touch it.
+ void (*get_parameters)(const struct audio_proxy_stream_out* stream,
+ const audio_proxy_key_val_t* context,
+ const char** keys,
+ audio_proxy_get_parameters_callback_t on_result,
+ void* obj);
+
+ // optional.
+ int (*dump)(const struct audio_proxy_stream_out* stream, int fd);
+
+ // Pointer to the next version structure, for compatibility.
+ void* extension;
+};
+
+typedef struct audio_proxy_stream_out audio_proxy_stream_out_t;
+
// Represents an audio HAL bus device.
struct audio_proxy_device {
// Returns the unique address of this device.
const char* (*get_address)(struct audio_proxy_device* device);
+ // Similar to IDevice::openOutputStream.
+ int (*open_output_stream)(struct audio_proxy_device* device,
+ audio_proxy_output_flags_t flags,
+ audio_proxy_config_t* config,
+ audio_proxy_stream_out_t** stream_out);
+
+ // Close |stream|. No more methods will be called on |stream| after this.
+ void (*close_output_stream)(struct audio_proxy_device* device,
+ struct audio_proxy_stream_out* stream);
+
// Points to next version's struct. Implementation should set this field to
// null if next version struct is not available.
// This allows library to work with applications integrated with older version