hal: add timestamp mode support for offload playback streams
Add timestamp mode support for offload playback streams.
Support is added through compressed path, which is configured
internally when timestamp mode is requested.
CRs-fixed: 2016014
Change-Id: I0cc982a51f87abed40e424bc31fe9e013342bb71
diff --git a/hal/audio_extn/utils.c b/hal/audio_extn/utils.c
index 27bbae8..669bdb4 100644
--- a/hal/audio_extn/utils.c
+++ b/hal/audio_extn/utils.c
@@ -111,6 +111,7 @@
STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_INCALL_MUSIC),
#endif
STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_COMPRESS_PASSTHROUGH),
+ STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_TIMESTAMP),
STRING_TO_ENUM(AUDIO_INPUT_FLAG_NONE),
STRING_TO_ENUM(AUDIO_INPUT_FLAG_FAST),
STRING_TO_ENUM(AUDIO_INPUT_FLAG_HW_HOTWORD),
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 4fa42e8..15ee7b4 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -2579,9 +2579,12 @@
{
struct stream_out *out = (struct stream_out *)stream;
- if (out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)
- return out->compr_config.fragment_size;
- else if(out->usecase == USECASE_COMPRESS_VOIP_CALL)
+ if (out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
+ if (out->flags & AUDIO_OUTPUT_FLAG_TIMESTAMP)
+ return out->compr_config.fragment_size - sizeof(struct snd_codec_metadata);
+ else
+ return out->compr_config.fragment_size;
+ } else if(out->usecase == USECASE_COMPRESS_VOIP_CALL)
return voice_extn_compress_voip_out_get_buffer_size(out);
else if (is_offload_usecase(out->usecase) &&
out->flags == AUDIO_OUTPUT_FLAG_DIRECT)
@@ -4097,6 +4100,11 @@
*/
if (!audio_extn_passthru_is_passthrough_stream(out))
out->bit_width = AUDIO_OUTPUT_BIT_WIDTH;
+
+ if (out->flags & AUDIO_OUTPUT_FLAG_TIMESTAMP)
+ out->compr_config.codec->flags |= COMPRESSED_TIMESTAMP_FLAG;
+ ALOGVV("%s : out->compr_config.codec->flags -> (%#x) ", __func__, out->compr_config.codec->flags);
+
/*TODO: Do we need to change it for passthrough */
out->compr_config.codec->format = SND_AUDIOSTREAMFORMAT_RAW;
@@ -4159,6 +4167,9 @@
out->compr_config.fragments = COMPRESS_OFFLOAD_NUM_FRAGMENTS;
}
+ if (out->flags & AUDIO_OUTPUT_FLAG_TIMESTAMP) {
+ out->compr_config.fragment_size += sizeof(struct snd_codec_metadata);
+ }
if (config->offload_info.format == AUDIO_FORMAT_FLAC)
out->compr_config.codec->options.flac_dec.sample_size = AUDIO_OUTPUT_BIT_WIDTH;
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index ff9149f..9f10efa 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -270,6 +270,7 @@
struct audio_out_start_delay_param delay_param; /*start delay*/
audio_offload_info_t info;
+ qahwi_stream_out_t qahwi_out;
};
struct stream_in {
diff --git a/hal/audio_hw_extn_api.c b/hal/audio_hw_extn_api.c
index a1bd04d..c50c504 100644
--- a/hal/audio_hw_extn_api.c
+++ b/hal/audio_hw_extn_api.c
@@ -31,6 +31,7 @@
/*#define LOG_NDEBUG 0*/
#define LOG_NDDEBUG 0
+#include <inttypes.h>
#include <errno.h>
#include <cutils/log.h>
@@ -286,6 +287,117 @@
return ret;
}
+ssize_t qahwi_out_write_v2(struct audio_stream_out *stream, const void* buffer,
+ size_t bytes, int64_t* timestamp)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+ struct snd_codec_metadata *mdata = NULL;
+ size_t mdata_size = 0, bytes_written = 0;
+ char *buf = NULL;
+ ssize_t ret = 0;
+
+ if (!out->qahwi_out.is_inititalized) {
+ ALOGE("%s: invalid state!", __func__);
+ return -EINVAL;
+ }
+ if (COMPRESSED_TIMESTAMP_FLAG &&
+ (out->flags & AUDIO_OUTPUT_FLAG_TIMESTAMP)) {
+
+ mdata_size = sizeof(struct snd_codec_metadata);
+ buf = (char *) out->qahwi_out.obuf;
+ if (timestamp) {
+ mdata = (struct snd_codec_metadata *) buf;
+ mdata->length = bytes;
+ mdata->offset = mdata_size;
+ mdata->timestamp = *timestamp;
+ }
+ memcpy(buf + mdata_size, buffer, bytes);
+ ret = out->qahwi_out.base.write(&out->stream, (void *)buf, out->qahwi_out.buf_size);
+ if (ret <= 0) {
+ ALOGE("%s: error! write returned %zd", __func__, ret);
+ } else {
+ bytes_written = bytes;
+ }
+ ALOGV("%s: flag 0x%x, bytes %zd, read %zd, ret %zd timestamp 0x%"PRIx64"",
+ __func__, out->flags, bytes, bytes_written, ret, *timestamp);
+ } else {
+ bytes_written = out->qahwi_out.base.write(&out->stream, buffer, bytes);
+ ALOGV("%s: flag 0x%x, bytes %zd, read %zd, ret %zd",
+ __func__, out->flags, bytes, bytes_written, ret);
+ }
+ return bytes_written;
+}
+
+static void qahwi_close_output_stream(struct audio_hw_device *dev,
+ struct audio_stream_out *stream_out)
+{
+ struct audio_device *adev = (struct audio_device *) dev;
+ struct stream_out *out = (struct stream_out *)stream_out;
+
+ ALOGV("%s", __func__);
+ if (!adev->qahwi_dev.is_inititalized || !out->qahwi_out.is_inititalized) {
+ ALOGE("%s: invalid state!", __func__);
+ return;
+ }
+ if (out->qahwi_out.obuf)
+ free(out->qahwi_out.obuf);
+ out->qahwi_out.buf_size = 0;
+ adev->qahwi_dev.base.close_output_stream(dev, stream_out);
+}
+
+static int qahwi_open_output_stream(struct audio_hw_device *dev,
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ audio_output_flags_t flags,
+ struct audio_config *config,
+ struct audio_stream_out **stream_out,
+ const char *address)
+{
+ struct audio_device *adev = (struct audio_device *) dev;
+ struct stream_out *out = NULL;
+ size_t buf_size = 0, mdata_size = 0;
+ int ret = 0;
+
+ ALOGV("%s: dev_init %d, flags 0x%x", __func__,
+ adev->qahwi_dev.is_inititalized, flags);
+ if (!adev->qahwi_dev.is_inititalized) {
+ ALOGE("%s: invalid state!", __func__);
+ return -EINVAL;
+ }
+
+ ret = adev->qahwi_dev.base.open_output_stream(dev, handle, devices, flags,
+ config, stream_out, address);
+ if (ret)
+ return ret;
+
+ out = (struct stream_out *)*stream_out;
+ // keep adev fptrs before overriding
+ out->qahwi_out.base = out->stream;
+
+ out->qahwi_out.is_inititalized = true;
+
+ if (COMPRESSED_TIMESTAMP_FLAG &&
+ (flags & AUDIO_OUTPUT_FLAG_TIMESTAMP)) {
+ // set write to NULL as this is not supported in timestamp mode
+ out->stream.write = NULL;
+
+ mdata_size = sizeof(struct snd_codec_metadata);
+ buf_size = out->qahwi_out.base.common.get_buffer_size(&out->stream.common);
+ buf_size += mdata_size;
+ out->qahwi_out.buf_size = buf_size;
+ out->qahwi_out.obuf = malloc(buf_size);
+ if (!out->qahwi_out.obuf) {
+ ALOGE("%s: allocation failed for timestamp metadata!", __func__);
+ qahwi_close_output_stream(dev, &out->stream);
+ *stream_out = NULL;
+ ret = -ENOMEM;
+ }
+ ALOGD("%s: obuf %p, buff_size %zd",
+ __func__, out->qahwi_out.obuf, buf_size);
+ }
+ return ret;
+}
+
void qahwi_init(hw_device_t *device)
{
struct audio_device *adev = (struct audio_device *) device;
@@ -299,6 +411,9 @@
adev->device.open_input_stream = qahwi_open_input_stream;
adev->device.close_input_stream = qahwi_close_input_stream;
+ adev->device.open_output_stream = qahwi_open_output_stream;
+ adev->device.close_output_stream = qahwi_close_output_stream;
+
adev->qahwi_dev.is_inititalized = true;
}
void qahwi_deinit(hw_device_t *device)
diff --git a/hal/audio_hw_extn_api.h b/hal/audio_hw_extn_api.h
index e5fa9ec..4123461 100644
--- a/hal/audio_hw_extn_api.h
+++ b/hal/audio_hw_extn_api.h
@@ -33,6 +33,7 @@
#ifdef AUDIO_HW_EXTN_API_ENABLED
#include <hardware/audio.h>
typedef struct qahwi_stream_in qahwi_stream_in_t;
+typedef struct qahwi_stream_out qahwi_stream_out_t;
typedef struct qahwi_device qahwi_device_t;
struct qahwi_stream_in {
@@ -41,6 +42,13 @@
void *ibuf;
};
+struct qahwi_stream_out {
+ struct audio_stream_out base;
+ bool is_inititalized;
+ size_t buf_size;
+ void *obuf;
+};
+
struct qahwi_device {
struct audio_hw_device base;
bool is_inititalized;
@@ -50,6 +58,7 @@
void qahwi_deinit(hw_device_t *device);
#else
typedef void *qahwi_stream_in_t;
+typedef void *qahwi_stream_out_t;
typedef void *qahwi_device_t;
#define qahwi_init(device) (0)
diff --git a/qahw_api/src/qahw.c b/qahw_api/src/qahw.c
index c5cd636..df69df5 100644
--- a/qahw_api/src/qahw.c
+++ b/qahw_api/src/qahw.c
@@ -47,6 +47,10 @@
*/
#define QAHW_MODULE_API_VERSION_CURRENT QAHW_MODULE_API_VERSION_0_0
+
+typedef uint64_t (*qahwi_out_write_v2_t)(audio_stream_out_t *out, const void* buffer,
+ size_t bytes, int64_t* timestamp);
+
typedef int (*qahwi_get_param_data_t) (const audio_hw_device_t *,
qahw_param_id, qahw_param_payload *);
@@ -90,6 +94,7 @@
pthread_mutex_t lock;
qahwi_out_set_param_data_t qahwi_out_get_param_data;
qahwi_out_get_param_data_t qahwi_out_set_param_data;
+ qahwi_out_write_v2_t qahwi_out_write_v2;
} qahw_stream_out_t;
typedef struct {
@@ -535,10 +540,13 @@
}
/*TBD:: validate other meta data parameters */
-
pthread_mutex_lock(&qahw_stream_out->lock);
out = qahw_stream_out->stream;
- if (out->write) {
+ if (qahw_stream_out->qahwi_out_write_v2) {
+ rc = qahw_stream_out->qahwi_out_write_v2(out, out_buf->buffer,
+ out_buf->bytes, out_buf->timestamp);
+ out_buf->offset = 0;
+ } else if (out->write) {
rc = out->write(out, out_buf->buffer, out_buf->bytes);
} else {
rc = -ENOSYS;
@@ -1468,6 +1476,19 @@
}
}
+ /* dlsym qahwi_out_write_v2 */
+ if (!rc) {
+ const char *error;
+
+ /* clear any existing errors */
+ dlerror();
+ qahw_stream_out->qahwi_out_write_v2 = (qahwi_out_write_v2_t)dlsym(qahw_module->module->dso, "qahwi_out_write_v2");
+ if ((error = dlerror()) != NULL) {
+ ALOGI("%s: dlsym error %s for qahwi_out_write_v2", __func__, error);
+ qahw_stream_out->qahwi_out_write_v2 = NULL;
+ }
+ }
+
exit:
pthread_mutex_unlock(&qahw_module->lock);
return rc;