From 757ac41a40a8a6e38434b00a564b02325bbbf3da Mon Sep 17 00:00:00 2001 From: "Christopher N. Hesse" Date: Sat, 28 Jan 2017 14:42:48 +0100 Subject: [PATCH] audio: Factor out offload code Change-Id: I94a592262016d954318dab3d1339504e54f83681 --- audio/Android.mk | 3 +- audio/audio_hw.c | 331 ++---------------------------- audio/compress_offload.c | 429 +++++++++++++++++++++++++++++++++++++++ audio/compress_offload.h | 52 +++++ 4 files changed, 502 insertions(+), 313 deletions(-) create mode 100644 audio/compress_offload.c create mode 100644 audio/compress_offload.h diff --git a/audio/Android.mk b/audio/Android.mk index 7b0ee428..cee96424 100644 --- a/audio/Android.mk +++ b/audio/Android.mk @@ -21,7 +21,8 @@ include $(CLEAR_VARS) LOCAL_ARM_MODE := arm LOCAL_SRC_FILES := \ - audio_hw.c + audio_hw.c \ + compress_offload.c # TODO: remove resampler if possible when AudioFlinger supports downsampling from 48 to 8 LOCAL_SHARED_LIBRARIES := \ diff --git a/audio/audio_hw.c b/audio/audio_hw.c index e260a775..3364e92c 100644 --- a/audio/audio_hw.c +++ b/audio/audio_hw.c @@ -32,8 +32,6 @@ #include #include #include -#include -#include #include #include @@ -46,10 +44,10 @@ #include #include #include "audio_hw.h" +#include "compress_offload.h" #include "sound/compress_params.h" -#define MIXER_CTL_COMPRESS_PLAYBACK_VOLUME "Compress Playback Volume" /* TODO: the following PCM device profiles could be read from a config file */ static struct pcm_device_profile pcm_device_playback = { @@ -753,7 +751,7 @@ static int enable_snd_device(struct audio_device *adev, return 0; } -static int disable_snd_device(struct audio_device *adev, +int disable_snd_device(struct audio_device *adev, struct audio_usecase *uc_info, snd_device_t snd_device, bool update_mixer) @@ -2105,140 +2103,6 @@ void lock_output_stream(struct stream_out *out) pthread_mutex_unlock(&out->pre_lock); } - -/* must be called with out->lock locked */ -static int send_offload_cmd_l(struct stream_out* out, int command) -{ - struct offload_cmd *cmd = (struct offload_cmd *)calloc(1, sizeof(struct offload_cmd)); - - ALOGVV("%s %d", __func__, command); - - cmd->cmd = command; - list_add_tail(&out->offload_cmd_list, &cmd->node); - pthread_cond_signal(&out->offload_cond); - return 0; -} - -/* must be called iwth out->lock locked */ -static void stop_compressed_output_l(struct stream_out *out) -{ - out->send_new_metadata = 1; - if (out->compr != NULL) { - compress_stop(out->compr); - while (out->offload_thread_blocked) { - pthread_cond_wait(&out->cond, &out->lock); - } - } -} - -static void *offload_thread_loop(void *context) -{ - struct stream_out *out = (struct stream_out *) context; - struct listnode *item; - - setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_AUDIO); - set_sched_policy(0, SP_FOREGROUND); - prctl(PR_SET_NAME, (unsigned long)"Offload Callback", 0, 0, 0); - - ALOGV("%s", __func__); - lock_output_stream(out); - for (;;) { - struct offload_cmd *cmd = NULL; - stream_callback_event_t event; - bool send_callback = false; - - ALOGVV("%s offload_cmd_list %d out->offload_state %d", - __func__, list_empty(&out->offload_cmd_list), - out->offload_state); - if (list_empty(&out->offload_cmd_list)) { - ALOGV("%s SLEEPING", __func__); - pthread_cond_wait(&out->offload_cond, &out->lock); - ALOGV("%s RUNNING", __func__); - continue; - } - - item = list_head(&out->offload_cmd_list); - cmd = node_to_item(item, struct offload_cmd, node); - list_remove(item); - - ALOGVV("%s STATE %d CMD %d out->compr %p", - __func__, out->offload_state, cmd->cmd, out->compr); - - if (cmd->cmd == OFFLOAD_CMD_EXIT) { - free(cmd); - break; - } - - if (out->compr == NULL) { - ALOGE("%s: Compress handle is NULL", __func__); - pthread_cond_signal(&out->cond); - continue; - } - out->offload_thread_blocked = true; - pthread_mutex_unlock(&out->lock); - send_callback = false; - switch(cmd->cmd) { - case OFFLOAD_CMD_WAIT_FOR_BUFFER: - compress_wait(out->compr, -1); - send_callback = true; - event = STREAM_CBK_EVENT_WRITE_READY; - break; - case OFFLOAD_CMD_PARTIAL_DRAIN: - compress_next_track(out->compr); - compress_partial_drain(out->compr); - send_callback = true; - event = STREAM_CBK_EVENT_DRAIN_READY; - break; - case OFFLOAD_CMD_DRAIN: - compress_drain(out->compr); - send_callback = true; - event = STREAM_CBK_EVENT_DRAIN_READY; - break; - default: - ALOGE("%s unknown command received: %d", __func__, cmd->cmd); - break; - } - lock_output_stream(out); - out->offload_thread_blocked = false; - pthread_cond_signal(&out->cond); - if (send_callback) { - out->offload_callback(event, NULL, out->offload_cookie); - } - free(cmd); - } - - pthread_cond_signal(&out->cond); - while (!list_empty(&out->offload_cmd_list)) { - item = list_head(&out->offload_cmd_list); - list_remove(item); - free(node_to_item(item, struct offload_cmd, node)); - } - pthread_mutex_unlock(&out->lock); - - return NULL; -} - -static int create_offload_callback_thread(struct stream_out *out) -{ - pthread_cond_init(&out->offload_cond, (const pthread_condattr_t *) NULL); - list_init(&out->offload_cmd_list); - pthread_create(&out->offload_thread, (const pthread_attr_t *) NULL, - offload_thread_loop, out); - return 0; -} - -static int destroy_offload_callback_thread(struct stream_out *out) -{ - lock_output_stream(out); - send_offload_cmd_l(out, OFFLOAD_CMD_EXIT); - - pthread_mutex_unlock(&out->lock); - pthread_join(out->offload_thread, (void **) NULL); - pthread_cond_destroy(&out->offload_cond); - - return 0; -} - static int uc_release_pcm_devices(struct audio_usecase *usecase) { struct stream_out *out = (struct stream_out *)usecase->stream; @@ -2358,7 +2222,7 @@ error_open: return ret; } -static int disable_output_path_l(struct stream_out *out) +int disable_output_path_l(struct stream_out *out) { struct audio_device *adev = out->dev; struct audio_usecase *uc_info; @@ -2377,7 +2241,7 @@ static int disable_output_path_l(struct stream_out *out) return 0; } -static void enable_output_path_l(struct stream_out *out) +void enable_output_path_l(struct stream_out *out) { struct audio_device *adev = out->dev; struct audio_usecase *uc_info; @@ -2404,15 +2268,8 @@ static int stop_output_stream(struct stream_out *out) ALOGV("%s: enter: usecase(%d: %s)", __func__, out->usecase, use_case_table[out->usecase]); - if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD && - adev->offload_fx_stop_output != NULL) { - adev->offload_fx_stop_output(out->handle); + stop_output_offload_stream(out, &do_disable); - if (out->offload_state == OFFLOAD_STATE_PAUSED || - out->offload_state == OFFLOAD_STATE_PAUSED_FLUSHED) - do_disable = false; - out->offload_state = OFFLOAD_STATE_IDLE; - } if (do_disable) ret = disable_output_path_l(out); @@ -2685,39 +2542,6 @@ static int out_dump(const struct audio_stream *stream, int fd) return 0; } -static int parse_compress_metadata(struct stream_out *out, struct str_parms *parms) -{ - int ret = 0; - char value[32]; - struct compr_gapless_mdata tmp_mdata; - - if (!out || !parms) { - return -EINVAL; - } - - ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES, value, sizeof(value)); - if (ret >= 0) { - tmp_mdata.encoder_delay = atoi(value); /* what is a good limit check? */ - } else { - return -EINVAL; - } - - ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES, value, sizeof(value)); - if (ret >= 0) { - tmp_mdata.encoder_padding = atoi(value); - } else { - return -EINVAL; - } - - out->gapless_mdata = tmp_mdata; - out->send_new_metadata = 1; - ALOGV("%s new encoder delay %u and padding %u", __func__, - out->gapless_mdata.encoder_delay, out->gapless_mdata.encoder_padding); - - return 0; -} - - static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) { struct stream_out *out = (struct stream_out *)stream; @@ -2777,18 +2601,8 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) if (do_standby) do_out_standby_l(out); else { - if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD) { - uc_info = get_usecase_from_id(adev, USECASE_AUDIO_PLAYBACK); - if (uc_info == NULL) { - ALOGE("%s: Could not find the usecase (%d) in the list", - __func__, USECASE_AUDIO_PLAYBACK); - } - if (uc_info != NULL && uc_info->out_snd_device == SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES) { - ALOGV("Out_set_param: spk+headset enabled\n"); - disable_snd_device(adev, uc_info, SND_DEVICE_OUT_SPEAKER, true); - uc_info->out_snd_device = SND_DEVICE_OUT_HEADPHONES; - } - } + if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD) + out_set_offload_parameters(adev, uc_info); select_devices(adev, out->usecase); } } @@ -2889,37 +2703,13 @@ static int out_set_volume(struct audio_stream_out *stream, float left, { struct stream_out *out = (struct stream_out *)stream; struct audio_device *adev = out->dev; - int offload_volume[2];//For stereo if (out->usecase == USECASE_AUDIO_PLAYBACK_MULTI_CH) { /* only take left channel into account: the API is for stereo anyway */ out->muted = (left == 0.0f); return 0; } else if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD) { - struct mixer_ctl *ctl; - struct mixer *mixer = NULL; - - offload_volume[0] = (int)(left * COMPRESS_PLAYBACK_VOLUME_MAX); - offload_volume[1] = (int)(right * COMPRESS_PLAYBACK_VOLUME_MAX); - - mixer = mixer_open(MIXER_CARD); - if (!mixer) { - ALOGE("%s unable to open the mixer for card %d, aborting.", - __func__, MIXER_CARD); - return -EINVAL; - } - ctl = mixer_get_ctl_by_name(mixer, MIXER_CTL_COMPRESS_PLAYBACK_VOLUME); - if (!ctl) { - ALOGE("%s: Could not get ctl for mixer cmd - %s", - __func__, MIXER_CTL_COMPRESS_PLAYBACK_VOLUME); - mixer_close(mixer); - return -EINVAL; - } - ALOGV("out_set_volume set offload volume (%f, %f)", left, right); - mixer_ctl_set_array(ctl, offload_volume, - sizeof(offload_volume)/sizeof(offload_volume[0])); - mixer_close(mixer); - return 0; + out_set_offload_volume(left, right); } return -ENOSYS; @@ -3022,37 +2812,7 @@ static ssize_t out_write(struct audio_stream_out *stream, const void *buffer, false_alarm: if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD) { - ALOGVV("%s: writing buffer (%d bytes) to compress device", __func__, bytes); - - if (out->offload_state == OFFLOAD_STATE_PAUSED_FLUSHED) { - ALOGV("start offload write from pause state"); - pthread_mutex_lock(&adev->lock); - enable_output_path_l(out); - pthread_mutex_unlock(&adev->lock); - } - - if (out->send_new_metadata) { - ALOGVV("send new gapless metadata"); - compress_set_gapless_metadata(out->compr, &out->gapless_mdata); - out->send_new_metadata = 0; - } - - ret = compress_write(out->compr, buffer, bytes); - ALOGVV("%s: writing buffer (%d bytes) to compress device returned %d", __func__, bytes, ret); - if (ret >= 0 && ret < (ssize_t)bytes) { - send_offload_cmd_l(out, OFFLOAD_CMD_WAIT_FOR_BUFFER); - } - if (out->offload_state != OFFLOAD_STATE_PLAYING) { - compress_start(out->compr); - out->offload_state = OFFLOAD_STATE_PLAYING; - } - pthread_mutex_unlock(&out->lock); -#ifdef PREPROCESSING_ENABLED - if (in) { - /* This mutex was left locked iff in != NULL */ - pthread_mutex_unlock(&adev->lock_inputs); - } -#endif + ret = out_write_offload(stream, buffer, bytes); return ret; } else { #ifdef PREPROCESSING_ENABLED @@ -3158,16 +2918,8 @@ static int out_get_render_position(const struct audio_stream_out *stream, { struct stream_out *out = (struct stream_out *)stream; *dsp_frames = 0; - if ((out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD) && (dsp_frames != NULL)) { - lock_output_stream(out); - if (out->compr != NULL) { - compress_get_tstamp(out->compr, (unsigned long *)dsp_frames, - &out->sample_rate); - ALOGVV("%s rendered frames %d sample_rate %d", - __func__, *dsp_frames, out->sample_rate); - } - pthread_mutex_unlock(&out->lock); - return 0; + if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD) { + return out_get_render_offload_position(out, dsp_frames); } else return -EINVAL; } @@ -3204,16 +2956,7 @@ static int out_get_presentation_position(const struct audio_stream_out *stream, lock_output_stream(out); if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD) { - if (out->compr != NULL) { - compress_get_tstamp(out->compr, &dsp_frames, - &out->sample_rate); - ALOGVV("%s rendered frames %ld sample_rate %d", - __func__, dsp_frames, out->sample_rate); - *frames = dsp_frames; - ret = 0; - /* this is the best we can do */ - clock_gettime(CLOCK_MONOTONIC, timestamp); - } + ret = out_get_presentation_offload_position(out, frames, timestamp); } else { /* FIXME: which device to read from? */ if (!list_empty(&out->pcm_dev_list)) { @@ -3261,17 +3004,8 @@ static int out_pause(struct audio_stream_out* stream) struct stream_out *out = (struct stream_out *)stream; int status = -ENOSYS; ALOGV("%s", __func__); - if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD) { - lock_output_stream(out); - if (out->compr != NULL && out->offload_state == OFFLOAD_STATE_PLAYING) { - status = compress_pause(out->compr); - out->offload_state = OFFLOAD_STATE_PAUSED; - pthread_mutex_lock(&out->dev->lock); - status = disable_output_path_l(out); - pthread_mutex_unlock(&out->dev->lock); - } - pthread_mutex_unlock(&out->lock); - } + if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD) + status = out_pause_offload(out); return status; } @@ -3280,18 +3014,8 @@ static int out_resume(struct audio_stream_out* stream) struct stream_out *out = (struct stream_out *)stream; int status = -ENOSYS; ALOGV("%s", __func__); - if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD) { - status = 0; - lock_output_stream(out); - if (out->compr != NULL && out->offload_state == OFFLOAD_STATE_PAUSED) { - pthread_mutex_lock(&out->dev->lock); - enable_output_path_l(out); - pthread_mutex_unlock(&out->dev->lock); - status = compress_resume(out->compr); - out->offload_state = OFFLOAD_STATE_PLAYING; - } - pthread_mutex_unlock(&out->lock); - } + if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD) + status = out_resume_offload(out); return status; } @@ -3300,14 +3024,8 @@ static int out_drain(struct audio_stream_out* stream, audio_drain_type_t type ) struct stream_out *out = (struct stream_out *)stream; int status = -ENOSYS; ALOGV("%s", __func__); - if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD) { - lock_output_stream(out); - if (type == AUDIO_DRAIN_EARLY_NOTIFY) - status = send_offload_cmd_l(out, OFFLOAD_CMD_PARTIAL_DRAIN); - else - status = send_offload_cmd_l(out, OFFLOAD_CMD_DRAIN); - pthread_mutex_unlock(&out->lock); - } + if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD) + status = out_drain_offload(out, type); return status; } @@ -3316,18 +3034,7 @@ static int out_flush(struct audio_stream_out* stream) struct stream_out *out = (struct stream_out *)stream; ALOGV("%s", __func__); if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD) { - lock_output_stream(out); - if (out->offload_state == OFFLOAD_STATE_PLAYING) { - ALOGE("out_flush() called in wrong state %d", out->offload_state); - pthread_mutex_unlock(&out->lock); - return -ENOSYS; - } - if (out->offload_state == OFFLOAD_STATE_PAUSED) { - stop_compressed_output_l(out); - out->offload_state = OFFLOAD_STATE_PAUSED_FLUSHED; - } - pthread_mutex_unlock(&out->lock); - return 0; + return out_flush_offload(out); } return -ENOSYS; } diff --git a/audio/compress_offload.c b/audio/compress_offload.c new file mode 100644 index 00000000..bdf3b0ad --- /dev/null +++ b/audio/compress_offload.c @@ -0,0 +1,429 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2017 Christopher N. Hesse + * + * 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_hw_primary" +/*#define LOG_NDEBUG 0*/ +/*#define VERY_VERY_VERBOSE_LOGGING*/ +#ifdef VERY_VERY_VERBOSE_LOGGING +#define ALOGVV ALOGV +#else +#define ALOGVV(a...) do { } while(0) +#endif + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include "audio_hw.h" + +#include "sound/compress_params.h" + +#define MIXER_CTL_COMPRESS_PLAYBACK_VOLUME "Compress Playback Volume" + + +/* Prototypes */ +void lock_input_stream(struct stream_in *in); +void lock_output_stream(struct stream_out *out); +int disable_snd_device(struct audio_device *adev, + struct audio_usecase *uc_info, + snd_device_t snd_device, + bool update_mixer); +void enable_output_path_l(struct stream_out *out); +int disable_output_path_l(struct stream_out *out); + +/* must be called with out->lock locked */ +static int send_offload_cmd_l(struct stream_out* out, int command) +{ + struct offload_cmd *cmd = (struct offload_cmd *)calloc(1, sizeof(struct offload_cmd)); + + ALOGVV("%s %d", __func__, command); + + cmd->cmd = command; + list_add_tail(&out->offload_cmd_list, &cmd->node); + pthread_cond_signal(&out->offload_cond); + return 0; +} + +/* must be called iwth out->lock locked */ +void stop_compressed_output_l(struct stream_out *out) +{ + out->send_new_metadata = 1; + if (out->compr != NULL) { + compress_stop(out->compr); + while (out->offload_thread_blocked) { + pthread_cond_wait(&out->cond, &out->lock); + } + } +} + +static void *offload_thread_loop(void *context) +{ + struct stream_out *out = (struct stream_out *) context; + struct listnode *item; + + setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_AUDIO); + set_sched_policy(0, SP_FOREGROUND); + prctl(PR_SET_NAME, (unsigned long)"Offload Callback", 0, 0, 0); + + ALOGV("%s", __func__); + lock_output_stream(out); + for (;;) { + struct offload_cmd *cmd = NULL; + stream_callback_event_t event; + bool send_callback = false; + + ALOGVV("%s offload_cmd_list %d out->offload_state %d", + __func__, list_empty(&out->offload_cmd_list), + out->offload_state); + if (list_empty(&out->offload_cmd_list)) { + ALOGV("%s SLEEPING", __func__); + pthread_cond_wait(&out->offload_cond, &out->lock); + ALOGV("%s RUNNING", __func__); + continue; + } + + item = list_head(&out->offload_cmd_list); + cmd = node_to_item(item, struct offload_cmd, node); + list_remove(item); + + ALOGVV("%s STATE %d CMD %d out->compr %p", + __func__, out->offload_state, cmd->cmd, out->compr); + + if (cmd->cmd == OFFLOAD_CMD_EXIT) { + free(cmd); + break; + } + + if (out->compr == NULL) { + ALOGE("%s: Compress handle is NULL", __func__); + pthread_cond_signal(&out->cond); + continue; + } + out->offload_thread_blocked = true; + pthread_mutex_unlock(&out->lock); + send_callback = false; + switch(cmd->cmd) { + case OFFLOAD_CMD_WAIT_FOR_BUFFER: + compress_wait(out->compr, -1); + send_callback = true; + event = STREAM_CBK_EVENT_WRITE_READY; + break; + case OFFLOAD_CMD_PARTIAL_DRAIN: + compress_next_track(out->compr); + compress_partial_drain(out->compr); + send_callback = true; + event = STREAM_CBK_EVENT_DRAIN_READY; + break; + case OFFLOAD_CMD_DRAIN: + compress_drain(out->compr); + send_callback = true; + event = STREAM_CBK_EVENT_DRAIN_READY; + break; + default: + ALOGE("%s unknown command received: %d", __func__, cmd->cmd); + break; + } + lock_output_stream(out); + out->offload_thread_blocked = false; + pthread_cond_signal(&out->cond); + if (send_callback) { + out->offload_callback(event, NULL, out->offload_cookie); + } + free(cmd); + } + + pthread_cond_signal(&out->cond); + while (!list_empty(&out->offload_cmd_list)) { + item = list_head(&out->offload_cmd_list); + list_remove(item); + free(node_to_item(item, struct offload_cmd, node)); + } + pthread_mutex_unlock(&out->lock); + + return NULL; +} + +int create_offload_callback_thread(struct stream_out *out) +{ + pthread_cond_init(&out->offload_cond, (const pthread_condattr_t *) NULL); + list_init(&out->offload_cmd_list); + pthread_create(&out->offload_thread, (const pthread_attr_t *) NULL, + offload_thread_loop, out); + return 0; +} + +int destroy_offload_callback_thread(struct stream_out *out) +{ + lock_output_stream(out); + send_offload_cmd_l(out, OFFLOAD_CMD_EXIT); + + pthread_mutex_unlock(&out->lock); + pthread_join(out->offload_thread, (void **) NULL); + pthread_cond_destroy(&out->offload_cond); + + return 0; +} + +int parse_compress_metadata(struct stream_out *out, struct str_parms *parms) +{ + int ret = 0; + char value[32]; + struct compr_gapless_mdata tmp_mdata; + + if (!out || !parms) { + return -EINVAL; + } + + ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES, value, sizeof(value)); + if (ret >= 0) { + tmp_mdata.encoder_delay = atoi(value); /* what is a good limit check? */ + } else { + return -EINVAL; + } + + ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES, value, sizeof(value)); + if (ret >= 0) { + tmp_mdata.encoder_padding = atoi(value); + } else { + return -EINVAL; + } + + out->gapless_mdata = tmp_mdata; + out->send_new_metadata = 1; + ALOGV("%s new encoder delay %u and padding %u", __func__, + out->gapless_mdata.encoder_delay, out->gapless_mdata.encoder_padding); + + return 0; +} + +int stop_output_offload_stream(struct stream_out *out, bool *disable) +{ + int ret = 0; + struct audio_device *adev = out->dev; + + if (adev->offload_fx_stop_output != NULL) { + adev->offload_fx_stop_output(out->handle); + + if (out->offload_state == OFFLOAD_STATE_PAUSED || + out->offload_state == OFFLOAD_STATE_PAUSED_FLUSHED) + *disable = false; + out->offload_state = OFFLOAD_STATE_IDLE; + } + + return ret; +} + +int out_set_offload_parameters(struct audio_device *adev, struct audio_usecase *uc_info) +{ + int ret = 0; + + if (uc_info == NULL) { + ALOGE("%s: Could not find the usecase (%d) in the list", + __func__, USECASE_AUDIO_PLAYBACK); + ret = -1; + } + if (uc_info != NULL && uc_info->out_snd_device == SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES) { + ALOGV("Out_set_param: spk+headset enabled\n"); + uc_info->out_snd_device = SND_DEVICE_OUT_HEADPHONES; + disable_snd_device(adev, uc_info, SND_DEVICE_OUT_SPEAKER, true); + } + + return ret; +} + +ssize_t out_write_offload(struct audio_stream_out *stream, const void *buffer, + size_t bytes) +{ + struct stream_out *out = (struct stream_out *)stream; + struct audio_device *adev = out->dev; + ssize_t ret = 0; + + ALOGVV("%s: writing buffer (%d bytes) to compress device", __func__, bytes); + + if (out->offload_state == OFFLOAD_STATE_PAUSED_FLUSHED) { + ALOGV("start offload write from pause state"); + pthread_mutex_lock(&adev->lock); + enable_output_path_l(out); + pthread_mutex_unlock(&adev->lock); + } + + if (out->send_new_metadata) { + ALOGVV("send new gapless metadata"); + compress_set_gapless_metadata(out->compr, &out->gapless_mdata); + out->send_new_metadata = 0; + } + + ret = compress_write(out->compr, buffer, bytes); + ALOGVV("%s: writing buffer (%d bytes) to compress device returned %d", __func__, bytes, ret); + if (ret >= 0 && ret < (ssize_t)bytes) { + send_offload_cmd_l(out, OFFLOAD_CMD_WAIT_FOR_BUFFER); + } + if (out->offload_state != OFFLOAD_STATE_PLAYING) { + compress_start(out->compr); + out->offload_state = OFFLOAD_STATE_PLAYING; + } + pthread_mutex_unlock(&out->lock); +#ifdef PREPROCESSING_ENABLED + if (in) { + /* This mutex was left locked iff in != NULL */ + pthread_mutex_unlock(&adev->lock_inputs); + } +#endif + + return ret; +} + +int out_get_render_offload_position(struct stream_out *out, + uint32_t *dsp_frames) +{ + *dsp_frames = 0; + if (dsp_frames != NULL) { + lock_output_stream(out); + if (out->compr != NULL) { + compress_get_tstamp(out->compr, (unsigned long *)dsp_frames, + &out->sample_rate); + ALOGVV("%s rendered frames %d sample_rate %d", + __func__, *dsp_frames, out->sample_rate); + } + pthread_mutex_unlock(&out->lock); + return 0; + } else + return -EINVAL; +} + +int out_get_presentation_offload_position(struct stream_out *out, uint64_t *frames, + struct timespec *timestamp) +{ + int ret = -1; + unsigned long dsp_frames; + + if (out->compr != NULL) { + compress_get_tstamp(out->compr, &dsp_frames, + &out->sample_rate); + ALOGVV("%s rendered frames %ld sample_rate %d", + __func__, dsp_frames, out->sample_rate); + *frames = dsp_frames; + ret = 0; + /* this is the best we can do */ + clock_gettime(CLOCK_MONOTONIC, timestamp); + } + + return ret; +} + +int out_pause_offload(struct stream_out *out) +{ + int status = -ENOSYS; + + lock_output_stream(out); + if (out->compr != NULL && out->offload_state == OFFLOAD_STATE_PLAYING) { + status = compress_pause(out->compr); + out->offload_state = OFFLOAD_STATE_PAUSED; + pthread_mutex_lock(&out->dev->lock); + status = disable_output_path_l(out); + pthread_mutex_unlock(&out->dev->lock); + } + pthread_mutex_unlock(&out->lock); + + return status; +} + +int out_resume_offload(struct stream_out *out) +{ + int status = -ENOSYS; + + status = 0; + lock_output_stream(out); + if (out->compr != NULL && out->offload_state == OFFLOAD_STATE_PAUSED) { + pthread_mutex_lock(&out->dev->lock); + enable_output_path_l(out); + pthread_mutex_unlock(&out->dev->lock); + status = compress_resume(out->compr); + out->offload_state = OFFLOAD_STATE_PLAYING; + } + pthread_mutex_unlock(&out->lock); + + return status; +} + +int out_drain_offload(struct stream_out *out, audio_drain_type_t type) +{ + int status = -ENOSYS; + + lock_output_stream(out); + if (type == AUDIO_DRAIN_EARLY_NOTIFY) + status = send_offload_cmd_l(out, OFFLOAD_CMD_PARTIAL_DRAIN); + else + status = send_offload_cmd_l(out, OFFLOAD_CMD_DRAIN); + pthread_mutex_unlock(&out->lock); + + return status; +} + +int out_flush_offload(struct stream_out *out) +{ + lock_output_stream(out); + if (out->offload_state == OFFLOAD_STATE_PLAYING) { + ALOGE("out_flush() called in wrong state %d", out->offload_state); + pthread_mutex_unlock(&out->lock); + return -ENOSYS; + } + if (out->offload_state == OFFLOAD_STATE_PAUSED) { + stop_compressed_output_l(out); + out->offload_state = OFFLOAD_STATE_PAUSED_FLUSHED; + } + pthread_mutex_unlock(&out->lock); + return 0; +} + +int out_set_offload_volume(float left, float right) +{ + int offload_volume[2];//For stereo + struct mixer_ctl *ctl; + struct mixer *mixer = NULL; + + offload_volume[0] = (int)(left * COMPRESS_PLAYBACK_VOLUME_MAX); + offload_volume[1] = (int)(right * COMPRESS_PLAYBACK_VOLUME_MAX); + + mixer = mixer_open(MIXER_CARD); + if (!mixer) { + ALOGE("%s unable to open the mixer for card %d, aborting.", + __func__, MIXER_CARD); + return -EINVAL; + } + ctl = mixer_get_ctl_by_name(mixer, MIXER_CTL_COMPRESS_PLAYBACK_VOLUME); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, MIXER_CTL_COMPRESS_PLAYBACK_VOLUME); + mixer_close(mixer); + return -EINVAL; + } + ALOGV("out_set_volume set offload volume (%f, %f)", left, right); + mixer_ctl_set_array(ctl, offload_volume, + sizeof(offload_volume)/sizeof(offload_volume[0])); + mixer_close(mixer); + return 0; +} diff --git a/audio/compress_offload.h b/audio/compress_offload.h new file mode 100644 index 00000000..601445f9 --- /dev/null +++ b/audio/compress_offload.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2017 Christopher N. Hesse + * + * 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. + */ + +#ifndef COMPRESS_OFFLOAD_H +#define COMPRESS_OFFLOAD_H + +void stop_compressed_output_l(struct stream_out *out); + +int create_offload_callback_thread(struct stream_out *out); + +int destroy_offload_callback_thread(struct stream_out *out); + +int parse_compress_metadata(struct stream_out *out, struct str_parms *parms); + +int stop_output_offload_stream(struct stream_out *out, bool *disable); + +int out_set_offload_parameters(struct audio_device *adev, struct audio_usecase *uc_info); + +ssize_t out_write_offload(struct audio_stream_out *stream, const void *buffer, + size_t bytes); + +int out_get_render_offload_position(struct stream_out *out, + uint32_t *dsp_frames); + +int out_get_presentation_offload_position(struct stream_out *out, uint64_t *frames, + struct timespec *timestamp); + +int out_pause_offload(struct stream_out *out); + +int out_resume_offload(struct stream_out *out); + +int out_drain_offload(struct stream_out *out, audio_drain_type_t type); + +int out_flush_offload(struct stream_out *out); + +int out_set_offload_volume(float left, float right); + +#endif // COMPRESS_OFFLOAD_H