From 2c74a94c6e4723bb3573ee846d420ee6e09c5fa0 Mon Sep 17 00:00:00 2001 From: Sourabh Banerjee Date: Wed, 30 Sep 2015 13:27:49 -0700 Subject: qcom audio hal cloned from git://codeaurora.org/quic/la/platform/hardware/qcom/audio revision: f44feece4775fdb1d1d26f6abc22e6deb389b63c Change-Id: Idd21f6da1181a8edfd99256d883fba07046b120e Signed-off-by: Sourabh Banerjee --- audio/Android.mk | 19 + audio/audiod/Android.mk | 21 + audio/audiod/AudioDaemon.cpp | 458 ++ audio/audiod/AudioDaemon.h | 92 + audio/audiod/audiod_main.cpp | 60 + audio/hal/Android.mk | 224 + audio/hal/audio_extn/audio_defs.h | 78 + audio/hal/audio_extn/audio_extn.c | 575 +++ audio/hal/audio_extn/audio_extn.h | 390 ++ audio/hal/audio_extn/compress_capture.c | 155 + audio/hal/audio_extn/dev_arbi.c | 169 + audio/hal/audio_extn/dolby.c | 685 +++ audio/hal/audio_extn/fm.c | 302 ++ audio/hal/audio_extn/hfp.c | 363 ++ audio/hal/audio_extn/listen.c | 260 + audio/hal/audio_extn/pm.c | 149 + audio/hal/audio_extn/pm.h | 68 + audio/hal/audio_extn/soundtrigger.c | 358 ++ audio/hal/audio_extn/spkr_protection.c | 916 ++++ audio/hal/audio_extn/ssr.c | 503 ++ audio/hal/audio_extn/usb.c | 730 +++ audio/hal/audio_extn/utils.c | 569 +++ audio/hal/audio_hw.c | 3499 ++++++++++++++ audio/hal/audio_hw.h | 334 ++ audio/hal/msm8916/hw_info.c | 281 ++ audio/hal/msm8916/platform.c | 2735 +++++++++++ audio/hal/msm8916/platform.h | 259 + audio/hal/msm8960/platform.c | 1088 +++++ audio/hal/msm8960/platform.h | 148 + audio/hal/msm8974/hw_info.c | 397 ++ audio/hal/msm8974/platform.c | 2825 +++++++++++ audio/hal/msm8974/platform.h | 345 ++ audio/hal/platform_api.h | 97 + audio/hal/platform_info.c | 311 ++ audio/hal/voice.c | 508 ++ audio/hal/voice.h | 96 + audio/hal/voice_extn/compress_voip.c | 811 ++++ audio/hal/voice_extn/voice_extn.c | 610 +++ audio/hal/voice_extn/voice_extn.h | 275 ++ audio/mm-audio/Android.mk | 3 + audio/mm-audio/Makefile | 10 + audio/mm-audio/aenc-aac/Android.mk | 8 + audio/mm-audio/aenc-aac/Makefile | 6 + audio/mm-audio/aenc-aac/qdsp6/Android.mk | 68 + audio/mm-audio/aenc-aac/qdsp6/Makefile | 81 + audio/mm-audio/aenc-aac/qdsp6/inc/Map.h | 244 + audio/mm-audio/aenc-aac/qdsp6/inc/aenc_svr.h | 120 + audio/mm-audio/aenc-aac/qdsp6/inc/omx_aac_aenc.h | 629 +++ audio/mm-audio/aenc-aac/qdsp6/src/aenc_svr.c | 206 + audio/mm-audio/aenc-aac/qdsp6/src/omx_aac_aenc.cpp | 5062 ++++++++++++++++++++ .../aenc-aac/qdsp6/test/omx_aac_enc_test.c | 1293 +++++ audio/mm-audio/aenc-amrnb/Android.mk | 8 + audio/mm-audio/aenc-amrnb/Makefile | 6 + audio/mm-audio/aenc-amrnb/qdsp6/Android.mk | 68 + audio/mm-audio/aenc-amrnb/qdsp6/Makefile | 81 + audio/mm-audio/aenc-amrnb/qdsp6/inc/Map.h | 244 + audio/mm-audio/aenc-amrnb/qdsp6/inc/aenc_svr.h | 120 + audio/mm-audio/aenc-amrnb/qdsp6/inc/omx_amr_aenc.h | 538 +++ audio/mm-audio/aenc-amrnb/qdsp6/src/aenc_svr.c | 205 + .../mm-audio/aenc-amrnb/qdsp6/src/omx_amr_aenc.cpp | 4532 ++++++++++++++++++ .../aenc-amrnb/qdsp6/test/omx_amr_enc_test.c | 1055 ++++ audio/mm-audio/aenc-evrc/Android.mk | 7 + audio/mm-audio/aenc-evrc/Makefile | 6 + audio/mm-audio/aenc-evrc/qdsp6/Android.mk | 67 + audio/mm-audio/aenc-evrc/qdsp6/Makefile | 81 + audio/mm-audio/aenc-evrc/qdsp6/inc/Map.h | 244 + audio/mm-audio/aenc-evrc/qdsp6/inc/aenc_svr.h | 122 + audio/mm-audio/aenc-evrc/qdsp6/inc/omx_evrc_aenc.h | 539 +++ audio/mm-audio/aenc-evrc/qdsp6/src/aenc_svr.c | 205 + .../mm-audio/aenc-evrc/qdsp6/src/omx_evrc_aenc.cpp | 4531 ++++++++++++++++++ .../aenc-evrc/qdsp6/test/omx_evrc_enc_test.c | 1098 +++++ audio/mm-audio/aenc-qcelp13/Android.mk | 7 + audio/mm-audio/aenc-qcelp13/Makefile | 6 + audio/mm-audio/aenc-qcelp13/qdsp6/Android.mk | 69 + audio/mm-audio/aenc-qcelp13/qdsp6/Makefile | 81 + audio/mm-audio/aenc-qcelp13/qdsp6/inc/Map.h | 244 + audio/mm-audio/aenc-qcelp13/qdsp6/inc/aenc_svr.h | 120 + .../aenc-qcelp13/qdsp6/inc/omx_qcelp13_aenc.h | 539 +++ audio/mm-audio/aenc-qcelp13/qdsp6/src/aenc_svr.c | 208 + .../aenc-qcelp13/qdsp6/src/omx_qcelp13_aenc.cpp | 4532 ++++++++++++++++++ .../aenc-qcelp13/qdsp6/test/omx_qcelp13_enc_test.c | 1101 +++++ audio/mm-audio/autogen.sh | 10 + audio/policy_hal/Android.mk | 87 + audio/policy_hal/AudioPolicyManager.cpp | 2351 +++++++++ audio/policy_hal/AudioPolicyManager.h | 139 + audio/post_proc/Android.mk | 34 + audio/post_proc/bass_boost.c | 280 ++ audio/post_proc/bass_boost.h | 59 + audio/post_proc/bundle.c | 779 +++ audio/post_proc/bundle.h | 92 + audio/post_proc/effect_api.c | 620 +++ audio/post_proc/effect_api.h | 151 + audio/post_proc/equalizer.c | 497 ++ audio/post_proc/equalizer.h | 63 + audio/post_proc/reverb.c | 613 +++ audio/post_proc/reverb.h | 84 + audio/post_proc/virtualizer.c | 503 ++ audio/post_proc/virtualizer.h | 60 + audio/visualizer/Android.mk | 36 + audio/visualizer/MODULE_LICENSE_APACHE2 | 0 audio/visualizer/NOTICE | 190 + audio/visualizer/offload_visualizer.c | 1267 +++++ audio/voice_processing/Android.mk | 23 + audio/voice_processing/voice_processing.c | 765 +++ 104 files changed, 58860 insertions(+) create mode 100644 audio/Android.mk create mode 100644 audio/audiod/Android.mk create mode 100644 audio/audiod/AudioDaemon.cpp create mode 100644 audio/audiod/AudioDaemon.h create mode 100644 audio/audiod/audiod_main.cpp create mode 100644 audio/hal/Android.mk create mode 100644 audio/hal/audio_extn/audio_defs.h create mode 100644 audio/hal/audio_extn/audio_extn.c create mode 100644 audio/hal/audio_extn/audio_extn.h create mode 100644 audio/hal/audio_extn/compress_capture.c create mode 100644 audio/hal/audio_extn/dev_arbi.c create mode 100644 audio/hal/audio_extn/dolby.c create mode 100644 audio/hal/audio_extn/fm.c create mode 100644 audio/hal/audio_extn/hfp.c create mode 100644 audio/hal/audio_extn/listen.c create mode 100644 audio/hal/audio_extn/pm.c create mode 100644 audio/hal/audio_extn/pm.h create mode 100644 audio/hal/audio_extn/soundtrigger.c create mode 100644 audio/hal/audio_extn/spkr_protection.c create mode 100644 audio/hal/audio_extn/ssr.c create mode 100644 audio/hal/audio_extn/usb.c create mode 100644 audio/hal/audio_extn/utils.c create mode 100644 audio/hal/audio_hw.c create mode 100644 audio/hal/audio_hw.h create mode 100644 audio/hal/msm8916/hw_info.c create mode 100644 audio/hal/msm8916/platform.c create mode 100644 audio/hal/msm8916/platform.h create mode 100644 audio/hal/msm8960/platform.c create mode 100644 audio/hal/msm8960/platform.h create mode 100644 audio/hal/msm8974/hw_info.c create mode 100644 audio/hal/msm8974/platform.c create mode 100644 audio/hal/msm8974/platform.h create mode 100644 audio/hal/platform_api.h create mode 100644 audio/hal/platform_info.c create mode 100644 audio/hal/voice.c create mode 100644 audio/hal/voice.h create mode 100644 audio/hal/voice_extn/compress_voip.c create mode 100644 audio/hal/voice_extn/voice_extn.c create mode 100644 audio/hal/voice_extn/voice_extn.h create mode 100644 audio/mm-audio/Android.mk create mode 100644 audio/mm-audio/Makefile create mode 100644 audio/mm-audio/aenc-aac/Android.mk create mode 100644 audio/mm-audio/aenc-aac/Makefile create mode 100644 audio/mm-audio/aenc-aac/qdsp6/Android.mk create mode 100644 audio/mm-audio/aenc-aac/qdsp6/Makefile create mode 100644 audio/mm-audio/aenc-aac/qdsp6/inc/Map.h create mode 100644 audio/mm-audio/aenc-aac/qdsp6/inc/aenc_svr.h create mode 100644 audio/mm-audio/aenc-aac/qdsp6/inc/omx_aac_aenc.h create mode 100644 audio/mm-audio/aenc-aac/qdsp6/src/aenc_svr.c create mode 100644 audio/mm-audio/aenc-aac/qdsp6/src/omx_aac_aenc.cpp create mode 100644 audio/mm-audio/aenc-aac/qdsp6/test/omx_aac_enc_test.c create mode 100644 audio/mm-audio/aenc-amrnb/Android.mk create mode 100644 audio/mm-audio/aenc-amrnb/Makefile create mode 100644 audio/mm-audio/aenc-amrnb/qdsp6/Android.mk create mode 100644 audio/mm-audio/aenc-amrnb/qdsp6/Makefile create mode 100644 audio/mm-audio/aenc-amrnb/qdsp6/inc/Map.h create mode 100644 audio/mm-audio/aenc-amrnb/qdsp6/inc/aenc_svr.h create mode 100644 audio/mm-audio/aenc-amrnb/qdsp6/inc/omx_amr_aenc.h create mode 100644 audio/mm-audio/aenc-amrnb/qdsp6/src/aenc_svr.c create mode 100644 audio/mm-audio/aenc-amrnb/qdsp6/src/omx_amr_aenc.cpp create mode 100644 audio/mm-audio/aenc-amrnb/qdsp6/test/omx_amr_enc_test.c create mode 100644 audio/mm-audio/aenc-evrc/Android.mk create mode 100644 audio/mm-audio/aenc-evrc/Makefile create mode 100644 audio/mm-audio/aenc-evrc/qdsp6/Android.mk create mode 100644 audio/mm-audio/aenc-evrc/qdsp6/Makefile create mode 100644 audio/mm-audio/aenc-evrc/qdsp6/inc/Map.h create mode 100644 audio/mm-audio/aenc-evrc/qdsp6/inc/aenc_svr.h create mode 100644 audio/mm-audio/aenc-evrc/qdsp6/inc/omx_evrc_aenc.h create mode 100644 audio/mm-audio/aenc-evrc/qdsp6/src/aenc_svr.c create mode 100644 audio/mm-audio/aenc-evrc/qdsp6/src/omx_evrc_aenc.cpp create mode 100644 audio/mm-audio/aenc-evrc/qdsp6/test/omx_evrc_enc_test.c create mode 100644 audio/mm-audio/aenc-qcelp13/Android.mk create mode 100644 audio/mm-audio/aenc-qcelp13/Makefile create mode 100644 audio/mm-audio/aenc-qcelp13/qdsp6/Android.mk create mode 100644 audio/mm-audio/aenc-qcelp13/qdsp6/Makefile create mode 100644 audio/mm-audio/aenc-qcelp13/qdsp6/inc/Map.h create mode 100644 audio/mm-audio/aenc-qcelp13/qdsp6/inc/aenc_svr.h create mode 100644 audio/mm-audio/aenc-qcelp13/qdsp6/inc/omx_qcelp13_aenc.h create mode 100644 audio/mm-audio/aenc-qcelp13/qdsp6/src/aenc_svr.c create mode 100644 audio/mm-audio/aenc-qcelp13/qdsp6/src/omx_qcelp13_aenc.cpp create mode 100644 audio/mm-audio/aenc-qcelp13/qdsp6/test/omx_qcelp13_enc_test.c create mode 100644 audio/mm-audio/autogen.sh create mode 100644 audio/policy_hal/Android.mk create mode 100644 audio/policy_hal/AudioPolicyManager.cpp create mode 100644 audio/policy_hal/AudioPolicyManager.h create mode 100644 audio/post_proc/Android.mk create mode 100644 audio/post_proc/bass_boost.c create mode 100644 audio/post_proc/bass_boost.h create mode 100644 audio/post_proc/bundle.c create mode 100644 audio/post_proc/bundle.h create mode 100644 audio/post_proc/effect_api.c create mode 100644 audio/post_proc/effect_api.h create mode 100644 audio/post_proc/equalizer.c create mode 100644 audio/post_proc/equalizer.h create mode 100644 audio/post_proc/reverb.c create mode 100644 audio/post_proc/reverb.h create mode 100644 audio/post_proc/virtualizer.c create mode 100644 audio/post_proc/virtualizer.h create mode 100644 audio/visualizer/Android.mk create mode 100644 audio/visualizer/MODULE_LICENSE_APACHE2 create mode 100644 audio/visualizer/NOTICE create mode 100644 audio/visualizer/offload_visualizer.c create mode 100644 audio/voice_processing/Android.mk create mode 100644 audio/voice_processing/voice_processing.c diff --git a/audio/Android.mk b/audio/Android.mk new file mode 100644 index 0000000..6f7e216 --- /dev/null +++ b/audio/Android.mk @@ -0,0 +1,19 @@ +ifneq ($(filter mpq8092 msm8960 msm8226 msm8x26 msm8610 msm8974 msm8x74 apq8084 msm8916 msm8994 msm8909,$(TARGET_BOARD_PLATFORM)),) + +MY_LOCAL_PATH := $(call my-dir) + +ifeq ($(BOARD_USES_LEGACY_ALSA_AUDIO),true) +include $(MY_LOCAL_PATH)/legacy/Android.mk +else +ifneq ($(filter mpq8092,$(TARGET_BOARD_PLATFORM)),) +include $(MY_LOCAL_PATH)/hal_mpq/Android.mk +else +include $(MY_LOCAL_PATH)/hal/Android.mk +endif +include $(MY_LOCAL_PATH)/voice_processing/Android.mk +include $(MY_LOCAL_PATH)/policy_hal/Android.mk +include $(MY_LOCAL_PATH)/visualizer/Android.mk +include $(MY_LOCAL_PATH)/post_proc/Android.mk +endif + +endif diff --git a/audio/audiod/Android.mk b/audio/audiod/Android.mk new file mode 100644 index 0000000..8f36ed8 --- /dev/null +++ b/audio/audiod/Android.mk @@ -0,0 +1,21 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +include external/stlport/libstlport.mk + +LOCAL_SRC_FILES:= \ + audiod_main.cpp \ + AudioDaemon.cpp \ + +LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libbinder \ + libmedia \ + libstlport + +LOCAL_MODULE:= audiod + +include $(BUILD_EXECUTABLE) diff --git a/audio/audiod/AudioDaemon.cpp b/audio/audiod/AudioDaemon.cpp new file mode 100644 index 0000000..b4857c4 --- /dev/null +++ b/audio/audiod/AudioDaemon.cpp @@ -0,0 +1,458 @@ +/* AudioDaemon.cpp +Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/ + +#define LOG_TAG "AudioDaemon" +#define LOG_NDEBUG 0 +#define LOG_NDDEBUG 0 + +#include +#include +#include + +#include "AudioDaemon.h" + +#define CPE_MAGIC_NUM 0x2000 +#define MAX_CPE_SLEEP_RETRY 2 +#define CPE_SLEEP_WAIT 100 + +#define MAX_SLEEP_RETRY 100 +#define AUDIO_INIT_SLEEP_WAIT 100 /* 100 ms */ + +int bootup_complete = 0; +bool cpe_bootup_complete = false; + +namespace android { + + AudioDaemon::AudioDaemon() : Thread(false) { + } + + AudioDaemon::~AudioDaemon() { + putStateFDs(mSndCardFd); + } + + void AudioDaemon::onFirstRef() { + ALOGV("Start audiod daemon"); + run("AudioDaemon", PRIORITY_URGENT_AUDIO); + } + + void AudioDaemon::binderDied(const wp& who) + { + requestExit(); + } + + bool AudioDaemon::getStateFDs(std::vector > &sndcardFdPair) + { + FILE *fp; + int fd; + char *ptr, *saveptr; + char buffer[128]; + int line = 0; + String8 path; + int sndcard; + const char* cards = "/proc/asound/cards"; + + if ((fp = fopen(cards, "r")) == NULL) { + ALOGE("Cannot open %s file to get list of sound cars", cards); + return false; + } + + sndcardFdPair.clear(); + memset(buffer, 0x0, sizeof(buffer)); + while ((fgets(buffer, sizeof(buffer), fp) != NULL)) { + if (line % 2) + continue; + ptr = strtok_r(buffer, " [", &saveptr); + if (ptr) { + path = "/proc/asound/card"; + path += ptr; + path += "/state"; + ALOGD("Opening sound card state : %s", path.string()); + fd = open(path.string(), O_RDONLY); + if (fd == -1) { + ALOGE("Open %s failed : %s", path.string(), strerror(errno)); + } else { + /* returns vector of pair */ + sndcard = atoi(ptr); + sndcardFdPair.push_back(std::make_pair(sndcard, fd)); + } + } + line++; + } + + ALOGV("%s: %d sound cards detected", __func__, sndcardFdPair.size()); + fclose(fp); + + return sndcardFdPair.size() > 0 ? true : false; + } + + void AudioDaemon::putStateFDs(std::vector > &sndcardFdPair) + { + unsigned int i; + for (i = 0; i < sndcardFdPair.size(); i++) + close(sndcardFdPair[i].second); + sndcardFdPair.clear(); + } + + bool AudioDaemon::getDeviceEventFDs() + { + const char* events_dir = "/sys/class/switch/"; + DIR *dp; + struct dirent* in_file; + int fd; + String8 path; + + if ((dp = opendir(events_dir)) == NULL) { + ALOGE("Cannot open switch directory to get list of audio events %s", events_dir); + return false; + } + + mAudioEvents.clear(); + mAudioEventsStatus.clear(); + + while ((in_file = readdir(dp)) != NULL) { + + if (!strstr(in_file->d_name, "qc_")) + continue; + ALOGD(" Found event file = %s", in_file->d_name); + path = "/sys/class/switch/"; + path += in_file->d_name; + path += "/state"; + + ALOGE("Opening audio event state : %s ", path.string()); + fd = open(path.string(), O_RDONLY); + if (fd == -1) { + ALOGE("Open %s failed : %s", path.string(), strerror(errno)); + } else { + mAudioEvents.push_back(std::make_pair(in_file->d_name, fd)); + mAudioEventsStatus.push_back(std::make_pair(in_file->d_name, 0)); + ALOGD("event status mAudioEventsStatus= %s", + mAudioEventsStatus[0].first.string()); + } + } + + ALOGV("%s: %d audio device event detected", + __func__, + mAudioEvents.size()); + + closedir(dp); + return mAudioEvents.size() > 0 ? true : false; + + } + + void AudioDaemon::putDeviceEventFDs() + { + unsigned int i; + for (i = 0; i < mAudioEvents.size(); i++) { + close(mAudioEvents[i].second); + delete(mAudioEvents[i].first); + } + mAudioEvents.clear(); + mAudioEventsStatus.clear(); + } + + void AudioDaemon::checkEventState(int fd, int index) + { + char state_buf[2]; + audio_event_status event_cur_state = audio_event_off; + + if (!read(fd, (void *)state_buf, 1)) { + ALOGE("Error receiving device state event (%s)", strerror(errno)); + } else { + state_buf[1] = '\0'; + if (atoi(state_buf) != mAudioEventsStatus[index].second) { + ALOGD("notify audio HAL %s", + mAudioEvents[index].first.string()); + mAudioEventsStatus[index].second = atoi(state_buf); + + if (mAudioEventsStatus[index].second == 1) + event_cur_state = audio_event_on; + else + event_cur_state = audio_event_off; + notifyAudioSystemEventStatus( + mAudioEventsStatus[index].first.string(), + event_cur_state); + } + } + lseek(fd, 0, SEEK_SET); + } + + status_t AudioDaemon::readyToRun() { + + ALOGV("readyToRun: open snd card state node files"); + return NO_ERROR; + } + + bool AudioDaemon::threadLoop() + { + int max = -1; + unsigned int i; + bool ret = true; + notify_status cur_state = snd_card_offline; + struct pollfd *pfd = NULL; + char rd_buf[9]; + unsigned int sleepRetry = 0; + bool audioInitDone = false; + int fd = 0; + char path[50]; + notify_status cur_cpe_state = cpe_offline; + notify_status prev_cpe_state = cpe_offline; + unsigned int cpe_cnt = CPE_MAGIC_NUM; + unsigned int num_snd_cards = 0; + + ALOGV("Start threadLoop()"); + while (audioInitDone == false && sleepRetry < MAX_SLEEP_RETRY) { + if (mSndCardFd.empty() && !getStateFDs(mSndCardFd)) { + ALOGE("Sleeping for 100 ms"); + usleep(AUDIO_INIT_SLEEP_WAIT*1000); + sleepRetry++; + } else { + audioInitDone = true; + } + } + + if (!getDeviceEventFDs()) { + ALOGE("No audio device events detected"); + } + + if (audioInitDone == false) { + ALOGE("Sound Card is empty!!!"); + goto thread_exit; + } + + /* soundcards are opened, now get the cpe state nodes */ + num_snd_cards = mSndCardFd.size(); + for (i = 0; i < num_snd_cards; i++) { + snprintf(path, sizeof(path), "/proc/asound/card%d/cpe0_state", mSndCardFd[i].first); + ALOGD("Opening cpe0_state : %s", path); + sleepRetry = 0; + do { + fd = open(path, O_RDONLY); + if (fd == -1) { + sleepRetry++; + ALOGE("CPE state open %s failed %s, Retrying %d", + path, strerror(errno), sleepRetry); + usleep(CPE_SLEEP_WAIT*1000); + } else { + ALOGD("cpe state opened: %s", path); + mSndCardFd.push_back(std::make_pair(cpe_cnt++, fd)); + } + }while ((fd == -1) && sleepRetry < MAX_CPE_SLEEP_RETRY); + } + ALOGD("number of sndcards %d CPEs %d", i, cpe_cnt - CPE_MAGIC_NUM); + + pfd = new pollfd[mSndCardFd.size() + mAudioEvents.size()]; + bzero(pfd, (sizeof(*pfd) * mSndCardFd.size() + + sizeof(*pfd) * mAudioEvents.size())); + for (i = 0; i < mSndCardFd.size(); i++) { + pfd[i].fd = mSndCardFd[i].second; + pfd[i].events = POLLPRI; + } + + /*insert all audio events*/ + for(i = 0; i < mAudioEvents.size(); i++) { + pfd[i+mSndCardFd.size()].fd = mAudioEvents[i].second; + pfd[i+mSndCardFd.size()].events = POLLPRI; + } + + ALOGD("read for sound card state change before while"); + for (i = 0; i < mSndCardFd.size(); i++) { + if (!read(pfd[i].fd, (void *)rd_buf, 8)) { + ALOGE("Error receiving sound card state event (%s)", strerror(errno)); + ret = false; + } else { + rd_buf[8] = '\0'; + lseek(pfd[i].fd, 0, SEEK_SET); + + if(mSndCardFd[i].first >= CPE_MAGIC_NUM) { + ALOGD("CPE %d state file content: %s before while", + mSndCardFd[i].first - CPE_MAGIC_NUM, rd_buf); + if (strstr(rd_buf, "OFFLINE")) { + ALOGD("CPE state offline"); + cur_cpe_state = cpe_offline; + } else if (strstr(rd_buf, "ONLINE")){ + ALOGD("CPE state online"); + cur_cpe_state = cpe_online; + } else { + ALOGE("ERROR CPE rd_buf %s", rd_buf); + } + if (cur_cpe_state == cpe_online && !cpe_bootup_complete) { + cpe_bootup_complete = true; + ALOGD("CPE boot up completed before polling"); + } + prev_cpe_state = cur_cpe_state; + } + else { + ALOGD("sound card state file content: %s before while",rd_buf); + if (strstr(rd_buf, "OFFLINE")) { + ALOGE("put cur_state to offline"); + cur_state = snd_card_offline; + } else if (strstr(rd_buf, "ONLINE")){ + ALOGE("put cur_state to online"); + cur_state = snd_card_online; + } else { + ALOGE("ERROR rd_buf %s", rd_buf); + } + + ALOGD("cur_state=%d, bootup_complete=%d", cur_state, cur_state ); + if (cur_state == snd_card_online && !bootup_complete) { + bootup_complete = 1; + ALOGE("sound card up is deteced before while"); + ALOGE("bootup_complete set to 1"); + } + } + } + } + + ALOGE("read for event state change before while"); + for (i = 0; i < mAudioEvents.size(); i++){ + checkEventState(pfd[i+mSndCardFd.size()].fd, i); + } + + while (1) { + ALOGD("poll() for sound card state change "); + if (poll(pfd, (mSndCardFd.size() + mAudioEvents.size()), -1) < 0) { + ALOGE("poll() failed (%s)", strerror(errno)); + ret = false; + break; + } + + ALOGD("out of poll() for sound card state change, SNDCARD size=%d", mSndCardFd.size()); + for (i = 0; i < mSndCardFd.size(); i++) { + if (pfd[i].revents & POLLPRI) { + if (!read(pfd[i].fd, (void *)rd_buf, 8)) { + ALOGE("Error receiving sound card %d state event (%s)", + mSndCardFd[i].first, strerror(errno)); + ret = false; + } else { + rd_buf[8] = '\0'; + lseek(pfd[i].fd, 0, SEEK_SET); + + if(mSndCardFd[i].first >= CPE_MAGIC_NUM) { + if (strstr(rd_buf, "OFFLINE")) + cur_cpe_state = cpe_offline; + else if (strstr(rd_buf, "ONLINE")) + cur_cpe_state = cpe_online; + else + ALOGE("ERROR CPE rd_buf %s", rd_buf); + + if (cpe_bootup_complete && (prev_cpe_state != cur_cpe_state)) { + ALOGD("CPE state is %d, nofity AudioSystem", cur_cpe_state); + notifyAudioSystem(mSndCardFd[i].first, cur_cpe_state, CPE_STATE); + } + if (!cpe_bootup_complete && (cur_cpe_state == cpe_online)) { + cpe_bootup_complete = true; + ALOGD("CPE boot up completed"); + } + prev_cpe_state = cur_cpe_state; + } + else { + ALOGV("sound card state file content: %s, bootup_complete=%d",rd_buf, bootup_complete); + if (strstr(rd_buf, "OFFLINE")) { + cur_state = snd_card_offline; + } else if (strstr(rd_buf, "ONLINE")){ + cur_state = snd_card_online; + } + + if (bootup_complete) { + ALOGV("bootup_complete, so NofityAudioSystem"); + notifyAudioSystem(mSndCardFd[i].first, cur_state, SND_CARD_STATE); + } + + if (cur_state == snd_card_online && !bootup_complete) { + bootup_complete = 1; + } + } + } + } + } + for (i = 0; i < mAudioEvents.size(); i++) { + if (pfd[i + mSndCardFd.size()].revents & POLLPRI) { + ALOGE("EVENT recieved pfd[i].revents= 0x%x %d", + pfd[i + mSndCardFd.size()].revents, + mAudioEvents[i].second); + + checkEventState(pfd[i + mSndCardFd.size()].fd, i); + } + } + } + + putStateFDs(mSndCardFd); + putDeviceEventFDs(); + delete [] pfd; + + thread_exit: + ALOGV("Exiting Poll ThreadLoop"); + return ret; + } + + void AudioDaemon::notifyAudioSystem(int snd_card, + notify_status status, + notify_status_type type) + { + + String8 str; + char buf[4] = {0,}; + + if (type == CPE_STATE) { + str = "CPE_STATUS="; + snprintf(buf, sizeof(buf), "%d", snd_card - CPE_MAGIC_NUM); + str += buf; + if (status == cpe_online) + str += ",ONLINE"; + else + str += ",OFFLINE"; + } + else { + str = "SND_CARD_STATUS="; + snprintf(buf, sizeof(buf), "%d", snd_card); + str += buf; + if (status == snd_card_online) + str += ",ONLINE"; + else + str += ",OFFLINE"; + } + ALOGV("%s: notifyAudioSystem : %s", __func__, str.string()); + AudioSystem::setParameters(0, str); + } + + void AudioDaemon::notifyAudioSystemEventStatus(const char* event, + audio_event_status status) { + + String8 str; + str += AUDIO_PARAMETER_KEY_EXT_AUDIO_DEVICE; + str += "="; + str += event; + + if (status == audio_event_on) + str += ",ON"; + else + str += ",OFF"; + ALOGD("%s: notifyAudioSystemEventStatus : %s", __func__, str.string()); + AudioSystem::setParameters(0, str); + } +} diff --git a/audio/audiod/AudioDaemon.h b/audio/audiod/AudioDaemon.h new file mode 100644 index 0000000..3359d72 --- /dev/null +++ b/audio/audiod/AudioDaemon.h @@ -0,0 +1,92 @@ +/* AudioDaemon.h + +Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/ + +#include +#include +#include +#include +#include +#include + +#include +#include + + +namespace android { + +enum notify_status { + snd_card_online, + snd_card_offline, + cpe_online, + cpe_offline +}; + +enum notify_status_type { + SND_CARD_STATE, + CPE_STATE +}; + +enum audio_event_status {audio_event_on, audio_event_off}; + +#define AUDIO_PARAMETER_KEY_EXT_AUDIO_DEVICE "ext_audio_device" + +class AudioDaemon:public Thread, public IBinder :: DeathRecipient +{ + /*Overrides*/ + virtual bool threadLoop(); + virtual status_t readyToRun(); + virtual void onFirstRef(); + virtual void binderDied(const wp < IBinder > &who); + + bool processUeventMessage(); + void notifyAudioSystem(int snd_card, + notify_status status, + notify_status_type type); + void notifyAudioSystemEventStatus(const char* event, audio_event_status status); + int mUeventSock; + bool getStateFDs(std::vector > &sndcardFdPair); + void putStateFDs(std::vector > &sndcardFdPair); + bool getDeviceEventFDs(); + void putDeviceEventFDs(); + void checkEventState(int fd, int index); + +public: + AudioDaemon(); + virtual ~AudioDaemon(); + +private: + std::vector > mSndCardFd; + + //file descriptors for audio device events and their statuses + std::vector > mAudioEvents; + std::vector > mAudioEventsStatus; + +}; + +} diff --git a/audio/audiod/audiod_main.cpp b/audio/audiod/audiod_main.cpp new file mode 100644 index 0000000..50691fd --- /dev/null +++ b/audio/audiod/audiod_main.cpp @@ -0,0 +1,60 @@ +/* Copyright (C) 2007 The Android Open Source Project + +Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + +Not a Contribution, Apache license notifications and license are retained +for attribution purposes only. + +* 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 "AudioDaemonMain" +#define LOG_NDEBUG 0 +#define LOG_NDDEBUG 0 + +#include + +#include +#include +#include + +#include +#include + +#if defined(HAVE_PTHREADS) +# include +# include +#endif + +#include "AudioDaemon.h" + +using namespace android; + +// --------------------------------------------------------------------------- + +int main(int argc, char** argv) +{ +#if defined(HAVE_PTHREADS) + setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_AUDIO); +#endif + + + ALOGV("Audio daemon starting sequence.."); + sp proc(ProcessState::self()); + ProcessState::self()->startThreadPool(); + + sp audioService = new AudioDaemon(); + IPCThreadState::self()->joinThreadPool(); + + return 0; +} diff --git a/audio/hal/Android.mk b/audio/hal/Android.mk new file mode 100644 index 0000000..15136d1 --- /dev/null +++ b/audio/hal/Android.mk @@ -0,0 +1,224 @@ +ifeq ($(strip $(BOARD_USES_ALSA_AUDIO)),true) + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_ARM_MODE := arm + +AUDIO_PLATFORM := $(TARGET_BOARD_PLATFORM) + +ifneq ($(filter msm8974 msm8226 msm8610 apq8084 msm8994,$(TARGET_BOARD_PLATFORM)),) + # B-family platform uses msm8974 code base + AUDIO_PLATFORM = msm8974 + MULTIPLE_HW_VARIANTS_ENABLED := true +ifneq ($(filter msm8610,$(TARGET_BOARD_PLATFORM)),) + LOCAL_CFLAGS := -DPLATFORM_MSM8610 +endif +ifneq ($(filter msm8226,$(TARGET_BOARD_PLATFORM)),) + LOCAL_CFLAGS := -DPLATFORM_MSM8x26 +endif +ifneq ($(filter apq8084,$(TARGET_BOARD_PLATFORM)),) + LOCAL_CFLAGS := -DPLATFORM_APQ8084 +endif +ifneq ($(filter msm8994,$(TARGET_BOARD_PLATFORM)),) + LOCAL_CFLAGS := -DPLATFORM_MSM8994 +endif +endif + +ifneq ($(filter msm8916 msm8909,$(TARGET_BOARD_PLATFORM)),) + AUDIO_PLATFORM = msm8916 + MULTIPLE_HW_VARIANTS_ENABLED := true + LOCAL_CFLAGS := -DPLATFORM_MSM8916 +ifneq ($(filter msm8909,$(TARGET_BOARD_PLATFORM)),) + LOCAL_CFLAGS := -DPLATFORM_MSM8909 +endif +endif + +LOCAL_SRC_FILES := \ + audio_hw.c \ + voice.c \ + platform_info.c \ + $(AUDIO_PLATFORM)/platform.c + +LOCAL_SRC_FILES += audio_extn/audio_extn.c \ + audio_extn/utils.c + +LOCAL_C_INCLUDES += hardware/bsp/qcom/soc/msm8916/kernel-headers + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_PCM_OFFLOAD)),true) + LOCAL_CFLAGS += -DPCM_OFFLOAD_ENABLED +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_ANC_HEADSET)),true) + LOCAL_CFLAGS += -DANC_HEADSET_ENABLED +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_FLUENCE)),true) + LOCAL_CFLAGS += -DFLUENCE_ENABLED +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_PROXY_DEVICE)),true) + LOCAL_CFLAGS += -DAFE_PROXY_ENABLED +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_KPI_OPTIMIZE)),true) + LOCAL_CFLAGS += -DKPI_OPTIMIZE_ENABLED +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_FM)),true) + LOCAL_CFLAGS += -DFM_ENABLED + LOCAL_SRC_FILES += audio_extn/fm.c +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_USBAUDIO)),true) + LOCAL_CFLAGS += -DUSB_HEADSET_ENABLED + LOCAL_SRC_FILES += audio_extn/usb.c +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_HFP)),true) + LOCAL_CFLAGS += -DHFP_ENABLED + LOCAL_SRC_FILES += audio_extn/hfp.c +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_CUSTOMSTEREO)),true) + LOCAL_CFLAGS += -DCUSTOM_STEREO_ENABLED +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_SSR)),true) + LOCAL_CFLAGS += -DSSR_ENABLED + LOCAL_SRC_FILES += audio_extn/ssr.c + LOCAL_C_INCLUDES += $(TARGET_OUT_HEADERS)/mm-audio/surround_sound/ + LOCAL_C_INCLUDES += $(TARGET_OUT_HEADERS)/common/inc/ +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_MULTI_VOICE_SESSIONS)),true) + LOCAL_CFLAGS += -DMULTI_VOICE_SESSION_ENABLED + LOCAL_SRC_FILES += voice_extn/voice_extn.c + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_INCALL_MUSIC)),true) + LOCAL_CFLAGS += -DINCALL_MUSIC_ENABLED +endif +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_COMPRESS_VOIP)),true) + LOCAL_CFLAGS += -DCOMPRESS_VOIP_ENABLED + LOCAL_SRC_FILES += voice_extn/compress_voip.c +endif + +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_EXTN_FORMATS)),true) +LOCAL_CFLAGS += -DFORMATS_ENABLED +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_SPKR_PROTECTION)),true) + LOCAL_CFLAGS += -DSPKR_PROT_ENABLED + LOCAL_SRC_FILES += audio_extn/spkr_protection.c +endif + +ifdef MULTIPLE_HW_VARIANTS_ENABLED + LOCAL_CFLAGS += -DHW_VARIANTS_ENABLED + LOCAL_SRC_FILES += $(AUDIO_PLATFORM)/hw_info.c +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_COMPRESS_CAPTURE)),true) + LOCAL_CFLAGS += -DCOMPRESS_CAPTURE_ENABLED + LOCAL_SRC_FILES += audio_extn/compress_capture.c +endif + +ifeq ($(strip $(DOLBY_DDP)),true) + LOCAL_CFLAGS += -DDS1_DOLBY_DDP_ENABLED + LOCAL_SRC_FILES += audio_extn/dolby.c +endif + +ifeq ($(strip $(DOLBY_DAP)),true) + LOCAL_CFLAGS += -DDS1_DOLBY_DAP_ENABLED +ifneq ($(strip $(DOLBY_DDP)),true) + LOCAL_SRC_FILES += audio_extn/dolby.c +endif +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_MULTIPLE_TUNNEL)), true) + LOCAL_CFLAGS += -DMULTIPLE_OFFLOAD_ENABLED +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_EXTN_FLAC_DECODER)),true) + LOCAL_CFLAGS += -DQTI_FLAC_DECODER +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_DEV_ARBI)),true) + LOCAL_CFLAGS += -DDEV_ARBI_ENABLED + LOCAL_SRC_FILES += audio_extn/dev_arbi.c +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_RECORD_PLAY_CONCURRENCY)),true) + LOCAL_CFLAGS += -DRECORD_PLAY_CONCURRENCY +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_ACDB_LICENSE)), true) + LOCAL_CFLAGS += -DDOLBY_ACDB_LICENSE +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_DS2_DOLBY_DAP)),true) + LOCAL_CFLAGS += -DDS2_DOLBY_DAP_ENABLED +ifneq ($(strip $(DOLBY_DDP)),true) + ifneq ($(strip $(DOLBY_DAP)),true) + LOCAL_SRC_FILES += audio_extn/dolby.c + endif +endif +endif + +LOCAL_SHARED_LIBRARIES := \ + liblog \ + libcutils \ + libtinyalsa \ + libtinycompress \ + libaudioroute \ + libdl \ + libexpat + +LOCAL_C_INCLUDES += \ + external/tinyalsa/include \ + external/tinycompress/include \ + external/expat/lib \ + $(call include-path-for, audio-route) \ + $(call include-path-for, audio-effects) \ + $(LOCAL_PATH)/$(AUDIO_PLATFORM) \ + $(LOCAL_PATH)/audio_extn \ + $(LOCAL_PATH)/voice_extn + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_LISTEN)),true) + LOCAL_CFLAGS += -DAUDIO_LISTEN_ENABLED + LOCAL_C_INCLUDES += $(TARGET_OUT_HEADERS)/mm-audio/audio-listen + LOCAL_SRC_FILES += audio_extn/listen.c +endif + +ifeq ($(strip $(BOARD_SUPPORTS_SOUND_TRIGGER)),true) + LOCAL_CFLAGS += -DSOUND_TRIGGER_ENABLED + LOCAL_CFLAGS += -DSOUND_TRIGGER_PLATFORM_NAME=$(TARGET_BOARD_PLATFORM) + LOCAL_C_INCLUDES += $(TARGET_OUT_HEADERS)/mm-audio/sound_trigger + LOCAL_SRC_FILES += audio_extn/soundtrigger.c +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_AUXPCM_BT)),true) + LOCAL_CFLAGS += -DAUXPCM_BT_ENABLED +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_PM_SUPPORT)),true) + LOCAL_CFLAGS += -DPM_SUPPORT_ENABLED + LOCAL_SRC_FILES += audio_extn/pm.c + LOCAL_C_INCLUDES += $(TARGET_OUT_HEADERS)/libperipheralclient/inc + LOCAL_SHARED_LIBRARIES += libperipheral_client +endif + +LOCAL_COPY_HEADERS_TO := mm-audio +LOCAL_COPY_HEADERS := audio_extn/audio_defs.h + +LOCAL_MODULE := audio.primary.$(TARGET_BOARD_PLATFORM) + +LOCAL_MODULE_RELATIVE_PATH := hw + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) + +endif diff --git a/audio/hal/audio_extn/audio_defs.h b/audio/hal/audio_extn/audio_defs.h new file mode 100644 index 0000000..335a629 --- /dev/null +++ b/audio/hal/audio_extn/audio_defs.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AUDIO_DEFS_H +#define AUDIO_DEFS_H + + +/** + * extended audio codec parameters + */ + +#define AUDIO_OFFLOAD_CODEC_WMA_FORMAT_TAG "music_offload_wma_format_tag" +#define AUDIO_OFFLOAD_CODEC_WMA_BLOCK_ALIGN "music_offload_wma_block_align" +#define AUDIO_OFFLOAD_CODEC_WMA_BIT_PER_SAMPLE "music_offload_wma_bit_per_sample" +#define AUDIO_OFFLOAD_CODEC_WMA_CHANNEL_MASK "music_offload_wma_channel_mask" +#define AUDIO_OFFLOAD_CODEC_WMA_ENCODE_OPTION "music_offload_wma_encode_option" +#define AUDIO_OFFLOAD_CODEC_WMA_ENCODE_OPTION1 "music_offload_wma_encode_option1" +#define AUDIO_OFFLOAD_CODEC_WMA_ENCODE_OPTION2 "music_offload_wma_encode_option2" +#define AUDIO_OFFLOAD_CODEC_FORMAT "music_offload_codec_format" +#define AUDIO_OFFLOAD_CODEC_FLAC_MIN_BLK_SIZE "music_offload_flac_min_blk_size" +#define AUDIO_OFFLOAD_CODEC_FLAC_MAX_BLK_SIZE "music_offload_flac_max_blk_size" +#define AUDIO_OFFLOAD_CODEC_FLAC_MIN_FRAME_SIZE "music_offload_flac_min_frame_size" +#define AUDIO_OFFLOAD_CODEC_FLAC_MAX_FRAME_SIZE "music_offload_flac_max_frame_size" + +/* Query handle fm parameter*/ +#define AUDIO_PARAMETER_KEY_HANDLE_FM "handle_fm" + +/* Query fm volume */ +#define AUDIO_PARAMETER_KEY_FM_VOLUME "fm_volume" + +/* Query Fluence type */ +#define AUDIO_PARAMETER_KEY_FLUENCE "fluence" +#define AUDIO_PARAMETER_VALUE_QUADMIC "quadmic" +#define AUDIO_PARAMETER_VALUE_DUALMIC "dualmic" +#define AUDIO_PARAMETER_KEY_NO_FLUENCE "none" + +/* Query if surround sound recording is supported */ +#define AUDIO_PARAMETER_KEY_SSR "ssr" + +/* Query if a2dp is supported */ +#define AUDIO_PARAMETER_KEY_HANDLE_A2DP_DEVICE "isA2dpDeviceSupported" + +/* Query ADSP Status */ +#define AUDIO_PARAMETER_KEY_ADSP_STATUS "ADSP_STATUS" + +/* Query Sound Card Status */ +#define AUDIO_PARAMETER_KEY_SND_CARD_STATUS "SND_CARD_STATUS" + +/* Query if Proxy can be Opend */ +#define AUDIO_PARAMETER_KEY_CAN_OPEN_PROXY "can_open_proxy" + +#endif /* AUDIO_DEFS_H */ diff --git a/audio/hal/audio_extn/audio_extn.c b/audio/hal/audio_extn/audio_extn.c new file mode 100644 index 0000000..cae961d --- /dev/null +++ b/audio/hal/audio_extn/audio_extn.c @@ -0,0 +1,575 @@ +/* + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2013 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_hw_extn" +/*#define LOG_NDEBUG 0*/ +#define LOG_NDDEBUG 0 + +#include +#include +#include +#include +#include + +#include "audio_hw.h" +#include "audio_extn.h" +#include "platform.h" +#include "platform_api.h" + +#define MAX_SLEEP_RETRY 100 +#define WIFI_INIT_WAIT_SLEEP 50 + +struct audio_extn_module { + bool anc_enabled; + bool aanc_enabled; + bool custom_stereo_enabled; + uint32_t proxy_channel_num; +}; + +static struct audio_extn_module aextnmod = { + .anc_enabled = 0, + .aanc_enabled = 0, + .custom_stereo_enabled = 0, + .proxy_channel_num = 2, +}; + +#define AUDIO_PARAMETER_KEY_ANC "anc_enabled" +#define AUDIO_PARAMETER_KEY_WFD "wfd_channel_cap" +#define AUDIO_PARAMETER_CAN_OPEN_PROXY "can_open_proxy" +#define AUDIO_PARAMETER_CUSTOM_STEREO "stereo_as_dual_mono" +/* Query offload playback instances count */ +#define AUDIO_PARAMETER_OFFLOAD_NUM_ACTIVE "offload_num_active" + +#ifndef FM_ENABLED +#define audio_extn_fm_set_parameters(adev, parms) (0) +#else +void audio_extn_fm_set_parameters(struct audio_device *adev, + struct str_parms *parms); +#endif +#ifndef HFP_ENABLED +#define audio_extn_hfp_set_parameters(adev, parms) (0) +#else +void audio_extn_hfp_set_parameters(struct audio_device *adev, + struct str_parms *parms); +#endif + +#ifndef CUSTOM_STEREO_ENABLED +#define audio_extn_customstereo_set_parameters(adev, parms) (0) +#else +void audio_extn_customstereo_set_parameters(struct audio_device *adev, + struct str_parms *parms) +{ + int ret = 0; + char value[32]={0}; + bool custom_stereo_state = false; + const char *mixer_ctl_name = "Set Custom Stereo OnOff"; + struct mixer_ctl *ctl; + + ALOGV("%s", __func__); + ret = str_parms_get_str(parms, AUDIO_PARAMETER_CUSTOM_STEREO, value, + sizeof(value)); + if (ret >= 0) { + if (!strncmp("true", value, sizeof("true")) || atoi(value)) + custom_stereo_state = true; + + if (custom_stereo_state == aextnmod.custom_stereo_enabled) + return; + + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + return; + } + if (mixer_ctl_set_value(ctl, 0, custom_stereo_state) < 0) { + ALOGE("%s: Could not set custom stereo state %d", + __func__, custom_stereo_state); + return; + } + aextnmod.custom_stereo_enabled = custom_stereo_state; + ALOGV("%s: Setting custom stereo state success", __func__); + } +} +#endif /* CUSTOM_STEREO_ENABLED */ + +#ifndef ANC_HEADSET_ENABLED +#define audio_extn_set_anc_parameters(adev, parms) (0) +#else +bool audio_extn_get_anc_enabled(void) +{ + ALOGD("%s: anc_enabled:%d", __func__, aextnmod.anc_enabled); + return (aextnmod.anc_enabled ? true: false); +} + +bool audio_extn_should_use_handset_anc(int in_channels) +{ + char prop_aanc[PROPERTY_VALUE_MAX] = "false"; + + property_get("persist.aanc.enable", prop_aanc, "0"); + if (!strncmp("true", prop_aanc, 4)) { + ALOGD("%s: AANC enabled in the property", __func__); + aextnmod.aanc_enabled = 1; + } + + return (aextnmod.aanc_enabled && aextnmod.anc_enabled + && (in_channels == 1)); +} + +bool audio_extn_should_use_fb_anc(void) +{ + char prop_anc[PROPERTY_VALUE_MAX] = "feedforward"; + + property_get("persist.headset.anc.type", prop_anc, "0"); + if (!strncmp("feedback", prop_anc, sizeof("feedback"))) { + ALOGD("%s: FB ANC headset type enabled\n", __func__); + return true; + } + return false; +} + +void audio_extn_set_anc_parameters(struct audio_device *adev, + struct str_parms *parms) +{ + int ret; + char value[32] ={0}; + struct listnode *node; + struct audio_usecase *usecase; + + ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_ANC, value, + sizeof(value)); + if (ret >= 0) { + if (strcmp(value, "true") == 0) + aextnmod.anc_enabled = true; + else + aextnmod.anc_enabled = false; + + list_for_each(node, &adev->usecase_list) { + usecase = node_to_item(node, struct audio_usecase, list); + if (usecase->type == PCM_PLAYBACK) { + if (usecase->stream.out->devices == \ + AUDIO_DEVICE_OUT_WIRED_HEADPHONE || + usecase->stream.out->devices == \ + AUDIO_DEVICE_OUT_WIRED_HEADSET) { + select_devices(adev, usecase->id); + ALOGV("%s: switching device", __func__); + break; + } + } + } + } + + ALOGD("%s: anc_enabled:%d", __func__, aextnmod.anc_enabled); +} +#endif /* ANC_HEADSET_ENABLED */ + +#ifndef FLUENCE_ENABLED +#define audio_extn_set_fluence_parameters(adev, parms) (0) +#define audio_extn_get_fluence_parameters(adev, query, reply) (0) +#else +void audio_extn_set_fluence_parameters(struct audio_device *adev, + struct str_parms *parms) +{ + int ret = 0, err; + char value[32]; + struct listnode *node; + struct audio_usecase *usecase; + + err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_FLUENCE, + value, sizeof(value)); + ALOGV_IF(err >= 0, "%s: Set Fluence Type to %s", __func__, value); + if (err >= 0) { + ret = platform_set_fluence_type(adev->platform, value); + if (ret != 0) { + ALOGE("platform_set_fluence_type returned error: %d", ret); + } else { + /* + *If the fluence is manually set/reset, devices + *need to get updated for all the usecases + *i.e. audio and voice. + */ + list_for_each(node, &adev->usecase_list) { + usecase = node_to_item(node, struct audio_usecase, list); + select_devices(adev, usecase->id); + } + } + } +} + +int audio_extn_get_fluence_parameters(const struct audio_device *adev, + struct str_parms *query, struct str_parms *reply) +{ + int ret = 0, err; + char value[256] = {0}; + + err = str_parms_get_str(query, AUDIO_PARAMETER_KEY_FLUENCE, value, + sizeof(value)); + if (err >= 0) { + ret = platform_get_fluence_type(adev->platform, value, sizeof(value)); + if (ret >= 0) { + ALOGV("%s: Fluence Type is %s", __func__, value); + str_parms_add_str(reply, AUDIO_PARAMETER_KEY_FLUENCE, value); + } else + goto done; + } +done: + return ret; +} +#endif /* FLUENCE_ENABLED */ + +#ifndef AFE_PROXY_ENABLED +#define audio_extn_set_afe_proxy_parameters(adev, parms) (0) +#define audio_extn_get_afe_proxy_parameters(query, reply) (0) +#else +/* Front left channel. */ +#define PCM_CHANNEL_FL 1 + +/* Front right channel. */ +#define PCM_CHANNEL_FR 2 + +/* Front center channel. */ +#define PCM_CHANNEL_FC 3 + +/* Left surround channel.*/ +#define PCM_CHANNEL_LS 4 + +/* Right surround channel.*/ +#define PCM_CHANNEL_RS 5 + +/* Low frequency effect channel. */ +#define PCM_CHANNEL_LFE 6 + +/* Left back channel; Rear left channel. */ +#define PCM_CHANNEL_LB 8 + +/* Right back channel; Rear right channel. */ +#define PCM_CHANNEL_RB 9 + +static int32_t afe_proxy_set_channel_mapping(struct audio_device *adev, + int channel_count) +{ + struct mixer_ctl *ctl; + const char *mixer_ctl_name = "Playback Channel Map"; + int set_values[8] = {0}; + int ret; + ALOGV("%s channel_count:%d",__func__, channel_count); + + switch (channel_count) { + case 2: + set_values[0] = PCM_CHANNEL_FL; + set_values[1] = PCM_CHANNEL_FR; + break; + case 6: + set_values[0] = PCM_CHANNEL_FL; + set_values[1] = PCM_CHANNEL_FR; + set_values[2] = PCM_CHANNEL_FC; + set_values[3] = PCM_CHANNEL_LFE; + set_values[4] = PCM_CHANNEL_LS; + set_values[5] = PCM_CHANNEL_RS; + break; + case 8: + set_values[0] = PCM_CHANNEL_FL; + set_values[1] = PCM_CHANNEL_FR; + set_values[2] = PCM_CHANNEL_FC; + set_values[3] = PCM_CHANNEL_LFE; + set_values[4] = PCM_CHANNEL_LS; + set_values[5] = PCM_CHANNEL_RS; + set_values[6] = PCM_CHANNEL_LB; + set_values[7] = PCM_CHANNEL_RB; + break; + default: + ALOGE("unsupported channels(%d) for setting channel map", + channel_count); + return -EINVAL; + } + + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + return -EINVAL; + } + ALOGV("AFE: set mapping(%d %d %d %d %d %d %d %d) for channel:%d", + set_values[0], set_values[1], set_values[2], set_values[3], set_values[4], + set_values[5], set_values[6], set_values[7], channel_count); + ret = mixer_ctl_set_array(ctl, set_values, channel_count); + return ret; +} + +int32_t audio_extn_set_afe_proxy_channel_mixer(struct audio_device *adev, + int channel_count) +{ + int32_t ret = 0; + const char *channel_cnt_str = NULL; + struct mixer_ctl *ctl = NULL; + const char *mixer_ctl_name = "PROXY_RX Channels"; + + ALOGD("%s: entry", __func__); + /* use the existing channel count set by hardware params to + configure the back end for stereo as usb/a2dp would be + stereo by default */ + ALOGD("%s: channels = %d", __func__, channel_count); + switch (channel_count) { + case 8: channel_cnt_str = "Eight"; break; + case 7: channel_cnt_str = "Seven"; break; + case 6: channel_cnt_str = "Six"; break; + case 5: channel_cnt_str = "Five"; break; + case 4: channel_cnt_str = "Four"; break; + case 3: channel_cnt_str = "Three"; break; + default: channel_cnt_str = "Two"; break; + } + + if(channel_count >= 2 && channel_count <= 8) { + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + return -EINVAL; + } + } + mixer_ctl_set_enum_by_string(ctl, channel_cnt_str); + + if (channel_count == 6 || channel_count == 8 || channel_count == 2) { + ret = afe_proxy_set_channel_mapping(adev, channel_count); + } else { + ALOGE("%s: set unsupported channel count(%d)", __func__, channel_count); + ret = -EINVAL; + } + + ALOGD("%s: exit", __func__); + return ret; +} + +void audio_extn_set_afe_proxy_parameters(struct audio_device *adev, + struct str_parms *parms) +{ + int ret, val; + char value[32]={0}; + + ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_WFD, value, + sizeof(value)); + if (ret >= 0) { + val = atoi(value); + aextnmod.proxy_channel_num = val; + adev->cur_wfd_channels = val; + ALOGD("%s: channel capability set to: %d", __func__, + aextnmod.proxy_channel_num); + } +} + +int audio_extn_get_afe_proxy_parameters(struct str_parms *query, + struct str_parms *reply) +{ + int ret, val; + char value[32]={0}; + char *str = NULL; + + ret = str_parms_get_str(query, AUDIO_PARAMETER_CAN_OPEN_PROXY, value, + sizeof(value)); + if (ret >= 0) { + if (audio_extn_usb_is_proxy_inuse()) + val = 0; + else + val = 1; + str_parms_add_int(reply, AUDIO_PARAMETER_CAN_OPEN_PROXY, val); + } + + return 0; +} + +/* must be called with hw device mutex locked */ +int32_t audio_extn_read_afe_proxy_channel_masks(struct stream_out *out) +{ + int ret = 0; + int channels = aextnmod.proxy_channel_num; + + switch (channels) { + /* + * Do not handle stereo output in Multi-channel cases + * Stereo case is handled in normal playback path + */ + case 6: + ALOGV("%s: AFE PROXY supports 5.1", __func__); + out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_5POINT1; + break; + case 8: + ALOGV("%s: AFE PROXY supports 5.1 and 7.1 channels", __func__); + out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_5POINT1; + out->supported_channel_masks[1] = AUDIO_CHANNEL_OUT_7POINT1; + break; + default: + ALOGE("AFE PROXY does not support multi channel playback"); + ret = -ENOSYS; + break; + } + return ret; +} + +int32_t audio_extn_get_afe_proxy_channel_count() +{ + return aextnmod.proxy_channel_num; +} + +#endif /* AFE_PROXY_ENABLED */ + +static int get_active_offload_usecases(const struct audio_device *adev, + struct str_parms *query, + struct str_parms *reply) +{ + int ret, count = 0; + char value[32]={0}; + struct listnode *node; + struct audio_usecase *usecase; + + ALOGV("%s", __func__); + ret = str_parms_get_str(query, AUDIO_PARAMETER_OFFLOAD_NUM_ACTIVE, value, + sizeof(value)); + if (ret >= 0) { + list_for_each(node, &adev->usecase_list) { + usecase = node_to_item(node, struct audio_usecase, list); + if (is_offload_usecase(usecase->id)) + count++; + } + ALOGV("%s, number of active offload usecases: %d", __func__, count); + str_parms_add_int(reply, AUDIO_PARAMETER_OFFLOAD_NUM_ACTIVE, count); + } + return ret; +} + +void audio_extn_set_parameters(struct audio_device *adev, + struct str_parms *parms) +{ + audio_extn_set_anc_parameters(adev, parms); + audio_extn_set_fluence_parameters(adev, parms); + audio_extn_set_afe_proxy_parameters(adev, parms); + audio_extn_fm_set_parameters(adev, parms); + audio_extn_sound_trigger_set_parameters(adev, parms); + audio_extn_listen_set_parameters(adev, parms); + audio_extn_hfp_set_parameters(adev, parms); + audio_extn_ddp_set_parameters(adev, parms); + audio_extn_ds2_set_parameters(adev, parms); + audio_extn_customstereo_set_parameters(adev, parms); + audio_extn_pm_set_parameters(parms); +} + +void audio_extn_get_parameters(const struct audio_device *adev, + struct str_parms *query, + struct str_parms *reply) +{ + char *kv_pairs = NULL; + audio_extn_get_afe_proxy_parameters(query, reply); + audio_extn_get_fluence_parameters(adev, query, reply); + get_active_offload_usecases(adev, query, reply); + + kv_pairs = str_parms_to_str(reply); + ALOGD_IF(kv_pairs != NULL, "%s: returns %s", __func__, kv_pairs); + free(kv_pairs); +} + +#ifdef AUXPCM_BT_ENABLED +int32_t audio_extn_read_xml(struct audio_device *adev, uint32_t mixer_card, + const char* mixer_xml_path, + const char* mixer_xml_path_auxpcm) +{ + char bt_soc[128]; + bool wifi_init_complete = false; + int sleep_retry = 0; + + while (!wifi_init_complete && sleep_retry < MAX_SLEEP_RETRY) { + property_get("qcom.bluetooth.soc", bt_soc, NULL); + if (strncmp(bt_soc, "unknown", sizeof("unknown"))) { + wifi_init_complete = true; + } else { + usleep(WIFI_INIT_WAIT_SLEEP*1000); + sleep_retry++; + } + } + + if (!strncmp(bt_soc, "ath3k", sizeof("ath3k"))) + adev->audio_route = audio_route_init(mixer_card, mixer_xml_path_auxpcm); + else + adev->audio_route = audio_route_init(mixer_card, mixer_xml_path); + + return 0; +} +#endif /* AUXPCM_BT_ENABLED */ + +#ifdef KPI_OPTIMIZE_ENABLED +typedef int (*perf_lock_acquire_t)(int, int, int*, int); +typedef int (*perf_lock_release_t)(int); + +static void *qcopt_handle; +static perf_lock_acquire_t perf_lock_acq; +static perf_lock_release_t perf_lock_rel; + +static int perf_lock_handle; +char opt_lib_path[512] = {0}; +int perf_lock_opts[1] = {0x20E}; + +int audio_extn_perf_lock_init(void) +{ + int ret = 0; + if (qcopt_handle == NULL) { + if (property_get("ro.vendor.extension_library", + opt_lib_path, NULL) <= 0) { + ALOGE("%s: Failed getting perf property \n", __func__); + ret = -EINVAL; + goto err; + } + if ((qcopt_handle = dlopen(opt_lib_path, RTLD_NOW)) == NULL) { + ALOGE("%s: Failed to open perf handle \n", __func__); + ret = -EINVAL; + goto err; + } else { + perf_lock_acq = (perf_lock_acquire_t)dlsym(qcopt_handle, + "perf_lock_acq"); + if (perf_lock_acq == NULL) { + ALOGE("%s: Perf lock Acquire NULL \n", __func__); + ret = -EINVAL; + goto err; + } + perf_lock_rel = (perf_lock_release_t)dlsym(qcopt_handle, + "perf_lock_rel"); + if (perf_lock_rel == NULL) { + ALOGE("%s: Perf lock Release NULL \n", __func__); + ret = -EINVAL; + goto err; + } + ALOGE("%s: Perf lock handles Success \n", __func__); + } + } +err: + return ret; +} + +void audio_extn_perf_lock_acquire(void) +{ + if (perf_lock_acq) + perf_lock_handle = perf_lock_acq(perf_lock_handle, 0, perf_lock_opts, 1); + else + ALOGE("%s: Perf lock acquire error \n", __func__); +} + +void audio_extn_perf_lock_release(void) +{ + if (perf_lock_rel && perf_lock_handle) + perf_lock_rel(perf_lock_handle); + else + ALOGE("%s: Perf lock release error \n", __func__); +} +#endif /* KPI_OPTIMIZE_ENABLED */ diff --git a/audio/hal/audio_extn/audio_extn.h b/audio/hal/audio_extn/audio_extn.h new file mode 100644 index 0000000..6baa37f --- /dev/null +++ b/audio/hal/audio_extn/audio_extn.h @@ -0,0 +1,390 @@ +/* + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2013 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. + */ + +#ifndef AUDIO_EXTN_H +#define AUDIO_EXTN_H + +#include + +#ifndef PCM_OFFLOAD_ENABLED +#define AUDIO_FORMAT_PCM_OFFLOAD 0x17000000UL +#define AUDIO_FORMAT_PCM_16_BIT_OFFLOAD (AUDIO_FORMAT_PCM_OFFLOAD | AUDIO_FORMAT_PCM_SUB_16_BIT) +#define AUDIO_FORMAT_PCM_24_BIT_OFFLOAD (AUDIO_FORMAT_PCM_OFFLOAD | AUDIO_FORMAT_PCM_SUB_8_24_BIT) +#define AUDIO_OFFLOAD_CODEC_FORMAT "music_offload_codec_format" +#define audio_is_offload_pcm(format) (0) +#endif + +#ifndef AFE_PROXY_ENABLED +#define AUDIO_DEVICE_OUT_PROXY 0x40000 +#endif + +#ifndef COMPRESS_VOIP_ENABLED +#define AUDIO_OUTPUT_FLAG_VOIP_RX 0x4000 +#endif + +#ifndef INCALL_MUSIC_ENABLED +#define AUDIO_OUTPUT_FLAG_INCALL_MUSIC 0x8000 +#endif + +#ifndef FM_ENABLED +#define AUDIO_DEVICE_OUT_FM 0x80000 +#define AUDIO_DEVICE_OUT_FM_TX 0x100000 +#define AUDIO_SOURCE_FM_RX 9 +#define AUDIO_SOURCE_FM_RX_A2DP 10 +#define AUDIO_DEVICE_IN_FM_RX (AUDIO_DEVICE_BIT_IN | 0x8000) +#define AUDIO_DEVICE_IN_FM_RX_A2DP (AUDIO_DEVICE_BIT_IN | 0x10000) +#endif + +#ifndef QTI_FLAC_DECODER +#define AUDIO_FORMAT_FLAC 0x19000000UL +#define AUDIO_OFFLOAD_CODEC_FLAC_MIN_BLK_SIZE "music_offload_flac_min_blk_size" +#define AUDIO_OFFLOAD_CODEC_FLAC_MAX_BLK_SIZE "music_offload_flac_max_blk_size" +#define AUDIO_OFFLOAD_CODEC_FLAC_MIN_FRAME_SIZE "music_offload_flac_min_frame_size" +#define AUDIO_OFFLOAD_CODEC_FLAC_MAX_FRAME_SIZE "music_offload_flac_max_frame_size" +#define PCM_OUTPUT_BIT_WIDTH (CODEC_BACKEND_DEFAULT_BIT_WIDTH) +#else +#define PCM_OUTPUT_BIT_WIDTH (config->offload_info.bit_width) +#endif + +#define MAX_LENGTH_MIXER_CONTROL_IN_INT (128) + +void audio_extn_set_parameters(struct audio_device *adev, + struct str_parms *parms); + +void audio_extn_get_parameters(const struct audio_device *adev, + struct str_parms *query, + struct str_parms *reply); + +#ifndef ANC_HEADSET_ENABLED +#define audio_extn_get_anc_enabled() (0) +#define audio_extn_should_use_fb_anc() (0) +#define audio_extn_should_use_handset_anc(in_channels) (0) +#else +bool audio_extn_get_anc_enabled(void); +bool audio_extn_should_use_fb_anc(void); +bool audio_extn_should_use_handset_anc(int in_channels); +#endif + +#ifndef FLUENCE_ENABLED +#define audio_extn_set_fluence_parameters(adev, parms) (0) +#define audio_extn_get_fluence_parameters(adev, query, reply) (0) +#else +void audio_extn_set_fluence_parameters(struct audio_device *adev, + struct str_parms *parms); +int audio_extn_get_fluence_parameters(const struct audio_device *adev, + struct str_parms *query, struct str_parms *reply); +#endif + +#ifndef AFE_PROXY_ENABLED +#define audio_extn_set_afe_proxy_channel_mixer(adev,channel_count) (0) +#define audio_extn_read_afe_proxy_channel_masks(out) (0) +#define audio_extn_get_afe_proxy_channel_count() (0) +#else +int32_t audio_extn_set_afe_proxy_channel_mixer(struct audio_device *adev, + int channel_count); +int32_t audio_extn_read_afe_proxy_channel_masks(struct stream_out *out); +int32_t audio_extn_get_afe_proxy_channel_count(); + +#endif + +#ifndef USB_HEADSET_ENABLED +#define audio_extn_usb_init(adev) (0) +#define audio_extn_usb_deinit() (0) +#define audio_extn_usb_start_playback(adev) (0) +#define audio_extn_usb_stop_playback() (0) +#define audio_extn_usb_start_capture(adev) (0) +#define audio_extn_usb_stop_capture() (0) +#define audio_extn_usb_set_proxy_sound_card(sndcard_idx) (0) +#define audio_extn_usb_is_proxy_inuse() (0) +#else +void initPlaybackVolume(); +void audio_extn_usb_init(void *adev); +void audio_extn_usb_deinit(); +void audio_extn_usb_start_playback(void *adev); +void audio_extn_usb_stop_playback(); +void audio_extn_usb_start_capture(void *adev); +void audio_extn_usb_stop_capture(); +void audio_extn_usb_set_proxy_sound_card(uint32_t sndcard_idx); +bool audio_extn_usb_is_proxy_inuse(); +#endif + +#ifndef SSR_ENABLED +#define audio_extn_ssr_init(in) (0) +#define audio_extn_ssr_deinit() (0) +#define audio_extn_ssr_update_enabled() (0) +#define audio_extn_ssr_get_enabled() (0) +#define audio_extn_ssr_read(stream, buffer, bytes) (0) +#else +int32_t audio_extn_ssr_init(struct stream_in *in); +int32_t audio_extn_ssr_deinit(); +void audio_extn_ssr_update_enabled(); +bool audio_extn_ssr_get_enabled(); +int32_t audio_extn_ssr_read(struct audio_stream_in *stream, + void *buffer, size_t bytes); +#endif + +#ifndef HW_VARIANTS_ENABLED +#define hw_info_init(snd_card_name) (0) +#define hw_info_deinit(hw_info) (0) +#define hw_info_append_hw_type(hw_info,\ + snd_device, device_name) (0) +#else +void *hw_info_init(const char *snd_card_name); +void hw_info_deinit(void *hw_info); +void hw_info_append_hw_type(void *hw_info, snd_device_t snd_device, + char *device_name); +#endif + +#ifndef AUDIO_LISTEN_ENABLED +#define audio_extn_listen_init(adev, snd_card) (0) +#define audio_extn_listen_deinit(adev) (0) +#define audio_extn_listen_update_device_status(snd_dev, event) (0) +#define audio_extn_listen_update_stream_status(uc_info, event) (0) +#define audio_extn_listen_set_parameters(adev, parms) (0) +#else +enum listen_event_type { + LISTEN_EVENT_SND_DEVICE_FREE, + LISTEN_EVENT_SND_DEVICE_BUSY, + LISTEN_EVENT_STREAM_FREE, + LISTEN_EVENT_STREAM_BUSY +}; +typedef enum listen_event_type listen_event_type_t; + +int audio_extn_listen_init(struct audio_device *adev, unsigned int snd_card); +void audio_extn_listen_deinit(struct audio_device *adev); +void audio_extn_listen_update_device_status(snd_device_t snd_device, + listen_event_type_t event); +void audio_extn_listen_update_stream_status(struct audio_usecase *uc_info, + listen_event_type_t event); +void audio_extn_listen_set_parameters(struct audio_device *adev, + struct str_parms *parms); +#endif /* AUDIO_LISTEN_ENABLED */ + +#ifndef SOUND_TRIGGER_ENABLED +#define audio_extn_sound_trigger_init(adev) (0) +#define audio_extn_sound_trigger_deinit(adev) (0) +#define audio_extn_sound_trigger_update_device_status(snd_dev, event) (0) +#define audio_extn_sound_trigger_update_stream_status(uc_info, event) (0) +#define audio_extn_sound_trigger_set_parameters(adev, parms) (0) +#define audio_extn_sound_trigger_check_and_get_session(in) (0) +#define audio_extn_sound_trigger_stop_lab(in) (0) +#else + +enum st_event_type { + ST_EVENT_SND_DEVICE_FREE, + ST_EVENT_SND_DEVICE_BUSY, + ST_EVENT_STREAM_FREE, + ST_EVENT_STREAM_BUSY +}; +typedef enum st_event_type st_event_type_t; + +int audio_extn_sound_trigger_init(struct audio_device *adev); +void audio_extn_sound_trigger_deinit(struct audio_device *adev); +void audio_extn_sound_trigger_update_device_status(snd_device_t snd_device, + st_event_type_t event); +void audio_extn_sound_trigger_update_stream_status(struct audio_usecase *uc_info, + st_event_type_t event); +void audio_extn_sound_trigger_set_parameters(struct audio_device *adev, + struct str_parms *parms); +void audio_extn_sound_trigger_check_and_get_session(struct stream_in *in); +void audio_extn_sound_trigger_stop_lab(struct stream_in *in); +#endif + +#ifndef AUXPCM_BT_ENABLED +#define audio_extn_read_xml(adev, mixer_card, MIXER_XML_PATH, \ + MIXER_XML_PATH_AUXPCM) (-ENOSYS) +#else +int32_t audio_extn_read_xml(struct audio_device *adev, uint32_t mixer_card, + const char* mixer_xml_path, + const char* mixer_xml_path_auxpcm); +#endif /* AUXPCM_BT_ENABLED */ +#ifndef SPKR_PROT_ENABLED +#define audio_extn_spkr_prot_init(adev) (0) +#define audio_extn_spkr_prot_start_processing(snd_device) (-EINVAL) +#define audio_extn_spkr_prot_calib_cancel(adev) (0) +#define audio_extn_spkr_prot_stop_processing(snd_device) (0) +#define audio_extn_spkr_prot_is_enabled() (false) +#define audio_extn_spkr_prot_get_acdb_id(snd_device) (-EINVAL) +#define audio_extn_get_spkr_prot_snd_device(snd_device) (snd_device) +#else +void audio_extn_spkr_prot_init(void *adev); +int audio_extn_spkr_prot_start_processing(snd_device_t snd_device); +void audio_extn_spkr_prot_stop_processing(snd_device_t snd_device); +bool audio_extn_spkr_prot_is_enabled(); +int audio_extn_spkr_prot_get_acdb_id(snd_device_t snd_device); +int audio_extn_get_spkr_prot_snd_device(snd_device_t snd_device); +void audio_extn_spkr_prot_calib_cancel(void *adev); +#endif + +#ifndef COMPRESS_CAPTURE_ENABLED +#define audio_extn_compr_cap_init(in) (0) +#define audio_extn_compr_cap_enabled() (0) +#define audio_extn_compr_cap_format_supported(format) (0) +#define audio_extn_compr_cap_usecase_supported(usecase) (0) +#define audio_extn_compr_cap_get_buffer_size(format) (0) +#define audio_extn_compr_cap_read(in, buffer, bytes) (0) +#define audio_extn_compr_cap_deinit() (0) +#else +void audio_extn_compr_cap_init(struct stream_in *in); +bool audio_extn_compr_cap_enabled(); +bool audio_extn_compr_cap_format_supported(audio_format_t format); +bool audio_extn_compr_cap_usecase_supported(audio_usecase_t usecase); +size_t audio_extn_compr_cap_get_buffer_size(audio_format_t format); +size_t audio_extn_compr_cap_read(struct stream_in *in, + void *buffer, size_t bytes); +void audio_extn_compr_cap_deinit(); +#endif + +#if defined(DS1_DOLBY_DDP_ENABLED) || defined(DS1_DOLBY_DAP_ENABLED) +void audio_extn_dolby_set_dmid(struct audio_device *adev); +#else +#define audio_extn_dolby_set_dmid(adev) (0) +#endif + + +#if defined(DS1_DOLBY_DDP_ENABLED) || defined(DS1_DOLBY_DAP_ENABLED) || defined(DS2_DOLBY_DAP_ENABLED) +void audio_extn_dolby_set_license(struct audio_device *adev); +#else +#define audio_extn_dolby_set_license(adev) (0) +#endif + +#ifndef DS1_DOLBY_DAP_ENABLED +#define audio_extn_dolby_set_endpoint(adev) (0) +#else +void audio_extn_dolby_set_endpoint(struct audio_device *adev); +#endif + + +#if defined(DS1_DOLBY_DDP_ENABLED) || defined(DS2_DOLBY_DAP_ENABLED) +bool audio_extn_is_dolby_format(audio_format_t format); +int audio_extn_dolby_get_snd_codec_id(struct audio_device *adev, + struct stream_out *out, + audio_format_t format); +#else +#define audio_extn_is_dolby_format(format) (0) +#define audio_extn_dolby_get_snd_codec_id(adev, out, format) (0) +#endif + +#ifndef DS1_DOLBY_DDP_ENABLED +#define audio_extn_ddp_set_parameters(adev, parms) (0) +#define audio_extn_dolby_send_ddp_endp_params(adev) (0) +#else +void audio_extn_ddp_set_parameters(struct audio_device *adev, + struct str_parms *parms); +void audio_extn_dolby_send_ddp_endp_params(struct audio_device *adev); +#endif + +#ifndef HFP_ENABLED +#define audio_extn_hfp_is_active(adev) (0) +#define audio_extn_hfp_get_usecase() (-1) +#else +bool audio_extn_hfp_is_active(struct audio_device *adev); +audio_usecase_t audio_extn_hfp_get_usecase(); +#endif + +#ifndef DEV_ARBI_ENABLED +#define audio_extn_dev_arbi_init() (0) +#define audio_extn_dev_arbi_deinit() (0) +#define audio_extn_dev_arbi_acquire(snd_device) (0) +#define audio_extn_dev_arbi_release(snd_device) (0) +#else +int audio_extn_dev_arbi_init(); +int audio_extn_dev_arbi_deinit(); +int audio_extn_dev_arbi_acquire(snd_device_t snd_device); +int audio_extn_dev_arbi_release(snd_device_t snd_device); +#endif + +#ifndef PM_SUPPORT_ENABLED +#define audio_extn_pm_set_parameters(params) (0) +#define audio_extn_pm_vote(void) (0) +#define audio_extn_pm_unvote(void) (0) +#else +void audio_extn_pm_set_parameters(struct str_parms *parms); +int audio_extn_pm_vote (void); +void audio_extn_pm_unvote(void); +#endif + +void audio_extn_utils_update_streams_output_cfg_list(void *platform, + struct mixer *mixer, + struct listnode *streams_output_cfg_list); +void audio_extn_utils_dump_streams_output_cfg_list( + struct listnode *streams_output_cfg_list); +void audio_extn_utils_release_streams_output_cfg_list( + struct listnode *streams_output_cfg_list); +void audio_extn_utils_update_stream_app_type_cfg(void *platform, + struct listnode *streams_output_cfg_list, + audio_devices_t devices, + audio_output_flags_t flags, + audio_format_t format, + uint32_t sample_rate, + uint32_t bit_width, + struct stream_app_type_cfg *app_type_cfg); +int audio_extn_utils_send_app_type_cfg(struct audio_usecase *usecase); +void audio_extn_utils_send_audio_calibration(struct audio_device *adev, + struct audio_usecase *usecase); +#ifdef DS2_DOLBY_DAP_ENABLED +#define LIB_DS2_DAP_HAL "vendor/lib/libhwdaphal.so" +#define SET_HW_INFO_FUNC "dap_hal_set_hw_info" +typedef enum { + SND_CARD = 0, + HW_ENDPOINT = 1, + DMID = 2, + DEVICE_BE_ID_MAP = 3, + DAP_BYPASS = 4, +} dap_hal_hw_info_t; +typedef int (*dap_hal_set_hw_info_t)(int32_t hw_info, void* data); +typedef struct { + int (*device_id_to_be_id)[2]; + int len; +} dap_hal_device_be_id_map_t; + +int audio_extn_dap_hal_init(int snd_card); +int audio_extn_dap_hal_deinit(); +void audio_extn_dolby_ds2_set_endpoint(struct audio_device *adev); +int audio_extn_ds2_enable(struct audio_device *adev); +int audio_extn_dolby_set_dap_bypass(struct audio_device *adev, int state); +void audio_extn_ds2_set_parameters(struct audio_device *adev, + struct str_parms *parms); + +#else +#define audio_extn_dap_hal_init(snd_card) (0) +#define audio_extn_dap_hal_deinit() (0) +#define audio_extn_dolby_ds2_set_endpoint(adev) (0) +#define audio_extn_ds2_enable(adev) (0) +#define audio_extn_dolby_set_dap_bypass(adev, state) (0) +#define audio_extn_ds2_set_parameters(adev, parms); (0) +#endif +typedef enum { + DAP_STATE_ON = 0, + DAP_STATE_BYPASS, +}; +#ifndef AUDIO_FORMAT_E_AC3_JOC +#define AUDIO_FORMAT_E_AC3_JOC 0x19000000UL +#endif + +#ifndef KPI_OPTIMIZE_ENABLED +#define audio_extn_perf_lock_init() (0) +#define audio_extn_perf_lock_acquire() (0) +#define audio_extn_perf_lock_release() (0) +#else +int audio_extn_perf_lock_init(void); +void audio_extn_perf_lock_acquire(void); +void audio_extn_perf_lock_release(void); +#endif /* KPI_OPTIMIZE_ENABLED */ +#endif /* AUDIO_EXTN_H */ diff --git a/audio/hal/audio_extn/compress_capture.c b/audio/hal/audio_extn/compress_capture.c new file mode 100644 index 0000000..47e6a9d --- /dev/null +++ b/audio/hal/audio_extn/compress_capture.c @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2013 - 2014, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2013 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_hw_compress" +/*#define LOG_NDEBUG 0*/ +#define LOG_NDDEBUG 0 + +#include +#include +#include +#include +#include +#include + +#include "audio_hw.h" +#include "platform.h" +#include "platform_api.h" + +#include "sound/compress_params.h" +#include "sound/compress_offload.h" + +#ifdef COMPRESS_CAPTURE_ENABLED + +#define COMPRESS_IN_CONFIG_CHANNELS 1 +#define COMPRESS_IN_CONFIG_PERIOD_SIZE 2048 +#define COMPRESS_IN_CONFIG_PERIOD_COUNT 16 + + +struct compress_in_module { + uint8_t *in_buf; +}; + +static struct compress_in_module c_in_mod = { + .in_buf = NULL, +}; + + +void audio_extn_compr_cap_init(struct stream_in *in) +{ + in->usecase = USECASE_AUDIO_RECORD_COMPRESS; + in->config.channels = COMPRESS_IN_CONFIG_CHANNELS; + in->config.period_size = COMPRESS_IN_CONFIG_PERIOD_SIZE; + in->config.period_count= COMPRESS_IN_CONFIG_PERIOD_COUNT; + in->config.format = AUDIO_FORMAT_AMR_WB; + c_in_mod.in_buf = (uint8_t*)calloc(1, in->config.period_size*2); +} + +void audio_extn_compr_cap_deinit() +{ + if (c_in_mod.in_buf) { + free(c_in_mod.in_buf); + c_in_mod.in_buf = NULL; + } +} + +bool audio_extn_compr_cap_enabled() +{ + char prop_value[PROPERTY_VALUE_MAX] = {0}; + bool tunnel_encode = false; + + property_get("tunnel.audio.encode",prop_value,"0"); + if (!strncmp("true", prop_value, sizeof("true"))) + return true; + else + return false; +} + +bool audio_extn_compr_cap_format_supported(audio_format_t format) +{ + if (format == AUDIO_FORMAT_AMR_WB) + return true; + else + return false; +} + + +bool audio_extn_compr_cap_usecase_supported(audio_usecase_t usecase) +{ + if ((usecase == USECASE_AUDIO_RECORD_COMPRESS) || + (usecase == USECASE_INCALL_REC_UPLINK_COMPRESS) || + (usecase == USECASE_INCALL_REC_DOWNLINK_COMPRESS) || + (usecase == USECASE_INCALL_REC_UPLINK_AND_DOWNLINK_COMPRESS)) + return true; + else + return false; +} + + +size_t audio_extn_compr_cap_get_buffer_size(audio_format_t format) +{ + if (format == AUDIO_FORMAT_AMR_WB) + /*One AMR WB frame is 61 bytes. Return that to the caller. + The buffer size is not altered, that is still period size.*/ + return AMR_WB_FRAMESIZE; + else + return 0; +} + +size_t audio_extn_compr_cap_read(struct stream_in * in, + void *buffer, size_t bytes) +{ + int ret; + struct snd_compr_audio_info *header; + uint32_t c_in_header; + uint32_t c_in_buf_size; + + c_in_buf_size = in->config.period_size*2; + + if (in->pcm) { + ret = pcm_read(in->pcm, c_in_mod.in_buf, c_in_buf_size); + if (ret < 0) { + ALOGE("pcm_read() returned failure: %d", ret); + return ret; + } else { + header = (struct snd_compr_audio_info *) c_in_mod.in_buf; + c_in_header = sizeof(*header) + header->reserved[0]; + if (header->frame_size > 0) { + if (c_in_header + header->frame_size > c_in_buf_size) { + ALOGW("AMR WB read buffer overflow."); + header->frame_size = + bytes - sizeof(*header) - header->reserved[0]; + } + ALOGV("c_in_buf: %p, data offset: %p, header size: %zu," + "reserved[0]: %u frame_size: %d", c_in_mod.in_buf, + c_in_mod.in_buf + c_in_header, + sizeof(*header), header->reserved[0], + header->frame_size); + memcpy(buffer, c_in_mod.in_buf + c_in_header, header->frame_size); + } else { + ALOGE("pcm_read() with zero frame size"); + ret = -EINVAL; + } + } + } + + return 0; +} + +#endif /* COMPRESS_CAPTURE_ENABLED end */ diff --git a/audio/hal/audio_extn/dev_arbi.c b/audio/hal/audio_extn/dev_arbi.c new file mode 100644 index 0000000..d3c01c5 --- /dev/null +++ b/audio/hal/audio_extn/dev_arbi.c @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "audio_hw_dev_arbi" +/*#define LOG_NDEBUG 0*/ +#define LOG_NDDEBUG 0 + +#include +#include +#include +#include "audio_hw.h" +#include "platform.h" +#include "platform_api.h" +#include +#include +#include +#include +#include "audio_extn.h" + +#ifdef DEV_ARBI_ENABLED + +typedef int (init_fn_t)(); +typedef int (deinit_fn_t)(); +typedef int (acquire_fn_t)(audio_devices_t aud_dev); +typedef int (release_fn_t)(audio_devices_t aud_dev); + +typedef struct { + snd_device_t snd_device; + audio_devices_t aud_device; +} snd_aud_dev_mapping_t; + +static void* lib_handle = NULL; + +static init_fn_t *init_fp = NULL; +static deinit_fn_t *deinit_fp = NULL; +static acquire_fn_t *acquire_fp = NULL; +static release_fn_t *release_fp = NULL; + +static int load_dev_arbi_lib() +{ + int rc = -EINVAL; + + if (lib_handle != NULL) { + ALOGE("%s: library already loaded", __func__); + return rc; + } + + lib_handle = dlopen("libaudiodevarb.so", RTLD_NOW); + if (lib_handle != NULL) { + init_fp = (init_fn_t*)dlsym(lib_handle, "aud_dev_arbi_server_init"); + deinit_fp = (deinit_fn_t*)dlsym(lib_handle, "aud_dev_arbi_server_deinit"); + acquire_fp = (acquire_fn_t*)dlsym(lib_handle, "aud_dev_arbi_server_acquire"); + release_fp = (release_fn_t*)dlsym(lib_handle, "aud_dev_arbi_server_release"); + + if ((init_fp == NULL) || + (deinit_fp == NULL) || + (acquire_fp == NULL) || + (release_fp == NULL)) { + + ALOGE("%s: error loading symbols from library", __func__); + + init_fp = NULL; + deinit_fp = NULL; + acquire_fp = NULL; + release_fp = NULL; + } else + return 0; + } + + return rc; +} + +int audio_extn_dev_arbi_init() +{ + int rc = load_dev_arbi_lib(); + if (!rc) + rc = init_fp(); + + return rc; +} + +int audio_extn_dev_arbi_deinit() +{ + int rc = -EINVAL; + + if(deinit_fp != NULL) { + rc = deinit_fp(); + + init_fp = NULL; + deinit_fp = NULL; + acquire_fp = NULL; + release_fp = NULL; + + dlclose(lib_handle); + lib_handle = NULL; + } + + return rc; +} + +static audio_devices_t get_audio_device(snd_device_t snd_device) +{ + static snd_aud_dev_mapping_t snd_aud_dev_map[] = { + {SND_DEVICE_OUT_HANDSET, AUDIO_DEVICE_OUT_EARPIECE}, + {SND_DEVICE_OUT_VOICE_HANDSET, AUDIO_DEVICE_OUT_EARPIECE} + }; + + audio_devices_t aud_device = AUDIO_DEVICE_NONE; + uint32_t ind = 0; + + for (ind = 0; ind < ARRAY_SIZE(snd_aud_dev_map); ++ind) { + if (snd_device == snd_aud_dev_map[ind].snd_device) { + aud_device = snd_aud_dev_map[ind].aud_device; + break; + } + } + + return aud_device; +} + +int audio_extn_dev_arbi_acquire(snd_device_t snd_device) +{ + int rc = -EINVAL; + audio_devices_t audio_device = get_audio_device(snd_device); + + if ((acquire_fp != NULL) && (audio_device != AUDIO_DEVICE_NONE)) + rc = acquire_fp(audio_device); + + return rc; +} + +int audio_extn_dev_arbi_release(snd_device_t snd_device) +{ + int rc = -EINVAL; + audio_devices_t audio_device = get_audio_device(snd_device); + + if ((release_fp != NULL) && (audio_device != AUDIO_DEVICE_NONE)) + rc = release_fp(audio_device); + + return rc; +} + +#endif /*DEV_ARBI_ENABLED*/ diff --git a/audio/hal/audio_extn/dolby.c b/audio/hal/audio_extn/dolby.c new file mode 100644 index 0000000..e1b64be --- /dev/null +++ b/audio/hal/audio_extn/dolby.c @@ -0,0 +1,685 @@ +/* + * Copyright (c) 2011-2014, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2010 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_hw_dolby" +#define LOG_NDEBUG 0 +#define LOG_NDDEBUG 0 + +#include +#include +#include +#include +#include +#include + +#include "audio_hw.h" +#include "platform.h" +#include "platform_api.h" +#include "audio_extn.h" +#include "sound/compress_params.h" +#include "sound/devdep_params.h" + +#ifdef DS1_DOLBY_DDP_ENABLED + +#define AUDIO_PARAMETER_DDP_DEV "ddp_device" +#define AUDIO_PARAMETER_DDP_CH_CAP "ddp_chancap" +#define AUDIO_PARAMETER_DDP_MAX_OUT_CHAN "ddp_maxoutchan" +#define AUDIO_PARAMETER_DDP_OUT_MODE "ddp_outmode" +#define AUDIO_PARAMETER_DDP_OUT_LFE_ON "ddp_outlfeon" +#define AUDIO_PARAMETER_DDP_COMP_MODE "ddp_compmode" +#define AUDIO_PARAMETER_DDP_STEREO_MODE "ddp_stereomode" + +#define PARAM_ID_MAX_OUTPUT_CHANNELS 0x00010DE2 +#define PARAM_ID_CTL_RUNNING_MODE 0x0 +#define PARAM_ID_CTL_ERROR_CONCEAL 0x00010DE3 +#define PARAM_ID_CTL_ERROR_MAX_RPTS 0x00010DE4 +#define PARAM_ID_CNV_ERROR_CONCEAL 0x00010DE5 +#define PARAM_ID_CTL_SUBSTREAM_SELECT 0x00010DE6 +#define PARAM_ID_CTL_INPUT_MODE 0x0 +#define PARAM_ID_OUT_CTL_OUTMODE 0x00010DE0 +#define PARAM_ID_OUT_CTL_OUTLFE_ON 0x00010DE1 +#define PARAM_ID_OUT_CTL_COMPMODE 0x00010D74 +#define PARAM_ID_OUT_CTL_STEREO_MODE 0x00010D76 +#define PARAM_ID_OUT_CTL_DUAL_MODE 0x00010D75 +#define PARAM_ID_OUT_CTL_DRCSCALE_HIGH 0x00010D7A +#define PARAM_ID_OUT_CTL_DRCSCALE_LOW 0x00010D79 +#define PARAM_ID_OUT_CTL_OUT_PCMSCALE 0x00010D78 +#define PARAM_ID_OUT_CTL_MDCT_BANDLIMIT 0x00010DE7 +#define PARAM_ID_OUT_CTL_DRC_SUPPRESS 0x00010DE8 + +/* DS1-DDP Endp Params */ +#define DDP_ENDP_NUM_PARAMS 17 +#define DDP_ENDP_NUM_DEVICES 21 +static int ddp_endp_params_id[DDP_ENDP_NUM_PARAMS] = { + PARAM_ID_MAX_OUTPUT_CHANNELS, PARAM_ID_CTL_RUNNING_MODE, + PARAM_ID_CTL_ERROR_CONCEAL, PARAM_ID_CTL_ERROR_MAX_RPTS, + PARAM_ID_CNV_ERROR_CONCEAL, PARAM_ID_CTL_SUBSTREAM_SELECT, + PARAM_ID_CTL_INPUT_MODE, PARAM_ID_OUT_CTL_OUTMODE, + PARAM_ID_OUT_CTL_OUTLFE_ON, PARAM_ID_OUT_CTL_COMPMODE, + PARAM_ID_OUT_CTL_STEREO_MODE, PARAM_ID_OUT_CTL_DUAL_MODE, + PARAM_ID_OUT_CTL_DRCSCALE_HIGH, PARAM_ID_OUT_CTL_DRCSCALE_LOW, + PARAM_ID_OUT_CTL_OUT_PCMSCALE, PARAM_ID_OUT_CTL_MDCT_BANDLIMIT, + PARAM_ID_OUT_CTL_DRC_SUPPRESS +}; + +static struct ddp_endp_params { + int device; + int dev_ch_cap; + int param_val[DDP_ENDP_NUM_PARAMS]; + bool is_param_valid[DDP_ENDP_NUM_PARAMS]; +} ddp_endp_params[DDP_ENDP_NUM_DEVICES] = { + {AUDIO_DEVICE_OUT_EARPIECE, 2, + {8, 0, 0, 0, 0, 0, 0, 21, 1, 6, 0, 0, 0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0 } }, + {AUDIO_DEVICE_OUT_SPEAKER, 2, + {8, 0, 0, 0, 0, 0, 0, 21, 1, 6, 0, 0, 0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0} }, + {AUDIO_DEVICE_OUT_WIRED_HEADSET, 2, + {8, 0, 0, 0, 0, 0, 0, 21, 1, 6, 0, 0, 0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0} }, + {AUDIO_DEVICE_OUT_WIRED_HEADPHONE, 2, + {8, 0, 0, 0, 0, 0, 0, 21, 1, 6, 0, 0, 0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0} }, + {AUDIO_DEVICE_OUT_BLUETOOTH_SCO, 2, + {8, 0, 0, 0, 0, 0, 0, 21, 1, 6, 0, 0, 0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0} }, + {AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, 2, + {8, 0, 0, 0, 0, 0, 0, 21, 1, 6, 0, 0, 0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0} }, + {AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT, 2, + {8, 0, 0, 0, 0, 0, 0, 21, 1, 6, 0, 0, 0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0} }, + {AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, 2, + {8, 0, 0, 0, 0, 0, 0, 21, 1, 6, 0, 0, 0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0} }, + {AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES, 2, + {8, 0, 0, 0, 0, 0, 0, 21, 1, 6, 0, 0, 0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0} }, + {AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER, 2, + {8, 0, 0, 0, 0, 0, 0, 21, 1, 6, 0, 0, 0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0} }, + {AUDIO_DEVICE_OUT_AUX_DIGITAL, 2, + {8, 0, 0, 0, 0, 0, 0, 21, 1, 2, 0, 0, 0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0} }, + {AUDIO_DEVICE_OUT_AUX_DIGITAL, 6, + {8, 0, 0, 0, 0, 0, 0, 21, 1, 2, 0, 0, 0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0} }, + {AUDIO_DEVICE_OUT_AUX_DIGITAL, 8, + {8, 0, 0, 0, 0, 0, 0, 21, 1, 2, 0, 0, 0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0} }, + {AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET, 2, + {8, 0, 0, 0, 0, 0, 0, 21, 1, 6, 0, 0, 0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0} }, + {AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET, 2, + {8, 0, 0, 0, 0, 0, 0, 21, 1, 6, 0, 0, 0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0} }, + {AUDIO_DEVICE_OUT_USB_ACCESSORY, 2, + {8, 0, 0, 0, 0, 0, 0, 21, 1, 6, 0, 0, 0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0} }, + {AUDIO_DEVICE_OUT_USB_DEVICE, 2, + {8, 0, 0, 0, 0, 0, 0, 21, 1, 6, 0, 0, 0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0} }, + {AUDIO_DEVICE_OUT_FM, 2, + {8, 0, 0, 0, 0, 0, 0, 21, 1, 6, 0, 0, 0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0} }, + {AUDIO_DEVICE_OUT_FM_TX, 2, + {8, 0, 0, 0, 0, 0, 0, 21, 1, 6, 0, 0, 0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0} }, + {AUDIO_DEVICE_OUT_PROXY, 2, + {8, 0, 0, 0, 0, 0, 0, 21, 1, 2, 0, 0, 0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0} }, + {AUDIO_DEVICE_OUT_PROXY, 6, + {8, 0, 0, 0, 0, 0, 0, 21, 1, 2, 0, 0, 0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0} }, +}; + +int update_ddp_endp_table(int device, int dev_ch_cap, int param_id, + int param_val) +{ + int idx = 0; + int param_idx = 0; + ALOGV("%s: dev 0x%x dev_ch_cap %d param_id 0x%x param_val %d", + __func__, device, dev_ch_cap , param_id, param_val); + + for(idx=0; idx=DDP_ENDP_NUM_DEVICES) { + ALOGE("%s: device not available in DDP endp config table", __func__); + return -EINVAL; + } + + for(param_idx=0; param_idx=DDP_ENDP_NUM_PARAMS) { + ALOGE("param not available in DDP endp config table"); + return -EINVAL; + } + + ALOGV("ddp_endp_params[%d].param_val[%d] = %d", idx, param_idx, param_val); + ddp_endp_params[idx].param_val[param_idx] = param_val; + return 0; +} + +void send_ddp_endp_params_stream(struct stream_out *out, + int device, int dev_ch_cap, + bool set_cache __unused) +{ + int idx, i; + int ddp_endp_params_data[2*DDP_ENDP_NUM_PARAMS + 1]; + int length = 0; + for(idx=0; idx=DDP_ENDP_NUM_DEVICES) { + ALOGE("device not available in DDP endp config table"); + return; + } + + length += 1; /* offset 0 is for num of parameter. increase offset by 1 */ + for (i=0; idev; + struct mixer_ctl *ctl; + int pcm_device_id = platform_get_pcm_device_id(out->usecase, + PCM_PLAYBACK); + snprintf(mixer_ctl_name, sizeof(mixer_ctl_name), + "Audio Stream %d Dec Params", pcm_device_id); + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + return; + } + mixer_ctl_set_array(ctl, ddp_endp_params_data, length); + } + return; +} + +void send_ddp_endp_params(struct audio_device *adev, + int ddp_dev, int dev_ch_cap) +{ + struct listnode *node; + struct audio_usecase *usecase; + + list_for_each(node, &adev->usecase_list) { + usecase = node_to_item(node, struct audio_usecase, list); + if ((usecase->type == PCM_PLAYBACK) && + (usecase->devices & ddp_dev) && + (usecase->stream.out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) && + ((usecase->stream.out->format == AUDIO_FORMAT_AC3) || + (usecase->stream.out->format == AUDIO_FORMAT_E_AC3) || + (usecase->stream.out->format == AUDIO_FORMAT_E_AC3_JOC))) { + send_ddp_endp_params_stream(usecase->stream.out, ddp_dev, + dev_ch_cap, false /* set cache */); + } + } +} + +void audio_extn_dolby_send_ddp_endp_params(struct audio_device *adev) +{ + struct listnode *node; + struct audio_usecase *usecase; + list_for_each(node, &adev->usecase_list) { + usecase = node_to_item(node, struct audio_usecase, list); + if ((usecase->type == PCM_PLAYBACK) && + (usecase->devices & AUDIO_DEVICE_OUT_ALL) && + (usecase->stream.out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) && + ((usecase->stream.out->format == AUDIO_FORMAT_AC3) || + (usecase->stream.out->format == AUDIO_FORMAT_E_AC3) || + (usecase->stream.out->format == AUDIO_FORMAT_E_AC3_JOC))) { + /* + * Use wfd /hdmi sink channel cap for dolby params if device is wfd + * or hdmi. Otherwise use stereo configuration + */ + int channel_cap = usecase->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL ? + adev->cur_hdmi_channels : + usecase->devices & AUDIO_DEVICE_OUT_PROXY ? + adev->cur_wfd_channels : 2; + send_ddp_endp_params_stream(usecase->stream.out, usecase->devices, + channel_cap, false /* set cache */); + } + } +} + +void audio_extn_ddp_set_parameters(struct audio_device *adev, + struct str_parms *parms) +{ + int ddp_dev, dev_ch_cap; + int val, ret; + char value[32]={0}; + + ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_SND_CARD_STATUS, value, + sizeof(value)); + if (ret >= 0) { + char *snd_card_status = value + 2; + if (strncmp(snd_card_status, "ONLINE", sizeof("ONLINE")) == 0) + audio_extn_dolby_set_license(adev); + } + + ret = str_parms_get_str(parms, AUDIO_PARAMETER_DDP_DEV, value, + sizeof(value)); + if (ret >= 0) { + ddp_dev = atoi(value); + if (!(AUDIO_DEVICE_OUT_ALL & ddp_dev)) + return; + } else + return; + + ret = str_parms_get_str(parms, AUDIO_PARAMETER_DDP_CH_CAP, value, + sizeof(value)); + if (ret >= 0) { + dev_ch_cap = atoi(value); + if ((dev_ch_cap != 2) && (dev_ch_cap != 6) && (dev_ch_cap != 8)) + return; + } else + return; + + ret = str_parms_get_str(parms, AUDIO_PARAMETER_DDP_MAX_OUT_CHAN, value, + sizeof(value)); + if (ret >= 0) { + val = atoi(value); + update_ddp_endp_table(ddp_dev, dev_ch_cap, + PARAM_ID_MAX_OUTPUT_CHANNELS, val); + } + + ret = str_parms_get_str(parms, AUDIO_PARAMETER_DDP_OUT_MODE, value, + sizeof(value)); + if (ret >= 0) { + val = atoi(value); + update_ddp_endp_table(ddp_dev, dev_ch_cap, + PARAM_ID_OUT_CTL_OUTMODE, val); + } + + ret = str_parms_get_str(parms, AUDIO_PARAMETER_DDP_OUT_LFE_ON, value, + sizeof(value)); + if (ret >= 0) { + val = atoi(value); + update_ddp_endp_table(ddp_dev, dev_ch_cap, + PARAM_ID_OUT_CTL_OUTLFE_ON, val); + } + + ret = str_parms_get_str(parms, AUDIO_PARAMETER_DDP_COMP_MODE, value, + sizeof(value)); + if (ret >= 0) { + val = atoi(value); + update_ddp_endp_table(ddp_dev, dev_ch_cap, + PARAM_ID_OUT_CTL_COMPMODE, val); + } + + ret = str_parms_get_str(parms, AUDIO_PARAMETER_DDP_STEREO_MODE, value, + sizeof(value)); + if (ret >= 0) { + val = atoi(value); + update_ddp_endp_table(ddp_dev, dev_ch_cap, + PARAM_ID_OUT_CTL_STEREO_MODE, val); + } + /* TODO: Do we need device channel caps here? + * We dont have that information as this is from dolby modules + */ + send_ddp_endp_params(adev, ddp_dev, dev_ch_cap); +} +#endif /* DS1_DOLBY_DDP_ENABLED */ + +#if defined(DS1_DOLBY_DDP_ENABLED) || defined(DS2_DOLBY_DAP_ENABLED) +int audio_extn_dolby_get_snd_codec_id(struct audio_device *adev, + struct stream_out *out, + audio_format_t format) +{ + int id = 0; + /* + * Use wfd /hdmi sink channel cap for dolby params if device is wfd + * or hdmi. Otherwise use stereo configuration + */ + int channel_cap = out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL ? + adev->cur_hdmi_channels : + out->devices & AUDIO_DEVICE_OUT_PROXY ? + adev->cur_wfd_channels : 2; + + switch (format) { + case AUDIO_FORMAT_AC3: + id = SND_AUDIOCODEC_AC3; +#ifdef DS1_DOLBY_DDP_ENABLED + send_ddp_endp_params_stream(out, out->devices, + channel_cap, true /* set_cache */); +#endif + audio_extn_dolby_set_dmid(adev); + break; + case AUDIO_FORMAT_E_AC3: + case AUDIO_FORMAT_E_AC3_JOC: + id = SND_AUDIOCODEC_EAC3; +#ifdef DS1_DOLBY_DDP_ENABLED + send_ddp_endp_params_stream(out, out->devices, + channel_cap, true /* set_cache */); +#endif + audio_extn_dolby_set_dmid(adev); + break; + default: + ALOGE("%s: Unsupported audio format :%x", __func__, format); + } + + return id; +} + +bool audio_extn_is_dolby_format(audio_format_t format) +{ + if (format == AUDIO_FORMAT_AC3 || + format == AUDIO_FORMAT_E_AC3 || + format == AUDIO_FORMAT_E_AC3_JOC) + return true; + else + return false; +} +#endif /* DS1_DOLBY_DDP_ENABLED || DS2_DOLBY_DAP_ENABLED */ + +#ifdef DS1_DOLBY_DAP_ENABLED +void audio_extn_dolby_set_endpoint(struct audio_device *adev) +{ + struct listnode *node; + struct audio_usecase *usecase; + struct mixer_ctl *ctl; + const char *mixer_ctl_name = "DS1 DAP Endpoint"; + int endpoint = 0, ret; + bool send = false; + + list_for_each(node, &adev->usecase_list) { + usecase = node_to_item(node, struct audio_usecase, list); + if ((usecase->type == PCM_PLAYBACK) && + (usecase->id != USECASE_AUDIO_PLAYBACK_LOW_LATENCY)) { + endpoint |= usecase->devices & AUDIO_DEVICE_OUT_ALL; + send = true; + } + } + if (!send) + return; + + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + return; + } + ret = mixer_ctl_set_value(ctl, 0, endpoint); + if (ret) + ALOGE("%s: Dolby set endpint cannot be set error:%d",__func__, ret); + + return; +} +#endif /* DS1_DOLBY_DAP_ENABLED */ + + +#if defined(DS1_DOLBY_DDP_ENABLED) || defined(DS1_DOLBY_DAP_ENABLED) +void audio_extn_dolby_set_dmid(struct audio_device *adev) +{ + struct listnode *node; + struct audio_usecase *usecase; + struct mixer_ctl *ctl; + const char *mixer_ctl_name = "DS1 Security"; + char c_dmid[128] = {0}; + int i_dmid, ret; + bool send = false; + + list_for_each(node, &adev->usecase_list) { + usecase = node_to_item(node, struct audio_usecase, list); + if (usecase->type == PCM_PLAYBACK) + send = true; + } + if (!send) + return; + + property_get("dmid",c_dmid,"0"); + i_dmid = atoll(c_dmid); + + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + return; + } + ALOGV("%s Dolby device manufacturer id is:%d",__func__,i_dmid); + ret = mixer_ctl_set_value(ctl, 0, i_dmid); + if (ret) + ALOGE("%s: Dolby DMID cannot be set error:%d",__func__, ret); + + return; +} + +#ifndef DS2_DOLBY_DAP_ENABLED +void audio_extn_dolby_set_license(struct audio_device *adev) +{ + int ret, key=0; + char value[128] = {0}; + struct mixer_ctl *ctl; + const char *mixer_ctl_name = "DS1 License"; + + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + return; + } + + property_get("audio.ds1.metainfo.key",value,"0"); +#ifdef DOLBY_ACDB_LICENSE + key = atoi(value); +#else + key = 0; +#endif + ALOGV("%s Setting DS1 License, key:0x%x",__func__, key); + ret = mixer_ctl_set_value(ctl, 0, key); + if (ret) + ALOGE("%s: cannot set license, error:%d",__func__, ret); + + return; +} +#endif +#endif /* DS1_DOLBY_DDP_ENABLED || DS1_DOLBY_DAP_ENABLED */ + +#ifdef DS2_DOLBY_DAP_ENABLED +struct ds2_extn_module { + void *ds2_handle; + dap_hal_set_hw_info_t dap_hal_set_hw_info; +}; + +static struct ds2_extn_module ds2extnmod = { + .ds2_handle = NULL, + .dap_hal_set_hw_info = NULL, +}; + +int audio_extn_dap_hal_init(int snd_card) { + char c_dmid[128] = {0}; + void *handle = NULL; + int i_dmid, ret = -EINVAL; + dap_hal_device_be_id_map_t device_be_id_map; + + ALOGV("%s: opening DAP HAL lib\n", __func__); + ds2extnmod.ds2_handle = dlopen(LIB_DS2_DAP_HAL, RTLD_NOW); + if (ds2extnmod.ds2_handle == NULL) { + ALOGE("%s: DLOPEN failed for %s error %s", __func__, LIB_DS2_DAP_HAL, + dlerror()); + goto ret; + } + ds2extnmod.dap_hal_set_hw_info = (dap_hal_set_hw_info_t)dlsym(ds2extnmod.ds2_handle, SET_HW_INFO_FUNC); + if (ds2extnmod.dap_hal_set_hw_info == NULL) { + ALOGE("%s: dlsym error %s for %s", __func__, SET_HW_INFO_FUNC, + dlerror()); + goto close; + } + ds2extnmod.dap_hal_set_hw_info(SND_CARD, (void*)(&snd_card)); + ALOGV("%s Sound card number is:%d",__func__,snd_card); + + platform_get_device_to_be_id_map(&device_be_id_map.device_id_to_be_id, &device_be_id_map.len); + ds2extnmod.dap_hal_set_hw_info(DEVICE_BE_ID_MAP, (void*)(&device_be_id_map)); + ALOGV("%s Set be id map len:%d",__func__,device_be_id_map.len); + ret = 0; + goto ret; + +close: + dlclose(ds2extnmod.ds2_handle); + ds2extnmod.ds2_handle = NULL; + ds2extnmod.dap_hal_set_hw_info = NULL; +ret: + return ret; +} + +int audio_extn_dap_hal_deinit() { + if (ds2extnmod.ds2_handle != NULL) { + dlclose(ds2extnmod.ds2_handle); + ds2extnmod.ds2_handle = NULL; + } + ds2extnmod.dap_hal_set_hw_info = NULL; + return 0; +} + +void audio_extn_dolby_ds2_set_endpoint(struct audio_device *adev) { + struct listnode *node; + struct audio_usecase *usecase; + struct mixer_ctl *ctl; + const char *mixer_ctl_name = "DS1 DAP Endpoint"; + int endpoint = 0, ret; + bool send = false; + + list_for_each(node, &adev->usecase_list) { + usecase = node_to_item(node, struct audio_usecase, list); + if ((usecase->type == PCM_PLAYBACK) && + (usecase->id != USECASE_AUDIO_PLAYBACK_LOW_LATENCY)) { + endpoint |= usecase->devices & AUDIO_DEVICE_OUT_ALL; + send = true; + } + } + if (!send) + return; + + if (ds2extnmod.dap_hal_set_hw_info) { + ds2extnmod.dap_hal_set_hw_info(HW_ENDPOINT, (void*)(&endpoint)); + ALOGE("%s: Dolby set endpint :0x%x",__func__, endpoint); + } else { + ALOGE("%s: dap_hal_set_hw_info is NULL",__func__); + } + + return; +} + +int audio_extn_ds2_enable(struct audio_device *adev) { + + char value[PROPERTY_VALUE_MAX] = {0}; + bool ds2_enabled = false; + const char *mixer_ctl_name = "DS2 OnOff"; + struct mixer_ctl *ctl; + + property_get("audio.dolby.ds2.enabled", value, NULL); + ds2_enabled = atoi(value) || !strncmp("true", value, 4); + + ALOGV("%s:", __func__); + if(ds2_enabled) { + ALOGD("%s:ds2_enabled %d", __func__, ds2_enabled); + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + return -EINVAL; + } + + if (mixer_ctl_set_value(ctl, 0, ds2_enabled) < 0) { + ALOGE("%s: Could not set ds2 enable %d", + __func__, ds2_enabled); + return -EINVAL; + } + } + return 0; +} + +int audio_extn_dolby_set_dap_bypass(struct audio_device *adev, int state) { + + ALOGV("%s: state %d", __func__, state); + if (ds2extnmod.dap_hal_set_hw_info) { + ds2extnmod.dap_hal_set_hw_info(DAP_BYPASS, (void*)(&state)); + ALOGV("%s: Dolby set bypas :0x%x", __func__, state); + } else { + ALOGV("%s: dap_hal_set_hw_info is NULL", __func__); + } + return 0; +} + +void audio_extn_dolby_set_license(struct audio_device *adev) +{ + int i_key=0; + char c_key[128] = {0}; + char c_dmid[128] = {0}; + int i_dmid, ret = -EINVAL; + struct dolby_param_license dolby_license; + +#ifdef DOLBY_ACDB_LICENSE + property_get("audio.ds1.metainfo.key",c_key,"0"); + i_key = atoi(c_key); +#else + /* As ACDB based license mechanism is disabled, force set the license key to 0*/ + i_key = 0; +#endif + property_get("dmid",c_dmid,"0"); + i_dmid = atoll(c_dmid); + ALOGV("%s Setting DS1 License, key:0x%x dmid %d",__func__, i_key,i_dmid); + dolby_license.dmid = i_dmid; + dolby_license.license_key = i_key; + if (ds2extnmod.dap_hal_set_hw_info) { + ds2extnmod.dap_hal_set_hw_info(DMID, (void*)(&dolby_license.dmid)); + } else { + ALOGV("%s: dap_hal_set_hw_info is NULL", __func__); + return ret; + } + return 0; +} + + +void audio_extn_ds2_set_parameters(struct audio_device *adev, + struct str_parms *parms) +{ + int val, ret; + char value[32]={0}; + + ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_SND_CARD_STATUS, value, + sizeof(value)); + if (ret >= 0) { + char *snd_card_status = value + 2; + if (strncmp(snd_card_status, "ONLINE", sizeof("ONLINE")) == 0){ + audio_extn_dolby_set_license(adev); + } + } +} +#endif diff --git a/audio/hal/audio_extn/fm.c b/audio/hal/audio_extn/fm.c new file mode 100644 index 0000000..ed3776c --- /dev/null +++ b/audio/hal/audio_extn/fm.c @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2013 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_hw_fm" +/*#define LOG_NDEBUG 0*/ +#define LOG_NDDEBUG 0 + +#include +#include +#include + +#include "audio_hw.h" +#include "platform.h" +#include "platform_api.h" +#include +#include + +#ifdef FM_ENABLED +#define AUDIO_PARAMETER_KEY_HANDLE_FM "handle_fm" +#define AUDIO_PARAMETER_KEY_FM_VOLUME "fm_volume" +#define AUDIO_PARAMETER_KEY_REC_PLAY_CONC "rec_play_conc_on" + +static struct pcm_config pcm_config_fm = { + .channels = 2, + .rate = 48000, + .period_size = 256, + .period_count = 4, + .format = PCM_FORMAT_S16_LE, + .start_threshold = 0, + .stop_threshold = INT_MAX, + .avail_min = 0, +}; + +struct fm_module { + struct pcm *fm_pcm_rx; + struct pcm *fm_pcm_tx; + bool is_fm_running; + float fm_volume; + bool restart_fm; + int scard_state; +}; + +static struct fm_module fmmod = { + .fm_pcm_rx = NULL, + .fm_pcm_tx = NULL, + .fm_volume = 0, + .is_fm_running = 0, + .restart_fm = 0, + .scard_state = SND_CARD_STATE_ONLINE, +}; + +static int32_t fm_set_volume(struct audio_device *adev, float value) +{ + int32_t vol, ret = 0; + struct mixer_ctl *ctl; + const char *mixer_ctl_name = FM_RX_VOLUME; + + ALOGV("%s: entry", __func__); + ALOGD("%s: (%f)\n", __func__, value); + + if (value < 0.0) { + ALOGW("%s: (%f) Under 0.0, assuming 0.0\n", __func__, value); + value = 0.0; + } else if (value > 1.0) { + ALOGW("%s: (%f) Over 1.0, assuming 1.0\n", __func__, value); + value = 1.0; + } + vol = lrint((value * 0x2000) + 0.5); + fmmod.fm_volume = value; + + if (!fmmod.is_fm_running) { + ALOGV("%s: FM not active, ignoring set_fm_volume call", __func__); + return -EIO; + } + + ALOGD("%s: Setting FM volume to %d \n", __func__, vol); + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + return -EINVAL; + } + mixer_ctl_set_value(ctl, 0, vol); + ALOGV("%s: exit", __func__); + return ret; +} + +static int32_t fm_stop(struct audio_device *adev) +{ + int32_t i, ret = 0; + struct audio_usecase *uc_info; + + ALOGD("%s: enter", __func__); + fmmod.is_fm_running = false; + + /* 1. Close the PCM devices */ + if (fmmod.fm_pcm_rx) { + pcm_close(fmmod.fm_pcm_rx); + fmmod.fm_pcm_rx = NULL; + } + if (fmmod.fm_pcm_tx) { + pcm_close(fmmod.fm_pcm_tx); + fmmod.fm_pcm_tx = NULL; + } + + uc_info = get_usecase_from_list(adev, USECASE_AUDIO_PLAYBACK_FM); + if (uc_info == NULL) { + ALOGE("%s: Could not find the usecase (%d) in the list", + __func__, USECASE_VOICE_CALL); + return -EINVAL; + } + + /* 2. Get and set stream specific mixer controls */ + disable_audio_route(adev, uc_info); + + /* 3. Disable the rx and tx devices */ + disable_snd_device(adev, uc_info->out_snd_device); + disable_snd_device(adev, uc_info->in_snd_device); + + list_remove(&uc_info->list); + free(uc_info); + + ALOGD("%s: exit: status(%d)", __func__, ret); + return ret; +} + +static int32_t fm_start(struct audio_device *adev) +{ + int32_t i, ret = 0; + struct audio_usecase *uc_info; + int32_t pcm_dev_rx_id, pcm_dev_tx_id; + + ALOGD("%s: enter", __func__); + + uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase)); + + if (!uc_info) + return -ENOMEM; + + uc_info->id = USECASE_AUDIO_PLAYBACK_FM; + uc_info->type = PCM_PLAYBACK; + uc_info->stream.out = adev->primary_output; + uc_info->devices = adev->primary_output->devices; + uc_info->in_snd_device = SND_DEVICE_NONE; + uc_info->out_snd_device = SND_DEVICE_NONE; + + list_add_tail(&adev->usecase_list, &uc_info->list); + + select_devices(adev, USECASE_AUDIO_PLAYBACK_FM); + + pcm_dev_rx_id = platform_get_pcm_device_id(uc_info->id, PCM_PLAYBACK); + pcm_dev_tx_id = platform_get_pcm_device_id(uc_info->id, PCM_CAPTURE); + + if (pcm_dev_rx_id < 0 || pcm_dev_tx_id < 0) { + ALOGE("%s: Invalid PCM devices (rx: %d tx: %d) for the usecase(%d)", + __func__, pcm_dev_rx_id, pcm_dev_tx_id, uc_info->id); + ret = -EIO; + goto exit; + } + + ALOGV("%s: FM PCM devices (rx: %d tx: %d) for the usecase(%d)", + __func__, pcm_dev_rx_id, pcm_dev_tx_id, uc_info->id); + + ALOGV("%s: Opening PCM playback device card_id(%d) device_id(%d)", + __func__, adev->snd_card, pcm_dev_rx_id); + fmmod.fm_pcm_rx = pcm_open(adev->snd_card, + pcm_dev_rx_id, + PCM_OUT, &pcm_config_fm); + if (fmmod.fm_pcm_rx && !pcm_is_ready(fmmod.fm_pcm_rx)) { + ALOGE("%s: %s", __func__, pcm_get_error(fmmod.fm_pcm_rx)); + ret = -EIO; + goto exit; + } + + ALOGV("%s: Opening PCM capture device card_id(%d) device_id(%d)", + __func__, adev->snd_card, pcm_dev_tx_id); + fmmod.fm_pcm_tx = pcm_open(adev->snd_card, + pcm_dev_tx_id, + PCM_IN, &pcm_config_fm); + if (fmmod.fm_pcm_tx && !pcm_is_ready(fmmod.fm_pcm_tx)) { + ALOGE("%s: %s", __func__, pcm_get_error(fmmod.fm_pcm_tx)); + ret = -EIO; + goto exit; + } + pcm_start(fmmod.fm_pcm_rx); + pcm_start(fmmod.fm_pcm_tx); + + fmmod.is_fm_running = true; + fm_set_volume(adev, fmmod.fm_volume); + + ALOGD("%s: exit: status(%d)", __func__, ret); + return 0; + +exit: + fm_stop(adev); + ALOGE("%s: Problem in FM start: status(%d)", __func__, ret); + return ret; +} + +void audio_extn_fm_set_parameters(struct audio_device *adev, + struct str_parms *parms) +{ + int ret, val; + char value[32]={0}; + float vol =0.0; + + ALOGV("%s: enter", __func__); + ret = str_parms_get_str(parms, "SND_CARD_STATUS", value, sizeof(value)); + if (ret >= 0) { + char *snd_card_status = value+2; + if (strstr(snd_card_status, "OFFLINE")) { + fmmod.scard_state = SND_CARD_STATE_OFFLINE; + } + else if (strstr(snd_card_status, "ONLINE")) { + fmmod.scard_state = SND_CARD_STATE_ONLINE; + } + } + if(fmmod.is_fm_running) { + if (fmmod.scard_state == SND_CARD_STATE_OFFLINE) { + ALOGD("sound card is OFFLINE, stop FM"); + fm_stop(adev); + fmmod.restart_fm = 1; + } + + ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, + value, sizeof(value)); + if (ret >= 0) { + val = atoi(value); + if(val > 0) + select_devices(adev, USECASE_AUDIO_PLAYBACK_FM); + } + } + if (fmmod.restart_fm && (fmmod.scard_state == SND_CARD_STATE_ONLINE)) { + ALOGD("sound card is ONLINE, restart FM"); + fmmod.restart_fm = 0; + fm_start(adev); + } + + ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_HANDLE_FM, + value, sizeof(value)); + if (ret >= 0) { + val = atoi(value); + ALOGD("%s: FM usecase", __func__); + if (val != 0) { + if(val & AUDIO_DEVICE_OUT_FM + && fmmod.is_fm_running == false) { + adev->primary_output->devices = val & ~AUDIO_DEVICE_OUT_FM; + fm_start(adev); + } else if (!(val & AUDIO_DEVICE_OUT_FM) + && fmmod.is_fm_running == true) + fm_stop(adev); + } + } + + memset(value, 0, sizeof(value)); + ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_FM_VOLUME, + value, sizeof(value)); + if (ret >= 0) { + if (sscanf(value, "%f", &vol) != 1){ + ALOGE("%s: error in retrieving fm volume", __func__); + ret = -EIO; + goto exit; + } + ALOGD("%s: set_fm_volume usecase", __func__); + fm_set_volume(adev, vol); + } + +#ifdef RECORD_PLAY_CONCURRENCY + ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_REC_PLAY_CONC, + value, sizeof(value)); + if ((ret >= 0) + && (fmmod.is_fm_running == true)) { + + if (!strncmp("true", value, sizeof("true"))) + ALOGD("Record play concurrency ON Forcing FM device reroute"); + else + ALOGD("Record play concurrency OFF Forcing FM device reroute"); + + select_devices(adev, USECASE_AUDIO_PLAYBACK_FM); + fm_set_volume(adev,fmmod.fm_volume); + } +#endif +exit: + ALOGV("%s: exit", __func__); +} +#endif /* FM_ENABLED end */ diff --git a/audio/hal/audio_extn/hfp.c b/audio/hal/audio_extn/hfp.c new file mode 100644 index 0000000..a0588a3 --- /dev/null +++ b/audio/hal/audio_extn/hfp.c @@ -0,0 +1,363 @@ +/* hfp.c +Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/ + +#define LOG_TAG "audio_hw_hfp" +/*#define LOG_NDEBUG 0*/ +#define LOG_NDDEBUG 0 + +#include +#include +#include + +#include "audio_hw.h" +#include "platform.h" +#include "platform_api.h" +#include +#include + +#ifdef HFP_ENABLED +#define AUDIO_PARAMETER_HFP_ENABLE "hfp_enable" +#define AUDIO_PARAMETER_HFP_SET_SAMPLING_RATE "hfp_set_sampling_rate" +#define AUDIO_PARAMETER_KEY_HFP_VOLUME "hfp_volume" + +#ifdef PLATFORM_MSM8994 +#define HFP_RX_VOLUME "SEC AUXPCM LOOPBACK Volume" +#else +#define HFP_RX_VOLUME "Internal HFP RX Volume" +#endif + +static int32_t start_hfp(struct audio_device *adev, + struct str_parms *parms); + +static int32_t stop_hfp(struct audio_device *adev); + +struct hfp_module { + struct pcm *hfp_sco_rx; + struct pcm *hfp_sco_tx; + struct pcm *hfp_pcm_rx; + struct pcm *hfp_pcm_tx; + bool is_hfp_running; + float hfp_volume; + audio_usecase_t ucid; +}; + +static struct hfp_module hfpmod = { + .hfp_sco_rx = NULL, + .hfp_sco_tx = NULL, + .hfp_pcm_rx = NULL, + .hfp_pcm_tx = NULL, + .hfp_volume = 0, + .is_hfp_running = 0, + .ucid = USECASE_AUDIO_HFP_SCO, +}; +static struct pcm_config pcm_config_hfp = { + .channels = 1, + .rate = 8000, + .period_size = 240, + .period_count = 2, + .format = PCM_FORMAT_S16_LE, + .start_threshold = 0, + .stop_threshold = INT_MAX, + .avail_min = 0, +}; + +static int32_t hfp_set_volume(struct audio_device *adev, float value) +{ + int32_t vol, ret = 0; + struct mixer_ctl *ctl; + const char *mixer_ctl_name = HFP_RX_VOLUME; + + ALOGV("%s: entry", __func__); + ALOGD("%s: (%f)\n", __func__, value); + + hfpmod.hfp_volume = value; + if (value < 0.0) { + ALOGW("%s: (%f) Under 0.0, assuming 0.0\n", __func__, value); + value = 0.0; + } else { + value = ((value > 15.000000) ? 1.0 : (value / 15)); + ALOGW("%s: Volume brought with in range (%f)\n", __func__, value); + } + vol = lrint((value * 0x2000) + 0.5); + + if (!hfpmod.is_hfp_running) { + ALOGV("%s: HFP not active, ignoring set_hfp_volume call", __func__); + return -EIO; + } + + ALOGD("%s: Setting HFP volume to %d \n", __func__, vol); + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + return -EINVAL; + } + if(mixer_ctl_set_value(ctl, 0, vol) < 0) { + ALOGE("%s: Couldn't set HFP Volume: [%d]", __func__, vol); + return -EINVAL; + } + + ALOGV("%s: exit", __func__); + return ret; +} + +static int32_t start_hfp(struct audio_device *adev, + struct str_parms *parms __unused) +{ + int32_t i, ret = 0; + struct audio_usecase *uc_info; + int32_t pcm_dev_rx_id, pcm_dev_tx_id, pcm_dev_asm_rx_id, pcm_dev_asm_tx_id; + + ALOGD("%s: enter", __func__); + + uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase)); + + if (!uc_info) + return -ENOMEM; + + uc_info->id = hfpmod.ucid; + uc_info->type = PCM_HFP_CALL; + uc_info->stream.out = adev->primary_output; + uc_info->devices = adev->primary_output->devices; + uc_info->in_snd_device = SND_DEVICE_NONE; + uc_info->out_snd_device = SND_DEVICE_NONE; + + list_add_tail(&adev->usecase_list, &uc_info->list); + + select_devices(adev, hfpmod.ucid); + + pcm_dev_rx_id = platform_get_pcm_device_id(uc_info->id, PCM_PLAYBACK); + pcm_dev_tx_id = platform_get_pcm_device_id(uc_info->id, PCM_CAPTURE); + pcm_dev_asm_rx_id = HFP_ASM_RX_TX; + pcm_dev_asm_tx_id = HFP_ASM_RX_TX; + if (pcm_dev_rx_id < 0 || pcm_dev_tx_id < 0 || + pcm_dev_asm_rx_id < 0 || pcm_dev_asm_tx_id < 0 ) { + ALOGE("%s: Invalid PCM devices (rx: %d tx: %d asm: rx tx %d) for the usecase(%d)", + __func__, pcm_dev_rx_id, pcm_dev_tx_id, pcm_dev_asm_rx_id, uc_info->id); + ret = -EIO; + goto exit; + } + + ALOGV("%s: HFP PCM devices (hfp rx tx: %d pcm rx tx: %d) for the usecase(%d)", + __func__, pcm_dev_rx_id, pcm_dev_tx_id, uc_info->id); + + ALOGV("%s: Opening PCM playback device card_id(%d) device_id(%d)", + __func__, adev->snd_card, pcm_dev_rx_id); + hfpmod.hfp_sco_rx = pcm_open(adev->snd_card, + pcm_dev_asm_rx_id, + PCM_OUT, &pcm_config_hfp); + if (hfpmod.hfp_sco_rx && !pcm_is_ready(hfpmod.hfp_sco_rx)) { + ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_sco_rx)); + ret = -EIO; + goto exit; + } + ALOGD("%s: Opening PCM capture device card_id(%d) device_id(%d)", + __func__, adev->snd_card, pcm_dev_tx_id); + hfpmod.hfp_pcm_rx = pcm_open(adev->snd_card, + pcm_dev_rx_id, + PCM_OUT, &pcm_config_hfp); + if (hfpmod.hfp_pcm_rx && !pcm_is_ready(hfpmod.hfp_pcm_rx)) { + ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_pcm_rx)); + ret = -EIO; + goto exit; + } + hfpmod.hfp_sco_tx = pcm_open(adev->snd_card, + pcm_dev_asm_tx_id, + PCM_IN, &pcm_config_hfp); + if (hfpmod.hfp_sco_tx && !pcm_is_ready(hfpmod.hfp_sco_tx)) { + ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_sco_tx)); + ret = -EIO; + goto exit; + } + ALOGV("%s: Opening PCM capture device card_id(%d) device_id(%d)", + __func__, adev->snd_card, pcm_dev_tx_id); + hfpmod.hfp_pcm_tx = pcm_open(adev->snd_card, + pcm_dev_tx_id, + PCM_IN, &pcm_config_hfp); + if (hfpmod.hfp_pcm_tx && !pcm_is_ready(hfpmod.hfp_pcm_tx)) { + ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_pcm_tx)); + ret = -EIO; + goto exit; + } + if (pcm_start(hfpmod.hfp_sco_rx) < 0) { + ALOGE("%s: pcm start for hfp sco rx failed", __func__); + ret = -EINVAL; + goto exit; + } + if (pcm_start(hfpmod.hfp_sco_tx) < 0) { + ALOGE("%s: pcm start for hfp sco tx failed", __func__); + ret = -EINVAL; + goto exit; + } + if (pcm_start(hfpmod.hfp_pcm_rx) < 0) { + ALOGE("%s: pcm start for hfp pcm rx failed", __func__); + ret = -EINVAL; + goto exit; + } + if (pcm_start(hfpmod.hfp_pcm_tx) < 0) { + ALOGE("%s: pcm start for hfp pcm tx failed", __func__); + ret = -EINVAL; + goto exit; + } + + hfpmod.is_hfp_running = true; + hfp_set_volume(adev, hfpmod.hfp_volume); + + ALOGD("%s: exit: status(%d)", __func__, ret); + return 0; + +exit: + stop_hfp(adev); + ALOGE("%s: Problem in HFP start: status(%d)", __func__, ret); + return ret; +} + +static int32_t stop_hfp(struct audio_device *adev) +{ + int32_t i, ret = 0; + struct audio_usecase *uc_info; + + ALOGD("%s: enter", __func__); + hfpmod.is_hfp_running = false; + + /* 1. Close the PCM devices */ + if (hfpmod.hfp_sco_rx) { + pcm_close(hfpmod.hfp_sco_rx); + hfpmod.hfp_sco_rx = NULL; + } + if (hfpmod.hfp_sco_tx) { + pcm_close(hfpmod.hfp_sco_tx); + hfpmod.hfp_sco_tx = NULL; + } + if (hfpmod.hfp_pcm_rx) { + pcm_close(hfpmod.hfp_pcm_rx); + hfpmod.hfp_pcm_rx = NULL; + } + if (hfpmod.hfp_pcm_tx) { + pcm_close(hfpmod.hfp_pcm_tx); + hfpmod.hfp_pcm_tx = NULL; + } + + uc_info = get_usecase_from_list(adev, hfpmod.ucid); + if (uc_info == NULL) { + ALOGE("%s: Could not find the usecase (%d) in the list", + __func__, hfpmod.ucid); + return -EINVAL; + } + + /* 2. Disable echo reference while stopping hfp */ + platform_set_echo_reference(adev->platform, false); + + /* 3. Get and set stream specific mixer controls */ + disable_audio_route(adev, uc_info); + + /* 4. Disable the rx and tx devices */ + disable_snd_device(adev, uc_info->out_snd_device); + disable_snd_device(adev, uc_info->in_snd_device); + + list_remove(&uc_info->list); + free(uc_info); + + ALOGD("%s: exit: status(%d)", __func__, ret); + return ret; +} + +bool audio_extn_hfp_is_active(struct audio_device *adev) +{ + struct audio_usecase *hfp_usecase = NULL; + hfp_usecase = get_usecase_from_list(adev, hfpmod.ucid); + + if (hfp_usecase != NULL) + return true; + else + return false; +} + +audio_usecase_t audio_extn_hfp_get_usecase() +{ + return hfpmod.ucid; +} + +void audio_extn_hfp_set_parameters(struct audio_device *adev, struct str_parms *parms) +{ + int ret; + int rate; + int val; + float vol; + char value[32]={0}; + + ret = str_parms_get_str(parms, AUDIO_PARAMETER_HFP_ENABLE, value, + sizeof(value)); + if (ret >= 0) { + if (!strncmp(value,"true",sizeof(value))) + ret = start_hfp(adev,parms); + else + stop_hfp(adev); + } + memset(value, 0, sizeof(value)); + ret = str_parms_get_str(parms,AUDIO_PARAMETER_HFP_SET_SAMPLING_RATE, value, + sizeof(value)); + if (ret >= 0) { + rate = atoi(value); + if (rate == 8000){ + hfpmod.ucid = USECASE_AUDIO_HFP_SCO; + pcm_config_hfp.rate = rate; + } else if (rate == 16000){ + hfpmod.ucid = USECASE_AUDIO_HFP_SCO_WB; + pcm_config_hfp.rate = rate; + } else + ALOGE("Unsupported rate.."); + } + + if (hfpmod.is_hfp_running) { + memset(value, 0, sizeof(value)); + ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, + value, sizeof(value)); + if (ret >= 0) { + val = atoi(value); + if (val > 0) + select_devices(adev, hfpmod.ucid); + } + } + + memset(value, 0, sizeof(value)); + ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_HFP_VOLUME, + value, sizeof(value)); + if (ret >= 0) { + if (sscanf(value, "%f", &vol) != 1){ + ALOGE("%s: error in retrieving hfp volume", __func__); + ret = -EIO; + goto exit; + } + ALOGD("%s: set_hfp_volume usecase, Vol: [%f]", __func__, vol); + hfp_set_volume(adev, vol); + } +exit: + ALOGV("%s Exit",__func__); +} +#endif /*HFP_ENABLED*/ diff --git a/audio/hal/audio_extn/listen.c b/audio/hal/audio_extn/listen.c new file mode 100644 index 0000000..4cb2d2d --- /dev/null +++ b/audio/hal/audio_extn/listen.c @@ -0,0 +1,260 @@ +/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#define LOG_TAG "listen_hal_loader" +/* #define LOG_NDEBUG 0 */ +/* #define LOG_NDDEBUG 0 */ +#include +#include +#include +#include +#ifdef AUDIO_LISTEN_ENABLED +#include +#endif +#include "audio_hw.h" +#include "audio_extn.h" +#include "platform.h" +#include "platform_api.h" + + +#ifdef AUDIO_LISTEN_ENABLED + +#define LIB_LISTEN_LOADER "/vendor/lib/liblistenhardware.so" + +#define LISTEN_LOAD_SYMBOLS(dev, func_p, func_type, symbol) \ +{\ + dev->func_p = (func_type)dlsym(dev->lib_handle,#symbol);\ + if (dev->func_p == NULL) {\ + ALOGE("%s: dlsym error %s for %s",\ + __func__, dlerror(), #symbol);\ + free(dev);\ + dev = NULL;\ + return -EINVAL;\ + }\ +} + + +typedef int (*create_listen_hw_t)(unsigned int snd_card, + struct audio_route *audio_route); +typedef void (*destroy_listen_hw_t)(); + +typedef int (*open_listen_session_t)(struct audio_hw_device *, + struct listen_open_params*, + struct listen_session**); + +typedef int (*close_listen_session_t)(struct audio_hw_device *dev, + struct listen_session* handle); + +typedef int (*set_mad_observer_t)(struct audio_hw_device *dev, + listen_callback_t cb_func); + +typedef int (*listen_set_parameters_t)(struct audio_hw_device *dev, + const char *kv_pairs); +typedef char* (*get_parameters_t)(const struct audio_hw_device *dev, + const char *keys); +typedef void (*listen_notify_event_t)(event_type_t event_type); + +struct listen_audio_device { + void *lib_handle; + struct audio_device *adev; + + create_listen_hw_t create_listen_hw; + destroy_listen_hw_t destroy_listen_hw; + open_listen_session_t open_listen_session; + close_listen_session_t close_listen_session; + set_mad_observer_t set_mad_observer; + listen_set_parameters_t listen_set_parameters; + get_parameters_t get_parameters; + listen_notify_event_t notify_event; +}; + +static struct listen_audio_device *listen_dev; + +void audio_extn_listen_update_device_status(snd_device_t snd_device, + listen_event_type_t event) +{ + bool raise_event = false; + int device_type = -1; + + if (snd_device >= SND_DEVICE_OUT_BEGIN && + snd_device < SND_DEVICE_OUT_END) + device_type = PCM_PLAYBACK; + else if (snd_device >= SND_DEVICE_IN_BEGIN && + snd_device < SND_DEVICE_IN_END) + device_type = PCM_CAPTURE; + else { + ALOGE("%s: invalid device 0x%x, for event %d", + __func__, snd_device, event); + return; + } + + if (listen_dev) { + raise_event = platform_listen_device_needs_event(snd_device); + ALOGI("%s(): device 0x%x of type %d for Event %d, with Raise=%d", + __func__, snd_device, device_type, event, raise_event); + if (raise_event && (device_type == PCM_CAPTURE)) { + switch(event) { + case LISTEN_EVENT_SND_DEVICE_FREE: + listen_dev->notify_event(AUDIO_DEVICE_IN_INACTIVE); + break; + case LISTEN_EVENT_SND_DEVICE_BUSY: + listen_dev->notify_event(AUDIO_DEVICE_IN_ACTIVE); + break; + default: + ALOGW("%s:invalid event %d for device 0x%x", + __func__, event, snd_device); + } + }/*Events for output device, if required can be placed here in else*/ + } +} + +void audio_extn_listen_update_stream_status(struct audio_usecase *uc_info, + listen_event_type_t event) +{ + bool raise_event = false; + audio_usecase_t uc_id; + int usecase_type = -1; + + if (uc_info == NULL) { + ALOGE("%s: usecase is NULL!!!", __func__); + return; + } + uc_id = uc_info->id; + usecase_type = uc_info->type; + + if (listen_dev) { + raise_event = platform_listen_usecase_needs_event(uc_id); + ALOGI("%s(): uc_id %d of type %d for Event %d, with Raise=%d", + __func__, uc_id, usecase_type, event, raise_event); + if (raise_event && (usecase_type == PCM_PLAYBACK)) { + switch(event) { + case LISTEN_EVENT_STREAM_FREE: + listen_dev->notify_event(AUDIO_STREAM_OUT_INACTIVE); + break; + case LISTEN_EVENT_STREAM_BUSY: + listen_dev->notify_event(AUDIO_STREAM_OUT_ACTIVE); + break; + default: + ALOGW("%s:invalid event %d, for usecase %d", + __func__, event, uc_id); + } + }/*Events for capture usecase, if required can be placed here in else*/ + } +} + +void audio_extn_listen_set_parameters(struct audio_device *adev, + struct str_parms *parms) +{ + ALOGV("%s: enter", __func__); + if (listen_dev) { + char *kv_pairs = str_parms_to_str(parms); + ALOGV_IF(kv_pairs != NULL, "%s: %s", __func__, kv_pairs); + listen_dev->listen_set_parameters(&adev->device, kv_pairs); + free(kv_pairs); + } + + return; +} + +int audio_extn_listen_init(struct audio_device *adev, unsigned int snd_card) +{ + int ret; + void *lib_handle; + + ALOGI("%s: Enter", __func__); + + lib_handle = dlopen(LIB_LISTEN_LOADER, RTLD_NOW); + + if (lib_handle == NULL) { + ALOGE("%s: DLOPEN failed for %s. error = %s", __func__, LIB_LISTEN_LOADER, + dlerror()); + return -EINVAL; + } else { + ALOGI("%s: DLOPEN successful for %s", __func__, LIB_LISTEN_LOADER); + + listen_dev = (struct listen_audio_device*) + calloc(1, sizeof(struct listen_audio_device)); + + if (!listen_dev) { + ALOGE("failed to allocate listen_dev mem"); + return -ENOMEM; + } + + listen_dev->lib_handle = lib_handle; + listen_dev->adev = adev; + + LISTEN_LOAD_SYMBOLS(listen_dev, create_listen_hw, + create_listen_hw_t, create_listen_hw); + + LISTEN_LOAD_SYMBOLS(listen_dev, destroy_listen_hw, + destroy_listen_hw_t, destroy_listen_hw); + + LISTEN_LOAD_SYMBOLS(listen_dev, open_listen_session, + open_listen_session_t, open_listen_session); + + adev->device.open_listen_session = listen_dev->open_listen_session; + + LISTEN_LOAD_SYMBOLS(listen_dev, close_listen_session, + close_listen_session_t, close_listen_session); + + adev->device.close_listen_session = listen_dev->close_listen_session; + + LISTEN_LOAD_SYMBOLS(listen_dev, set_mad_observer, + set_mad_observer_t, set_mad_observer); + + adev->device.set_mad_observer = listen_dev->set_mad_observer; + + LISTEN_LOAD_SYMBOLS(listen_dev, listen_set_parameters, + listen_set_parameters_t, listen_hw_set_parameters); + + adev->device.listen_set_parameters = listen_dev->listen_set_parameters; + + LISTEN_LOAD_SYMBOLS(listen_dev, get_parameters, + get_parameters_t, listen_hw_get_parameters); + + LISTEN_LOAD_SYMBOLS(listen_dev, notify_event, + listen_notify_event_t, listen_hw_notify_event); + + listen_dev->create_listen_hw(snd_card, adev->audio_route); + } + return 0; +} + +void audio_extn_listen_deinit(struct audio_device *adev) +{ + ALOGI("%s: Enter", __func__); + + if (listen_dev && (listen_dev->adev == adev) && listen_dev->lib_handle) { + listen_dev->destroy_listen_hw(); + dlclose(listen_dev->lib_handle); + free(listen_dev); + listen_dev = NULL; + } +} + +#endif /* AUDIO_LISTEN_ENABLED */ diff --git a/audio/hal/audio_extn/pm.c b/audio/hal/audio_extn/pm.c new file mode 100644 index 0000000..7b76f60 --- /dev/null +++ b/audio/hal/audio_extn/pm.c @@ -0,0 +1,149 @@ +/* Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#define LOG_TAG "audio_hw_pm" +/*#define LOG_NDEBUG 0*/ + +#include "pm.h" +#include + +static s_audio_subsys audio_ss; + +int audio_extn_pm_vote(void) +{ + int err, intfd, ret; + FILE *fd; + enum pm_event subsys_state; + char halPropVal[PROPERTY_VALUE_MAX]; + bool prop_unload_image = false; + bool pm_reg = false; + bool pm_supp = false; + + platform_get_subsys_image_name((char *)&audio_ss.img_name); + ALOGD("%s: register with peripheral manager for %s",__func__, audio_ss.img_name); + ret = pm_client_register(audio_extn_pm_event_notifier, + &audio_ss, + audio_ss.img_name, + PM_CLIENT_NAME, + &subsys_state, + &audio_ss.pm_handle); + if (ret == PM_RET_SUCCESS) { + pm_reg = true; + pm_supp = true; + ALOGV("%s: registered with peripheral manager for %s", + __func__, audio_ss.img_name); + } else if (ret == PM_RET_UNSUPPORTED) { + pm_reg = true; + pm_supp = false; + ALOGV("%s: peripheral mgr unsupported for %s", + __func__, audio_ss.img_name); + return ret; + } else { + return ret; + } + if (pm_supp == true && + pm_reg == true) { + ALOGD("%s: Voting for subsystem power up", __func__); + pm_client_connect(audio_ss.pm_handle); + + if (property_get("sys.audio.init", halPropVal, NULL)) { + prop_unload_image = !(strncmp("false", halPropVal, sizeof("false"))); + } + /* + * adsp-loader loads modem/adsp image at boot up to play boot tone, + * before peripheral manager service is up. Once PM is up, vote to PM + * and unload the image to give control to PM to load/unload image + */ + if (prop_unload_image) { + intfd = open(BOOT_IMG_SYSFS_PATH, O_WRONLY); + if (intfd == -1) { + ALOGE("failed to open fd in write mode, %d", errno); + } else { + ALOGD("%s: write to sysfs to unload image", __func__); + err = write(intfd, UNLOAD_IMAGE, 1); + close(intfd); + property_set("sys.audio.init", "true"); + } + } + } + return 0; +} + +void audio_extn_pm_unvote(void) +{ + ALOGD("%s", __func__); + if (audio_ss.pm_handle) { + pm_client_disconnect(audio_ss.pm_handle); + pm_client_unregister(audio_ss.pm_handle); + } +} + +void audio_extn_pm_set_parameters(struct str_parms *parms) +{ + int ret; + char value[32]; + + ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_DEV_SHUTDOWN, value, sizeof(value)); + if (ret >= 0) { + if (strstr(value, "true")) { + ALOGD("Device shutdown notification received, unregister with PM"); + audio_extn_pm_unvote(); + } + } +} + +void audio_extn_pm_event_notifier(void *client_data, enum pm_event event) +{ + pm_client_event_acknowledge(audio_ss.pm_handle, event); + + /* Closing and re-opening of session is done based on snd card status given + * by AudioDaemon during SS offline/online (legacy code). Just return for now. + */ + switch (event) { + case EVENT_PERIPH_GOING_OFFLINE: + ALOGV("%s: %s is going offline", __func__, audio_ss.img_name); + break; + + case EVENT_PERIPH_IS_OFFLINE: + ALOGV("%s: %s is offline", __func__, audio_ss.img_name); + break; + + case EVENT_PERIPH_GOING_ONLINE: + ALOGV("%s: %s is going online", __func__, audio_ss.img_name); + break; + + case EVENT_PERIPH_IS_ONLINE: + ALOGV("%s: %s is online", __func__, audio_ss.img_name); + break; + + default: + ALOGV("%s: invalid event received from PM", __func__); + break; + } +} diff --git a/audio/hal/audio_extn/pm.h b/audio/hal/audio_extn/pm.h new file mode 100644 index 0000000..daa376e --- /dev/null +++ b/audio/hal/audio_extn/pm.h @@ -0,0 +1,68 @@ +/* Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef AUDIO_EXTN_PM_H +#define AUDIO_EXTN_PM_H + +#include +#include +#include +#include +#include +#include +#include "audio_hw.h" +#include +#include +#include + + +/* Client name to be registered with PM */ +#define PM_CLIENT_NAME "audio" +/* Command to sysfs to unload image */ +#define UNLOAD_IMAGE "0" +#define MAX_NAME_LEN 32 +#define BOOT_IMG_SYSFS_PATH "/sys/kernel/boot_adsp/boot" + +typedef struct { + //MAX_NAME_LEN defined in mdm_detect.h + char img_name[MAX_NAME_LEN]; + //this handle is used by peripheral mgr + void *pm_handle; +}s_audio_subsys; + +/* Vote to peripheral manager for required subsystem */ +int audio_extn_pm_vote (void); + +/* Unvote to peripheral manager */ +void audio_extn_pm_unvote (void); + +/* Get subsytem status notification from PM */ +void audio_extn_pm_event_notifier (void *client_data, enum pm_event event); + +#endif // AUDIO_EXTN_PM_H diff --git a/audio/hal/audio_extn/soundtrigger.c b/audio/hal/audio_extn/soundtrigger.c new file mode 100644 index 0000000..5f4c6ba --- /dev/null +++ b/audio/hal/audio_extn/soundtrigger.c @@ -0,0 +1,358 @@ +/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#define LOG_TAG "soundtrigger" +/* #define LOG_NDEBUG 0 */ +#define LOG_NDDEBUG 0 + +#include +#include +#include +#include +#include "audio_hw.h" +#include "audio_extn.h" +#include "platform.h" +#include "platform_api.h" +#include "sound_trigger_prop_intf.h" + +#define XSTR(x) STR(x) +#define STR(x) #x + +struct sound_trigger_info { + struct sound_trigger_session_info st_ses; + bool lab_stopped; + struct listnode list; +}; + +struct sound_trigger_audio_device { + void *lib_handle; + struct audio_device *adev; + sound_trigger_hw_call_back_t st_callback; + struct listnode st_ses_list; + pthread_mutex_t lock; +}; + +static struct sound_trigger_audio_device *st_dev; + +static struct sound_trigger_info * +get_sound_trigger_info(int capture_handle) +{ + struct sound_trigger_info *st_ses_info = NULL; + struct listnode *node; + ALOGD("%s: list %d capture_handle %d", __func__, + list_empty(&st_dev->st_ses_list), capture_handle); + list_for_each(node, &st_dev->st_ses_list) { + st_ses_info = node_to_item(node, struct sound_trigger_info , list); + if (st_ses_info->st_ses.capture_handle == capture_handle) + return st_ses_info; + } + return NULL; +} + +int audio_hw_call_back(sound_trigger_event_type_t event, + sound_trigger_event_info_t* config) +{ + int status = 0; + struct sound_trigger_info *st_ses_info; + + if (!st_dev) + return -EINVAL; + + pthread_mutex_lock(&st_dev->lock); + switch (event) { + case ST_EVENT_SESSION_REGISTER: + if (!config) { + ALOGE("%s: NULL config", __func__); + status = -EINVAL; + break; + } + st_ses_info= calloc(1, sizeof(struct sound_trigger_info )); + if (!st_ses_info) { + ALOGE("%s: st_ses_info alloc failed", __func__); + status = -ENOMEM; + break; + } + memcpy(&st_ses_info->st_ses, &config->st_ses, sizeof (config->st_ses)); + ALOGV("%s: add capture_handle %d pcm %p", __func__, + st_ses_info->st_ses.capture_handle, st_ses_info->st_ses.pcm); + list_add_tail(&st_dev->st_ses_list, &st_ses_info->list); + break; + + case ST_EVENT_SESSION_DEREGISTER: + if (!config) { + ALOGE("%s: NULL config", __func__); + status = -EINVAL; + break; + } + st_ses_info = get_sound_trigger_info(config->st_ses.capture_handle); + if (!st_ses_info) { + ALOGE("%s: pcm %p not in the list!", __func__, config->st_ses.pcm); + status = -EINVAL; + break; + } + ALOGV("%s: remove capture_handle %d pcm %p", __func__, + st_ses_info->st_ses.capture_handle, st_ses_info->st_ses.pcm); + list_remove(&st_ses_info->list); + free(st_ses_info); + break; + default: + ALOGW("%s: Unknown event %d", __func__, event); + break; + } + pthread_mutex_unlock(&st_dev->lock); + return status; +} + +void audio_extn_sound_trigger_stop_lab(struct stream_in *in) +{ + int status = 0; + struct sound_trigger_info *st_ses_info = NULL; + audio_event_info_t event; + + if (!st_dev || !in) + return; + + pthread_mutex_lock(&st_dev->lock); + st_ses_info = get_sound_trigger_info(in->capture_handle); + pthread_mutex_unlock(&st_dev->lock); + if (st_ses_info) { + event.u.ses_info = st_ses_info->st_ses; + ALOGV("%s: AUDIO_EVENT_STOP_LAB pcm %p", __func__, st_ses_info->st_ses.pcm); + st_dev->st_callback(AUDIO_EVENT_STOP_LAB, &event); + } +} +void audio_extn_sound_trigger_check_and_get_session(struct stream_in *in) +{ + struct sound_trigger_info *st_ses_info = NULL; + struct listnode *node; + + if (!st_dev || !in) + return; + + pthread_mutex_lock(&st_dev->lock); + in->is_st_session = false; + ALOGV("%s: list %d capture_handle %d", __func__, + list_empty(&st_dev->st_ses_list), in->capture_handle); + list_for_each(node, &st_dev->st_ses_list) { + st_ses_info = node_to_item(node, struct sound_trigger_info , list); + if (st_ses_info->st_ses.capture_handle == in->capture_handle) { + in->pcm = st_ses_info->st_ses.pcm; + in->config = st_ses_info->st_ses.config; + in->channel_mask = audio_channel_in_mask_from_count(in->config.channels); + in->is_st_session = true; + ALOGD("%s: capture_handle %d is sound trigger", __func__, in->capture_handle); + break; + } + } + pthread_mutex_unlock(&st_dev->lock); +} + +void audio_extn_sound_trigger_update_device_status(snd_device_t snd_device, + st_event_type_t event) +{ + bool raise_event = false; + int device_type = -1; + + if (!st_dev) + return; + + if (snd_device >= SND_DEVICE_OUT_BEGIN && + snd_device < SND_DEVICE_OUT_END) + device_type = PCM_PLAYBACK; + else if (snd_device >= SND_DEVICE_IN_BEGIN && + snd_device < SND_DEVICE_IN_END) + device_type = PCM_CAPTURE; + else { + ALOGE("%s: invalid device 0x%x, for event %d", + __func__, snd_device, event); + return; + } + + raise_event = platform_sound_trigger_device_needs_event(snd_device); + ALOGI("%s: device 0x%x of type %d for Event %d, with Raise=%d", + __func__, snd_device, device_type, event, raise_event); + if (raise_event && (device_type == PCM_CAPTURE)) { + switch(event) { + case ST_EVENT_SND_DEVICE_FREE: + st_dev->st_callback(AUDIO_EVENT_CAPTURE_DEVICE_INACTIVE, NULL); + break; + case ST_EVENT_SND_DEVICE_BUSY: + st_dev->st_callback(AUDIO_EVENT_CAPTURE_DEVICE_ACTIVE, NULL); + break; + default: + ALOGW("%s:invalid event %d for device 0x%x", + __func__, event, snd_device); + } + }/*Events for output device, if required can be placed here in else*/ +} + +void audio_extn_sound_trigger_update_stream_status(struct audio_usecase *uc_info, + st_event_type_t event) +{ + bool raise_event = false; + audio_usecase_t uc_id; + int usecase_type = -1; + + if (!st_dev) { + return; + } + + if (uc_info == NULL) { + ALOGE("%s: usecase is NULL!!!", __func__); + return; + } + uc_id = uc_info->id; + usecase_type = uc_info->type; + + raise_event = platform_sound_trigger_usecase_needs_event(uc_id); + ALOGD("%s: uc_id %d of type %d for Event %d, with Raise=%d", + __func__, uc_id, usecase_type, event, raise_event); + if (raise_event && (usecase_type == PCM_PLAYBACK)) { + switch(event) { + case ST_EVENT_STREAM_FREE: + st_dev->st_callback(AUDIO_EVENT_PLAYBACK_STREAM_INACTIVE, NULL); + break; + case ST_EVENT_STREAM_BUSY: + st_dev->st_callback(AUDIO_EVENT_PLAYBACK_STREAM_ACTIVE, NULL); + break; + default: + ALOGW("%s:invalid event %d, for usecase %d", + __func__, event, uc_id); + } + }/*Events for capture usecase, if required can be placed here in else*/ +} + +void audio_extn_sound_trigger_set_parameters(struct audio_device *adev __unused, + struct str_parms *params) +{ + audio_event_info_t event; + char value[32]; + int ret, val; + + if(!st_dev || !params) { + ALOGE("%s: str_params NULL", __func__); + return; + } + + ret = str_parms_get_str(params, "SND_CARD_STATUS", value, + sizeof(value)); + if (ret > 0) { + if (strstr(value, "OFFLINE")) { + event.u.status = SND_CARD_STATUS_OFFLINE; + st_dev->st_callback(AUDIO_EVENT_SSR, &event); + } + else if (strstr(value, "ONLINE")) { + event.u.status = SND_CARD_STATUS_ONLINE; + st_dev->st_callback(AUDIO_EVENT_SSR, &event); + } + else + ALOGE("%s: unknown snd_card_status", __func__); + } + + ret = str_parms_get_str(params, "CPE_STATUS", value, sizeof(value)); + if (ret > 0) { + if (strstr(value, "OFFLINE")) { + event.u.status = CPE_STATUS_OFFLINE; + st_dev->st_callback(AUDIO_EVENT_SSR, &event); + } + else if (strstr(value, "ONLINE")) { + event.u.status = CPE_STATUS_ONLINE; + st_dev->st_callback(AUDIO_EVENT_SSR, &event); + } + else + ALOGE("%s: unknown CPE status", __func__); + } + + ret = str_parms_get_int(params, "SVA_NUM_SESSIONS", &val); + if (ret >= 0) { + event.u.value = val; + st_dev->st_callback(AUDIO_EVENT_NUM_ST_SESSIONS, &event); + } +} + +int audio_extn_sound_trigger_init(struct audio_device *adev) +{ + int status = 0; + char sound_trigger_lib[100]; + void *lib_handle; + + ALOGI("%s: Enter", __func__); + + st_dev = (struct sound_trigger_audio_device*) + calloc(1, sizeof(struct sound_trigger_audio_device)); + if (!st_dev) { + ALOGE("%s: ERROR. sound trigger alloc failed", __func__); + return -ENOMEM; + } + + snprintf(sound_trigger_lib, sizeof(sound_trigger_lib), + "/system/vendor/lib/hw/sound_trigger.primary.%s.so", + XSTR(SOUND_TRIGGER_PLATFORM_NAME)); + + st_dev->lib_handle = dlopen(sound_trigger_lib, RTLD_NOW); + + if (st_dev->lib_handle == NULL) { + ALOGE("%s: DLOPEN failed for %s. error = %s", __func__, sound_trigger_lib, + dlerror()); + status = -EINVAL; + goto cleanup; + } + ALOGI("%s: DLOPEN successful for %s", __func__, sound_trigger_lib); + + st_dev->st_callback = (sound_trigger_hw_call_back_t) + dlsym(st_dev->lib_handle, "sound_trigger_hw_call_back"); + + if (st_dev->st_callback == NULL) { + ALOGE("%s: ERROR. dlsym Error:%s sound_trigger_hw_call_back", __func__, + dlerror()); + goto cleanup; + } + + st_dev->adev = adev; + list_init(&st_dev->st_ses_list); + + return 0; + +cleanup: + if (st_dev->lib_handle) + dlclose(st_dev->lib_handle); + free(st_dev); + st_dev = NULL; + return status; + +} + +void audio_extn_sound_trigger_deinit(struct audio_device *adev) +{ + ALOGI("%s: Enter", __func__); + if (st_dev && (st_dev->adev == adev) && st_dev->lib_handle) { + dlclose(st_dev->lib_handle); + free(st_dev); + st_dev = NULL; + } +} diff --git a/audio/hal/audio_extn/spkr_protection.c b/audio/hal/audio_extn/spkr_protection.c new file mode 100644 index 0000000..a350198 --- /dev/null +++ b/audio/hal/audio_extn/spkr_protection.c @@ -0,0 +1,916 @@ +/* + * Copyright (c) 2013 - 2014, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "audio_hw_spkr_prot" +/*#define LOG_NDEBUG 0*/ +#define LOG_NDDEBUG 0 + +#include +#include +#include +#include +#include "audio_hw.h" +#include "platform.h" +#include "platform_api.h" +#include +#include +#include +#include +#include +#include "audio_extn.h" +#include + +#ifdef SPKR_PROT_ENABLED + +/*Range of spkr temparatures -30C to 80C*/ +#define MIN_SPKR_TEMP_Q6 (-30 * (1 << 6)) +#define MAX_SPKR_TEMP_Q6 (80 * (1 << 6)) +#define VI_FEED_CHANNEL "VI_FEED_TX Channels" + +/*Set safe temp value to 40C*/ +#define SAFE_SPKR_TEMP 40 +#define SAFE_SPKR_TEMP_Q6 (SAFE_SPKR_TEMP * (1 << 6)) + +/*Range of resistance values 2ohms to 40 ohms*/ +#define MIN_RESISTANCE_SPKR_Q24 (2 * (1 << 24)) +#define MAX_RESISTANCE_SPKR_Q24 (40 * (1 << 24)) + +/*Path where the calibration file will be stored*/ +#define CALIB_FILE "/data/misc/audio/audio.cal" + +/*Time between retries for calibartion or intial wait time + after boot up*/ +#define WAIT_TIME_SPKR_CALIB (60 * 1000 * 1000) + +#define MIN_SPKR_IDLE_SEC (60 * 30) + +/*Once calibration is started sleep for 1 sec to allow + the calibration to kick off*/ +#define SLEEP_AFTER_CALIB_START (3000) + +/*If calibration is in progress wait for 200 msec before querying + for status again*/ +#define WAIT_FOR_GET_CALIB_STATUS (200 * 1000) + +/*Speaker states*/ +#define SPKR_NOT_CALIBRATED -1 +#define SPKR_CALIBRATED 1 + +/*Speaker processing state*/ +#define SPKR_PROCESSING_IN_PROGRESS 1 +#define SPKR_PROCESSING_IN_IDLE 0 + +/*Modes of Speaker Protection*/ +enum speaker_protection_mode { + SPKR_PROTECTION_DISABLED = -1, + SPKR_PROTECTION_MODE_PROCESSING = 0, + SPKR_PROTECTION_MODE_CALIBRATE = 1, +}; + +struct speaker_prot_session { + int spkr_prot_mode; + int spkr_processing_state; + int thermal_client_handle; + pthread_mutex_t mutex_spkr_prot; + pthread_t spkr_calibration_thread; + pthread_mutex_t spkr_prot_thermalsync_mutex; + pthread_cond_t spkr_prot_thermalsync; + int cancel_spkr_calib; + pthread_cond_t spkr_calib_cancel; + pthread_mutex_t spkr_calib_cancelack_mutex; + pthread_cond_t spkr_calibcancel_ack; + pthread_t speaker_prot_threadid; + void *thermal_handle; + void *adev_handle; + int spkr_prot_t0; + struct pcm *pcm_rx; + struct pcm *pcm_tx; + int (*client_register_callback) + (char *client_name, int (*callback)(int), void *data); + void (*thermal_client_unregister_callback)(int handle); + int (*thermal_client_request)(char *client_name, int req_data); + bool spkr_prot_enable; + bool spkr_in_use; + struct timespec spkr_last_time_used; +}; + +static struct pcm_config pcm_config_skr_prot = { + .channels = 4, + .rate = 48000, + .period_size = 256, + .period_count = 4, + .format = PCM_FORMAT_S16_LE, + .start_threshold = 0, + .stop_threshold = INT_MAX, + .avail_min = 0, +}; + +static struct speaker_prot_session handle; +static int vi_feed_no_channels; + +static void spkr_prot_set_spkrstatus(bool enable) +{ + struct timespec ts; + if (enable) + handle.spkr_in_use = true; + else { + handle.spkr_in_use = false; + clock_gettime(CLOCK_MONOTONIC, &handle.spkr_last_time_used); + } +} + +void audio_extn_spkr_prot_calib_cancel(void *adev) +{ + pthread_t threadid; + struct audio_usecase *uc_info; + int count = 0; + threadid = pthread_self(); + ALOGV("%s: Entry", __func__); + if (pthread_equal(handle.speaker_prot_threadid, threadid) || !adev) { + ALOGE("%s: Invalid params", __func__); + return; + } + uc_info = get_usecase_from_list(adev, USECASE_AUDIO_SPKR_CALIB_RX); + if (uc_info) { + pthread_mutex_lock(&handle.mutex_spkr_prot); + pthread_mutex_lock(&handle.spkr_calib_cancelack_mutex); + handle.cancel_spkr_calib = 1; + pthread_cond_signal(&handle.spkr_calib_cancel); + pthread_mutex_unlock(&handle.mutex_spkr_prot); + pthread_cond_wait(&handle.spkr_calibcancel_ack, + &handle.spkr_calib_cancelack_mutex); + pthread_mutex_unlock(&handle.spkr_calib_cancelack_mutex); + } + ALOGV("%s: Exit", __func__); +} + +static bool is_speaker_in_use(unsigned long *sec) +{ + struct timespec temp; + if (!sec) { + ALOGE("%s: Invalid params", __func__); + return true; + } + if (handle.spkr_in_use) { + *sec = 0; + return true; + } else { + clock_gettime(CLOCK_MONOTONIC, &temp); + *sec = temp.tv_sec - handle.spkr_last_time_used.tv_sec; + return false; + } +} + + +static int get_spkr_prot_cal(int cal_fd, + struct audio_cal_info_msm_spk_prot_status *status) +{ + int ret = 0; + struct audio_cal_fb_spk_prot_status cal_data; + + if (cal_fd < 0) { + ALOGE("%s: Error: cal_fd = %d", __func__, cal_fd); + ret = -EINVAL; + goto done; + } + + if (status == NULL) { + ALOGE("%s: Error: status NULL", __func__); + ret = -EINVAL; + goto done; + } + + cal_data.hdr.data_size = sizeof(cal_data); + cal_data.hdr.version = VERSION_0_0; + cal_data.hdr.cal_type = AFE_FB_SPKR_PROT_CAL_TYPE; + cal_data.hdr.cal_type_size = sizeof(cal_data.cal_type); + cal_data.cal_type.cal_hdr.version = VERSION_0_0; + cal_data.cal_type.cal_hdr.buffer_number = 0; + cal_data.cal_type.cal_data.mem_handle = -1; + + if (ioctl(cal_fd, AUDIO_GET_CALIBRATION, &cal_data)) { + ALOGE("%s: Error: AUDIO_GET_CALIBRATION failed!", + __func__); + ret = -ENODEV; + goto done; + } + + status->r0[SP_V2_SPKR_1] = cal_data.cal_type.cal_info.r0[SP_V2_SPKR_1]; + status->r0[SP_V2_SPKR_2] = cal_data.cal_type.cal_info.r0[SP_V2_SPKR_2]; + status->status = cal_data.cal_type.cal_info.status; +done: + return ret; +} + +static int set_spkr_prot_cal(int cal_fd, + struct audio_cal_info_spk_prot_cfg *protCfg) +{ + int ret = 0; + struct audio_cal_fb_spk_prot_cfg cal_data; + char value[PROPERTY_VALUE_MAX]; + + if (cal_fd < 0) { + ALOGE("%s: Error: cal_fd = %d", __func__, cal_fd); + ret = -EINVAL; + goto done; + } + + if (protCfg == NULL) { + ALOGE("%s: Error: status NULL", __func__); + ret = -EINVAL; + goto done; + } + + memset(&cal_data, 0, sizeof(cal_data)); + cal_data.hdr.data_size = sizeof(cal_data); + cal_data.hdr.version = VERSION_0_0; + cal_data.hdr.cal_type = AFE_FB_SPKR_PROT_CAL_TYPE; + cal_data.hdr.cal_type_size = sizeof(cal_data.cal_type); + cal_data.cal_type.cal_hdr.version = VERSION_0_0; + cal_data.cal_type.cal_hdr.buffer_number = 0; + cal_data.cal_type.cal_info.r0[SP_V2_SPKR_1] = protCfg->r0[SP_V2_SPKR_1]; + cal_data.cal_type.cal_info.r0[SP_V2_SPKR_2] = protCfg->r0[SP_V2_SPKR_2]; + cal_data.cal_type.cal_info.t0[SP_V2_SPKR_1] = protCfg->t0[SP_V2_SPKR_1]; + cal_data.cal_type.cal_info.t0[SP_V2_SPKR_2] = protCfg->t0[SP_V2_SPKR_2]; + cal_data.cal_type.cal_info.mode = protCfg->mode; + property_get("persist.spkr.cal.duration", value, "0"); + if (atoi(value) > 0) { + ALOGD("%s: quick calibration enabled", __func__); + cal_data.cal_type.cal_info.quick_calib_flag = 1; + } else { + ALOGD("%s: quick calibration disabled", __func__); + cal_data.cal_type.cal_info.quick_calib_flag = 0; + } + + cal_data.cal_type.cal_data.mem_handle = -1; + + if (ioctl(cal_fd, AUDIO_SET_CALIBRATION, &cal_data)) { + ALOGE("%s: Error: AUDIO_SET_CALIBRATION failed!", + __func__); + ret = -ENODEV; + goto done; + } +done: + return ret; +} + +static int vi_feed_get_channels(struct audio_device *adev) +{ + struct mixer_ctl *ctl; + const char *mixer_ctl_name = VI_FEED_CHANNEL; + int value; + + ALOGV("%s: entry", __func__); + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + goto error; + } + value = mixer_ctl_get_value(ctl, 0); + if (value < 0) + goto error; + else + return value+1; +error: + return -EINVAL; +} + +static int spkr_calibrate(int t0) +{ + struct audio_device *adev = handle.adev_handle; + struct audio_cal_info_spk_prot_cfg protCfg; + struct audio_cal_info_msm_spk_prot_status status; + bool cleanup = false, disable_rx = false, disable_tx = false; + int acdb_fd = -1; + struct audio_usecase *uc_info_rx = NULL, *uc_info_tx = NULL; + int32_t pcm_dev_rx_id = -1, pcm_dev_tx_id = -1; + struct timespec ts; + bool acquire_device = false; + + if (!adev) { + ALOGE("%s: Invalid params", __func__); + return -EINVAL; + } + if (!list_empty(&adev->usecase_list)) { + ALOGD("%s: Usecase present retry speaker protection", __func__); + return -EAGAIN; + } + acdb_fd = open("/dev/msm_audio_cal",O_RDWR | O_NONBLOCK); + if (acdb_fd < 0) { + ALOGE("%s: spkr_prot_thread open msm_acdb failed", __func__); + return -ENODEV; + } else { + protCfg.mode = MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS; + /* HAL for speaker protection gets only one Temperature */ + protCfg.t0[SP_V2_SPKR_1] = t0; + protCfg.t0[SP_V2_SPKR_2] = t0; + if (set_spkr_prot_cal(acdb_fd, &protCfg)) { + ALOGE("%s: spkr_prot_thread set failed AUDIO_SET_SPEAKER_PROT", + __func__); + status.status = -ENODEV; + goto exit; + } + } + uc_info_rx = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase)); + if (!uc_info_rx) { + return -ENOMEM; + } + uc_info_rx->id = USECASE_AUDIO_SPKR_CALIB_RX; + uc_info_rx->type = PCM_PLAYBACK; + uc_info_rx->in_snd_device = SND_DEVICE_NONE; + uc_info_rx->stream.out = adev->primary_output; + uc_info_rx->out_snd_device = SND_DEVICE_OUT_SPEAKER_PROTECTED; + disable_rx = true; + list_add_tail(&adev->usecase_list, &uc_info_rx->list); + enable_snd_device(adev, SND_DEVICE_OUT_SPEAKER_PROTECTED); + enable_audio_route(adev, uc_info_rx); + + pcm_dev_rx_id = platform_get_pcm_device_id(uc_info_rx->id, PCM_PLAYBACK); + ALOGV("%s: pcm device id %d", __func__, pcm_dev_rx_id); + if (pcm_dev_rx_id < 0) { + ALOGE("%s: Invalid pcm device for usecase (%d)", + __func__, uc_info_rx->id); + status.status = -ENODEV; + goto exit; + } + handle.pcm_rx = handle.pcm_tx = NULL; + handle.pcm_rx = pcm_open(adev->snd_card, + pcm_dev_rx_id, + PCM_OUT, &pcm_config_skr_prot); + if (handle.pcm_rx && !pcm_is_ready(handle.pcm_rx)) { + ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_rx)); + status.status = -EIO; + goto exit; + } + uc_info_tx = (struct audio_usecase *) + calloc(1, sizeof(struct audio_usecase)); + if (!uc_info_tx) { + status.status = -ENOMEM; + goto exit; + } + uc_info_tx->id = USECASE_AUDIO_SPKR_CALIB_TX; + uc_info_tx->type = PCM_CAPTURE; + uc_info_tx->in_snd_device = SND_DEVICE_IN_CAPTURE_VI_FEEDBACK; + uc_info_tx->out_snd_device = SND_DEVICE_NONE; + + disable_tx = true; + list_add_tail(&adev->usecase_list, &uc_info_tx->list); + enable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK); + enable_audio_route(adev, uc_info_tx); + + pcm_dev_tx_id = platform_get_pcm_device_id(uc_info_tx->id, PCM_CAPTURE); + if (pcm_dev_tx_id < 0) { + ALOGE("%s: Invalid pcm device for usecase (%d)", + __func__, uc_info_tx->id); + status.status = -ENODEV; + goto exit; + } + handle.pcm_tx = pcm_open(adev->snd_card, + pcm_dev_tx_id, + PCM_IN, &pcm_config_skr_prot); + if (handle.pcm_tx && !pcm_is_ready(handle.pcm_tx)) { + ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_tx)); + status.status = -EIO; + goto exit; + } + if (pcm_start(handle.pcm_rx) < 0) { + ALOGE("%s: pcm start for RX failed", __func__); + status.status = -EINVAL; + goto exit; + } + if (pcm_start(handle.pcm_tx) < 0) { + ALOGE("%s: pcm start for TX failed", __func__); + status.status = -EINVAL; + goto exit; + } + cleanup = true; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += (SLEEP_AFTER_CALIB_START/1000); + ts.tv_nsec = 0; + pthread_mutex_lock(&handle.mutex_spkr_prot); + pthread_mutex_unlock(&adev->lock); + acquire_device = true; + (void)pthread_cond_timedwait(&handle.spkr_calib_cancel, + &handle.mutex_spkr_prot, &ts); + ALOGD("%s: Speaker calibration done", __func__); + cleanup = true; + pthread_mutex_lock(&handle.spkr_calib_cancelack_mutex); + if (handle.cancel_spkr_calib) { + status.status = -EAGAIN; + goto exit; + } + if (acdb_fd > 0) { + status.status = -EINVAL; + while (!get_spkr_prot_cal(acdb_fd, &status)) { + /*sleep for 200 ms to check for status check*/ + if (!status.status) { + ALOGD("%s: spkr_prot_thread calib Success R0 %d %d", + __func__, status.r0[SP_V2_SPKR_1], status.r0[SP_V2_SPKR_2]); + FILE *fp; + + vi_feed_no_channels = vi_feed_get_channels(adev); + ALOGD("%s: vi_feed_no_channels %d", __func__, vi_feed_no_channels); + if (vi_feed_no_channels < 0) { + ALOGE("%s: no of channels negative !!", __func__); + /* limit the number of channels to 2*/ + vi_feed_no_channels = 2; + } + + fp = fopen(CALIB_FILE,"wb"); + if (!fp) { + ALOGE("%s: spkr_prot_thread File open failed %s", + __func__, strerror(errno)); + status.status = -ENODEV; + } else { + int i; + /* HAL for speaker protection is always calibrating for stereo usecase*/ + for (i = 0; i < vi_feed_no_channels; i++) { + fwrite(&status.r0[i], sizeof(status.r0[i]), 1, fp); + fwrite(&protCfg.t0[i], sizeof(protCfg.t0[i]), 1, fp); + } + fclose(fp); + } + break; + } else if (status.status == -EAGAIN) { + ALOGD("%s: spkr_prot_thread try again", __func__); + usleep(WAIT_FOR_GET_CALIB_STATUS); + } else { + ALOGE("%s: spkr_prot_thread get failed status %d", + __func__, status.status); + break; + } + } +exit: + if (handle.pcm_rx) + pcm_close(handle.pcm_rx); + handle.pcm_rx = NULL; + if (handle.pcm_tx) + pcm_close(handle.pcm_tx); + handle.pcm_tx = NULL; + /* Clear TX calibration to handset mic */ + platform_send_audio_calibration(adev->platform, + SND_DEVICE_IN_HANDSET_MIC, + platform_get_default_app_type(adev->platform), 8000); + if (!status.status) { + protCfg.mode = MSM_SPKR_PROT_CALIBRATED; + protCfg.r0[SP_V2_SPKR_1] = status.r0[SP_V2_SPKR_1]; + protCfg.r0[SP_V2_SPKR_2] = status.r0[SP_V2_SPKR_2]; + if (set_spkr_prot_cal(acdb_fd, &protCfg)) + ALOGE("%s: spkr_prot_thread disable calib mode", __func__); + else + handle.spkr_prot_mode = MSM_SPKR_PROT_CALIBRATED; + } else { + protCfg.mode = MSM_SPKR_PROT_NOT_CALIBRATED; + handle.spkr_prot_mode = MSM_SPKR_PROT_NOT_CALIBRATED; + if (set_spkr_prot_cal(acdb_fd, &protCfg)) + ALOGE("%s: spkr_prot_thread disable calib mode failed", __func__); + } + if (acdb_fd > 0) + close(acdb_fd); + + if (!handle.cancel_spkr_calib && cleanup) { + pthread_mutex_unlock(&handle.spkr_calib_cancelack_mutex); + pthread_cond_wait(&handle.spkr_calib_cancel, + &handle.mutex_spkr_prot); + pthread_mutex_lock(&handle.spkr_calib_cancelack_mutex); + } + if (disable_rx) { + list_remove(&uc_info_rx->list); + disable_snd_device(adev, SND_DEVICE_OUT_SPEAKER_PROTECTED); + disable_audio_route(adev, uc_info_rx); + } + if (disable_tx) { + list_remove(&uc_info_tx->list); + disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK); + disable_audio_route(adev, uc_info_tx); + } + if (uc_info_rx) free(uc_info_rx); + if (uc_info_tx) free(uc_info_tx); + if (cleanup) { + if (handle.cancel_spkr_calib) + pthread_cond_signal(&handle.spkr_calibcancel_ack); + handle.cancel_spkr_calib = 0; + pthread_mutex_unlock(&handle.spkr_calib_cancelack_mutex); + pthread_mutex_unlock(&handle.mutex_spkr_prot); + } + } + if (acquire_device) + pthread_mutex_lock(&adev->lock); + return status.status; +} + +static void* spkr_calibration_thread() +{ + unsigned long sec = 0; + int t0; + bool goahead = false; + struct audio_cal_info_spk_prot_cfg protCfg; + FILE *fp; + int acdb_fd; + struct audio_device *adev = handle.adev_handle; + unsigned long min_idle_time = MIN_SPKR_IDLE_SEC; + char value[PROPERTY_VALUE_MAX]; + + /* If the value of this persist.spkr.cal.duration is 0 + * then it means it will take 30min to calibrate + * and if the value is greater than zero then it would take + * that much amount of time to calibrate. + */ + property_get("persist.spkr.cal.duration", value, "0"); + if (atoi(value) > 0) + min_idle_time = atoi(value); + handle.speaker_prot_threadid = pthread_self(); + ALOGD("spkr_prot_thread enable prot Entry"); + acdb_fd = open("/dev/msm_audio_cal",O_RDWR | O_NONBLOCK); + if (acdb_fd > 0) { + /*Set processing mode with t0/r0*/ + protCfg.mode = MSM_SPKR_PROT_NOT_CALIBRATED; + if (set_spkr_prot_cal(acdb_fd, &protCfg)) { + ALOGE("%s: spkr_prot_thread enable prot failed", __func__); + handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED; + close(acdb_fd); + } else + handle.spkr_prot_mode = MSM_SPKR_PROT_NOT_CALIBRATED; + } else { + handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED; + ALOGE("%s: Failed to open acdb node", __func__); + } + if (handle.spkr_prot_mode == MSM_SPKR_PROT_DISABLED) { + ALOGD("%s: Speaker protection disabled", __func__); + pthread_exit(0); + return NULL; + } + + fp = fopen(CALIB_FILE,"rb"); + if (fp) { + int i; + bool spkr_calibrated = true; + /* HAL for speaker protection is always calibrating for stereo usecase*/ + vi_feed_no_channels = vi_feed_get_channels(adev); + ALOGD("%s: vi_feed_no_channels %d", __func__, vi_feed_no_channels); + if (vi_feed_no_channels < 0) { + ALOGE("%s: no of channels negative !!", __func__); + /* limit the number of channels to 2*/ + vi_feed_no_channels = 2; + } + for (i = 0; i < vi_feed_no_channels; i++) { + fread(&protCfg.r0[i], sizeof(protCfg.r0[i]), 1, fp); + fread(&protCfg.t0[i], sizeof(protCfg.t0[i]), 1, fp); + } + ALOGD("%s: spkr_prot_thread r0 value %d %d", + __func__, protCfg.r0[SP_V2_SPKR_1], protCfg.r0[SP_V2_SPKR_2]); + ALOGD("%s: spkr_prot_thread t0 value %d %d", + __func__, protCfg.t0[SP_V2_SPKR_1], protCfg.t0[SP_V2_SPKR_2]); + fclose(fp); + /*Valid tempature range: -30C to 80C(in q6 format) + Valid Resistance range: 2 ohms to 40 ohms(in q24 format)*/ + for (i = 0; i < vi_feed_no_channels; i++) { + if (!((protCfg.t0[i] > MIN_SPKR_TEMP_Q6) && (protCfg.t0[i] < MAX_SPKR_TEMP_Q6) + && (protCfg.r0[i] >= MIN_RESISTANCE_SPKR_Q24) + && (protCfg.r0[i] < MAX_RESISTANCE_SPKR_Q24))) { + spkr_calibrated = false; + break; + } + } + if (spkr_calibrated) { + ALOGD("%s: Spkr calibrated", __func__); + protCfg.mode = MSM_SPKR_PROT_CALIBRATED; + if (set_spkr_prot_cal(acdb_fd, &protCfg)) { + ALOGE("%s: enable prot failed", __func__); + handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED; + } else + handle.spkr_prot_mode = MSM_SPKR_PROT_CALIBRATED; + close(acdb_fd); + pthread_exit(0); + return NULL; + } + close(acdb_fd); + } + + while (1) { + ALOGV("%s: start calibration", __func__); + if (!handle.thermal_client_request("spkr",1)) { + ALOGD("%s: wait for callback from thermal daemon", __func__); + pthread_mutex_lock(&handle.spkr_prot_thermalsync_mutex); + pthread_cond_wait(&handle.spkr_prot_thermalsync, + &handle.spkr_prot_thermalsync_mutex); + /*Convert temp into q6 format*/ + t0 = (handle.spkr_prot_t0 * (1 << 6)); + pthread_mutex_unlock(&handle.spkr_prot_thermalsync_mutex); + if (t0 < MIN_SPKR_TEMP_Q6 || t0 > MAX_SPKR_TEMP_Q6) { + ALOGE("%s: Calibration temparature error %d", __func__, + handle.spkr_prot_t0); + continue; + } + ALOGD("%s: Request t0 success value %d", __func__, + handle.spkr_prot_t0); + } else { + ALOGE("%s: Request t0 failed", __func__); + /*Assume safe value for temparature*/ + t0 = SAFE_SPKR_TEMP_Q6; + } + goahead = false; + pthread_mutex_lock(&adev->lock); + if (is_speaker_in_use(&sec)) { + ALOGD("%s: Speaker in use retry calibration", __func__); + pthread_mutex_unlock(&adev->lock); + continue; + } else { + ALOGD("%s: speaker idle %ld min time %ld", __func__, sec, min_idle_time); + if (sec < min_idle_time) { + ALOGD("%s: speaker idle is less retry", __func__); + pthread_mutex_unlock(&adev->lock); + continue; + } + goahead = true; + } + if (!list_empty(&adev->usecase_list)) { + ALOGD("%s: Usecase active re-try calibration", __func__); + goahead = false; + pthread_mutex_unlock(&adev->lock); + } + if (goahead) { + int status; + status = spkr_calibrate(t0); + pthread_mutex_unlock(&adev->lock); + if (status == -EAGAIN) { + ALOGE("%s: failed to calibrate try again %s", + __func__, strerror(status)); + continue; + } else { + ALOGE("%s: calibrate status %s", __func__, strerror(status)); + } + ALOGD("%s: spkr_prot_thread end calibration", __func__); + break; + } + } + if (handle.thermal_client_handle) + handle.thermal_client_unregister_callback(handle.thermal_client_handle); + handle.thermal_client_handle = 0; + if (handle.thermal_handle) + dlclose(handle.thermal_handle); + handle.thermal_handle = NULL; + pthread_exit(0); + return NULL; +} + +static int thermal_client_callback(int temp) +{ + pthread_mutex_lock(&handle.spkr_prot_thermalsync_mutex); + ALOGD("%s: spkr_prot set t0 %d and signal", __func__, temp); + if (handle.spkr_prot_mode == MSM_SPKR_PROT_NOT_CALIBRATED) + handle.spkr_prot_t0 = temp; + pthread_cond_signal(&handle.spkr_prot_thermalsync); + pthread_mutex_unlock(&handle.spkr_prot_thermalsync_mutex); + return 0; +} + +void audio_extn_spkr_prot_init(void *adev) +{ + char value[PROPERTY_VALUE_MAX]; + ALOGD("%s: Initialize speaker protection module", __func__); + memset(&handle, 0, sizeof(handle)); + if (!adev) { + ALOGE("%s: Invalid params", __func__); + return; + } + property_get("persist.speaker.prot.enable", value, ""); + handle.spkr_prot_enable = false; + if (!strncmp("true", value, 4)) + handle.spkr_prot_enable = true; + if (!handle.spkr_prot_enable) { + ALOGD("%s: Speaker protection disabled", __func__); + return; + } + handle.adev_handle = adev; + handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED; + handle.spkr_processing_state = SPKR_PROCESSING_IN_IDLE; + handle.spkr_prot_t0 = -1; + pthread_cond_init(&handle.spkr_prot_thermalsync, NULL); + pthread_cond_init(&handle.spkr_calib_cancel, NULL); + pthread_cond_init(&handle.spkr_calibcancel_ack, NULL); + pthread_mutex_init(&handle.mutex_spkr_prot, NULL); + pthread_mutex_init(&handle.spkr_calib_cancelack_mutex, NULL); + pthread_mutex_init(&handle.spkr_prot_thermalsync_mutex, NULL); + handle.thermal_handle = dlopen("/vendor/lib/libthermalclient.so", + RTLD_NOW); + if (!handle.thermal_handle) { + ALOGE("%s: DLOPEN for thermal client failed", __func__); + } else { + /*Query callback function symbol*/ + handle.client_register_callback = + (int (*)(char *, int (*)(int),void *)) + dlsym(handle.thermal_handle, "thermal_client_register_callback"); + handle.thermal_client_unregister_callback = + (void (*)(int) ) + dlsym(handle.thermal_handle, "thermal_client_unregister_callback"); + if (!handle.client_register_callback || + !handle.thermal_client_unregister_callback) { + ALOGE("%s: DLSYM thermal_client_register_callback failed", __func__); + } else { + /*Register callback function*/ + handle.thermal_client_handle = + handle.client_register_callback("spkr", thermal_client_callback, NULL); + if (!handle.thermal_client_handle) { + ALOGE("%s: client_register_callback failed", __func__); + } else { + ALOGD("%s: spkr_prot client_register_callback success", __func__); + handle.thermal_client_request = (int (*)(char *, int)) + dlsym(handle.thermal_handle, "thermal_client_request"); + } + } + } + if (handle.thermal_client_request) { + ALOGD("%s: Create calibration thread", __func__); + (void)pthread_create(&handle.spkr_calibration_thread, + (const pthread_attr_t *) NULL, spkr_calibration_thread, &handle); + } else { + ALOGE("%s: thermal_client_request failed", __func__); + if (handle.thermal_client_handle && + handle.thermal_client_unregister_callback) + handle.thermal_client_unregister_callback(handle.thermal_client_handle); + if (handle.thermal_handle) + dlclose(handle.thermal_handle); + handle.thermal_handle = NULL; + handle.spkr_prot_enable = false; + } + + if (handle.spkr_prot_enable) { + char platform[PROPERTY_VALUE_MAX]; + property_get("ro.board.platform", platform, ""); + if (!strncmp("apq8084", platform, sizeof("apq8084"))) { + platform_set_snd_device_backend(SND_DEVICE_OUT_VOICE_SPEAKER, + "speaker-protected"); + } + } +} + +int audio_extn_spkr_prot_get_acdb_id(snd_device_t snd_device) +{ + int acdb_id; + + switch(snd_device) { + case SND_DEVICE_OUT_SPEAKER: + acdb_id = platform_get_snd_device_acdb_id(SND_DEVICE_OUT_SPEAKER_PROTECTED); + break; + case SND_DEVICE_OUT_VOICE_SPEAKER: + acdb_id = platform_get_snd_device_acdb_id(SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED); + break; + default: + acdb_id = -EINVAL; + break; + } + return acdb_id; +} + +int audio_extn_get_spkr_prot_snd_device(snd_device_t snd_device) +{ + if (!handle.spkr_prot_enable) + return snd_device; + + switch(snd_device) { + case SND_DEVICE_OUT_SPEAKER: + return SND_DEVICE_OUT_SPEAKER_PROTECTED; + case SND_DEVICE_OUT_VOICE_SPEAKER: + return SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED; + default: + return snd_device; + } +} + +int audio_extn_spkr_prot_start_processing(snd_device_t snd_device) +{ + struct audio_usecase *uc_info_tx; + struct audio_device *adev = handle.adev_handle; + int32_t pcm_dev_tx_id = -1, ret = 0; + + ALOGV("%s: Entry", __func__); + /* cancel speaker calibration */ + if (!adev) { + ALOGE("%s: Invalid params", __func__); + return -EINVAL; + } + snd_device = audio_extn_get_spkr_prot_snd_device(snd_device); + spkr_prot_set_spkrstatus(true); + uc_info_tx = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase)); + if (!uc_info_tx) { + return -ENOMEM; + } + ALOGV("%s: snd_device(%d: %s)", __func__, snd_device, + platform_get_snd_device_name(snd_device)); + audio_route_apply_and_update_path(adev->audio_route, + platform_get_snd_device_name(snd_device)); + + pthread_mutex_lock(&handle.mutex_spkr_prot); + if (handle.spkr_processing_state == SPKR_PROCESSING_IN_IDLE) { + uc_info_tx->id = USECASE_AUDIO_SPKR_CALIB_TX; + uc_info_tx->type = PCM_CAPTURE; + uc_info_tx->in_snd_device = SND_DEVICE_IN_CAPTURE_VI_FEEDBACK; + uc_info_tx->out_snd_device = SND_DEVICE_NONE; + handle.pcm_tx = NULL; + list_add_tail(&adev->usecase_list, &uc_info_tx->list); + enable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK); + enable_audio_route(adev, uc_info_tx); + + pcm_dev_tx_id = platform_get_pcm_device_id(uc_info_tx->id, PCM_CAPTURE); + if (pcm_dev_tx_id < 0) { + ALOGE("%s: Invalid pcm device for usecase (%d)", + __func__, uc_info_tx->id); + ret = -ENODEV; + goto exit; + } + handle.pcm_tx = pcm_open(adev->snd_card, + pcm_dev_tx_id, + PCM_IN, &pcm_config_skr_prot); + if (handle.pcm_tx && !pcm_is_ready(handle.pcm_tx)) { + ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_tx)); + ret = -EIO; + goto exit; + } + if (pcm_start(handle.pcm_tx) < 0) { + ALOGE("%s: pcm start for TX failed", __func__); + ret = -EINVAL; + } + } + +exit: + /* Clear VI feedback cal and replace with handset MIC */ + platform_send_audio_calibration(adev->platform, + SND_DEVICE_IN_HANDSET_MIC, + platform_get_default_app_type(adev->platform), 8000); + if (ret) { + if (handle.pcm_tx) + pcm_close(handle.pcm_tx); + handle.pcm_tx = NULL; + list_remove(&uc_info_tx->list); + disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK); + disable_audio_route(adev, uc_info_tx); + free(uc_info_tx); + } else + handle.spkr_processing_state = SPKR_PROCESSING_IN_PROGRESS; + pthread_mutex_unlock(&handle.mutex_spkr_prot); + ALOGV("%s: Exit", __func__); + return ret; +} + +void audio_extn_spkr_prot_stop_processing(snd_device_t snd_device) +{ + struct audio_usecase *uc_info_tx; + struct audio_device *adev = handle.adev_handle; + + ALOGV("%s: Entry", __func__); + snd_device = audio_extn_get_spkr_prot_snd_device(snd_device); + spkr_prot_set_spkrstatus(false); + pthread_mutex_lock(&handle.mutex_spkr_prot); + if (adev && handle.spkr_processing_state == SPKR_PROCESSING_IN_PROGRESS) { + uc_info_tx = get_usecase_from_list(adev, USECASE_AUDIO_SPKR_CALIB_TX); + if (handle.pcm_tx) + pcm_close(handle.pcm_tx); + handle.pcm_tx = NULL; + disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK); + if (uc_info_tx) { + list_remove(&uc_info_tx->list); + disable_audio_route(adev, uc_info_tx); + free(uc_info_tx); + } + } + handle.spkr_processing_state = SPKR_PROCESSING_IN_IDLE; + pthread_mutex_unlock(&handle.mutex_spkr_prot); + if (adev) + audio_route_reset_and_update_path(adev->audio_route, + platform_get_snd_device_name(snd_device)); + ALOGV("%s: Exit", __func__); +} + +bool audio_extn_spkr_prot_is_enabled() +{ + return handle.spkr_prot_enable; +} +#endif /*SPKR_PROT_ENABLED*/ diff --git a/audio/hal/audio_extn/ssr.c b/audio/hal/audio_extn/ssr.c new file mode 100644 index 0000000..07b28b0 --- /dev/null +++ b/audio/hal/audio_extn/ssr.c @@ -0,0 +1,503 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2013 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_hw_ssr" +/*#define LOG_NDEBUG 0*/ +#define LOG_NDDEBUG 0 + +#include +#include +#include +#include +#include +#include + +#include "audio_hw.h" +#include "platform.h" +#include "platform_api.h" +#include "surround_filters_interface.h" + +#ifdef SSR_ENABLED +#define COEFF_ARRAY_SIZE 4 +#define FILT_SIZE ((512+1)* 6) /* # ((FFT bins)/2+1)*numOutputs */ +#define SSR_CHANNEL_INPUT_NUM 4 +#define SSR_CHANNEL_OUTPUT_NUM 6 +#define SSR_PERIOD_COUNT 8 +#define SSR_PERIOD_SIZE 512 +#define SSR_INPUT_FRAME_SIZE (SSR_PERIOD_SIZE * SSR_PERIOD_COUNT) + +#define SURROUND_FILE_1R "/system/etc/surround_sound/filter1r.pcm" +#define SURROUND_FILE_2R "/system/etc/surround_sound/filter2r.pcm" +#define SURROUND_FILE_3R "/system/etc/surround_sound/filter3r.pcm" +#define SURROUND_FILE_4R "/system/etc/surround_sound/filter4r.pcm" + +#define SURROUND_FILE_1I "/system/etc/surround_sound/filter1i.pcm" +#define SURROUND_FILE_2I "/system/etc/surround_sound/filter2i.pcm" +#define SURROUND_FILE_3I "/system/etc/surround_sound/filter3i.pcm" +#define SURROUND_FILE_4I "/system/etc/surround_sound/filter4i.pcm" + +#define LIB_SURROUND_PROC "libsurround_proc.so" + +typedef int (*surround_filters_init_t)(void *, int, int, Word16 **, + Word16 **, int, int, int, Profiler *); +typedef void (*surround_filters_release_t)(void *); +typedef int (*surround_filters_set_channel_map_t)(void *, const int *); +typedef void (*surround_filters_intl_process_t)(void *, Word16 *, Word16 *); + +struct ssr_module { + FILE *fp_4ch; + FILE *fp_6ch; + Word16 **real_coeffs; + Word16 **imag_coeffs; + void *surround_obj; + Word16 *surround_raw_buffer; + bool is_ssr_enabled; + + void *surround_filters_handle; + surround_filters_init_t surround_filters_init; + surround_filters_release_t surround_filters_release; + surround_filters_set_channel_map_t surround_filters_set_channel_map; + surround_filters_intl_process_t surround_filters_intl_process; +}; + +static struct ssr_module ssrmod = { + .fp_4ch = NULL, + .fp_6ch = NULL, + .real_coeffs = NULL, + .imag_coeffs = NULL, + .surround_obj = NULL, + .surround_raw_buffer = NULL, + .is_ssr_enabled = 0, + + .surround_filters_handle = NULL, + .surround_filters_init = NULL, + .surround_filters_release = NULL, + .surround_filters_set_channel_map = NULL, + .surround_filters_intl_process = NULL, +}; + +/* Use AAC/DTS channel mapping as default channel mapping: C,FL,FR,Ls,Rs,LFE */ +static const int chan_map[] = { 1, 2, 4, 3, 0, 5}; + +/* Rotine to read coeffs from File and updates real and imaginary + coeff array member variable */ +static int32_t ssr_read_coeffs_from_file() +{ + FILE *flt1r; + FILE *flt2r; + FILE *flt3r; + FILE *flt4r; + FILE *flt1i; + FILE *flt2i; + FILE *flt3i; + FILE *flt4i; + int i; + + if ( (flt1r = fopen(SURROUND_FILE_1R, "rb")) == NULL ) { + ALOGE("%s: Cannot open filter co-efficient " + "file %s", __func__, SURROUND_FILE_1R); + return -EINVAL; + } + + if ( (flt2r = fopen(SURROUND_FILE_2R, "rb")) == NULL ) { + ALOGE("%s: Cannot open filter " + "co-efficient file %s", __func__, SURROUND_FILE_2R); + return -EINVAL; + } + + if ( (flt3r = fopen(SURROUND_FILE_3R, "rb")) == NULL ) { + ALOGE("%s: Cannot open filter " + "co-efficient file %s", __func__, SURROUND_FILE_3R); + return -EINVAL; + } + + if ( (flt4r = fopen(SURROUND_FILE_4R, "rb")) == NULL ) { + ALOGE("%s: Cannot open filter " + "co-efficient file %s", __func__, SURROUND_FILE_4R); + return -EINVAL; + } + + if ( (flt1i = fopen(SURROUND_FILE_1I, "rb")) == NULL ) { + ALOGE("%s: Cannot open filter " + "co-efficient file %s", __func__, SURROUND_FILE_1I); + return -EINVAL; + } + + if ( (flt2i = fopen(SURROUND_FILE_2I, "rb")) == NULL ) { + ALOGE("%s: Cannot open filter " + "co-efficient file %s", __func__, SURROUND_FILE_2I); + return -EINVAL; + } + + if ( (flt3i = fopen(SURROUND_FILE_3I, "rb")) == NULL ) { + ALOGE("%s: Cannot open filter " + "co-efficient file %s", __func__, SURROUND_FILE_3I); + return -EINVAL; + } + + if ( (flt4i = fopen(SURROUND_FILE_4I, "rb")) == NULL ) { + ALOGE("%s: Cannot open filter " + "co-efficient file %s", __func__, SURROUND_FILE_4I); + return -EINVAL; + } + ALOGV("%s: readCoeffsFromFile all filter " + "files opened", __func__); + + for (i=0; i 0 ) { + ALOGV("%s: Allocating surroundObj size is %d", __func__, ret); + ssrmod.surround_obj = (void *)malloc(ret); + if (NULL != ssrmod.surround_obj) { + memset(ssrmod.surround_obj,0,ret); + /* initialize after allocating the memory for surround_obj */ + ret = ssrmod.surround_filters_init(ssrmod.surround_obj, + 6, + 4, + ssrmod.real_coeffs, + ssrmod.imag_coeffs, + sub_woofer, + low_freq, + high_freq, + NULL); + if (0 != ret) { + ALOGE("%s: surround_filters_init failed with ret:%d",__func__, ret); + ssrmod.surround_filters_release(ssrmod.surround_obj); + goto init_fail; + } + } else { + ALOGE("%s: Allocationg surround_obj failed", __func__); + goto init_fail; + } + } else { + ALOGE("%s: surround_filters_init(surround_obj=Null) " + "failed with ret: %d", __func__, ret); + goto init_fail; + } + + (void) ssrmod.surround_filters_set_channel_map(ssrmod.surround_obj, chan_map); + + return 0; + +init_fail: + if (ssrmod.surround_obj) { + free(ssrmod.surround_obj); + ssrmod.surround_obj = NULL; + } + if (ssrmod.surround_raw_buffer) { + free(ssrmod.surround_raw_buffer); + ssrmod.surround_raw_buffer = NULL; + } + if (ssrmod.real_coeffs){ + for (i =0; iconfig.channels = SSR_CHANNEL_INPUT_NUM; + in->config.period_size = SSR_PERIOD_SIZE; + in->config.period_count = SSR_PERIOD_COUNT; + + /* use 4k hardcoded buffer size for ssr*/ + buffer_size = SSR_INPUT_FRAME_SIZE; + ALOGV("%s: buffer_size: %d", __func__, buffer_size); + + ret = ssr_init_surround_sound_lib(buffer_size); + if (0 != ret) { + ALOGE("%s: initSurroundSoundLibrary failed: %d " + "handle->bufferSize:%d", __func__, ret, buffer_size); + return ret; + } + + property_get("ssr.pcmdump",c_multi_ch_dump,"0"); + if (0 == strncmp("true", c_multi_ch_dump, sizeof("ssr.dump-pcm"))) { + /* Remember to change file system permission of data(e.g. chmod 777 data/), + otherwise, fopen may fail */ + if ( !ssrmod.fp_4ch) + ssrmod.fp_4ch = fopen("/data/4ch.pcm", "wb"); + if ( !ssrmod.fp_6ch) + ssrmod.fp_6ch = fopen("/data/6ch.pcm", "wb"); + if ((!ssrmod.fp_4ch) || (!ssrmod.fp_6ch)) + ALOGE("%s: mfp_4ch or mfp_6ch open failed: mfp_4ch:%p mfp_6ch:%p", + __func__, ssrmod.fp_4ch, ssrmod.fp_6ch); + } + + return 0; +} + +int32_t audio_extn_ssr_deinit() +{ + int i; + + if (ssrmod.surround_obj) { + ALOGV("%s: entry", __func__); + ssrmod.surround_filters_release(ssrmod.surround_obj); + if (ssrmod.surround_obj) + free(ssrmod.surround_obj); + ssrmod.surround_obj = NULL; + if (ssrmod.real_coeffs){ + for (i =0; idev; + size_t peroid_bytes; + int32_t ret; + + /* Convert bytes for 6ch to 4ch*/ + peroid_bytes = (bytes / SSR_CHANNEL_OUTPUT_NUM) * SSR_CHANNEL_INPUT_NUM; + + if (!ssrmod.surround_obj) { + ALOGE("%s: surround_obj not initialized", __func__); + return -ENOMEM; + } + + ret = pcm_read(in->pcm, ssrmod.surround_raw_buffer, peroid_bytes); + if (ret < 0) { + ALOGE("%s: %s ret:%d", __func__, pcm_get_error(in->pcm),ret); + return ret; + } + + /* apply ssr libs to conver 4ch to 6ch */ + ssrmod.surround_filters_intl_process(ssrmod.surround_obj, + buffer, ssrmod.surround_raw_buffer); + + /*dump for raw pcm data*/ + if (ssrmod.fp_4ch) + fwrite(ssrmod.surround_raw_buffer, 1, peroid_bytes, ssrmod.fp_4ch); + if (ssrmod.fp_6ch) + fwrite(buffer, 1, bytes, ssrmod.fp_6ch); + + return ret; +} + +#endif /* SSR_ENABLED */ diff --git a/audio/hal/audio_extn/usb.c b/audio/hal/audio_extn/usb.c new file mode 100644 index 0000000..13e3138 --- /dev/null +++ b/audio/hal/audio_extn/usb.c @@ -0,0 +1,730 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2013 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_hw_usb" +#define LOG_NDEBUG 0 +#define LOG_NDDEBUG 0 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef USB_HEADSET_ENABLED +#define USB_LOW_LATENCY_OUTPUT_PERIOD_SIZE 512 +#define USB_LOW_LATENCY_OUTPUT_PERIOD_COUNT 8 +#define USB_DEFAULT_OUTPUT_SAMPLING_RATE 48000 + +#define USB_PROXY_DEFAULT_SAMPLING_RATE 48000 +#define USB_PROXY_OPEN_RETRY_COUNT 100 +#define USB_PROXY_OPEN_WAIT_TIME 20 +#define USB_PROXY_PERIOD_SIZE 3072 +#define USB_PROXY_RATE_8000 8000 +#define USB_PROXY_RATE_16000 16000 +#define USB_PROXY_RATE_48000 48000 +#define USB_PERIOD_SIZE 2048 +#define USB_BUFF_SIZE 2048 +#define AFE_PROXY_PERIOD_COUNT 32 +#define AFE_PROXY_PLAYBACK_DEVICE 8 +#define AFE_PROXY_CAPTURE_DEVICE 7 + +struct usb_module { + uint32_t usb_card; + uint32_t proxy_card; + uint32_t usb_device_id; + uint32_t proxy_device_id; + + int32_t channels_playback; + int32_t sample_rate_playback; + int32_t channels_record; + int32_t sample_rate_record; + + bool is_playback_running; + bool is_record_running; + + pthread_t usb_playback_thr; + pthread_t usb_record_thr; + pthread_mutex_t usb_playback_lock; + pthread_mutex_t usb_record_lock; + + struct pcm *proxy_pcm_playback_handle; + struct pcm *usb_pcm_playback_handle; + struct pcm *proxy_pcm_record_handle; + struct pcm *usb_pcm_record_handle; + struct audio_device *adev; +}; + +static struct usb_module *usbmod = NULL; +static pthread_once_t alloc_usbmod_once_ctl = PTHREAD_ONCE_INIT; + +struct pcm_config pcm_config_usbmod = { + .channels = 2, + .rate = USB_DEFAULT_OUTPUT_SAMPLING_RATE, + .period_size = USB_LOW_LATENCY_OUTPUT_PERIOD_SIZE, + .period_count = USB_LOW_LATENCY_OUTPUT_PERIOD_COUNT, + .format = PCM_FORMAT_S16_LE, + .start_threshold = USB_LOW_LATENCY_OUTPUT_PERIOD_SIZE / 4, + .stop_threshold = INT_MAX, + .avail_min = USB_LOW_LATENCY_OUTPUT_PERIOD_SIZE / 4, +}; + +static void usb_alloc() +{ + usbmod = calloc(1, sizeof(struct usb_module)); +} + +// Some USB audio accessories have a really low default volume set. Look for a suitable +// volume control and set the volume to default volume level. +static void initPlaybackVolume() { + ALOGD("initPlaybackVolume"); + struct mixer *usbMixer = mixer_open(1); + + if (usbMixer) { + struct mixer_ctl *ctl = NULL; + unsigned int usbPlaybackVolume; + unsigned int i; + unsigned int num_ctls = mixer_get_num_ctls(usbMixer); + + // Look for the first control named ".*Playback Volume" that isn't for a microphone + for (i = 0; i < num_ctls; i++) { + ctl = mixer_get_ctl(usbMixer, i); + if ((ctl) && (strstr((const char *)mixer_ctl_get_name(ctl), "Playback Volume") && + !strstr((const char *)mixer_ctl_get_name(ctl), "Mic"))) { + break; + } + } + if (ctl != NULL) { + ALOGD("Found a volume control for USB: %s", mixer_ctl_get_name(ctl) ); + usbPlaybackVolume = mixer_ctl_get_value(ctl, 0); + ALOGD("Value got from mixer_ctl_get is:%u", usbPlaybackVolume); + if (mixer_ctl_set_value(ctl,0,usbPlaybackVolume) < 0) { + ALOGE("Failed to set volume; default volume might be used"); + } + } else { + ALOGE("No playback volume control found; default volume will be used"); + } + mixer_close(usbMixer); + } else { + ALOGE("Failed to open mixer for card 1"); + } +} + +static int usb_get_numof_rates(char *rates_str) +{ + int i, size = 0; + char *next_sr_string, *temp_ptr; + next_sr_string = strtok_r(rates_str, " ,", &temp_ptr); + + if (next_sr_string == NULL) { + ALOGE("%s: get_numof_rates: could not find rates string", __func__); + return 0; + } + + for (i = 1; next_sr_string != NULL; i++) { + size ++; + next_sr_string = strtok_r(NULL, " ,.-", &temp_ptr); + } + return size; +} + +static int usb_get_capability(char *type, int32_t *channels, + int32_t *sample_rate) +{ + ALOGD("%s: for %s", __func__, type); + long unsigned file_size; + FILE *fp; + char *buffer; + int32_t err = 1; + int32_t size = 0; + int32_t fd=-1, i, channels_playback; + char *str_start, *channel_start, *rates_str_start, *next_sr_str, + *next_sr_string, *temp_ptr; + struct stat st; + char *read_buf = NULL; + char *rates_str = NULL; + char *rates_str_for_val = NULL; + int *rates_supported = NULL; + char path[128]; + int ret = 0; + + memset(&st, 0x0, sizeof(struct stat)); + *sample_rate = 0; + snprintf(path, sizeof(path), "/proc/asound/card%u/stream0", + usbmod->usb_card); + + fd = open(path, O_RDONLY); + if (fd <0) { + ALOGE("%s: error failed to open config file %s error: %d\n", + __func__, path, errno); + ret = -EINVAL; + goto done; + } + + if (fstat(fd, &st) < 0) { + ALOGE("%s: error failed to stat %s error %d\n", + __func__, path, errno); + ret = -EINVAL; + goto done; + } + + file_size = st.st_size; + + read_buf = (char *)calloc(1, USB_BUFF_SIZE + 1); + + if (!read_buf) { + ALOGE("Failed to create read_buf"); + ret = -ENOMEM; + goto done; + } + + err = read(fd, read_buf, USB_BUFF_SIZE); + str_start = strstr(read_buf, type); + if (str_start == NULL) { + ALOGE("%s: error %s section not found in usb config file", + __func__, type); + ret = -EINVAL; + goto done; + } + + channel_start = strstr(str_start, "Channels:"); + if (channel_start == NULL) { + ALOGE("%s: error could not find Channels information", __func__); + ret = -EINVAL; + goto done; + } + + channel_start = strstr(channel_start, " "); + if (channel_start == NULL) { + ALOGE("%s: error channel section not found in usb config file", + __func__); + ret = -EINVAL; + goto done; + } + + channels_playback = atoi(channel_start); + if (channels_playback == 1) { + *channels = 1; + } else { + *channels = 2; + } + + ALOGD("%s: channels supported by device: %d", __func__, *channels); + rates_str_start = strstr(str_start, "Rates:"); + if (rates_str_start == NULL) { + ALOGE("%s: error cant find rates information", __func__); + ret = -EINVAL; + goto done; + } + + rates_str_start = strstr(rates_str_start, " "); + if (rates_str_start == NULL) { + ALOGE("%s: error channel section not found in usb config file", + __func__); + ret = -EINVAL; + goto done; + } + + char *target = strchr(rates_str_start, '\n'); + if (target == NULL) { + ALOGE("%s: error end of line not found", __func__); + ret = -EINVAL; + goto done; + } + + size = target - rates_str_start; + if ((rates_str = (char *)malloc(size + 1)) == NULL) { + ALOGE("%s: error unable to allocate memory to hold sample rate strings", + __func__); + ret = -EINVAL; + goto done; + } + + if ((rates_str_for_val = (char *)malloc(size + 1)) == NULL) { + ALOGE("%s: error unable to allocate memory to hold sample rate string", + __func__); + ret = -EINVAL; + goto done; + } + + memcpy(rates_str, rates_str_start, size); + memcpy(rates_str_for_val, rates_str_start, size); + rates_str[size] = '\0'; + rates_str_for_val[size] = '\0'; + + size = usb_get_numof_rates(rates_str); + if (!size) { + ALOGE("%s: error could not get rate size, returning", __func__); + ret = -EINVAL; + goto done; + } + + rates_supported = (int *)malloc(sizeof(int) * size); + + if (!rates_supported) { + ALOGE("couldn't allocate mem for rates_supported"); + ret = -EINVAL; + goto done; + } + + next_sr_string = strtok_r(rates_str_for_val, " ,", &temp_ptr); + if (next_sr_string == NULL) { + ALOGE("%s: error could not get first rate val", __func__); + ret = -EINVAL; + goto done; + } + + rates_supported[0] = atoi(next_sr_string); + ALOGD("%s: rates_supported[0] for playback: %d", + __func__, rates_supported[0]); + for (i = 1; i *sample_rate) && + (rates_supported[i] <= 48000)) { + /* Sample Rate should be one of the proxy supported rates only + This is because proxy port is used to read from/write to DSP */ + if ((rates_supported[i] == USB_PROXY_RATE_8000) || + (rates_supported[i] == USB_PROXY_RATE_16000) || + (rates_supported[i] == USB_PROXY_RATE_48000)) { + *sample_rate = rates_supported[i]; + } + } + } + ALOGD("%s: sample_rate: %d", __func__, *sample_rate); + +done: + if (fd >= 0) close(fd); + if (rates_str_for_val) free(rates_str_for_val); + if (rates_str) free(rates_str); + if (rates_supported) free(rates_supported); + if (read_buf) free(read_buf); + return ret; +} + +static int32_t usb_playback_entry(void *adev) +{ + unsigned char usbbuf[USB_PROXY_PERIOD_SIZE] = {0}; + int32_t ret, bytes, proxy_open_retry_count; + + ALOGD("%s: entry", __func__); + /* update audio device pointer */ + usbmod->adev = (struct audio_device*)adev; + proxy_open_retry_count = USB_PROXY_OPEN_RETRY_COUNT; + + /* get capabilities */ + pthread_mutex_lock(&usbmod->usb_playback_lock); + ret = usb_get_capability((char *)"Playback:", + &usbmod->channels_playback, &usbmod->sample_rate_playback); + if (ret) { + ALOGE("%s: could not get playback capabilities from usb device", + __func__); + pthread_mutex_unlock(&usbmod->usb_playback_lock); + return -EINVAL; + } + /* update config for usb + 1 pcm frame(sample)= 4 bytes since two channels*/ + pcm_config_usbmod.period_size = USB_PERIOD_SIZE/4; + pcm_config_usbmod.channels = usbmod->channels_playback; + pcm_config_usbmod.rate = usbmod->sample_rate_playback; + ALOGV("%s: usb device %u:period %u:channels %u:sample", __func__, + pcm_config_usbmod.period_size, pcm_config_usbmod.channels, + pcm_config_usbmod.rate); + + usbmod->usb_pcm_playback_handle = pcm_open(usbmod->usb_card, \ + usbmod->usb_device_id, PCM_OUT | + PCM_MMAP | PCM_NOIRQ , &pcm_config_usbmod); + + if ((usbmod->usb_pcm_playback_handle \ + && !pcm_is_ready(usbmod->usb_pcm_playback_handle)) + || (!usbmod->is_playback_running)) { + ALOGE("%s: failed: %s", __func__, + pcm_get_error(usbmod->usb_pcm_playback_handle)); + pcm_close(usbmod->usb_pcm_playback_handle); + usbmod->usb_pcm_playback_handle = NULL; + pthread_mutex_unlock(&usbmod->usb_playback_lock); + return -ENOMEM; + } + ALOGD("%s: USB configured for playback", __func__); + + /* update config for proxy*/ + pcm_config_usbmod.period_size = USB_PROXY_PERIOD_SIZE/3; + pcm_config_usbmod.rate = usbmod->sample_rate_playback; + pcm_config_usbmod.channels = usbmod->channels_playback; + pcm_config_usbmod.period_count = AFE_PROXY_PERIOD_COUNT; + usbmod->proxy_device_id = AFE_PROXY_PLAYBACK_DEVICE; + ALOGD("%s: proxy device %u:period %u:channels %u:sample", __func__, + pcm_config_usbmod.period_size, pcm_config_usbmod.channels, + pcm_config_usbmod.rate); + + while(proxy_open_retry_count){ + usbmod->proxy_pcm_playback_handle = pcm_open(usbmod->proxy_card, + usbmod->proxy_device_id, PCM_IN | + PCM_MMAP | PCM_NOIRQ, &pcm_config_usbmod); + if(usbmod->proxy_pcm_playback_handle + && !pcm_is_ready(usbmod->proxy_pcm_playback_handle)){ + pcm_close(usbmod->proxy_pcm_playback_handle); + proxy_open_retry_count--; + usleep(USB_PROXY_OPEN_WAIT_TIME * 1000); + ALOGE("%s: pcm_open for proxy failed retrying = %d", + __func__, proxy_open_retry_count); + } + else{ + break; + } + } + + if ((usbmod->proxy_pcm_playback_handle + && !pcm_is_ready(usbmod->proxy_pcm_playback_handle)) + || (!usbmod->is_playback_running)) { + ALOGE("%s: failed: %s", __func__, + pcm_get_error(usbmod->proxy_pcm_playback_handle)); + pcm_close(usbmod->proxy_pcm_playback_handle); + usbmod->proxy_pcm_playback_handle = NULL; + pthread_mutex_unlock(&usbmod->usb_playback_lock); + return -ENOMEM; + } + ALOGD("%s: PROXY configured for playback", __func__); + pthread_mutex_unlock(&usbmod->usb_playback_lock); + + ALOGD("Init USB volume"); + initPlaybackVolume(); + /* main loop to read from proxy and write to usb */ + while (usbmod->is_playback_running) { + /* read data from proxy */ + ret = pcm_mmap_read(usbmod->proxy_pcm_playback_handle, + (void *)usbbuf, USB_PROXY_PERIOD_SIZE); + /* Write to usb */ + ret = pcm_mmap_write(usbmod->usb_pcm_playback_handle, + (void *)usbbuf, USB_PROXY_PERIOD_SIZE); + if(!usbmod->is_playback_running) + break; + + memset(usbbuf, 0, USB_PROXY_PERIOD_SIZE); + } /* main loop end */ + + ALOGD("%s: exiting USB playback thread",__func__); + return 0; +} + +static void* usb_playback_launcher(void *adev) +{ + int32_t ret; + + usbmod->is_playback_running = true; + ret = usb_playback_entry(adev); + + if (ret) { + ALOGE("%s: failed with err:%d", __func__, ret); + usbmod->is_playback_running = false; + } + return NULL; +} + +static int32_t usb_record_entry(void *adev) +{ + unsigned char usbbuf[USB_PROXY_PERIOD_SIZE] = {0}; + int32_t ret, bytes, proxy_open_retry_count; + ALOGD("%s: entry", __func__); + + /* update audio device pointer */ + usbmod->adev = (struct audio_device*)adev; + proxy_open_retry_count = USB_PROXY_OPEN_RETRY_COUNT; + + /* get capabilities */ + pthread_mutex_lock(&usbmod->usb_record_lock); + ret = usb_get_capability((char *)"Capture:", + &usbmod->channels_record, &usbmod->sample_rate_record); + if (ret) { + ALOGE("%s: could not get capture capabilities from usb device", + __func__); + pthread_mutex_unlock(&usbmod->usb_record_lock); + return -EINVAL; + } + /* update config for usb + 1 pcm frame(sample)= 4 bytes since two channels*/ + pcm_config_usbmod.period_size = USB_PERIOD_SIZE/4; + pcm_config_usbmod.channels = usbmod->channels_record; + pcm_config_usbmod.rate = usbmod->sample_rate_record; + ALOGV("%s: usb device %u:period %u:channels %u:sample", __func__, + pcm_config_usbmod.period_size, pcm_config_usbmod.channels, + pcm_config_usbmod.rate); + + usbmod->usb_pcm_record_handle = pcm_open(usbmod->usb_card, \ + usbmod->usb_device_id, PCM_IN | + PCM_MMAP | PCM_NOIRQ , &pcm_config_usbmod); + + if ((usbmod->usb_pcm_record_handle \ + && !pcm_is_ready(usbmod->usb_pcm_record_handle)) + || (!usbmod->is_record_running)) { + ALOGE("%s: failed: %s", __func__, + pcm_get_error(usbmod->usb_pcm_record_handle)); + pcm_close(usbmod->usb_pcm_record_handle); + usbmod->usb_pcm_record_handle = NULL; + pthread_mutex_unlock(&usbmod->usb_record_lock); + return -ENOMEM; + } + ALOGD("%s: USB configured for capture", __func__); + + /* update config for proxy*/ + pcm_config_usbmod.period_size = USB_PROXY_PERIOD_SIZE/4; + pcm_config_usbmod.rate = usbmod->sample_rate_record; + pcm_config_usbmod.channels = usbmod->channels_record; + pcm_config_usbmod.period_count = AFE_PROXY_PERIOD_COUNT * 2; + usbmod->proxy_device_id = AFE_PROXY_CAPTURE_DEVICE; + ALOGV("%s: proxy device %u:period %u:channels %u:sample", __func__, + pcm_config_usbmod.period_size, pcm_config_usbmod.channels, + pcm_config_usbmod.rate); + + while(proxy_open_retry_count){ + usbmod->proxy_pcm_record_handle = pcm_open(usbmod->proxy_card, + usbmod->proxy_device_id, PCM_OUT | + PCM_MMAP | PCM_NOIRQ, &pcm_config_usbmod); + if(usbmod->proxy_pcm_record_handle + && !pcm_is_ready(usbmod->proxy_pcm_record_handle)){ + pcm_close(usbmod->proxy_pcm_record_handle); + proxy_open_retry_count--; + usleep(USB_PROXY_OPEN_WAIT_TIME * 1000); + ALOGE("%s: pcm_open for proxy(recording) failed retrying = %d", + __func__, proxy_open_retry_count); + } + else{ + break; + } + } + if ((usbmod->proxy_pcm_record_handle + && !pcm_is_ready(usbmod->proxy_pcm_record_handle)) + || (!usbmod->is_record_running)) { + ALOGE("%s: failed: %s", __func__, + pcm_get_error(usbmod->proxy_pcm_record_handle)); + pcm_close(usbmod->proxy_pcm_record_handle); + usbmod->proxy_pcm_record_handle = NULL; + pthread_mutex_unlock(&usbmod->usb_record_lock); + return -ENOMEM; + } + ALOGD("%s: PROXY configured for capture", __func__); + pthread_mutex_unlock(&usbmod->usb_record_lock); + + /* main loop to read from usb and write to proxy */ + while (usbmod->is_record_running) { + /* read data from usb */ + ret = pcm_mmap_read(usbmod->usb_pcm_record_handle, + (void *)usbbuf, USB_PROXY_PERIOD_SIZE); + /* Write to proxy */ + ret = pcm_mmap_write(usbmod->proxy_pcm_record_handle, + (void *)usbbuf, USB_PROXY_PERIOD_SIZE); + if(!usbmod->is_record_running) + break; + + memset(usbbuf, 0, USB_PROXY_PERIOD_SIZE); + } /* main loop end */ + + ALOGD("%s: exiting USB capture thread",__func__); + return 0; +} + +static void* usb_capture_launcher(void *adev) +{ + int32_t ret; + + usbmod->is_record_running = true; + ret = usb_record_entry(adev); + + if (ret) { + ALOGE("%s: failed with err:%d", __func__, ret); + usbmod->is_record_running = false; + } + return NULL; +} + +void audio_extn_usb_init(void *adev) +{ + pthread_once(&alloc_usbmod_once_ctl, usb_alloc); + + usbmod->is_playback_running = false; + usbmod->is_record_running = false; + + usbmod->usb_pcm_playback_handle = NULL; + usbmod->proxy_pcm_playback_handle = NULL; + + usbmod->usb_pcm_record_handle = NULL; + usbmod->proxy_pcm_record_handle = NULL; + + usbmod->usb_card = 1; + usbmod->usb_device_id = 0; + usbmod->proxy_card = 0; + usbmod->proxy_device_id = AFE_PROXY_PLAYBACK_DEVICE; + usbmod->adev = (struct audio_device*)adev; + + pthread_mutex_init(&usbmod->usb_playback_lock, + (const pthread_mutexattr_t *) NULL); + pthread_mutex_init(&usbmod->usb_record_lock, + (const pthread_mutexattr_t *) NULL); +} + +void audio_extn_usb_deinit() +{ + if (NULL != usbmod){ + free(usbmod); + usbmod = NULL; + } +} + +void audio_extn_usb_set_proxy_sound_card(uint32_t sndcard_idx) +{ + /* Proxy port and USB headset are related to two different sound cards */ + if (sndcard_idx == usbmod->usb_card) { + usbmod->usb_card = usbmod->proxy_card; + } + + usbmod->proxy_card = sndcard_idx; +} + +void audio_extn_usb_start_playback(void *adev) +{ + int32_t ret; + + if (NULL == usbmod){ + ALOGE("%s: USB device object is NULL", __func__); + return; + } + + if (usbmod->is_playback_running){ + ALOGE("%s: USB playback thread already running", __func__); + return; + } + + ALOGD("%s: creating USB playback thread", __func__); + ret = pthread_create(&usbmod->usb_playback_thr, NULL, + usb_playback_launcher, (void*)adev); + if (ret) + ALOGE("%s: failed to create USB playback thread with err:%d", + __func__, ret); +} + +void audio_extn_usb_stop_playback() +{ + int32_t ret; + ALOGD("%s: entry", __func__); + + usbmod->is_playback_running = false; + if (NULL != usbmod->proxy_pcm_playback_handle) + pcm_stop(usbmod->proxy_pcm_playback_handle); + + if (NULL != usbmod->usb_pcm_playback_handle) + pcm_stop(usbmod->usb_pcm_playback_handle); + + if(usbmod->usb_playback_thr) { + ret = pthread_join(usbmod->usb_playback_thr,NULL); + ALOGE("%s: return for pthread_join = %d", __func__, ret); + usbmod->usb_playback_thr = (pthread_t)NULL; + } + + pthread_mutex_lock(&usbmod->usb_playback_lock); + if (NULL != usbmod->usb_pcm_playback_handle){ + pcm_close(usbmod->usb_pcm_playback_handle); + usbmod->usb_pcm_playback_handle = NULL; + } + + if (NULL != usbmod->proxy_pcm_playback_handle){ + pcm_close(usbmod->proxy_pcm_playback_handle); + usbmod->proxy_pcm_playback_handle = NULL; + } + pthread_mutex_unlock(&usbmod->usb_playback_lock); + + ALOGD("%s: exiting",__func__); +} + +void audio_extn_usb_start_capture(void *adev) +{ + int32_t ret; + + if (NULL == usbmod){ + ALOGE("%s: USB device object is NULL", __func__); + return; + } + + if (usbmod->is_record_running){ + ALOGE("%s: USB capture thread already running", __func__); + return; + } + + ALOGD("%s: creating USB capture thread", __func__); + ret = pthread_create(&usbmod->usb_record_thr, NULL, + usb_capture_launcher, (void*)adev); + if (ret) + ALOGE("%s: failed to create USB capture thread with err:%d", + __func__, ret); +} + +void audio_extn_usb_stop_capture() +{ + int32_t ret; + ALOGD("%s: entry", __func__); + + usbmod->is_record_running = false; + if (NULL != usbmod->proxy_pcm_record_handle) + pcm_stop(usbmod->proxy_pcm_record_handle); + + if (NULL != usbmod->usb_pcm_record_handle) + pcm_stop(usbmod->usb_pcm_record_handle); + + if(usbmod->usb_record_thr) { + ret = pthread_join(usbmod->usb_record_thr,NULL); + ALOGE("%s: return for pthread_join = %d", __func__, ret); + usbmod->usb_record_thr = (pthread_t)NULL; + } + + pthread_mutex_lock(&usbmod->usb_record_lock); + if (NULL != usbmod->usb_pcm_record_handle){ + pcm_close(usbmod->usb_pcm_record_handle); + usbmod->usb_pcm_record_handle = NULL; + } + + if (NULL != usbmod->proxy_pcm_record_handle){ + pcm_close(usbmod->proxy_pcm_record_handle); + usbmod->proxy_pcm_record_handle = NULL; + } + pthread_mutex_unlock(&usbmod->usb_record_lock); + + ALOGD("%s: exiting",__func__); +} + +bool audio_extn_usb_is_proxy_inuse() +{ + if( usbmod->is_record_running || usbmod->is_playback_running) + return true; + else + return false; +} +#endif /*USB_HEADSET_ENABLED end*/ diff --git a/audio/hal/audio_extn/utils.c b/audio/hal/audio_extn/utils.c new file mode 100644 index 0000000..273a194 --- /dev/null +++ b/audio/hal/audio_extn/utils.c @@ -0,0 +1,569 @@ +/* + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2014 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_hw_utils" +/* #define LOG_NDEBUG 0 */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "audio_hw.h" +#include "platform.h" +#include "platform_api.h" +#include "audio_extn.h" + +#define AUDIO_OUTPUT_POLICY_VENDOR_CONFIG_FILE "/vendor/etc/audio_output_policy.conf" + +#define OUTPUTS_TAG "outputs" + +#define DYNAMIC_VALUE_TAG "dynamic" +#define FLAGS_TAG "flags" +#define FORMATS_TAG "formats" +#define SAMPLING_RATES_TAG "sampling_rates" +#define BIT_WIDTH_TAG "bit_width" +#define APP_TYPE_TAG "app_type" + +#define STRING_TO_ENUM(string) { #string, string } +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +struct string_to_enum { + const char *name; + uint32_t value; +}; + +const struct string_to_enum s_flag_name_to_enum_table[] = { + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_DIRECT), + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_PRIMARY), + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_FAST), + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_DEEP_BUFFER), + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD), + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_NON_BLOCKING), +#ifdef INCALL_MUSIC_ENABLED + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_INCALL_MUSIC), +#endif +#ifdef COMPRESS_VOIP_ENABLED + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_VOIP_RX), +#endif +}; + +const struct string_to_enum s_format_name_to_enum_table[] = { + STRING_TO_ENUM(AUDIO_FORMAT_PCM_16_BIT), + STRING_TO_ENUM(AUDIO_FORMAT_PCM_8_BIT), + STRING_TO_ENUM(AUDIO_FORMAT_MP3), + STRING_TO_ENUM(AUDIO_FORMAT_AAC), + STRING_TO_ENUM(AUDIO_FORMAT_VORBIS), + STRING_TO_ENUM(AUDIO_FORMAT_AMR_NB), + STRING_TO_ENUM(AUDIO_FORMAT_AMR_WB), + STRING_TO_ENUM(AUDIO_FORMAT_AC3), + STRING_TO_ENUM(AUDIO_FORMAT_E_AC3), +#ifdef FORMATS_ENABLED + STRING_TO_ENUM(AUDIO_FORMAT_DTS), + STRING_TO_ENUM(AUDIO_FORMAT_DTS_LBR), + STRING_TO_ENUM(AUDIO_FORMAT_WMA), + STRING_TO_ENUM(AUDIO_FORMAT_WMA_PRO), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_ADIF), + STRING_TO_ENUM(AUDIO_FORMAT_AMR_WB_PLUS), + STRING_TO_ENUM(AUDIO_FORMAT_EVRC), + STRING_TO_ENUM(AUDIO_FORMAT_EVRCB), + STRING_TO_ENUM(AUDIO_FORMAT_EVRCWB), + STRING_TO_ENUM(AUDIO_FORMAT_QCELP), + STRING_TO_ENUM(AUDIO_FORMAT_MP2), + STRING_TO_ENUM(AUDIO_FORMAT_EVRCNW), + STRING_TO_ENUM(AUDIO_FORMAT_PCM_16_BIT_OFFLOAD), + STRING_TO_ENUM(AUDIO_FORMAT_PCM_24_BIT_OFFLOAD), + STRING_TO_ENUM(AUDIO_FORMAT_FLAC), +#endif +}; + +static uint32_t string_to_enum(const struct string_to_enum *table, size_t size, + const char *name) +{ + size_t i; + for (i = 0; i < size; i++) { + if (strcmp(table[i].name, name) == 0) { + ALOGV("%s found %s", __func__, table[i].name); + return table[i].value; + } + } + return 0; +} + +static audio_output_flags_t parse_flag_names(char *name) +{ + uint32_t flag = 0; + char *flag_name = strtok(name, "|"); + while (flag_name != NULL) { + if (strlen(flag_name) != 0) { + flag |= string_to_enum(s_flag_name_to_enum_table, + ARRAY_SIZE(s_flag_name_to_enum_table), + flag_name); + } + flag_name = strtok(NULL, "|"); + } + + ALOGV("parse_flag_names: flag - %d", flag); + return (audio_output_flags_t)flag; +} + +static void parse_format_names(char *name, struct streams_output_cfg *so_info) +{ + struct stream_format *sf_info = NULL; + char *str = strtok(name, "|"); + + if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) + return; + + list_init(&so_info->format_list); + while (str != NULL) { + audio_format_t format = (audio_format_t)string_to_enum(s_format_name_to_enum_table, + ARRAY_SIZE(s_format_name_to_enum_table), str); + ALOGV("%s: format - %d", __func__, format); + if (format != 0) { + sf_info = (struct stream_format *)calloc(1, sizeof(struct stream_format)); + if (sf_info == NULL) + break; /* return whatever was parsed */ + + sf_info->format = format; + list_add_tail(&so_info->format_list, &sf_info->list); + } + str = strtok(NULL, "|"); + } +} + +static void parse_sample_rate_names(char *name, struct streams_output_cfg *so_info) +{ + struct stream_sample_rate *ss_info = NULL; + uint32_t sample_rate = 48000; + char *str = strtok(name, "|"); + + if (str != NULL && 0 == strcmp(str, DYNAMIC_VALUE_TAG)) + return; + + list_init(&so_info->sample_rate_list); + while (str != NULL) { + sample_rate = (uint32_t)strtol(str, (char **)NULL, 10); + ALOGV("%s: sample_rate - %d", __func__, sample_rate); + if (0 != sample_rate) { + ss_info = (struct stream_sample_rate *)calloc(1, sizeof(struct stream_sample_rate)); + if (ss_info == NULL) + break; /* return whatever was parsed */ + + ss_info->sample_rate = sample_rate; + list_add_tail(&so_info->sample_rate_list, &ss_info->list); + } + str = strtok(NULL, "|"); + } +} + +static int parse_bit_width_names(char *name) +{ + int bit_width = 16; + char *str = strtok(name, "|"); + + if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG)) + bit_width = (int)strtol(str, (char **)NULL, 10); + + ALOGV("%s: bit_width - %d", __func__, bit_width); + return bit_width; +} + +static int parse_app_type_names(void *platform, char *name) +{ + int app_type = platform_get_default_app_type(platform); + char *str = strtok(name, "|"); + + if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG)) + app_type = (int)strtol(str, (char **)NULL, 10); + + ALOGV("%s: app_type - %d", __func__, app_type); + return app_type; +} + +static void update_streams_output_cfg_list(cnode *root, void *platform, + struct listnode *streams_output_cfg_list) +{ + cnode *node = root->first_child; + struct streams_output_cfg *so_info; + + ALOGV("%s", __func__); + so_info = (struct streams_output_cfg *)calloc(1, sizeof(struct streams_output_cfg)); + + if (!so_info) { + ALOGE("failed to allocate mem for so_info list element"); + return; + } + + while (node) { + if (strcmp(node->name, FLAGS_TAG) == 0) { + so_info->flags = parse_flag_names((char *)node->value); + } else if (strcmp(node->name, FORMATS_TAG) == 0) { + parse_format_names((char *)node->value, so_info); + } else if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) { + so_info->app_type_cfg.sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE; + parse_sample_rate_names((char *)node->value, so_info); + } else if (strcmp(node->name, BIT_WIDTH_TAG) == 0) { + so_info->app_type_cfg.bit_width = parse_bit_width_names((char *)node->value); + } else if (strcmp(node->name, APP_TYPE_TAG) == 0) { + so_info->app_type_cfg.app_type = parse_app_type_names(platform, (char *)node->value); + } + node = node->next; + } + list_add_tail(streams_output_cfg_list, &so_info->list); +} + +static void load_output(cnode *root, void *platform, + struct listnode *streams_output_cfg_list) +{ + cnode *node = config_find(root, OUTPUTS_TAG); + if (node == NULL) { + ALOGE("%s: could not load output, node is NULL", __func__); + return; + } + + node = node->first_child; + while (node) { + ALOGV("%s: loading output %s", __func__, node->name); + update_streams_output_cfg_list(node, platform, streams_output_cfg_list); + node = node->next; + } +} + +static void send_app_type_cfg(void *platform, struct mixer *mixer, + struct listnode *streams_output_cfg_list) +{ + int app_type_cfg[MAX_LENGTH_MIXER_CONTROL_IN_INT] = {-1}; + int length = 0, i, num_app_types = 0; + struct listnode *node; + bool update; + struct mixer_ctl *ctl = NULL; + const char *mixer_ctl_name = "App Type Config"; + struct streams_output_cfg *so_info; + + if (!mixer) { + ALOGE("%s: mixer is null",__func__); + return; + } + ctl = mixer_get_ctl_by_name(mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s",__func__, mixer_ctl_name); + return; + } + if (streams_output_cfg_list == NULL) { + app_type_cfg[length++] = 1; + app_type_cfg[length++] = platform_get_default_app_type(platform); + app_type_cfg[length++] = 48000; + app_type_cfg[length++] = 16; + mixer_ctl_set_array(ctl, app_type_cfg, length); + return; + } + + app_type_cfg[length++] = num_app_types; + list_for_each(node, streams_output_cfg_list) { + so_info = node_to_item(node, struct streams_output_cfg, list); + update = true; + for (i=0; iapp_type_cfg.app_type) { + update = false; + break; + } + } + if (update && ((length + 3) <= MAX_LENGTH_MIXER_CONTROL_IN_INT)) { + num_app_types += 1 ; + app_type_cfg[length++] = so_info->app_type_cfg.app_type; + app_type_cfg[length++] = so_info->app_type_cfg.sample_rate; + app_type_cfg[length++] = so_info->app_type_cfg.bit_width; + } + } + ALOGV("%s: num_app_types: %d", __func__, num_app_types); + if (num_app_types) { + app_type_cfg[0] = num_app_types; + mixer_ctl_set_array(ctl, app_type_cfg, length); + } +} + +void audio_extn_utils_update_streams_output_cfg_list(void *platform, + struct mixer *mixer, + struct listnode *streams_output_cfg_list) +{ + cnode *root; + char *data; + + ALOGV("%s", __func__); + list_init(streams_output_cfg_list); + data = (char *)load_file(AUDIO_OUTPUT_POLICY_VENDOR_CONFIG_FILE, NULL); + if (data == NULL) { + send_app_type_cfg(platform, mixer, NULL); + ALOGE("%s: could not load output policy config file", __func__); + return; + } + + root = config_node("", ""); + if (root == NULL) { + ALOGE("cfg_list, NULL config root"); + return; + } + + config_load(root, data); + load_output(root, platform, streams_output_cfg_list); + + send_app_type_cfg(platform, mixer, streams_output_cfg_list); +} + +void audio_extn_utils_dump_streams_output_cfg_list( + struct listnode *streams_output_cfg_list) +{ + int i=0; + struct listnode *node_i, *node_j; + struct streams_output_cfg *so_info; + struct stream_format *sf_info; + struct stream_sample_rate *ss_info; + ALOGV("%s", __func__); + list_for_each(node_i, streams_output_cfg_list) { + so_info = node_to_item(node_i, struct streams_output_cfg, list); + ALOGV("%s: flags-%d, output_sample_rate-%d, output_bit_width-%d, app_type-%d", + __func__, so_info->flags, so_info->app_type_cfg.sample_rate, + so_info->app_type_cfg.bit_width, so_info->app_type_cfg.app_type); + list_for_each(node_j, &so_info->format_list) { + sf_info = node_to_item(node_j, struct stream_format, list); + ALOGV("format-%x", sf_info->format); + } + list_for_each(node_j, &so_info->sample_rate_list) { + ss_info = node_to_item(node_j, struct stream_sample_rate, list); + ALOGV("sample rate-%d", ss_info->sample_rate); + } + } +} + +void audio_extn_utils_release_streams_output_cfg_list( + struct listnode *streams_output_cfg_list) +{ + struct listnode *node_i, *node_j; + struct streams_output_cfg *so_info; + struct stream_format *sf_info; + + ALOGV("%s", __func__); + while (!list_empty(streams_output_cfg_list)) { + node_i = list_head(streams_output_cfg_list); + so_info = node_to_item(node_i, struct streams_output_cfg, list); + while (!list_empty(&so_info->format_list)) { + node_j = list_head(&so_info->format_list); + list_remove(node_j); + free(node_to_item(node_j, struct stream_format, list)); + } + while (!list_empty(&so_info->sample_rate_list)) { + node_j = list_head(&so_info->sample_rate_list); + list_remove(node_j); + free(node_to_item(node_j, struct stream_sample_rate, list)); + } + list_remove(node_i); + free(node_to_item(node_i, struct streams_output_cfg, list)); + } +} + +static bool set_output_cfg(struct streams_output_cfg *so_info, + struct stream_app_type_cfg *app_type_cfg, + uint32_t sample_rate, uint32_t bit_width) + { + struct listnode *node_i; + struct stream_sample_rate *ss_info; + list_for_each(node_i, &so_info->sample_rate_list) { + ss_info = node_to_item(node_i, struct stream_sample_rate, list); + if ((sample_rate <= ss_info->sample_rate) && + (bit_width == so_info->app_type_cfg.bit_width)) { + app_type_cfg->app_type = so_info->app_type_cfg.app_type; + app_type_cfg->sample_rate = ss_info->sample_rate; + app_type_cfg->bit_width = so_info->app_type_cfg.bit_width; + ALOGV("%s app_type_cfg->app_type %d, app_type_cfg->sample_rate %d, app_type_cfg->bit_width %d", + __func__, app_type_cfg->app_type, app_type_cfg->sample_rate, app_type_cfg->bit_width); + return true; + } + } + /* + * Reiterate through the list assuming dafault sample rate. + * Handles scenario where input sample rate is higher + * than all sample rates in list for the input bit width. + */ + sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE; + list_for_each(node_i, &so_info->sample_rate_list) { + ss_info = node_to_item(node_i, struct stream_sample_rate, list); + if ((sample_rate <= ss_info->sample_rate) && + (bit_width == so_info->app_type_cfg.bit_width)) { + app_type_cfg->app_type = so_info->app_type_cfg.app_type; + app_type_cfg->sample_rate = sample_rate; + app_type_cfg->bit_width = so_info->app_type_cfg.bit_width; + ALOGV("%s Assuming default sample rate. app_type_cfg->app_type %d, app_type_cfg->sample_rate %d, app_type_cfg->bit_width %d", + __func__, app_type_cfg->app_type, app_type_cfg->sample_rate, app_type_cfg->bit_width); + return true; + } + } + return false; +} + +void audio_extn_utils_update_stream_app_type_cfg(void *platform, + struct listnode *streams_output_cfg_list, + audio_devices_t devices, + audio_output_flags_t flags, + audio_format_t format, + uint32_t sample_rate, + uint32_t bit_width, + struct stream_app_type_cfg *app_type_cfg) +{ + struct listnode *node_i, *node_j, *node_k; + struct streams_output_cfg *so_info; + struct stream_format *sf_info; + struct stream_sample_rate *ss_info; + + if ((24 == bit_width) && + (devices & AUDIO_DEVICE_OUT_SPEAKER)) { + sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE; + ALOGI("%s Allowing 24-bit playback on speaker ONLY at default sampling rate", __func__); + } + + ALOGV("%s: flags: %x, format: %x sample_rate %d", + __func__, flags, format, sample_rate); + list_for_each(node_i, streams_output_cfg_list) { + so_info = node_to_item(node_i, struct streams_output_cfg, list); + if (so_info->flags == flags) { + list_for_each(node_j, &so_info->format_list) { + sf_info = node_to_item(node_j, struct stream_format, list); + if (sf_info->format == format) { + if (set_output_cfg(so_info, app_type_cfg, sample_rate, bit_width)) + return; + } + } + } + } + list_for_each(node_i, streams_output_cfg_list) { + so_info = node_to_item(node_i, struct streams_output_cfg, list); + if (so_info->flags == AUDIO_OUTPUT_FLAG_PRIMARY) { + ALOGV("Compatible output profile not found."); + app_type_cfg->app_type = so_info->app_type_cfg.app_type; + app_type_cfg->sample_rate = so_info->app_type_cfg.sample_rate; + app_type_cfg->bit_width = so_info->app_type_cfg.bit_width; + ALOGV("%s Default to primary output: App type: %d sample_rate %d", + __func__, so_info->app_type_cfg.app_type, app_type_cfg->sample_rate); + return; + } + } + ALOGW("%s: App type could not be selected. Falling back to default", __func__); + app_type_cfg->app_type = platform_get_default_app_type(platform); + app_type_cfg->sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE; + app_type_cfg->bit_width = 16; +} + +int audio_extn_utils_send_app_type_cfg(struct audio_usecase *usecase) +{ + char mixer_ctl_name[MAX_LENGTH_MIXER_CONTROL_IN_INT]; + int app_type_cfg[MAX_LENGTH_MIXER_CONTROL_IN_INT], len = 0, rc; + struct stream_out *out; + struct audio_device *adev; + struct mixer_ctl *ctl; + int pcm_device_id, acdb_dev_id, snd_device = usecase->out_snd_device; + int32_t sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE; + + ALOGV("%s", __func__); + + if (usecase->type != PCM_PLAYBACK) { + ALOGV("%s: not a playback path, no need to cfg app type", __func__); + rc = 0; + goto exit_send_app_type_cfg; + } + if ((usecase->id != USECASE_AUDIO_PLAYBACK_DEEP_BUFFER) && + (usecase->id != USECASE_AUDIO_PLAYBACK_LOW_LATENCY) && + (usecase->id != USECASE_AUDIO_PLAYBACK_MULTI_CH) && + (usecase->id != USECASE_AUDIO_PLAYBACK_OFFLOAD)) { + ALOGV("%s: a playback path where app type cfg is not required", __func__); + rc = 0; + goto exit_send_app_type_cfg; + } + out = usecase->stream.out; + adev = out->dev; + + snd_device = usecase->out_snd_device; + + pcm_device_id = platform_get_pcm_device_id(out->usecase, PCM_PLAYBACK); + + snprintf(mixer_ctl_name, sizeof(mixer_ctl_name), + "Audio Stream %d App Type Cfg", pcm_device_id); + + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", __func__, + mixer_ctl_name); + rc = -EINVAL; + goto exit_send_app_type_cfg; + } + snd_device = (snd_device == SND_DEVICE_OUT_SPEAKER) ? + audio_extn_get_spkr_prot_snd_device(snd_device) : snd_device; + acdb_dev_id = platform_get_snd_device_acdb_id(snd_device); + if (acdb_dev_id < 0) { + ALOGE("%s: Couldn't get the acdb dev id", __func__); + rc = -EINVAL; + goto exit_send_app_type_cfg; + } + + if ((24 == usecase->stream.out->bit_width) && + (usecase->stream.out->devices & AUDIO_DEVICE_OUT_SPEAKER)) { + sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE; + } else { + sample_rate = out->app_type_cfg.sample_rate; + } + + app_type_cfg[len++] = out->app_type_cfg.app_type; + app_type_cfg[len++] = acdb_dev_id; + app_type_cfg[len++] = sample_rate; + + mixer_ctl_set_array(ctl, app_type_cfg, len); + ALOGI("%s app_type %d, acdb_dev_id %d, sample_rate %d", + __func__, out->app_type_cfg.app_type, acdb_dev_id, sample_rate); + rc = 0; +exit_send_app_type_cfg: + return rc; +} + +void audio_extn_utils_send_audio_calibration(struct audio_device *adev, + struct audio_usecase *usecase) +{ + int type = usecase->type; + + if (type == PCM_PLAYBACK) { + struct stream_out *out = usecase->stream.out; + int snd_device = usecase->out_snd_device; + snd_device = (snd_device == SND_DEVICE_OUT_SPEAKER) ? + audio_extn_get_spkr_prot_snd_device(snd_device) : snd_device; + platform_send_audio_calibration(adev->platform, usecase, + out->app_type_cfg.app_type, + out->app_type_cfg.sample_rate); + } + if ((type == PCM_HFP_CALL) || (type == PCM_CAPTURE)) { + /* when app type is default. the sample rate is not used to send cal */ + platform_send_audio_calibration(adev->platform, usecase, + platform_get_default_app_type(adev->platform), + 48000); + } +} + diff --git a/audio/hal/audio_hw.c b/audio/hal/audio_hw.c new file mode 100644 index 0000000..70fbbf1 --- /dev/null +++ b/audio/hal/audio_hw.c @@ -0,0 +1,3499 @@ +/* + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2013 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_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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "audio_hw.h" +#include "platform_api.h" +#include +#include "audio_extn.h" +#include "voice_extn.h" + +#include "sound/compress_params.h" +#include "sound/asound.h" + +#define COMPRESS_OFFLOAD_NUM_FRAGMENTS 4 +/* ToDo: Check and update a proper value in msec */ +#define COMPRESS_OFFLOAD_PLAYBACK_LATENCY 96 +#define COMPRESS_PLAYBACK_VOLUME_MAX 0x2000 + +#define PROXY_OPEN_RETRY_COUNT 100 +#define PROXY_OPEN_WAIT_TIME 20 + +#define USECASE_AUDIO_PLAYBACK_PRIMARY USECASE_AUDIO_PLAYBACK_DEEP_BUFFER + +static unsigned int configured_low_latency_capture_period_size = + LOW_LATENCY_CAPTURE_PERIOD_SIZE; + +struct pcm_config pcm_config_deep_buffer = { + .channels = 2, + .rate = DEFAULT_OUTPUT_SAMPLING_RATE, + .period_size = DEEP_BUFFER_OUTPUT_PERIOD_SIZE, + .period_count = DEEP_BUFFER_OUTPUT_PERIOD_COUNT, + .format = PCM_FORMAT_S16_LE, + .start_threshold = DEEP_BUFFER_OUTPUT_PERIOD_SIZE / 4, + .stop_threshold = INT_MAX, + .avail_min = DEEP_BUFFER_OUTPUT_PERIOD_SIZE / 4, +}; + +struct pcm_config pcm_config_low_latency = { + .channels = 2, + .rate = DEFAULT_OUTPUT_SAMPLING_RATE, + .period_size = LOW_LATENCY_OUTPUT_PERIOD_SIZE, + .period_count = LOW_LATENCY_OUTPUT_PERIOD_COUNT, + .format = PCM_FORMAT_S16_LE, + .start_threshold = LOW_LATENCY_OUTPUT_PERIOD_SIZE / 4, + .stop_threshold = INT_MAX, + .avail_min = LOW_LATENCY_OUTPUT_PERIOD_SIZE / 4, +}; + +struct pcm_config pcm_config_hdmi_multi = { + .channels = HDMI_MULTI_DEFAULT_CHANNEL_COUNT, /* changed when the stream is opened */ + .rate = DEFAULT_OUTPUT_SAMPLING_RATE, /* changed when the stream is opened */ + .period_size = HDMI_MULTI_PERIOD_SIZE, + .period_count = HDMI_MULTI_PERIOD_COUNT, + .format = PCM_FORMAT_S16_LE, + .start_threshold = 0, + .stop_threshold = INT_MAX, + .avail_min = 0, +}; + +struct pcm_config pcm_config_audio_capture = { + .channels = 2, + .period_count = AUDIO_CAPTURE_PERIOD_COUNT, + .format = PCM_FORMAT_S16_LE, +}; + +#define AFE_PROXY_CHANNEL_COUNT 2 +#define AFE_PROXY_SAMPLING_RATE 48000 + +#define AFE_PROXY_PLAYBACK_PERIOD_SIZE 768 +#define AFE_PROXY_PLAYBACK_PERIOD_COUNT 4 + +struct pcm_config pcm_config_afe_proxy_playback = { + .channels = AFE_PROXY_CHANNEL_COUNT, + .rate = AFE_PROXY_SAMPLING_RATE, + .period_size = AFE_PROXY_PLAYBACK_PERIOD_SIZE, + .period_count = AFE_PROXY_PLAYBACK_PERIOD_COUNT, + .format = PCM_FORMAT_S16_LE, + .start_threshold = AFE_PROXY_PLAYBACK_PERIOD_SIZE, + .stop_threshold = INT_MAX, + .avail_min = AFE_PROXY_PLAYBACK_PERIOD_SIZE, +}; + +#define AFE_PROXY_RECORD_PERIOD_SIZE 768 +#define AFE_PROXY_RECORD_PERIOD_COUNT 4 + +struct pcm_config pcm_config_afe_proxy_record = { + .channels = AFE_PROXY_CHANNEL_COUNT, + .rate = AFE_PROXY_SAMPLING_RATE, + .period_size = AFE_PROXY_RECORD_PERIOD_SIZE, + .period_count = AFE_PROXY_RECORD_PERIOD_COUNT, + .format = PCM_FORMAT_S16_LE, + .start_threshold = AFE_PROXY_RECORD_PERIOD_SIZE, + .stop_threshold = INT_MAX, + .avail_min = AFE_PROXY_RECORD_PERIOD_SIZE, +}; + +const char * const use_case_table[AUDIO_USECASE_MAX] = { + [USECASE_AUDIO_PLAYBACK_DEEP_BUFFER] = "deep-buffer-playback", + [USECASE_AUDIO_PLAYBACK_LOW_LATENCY] = "low-latency-playback", + [USECASE_AUDIO_PLAYBACK_MULTI_CH] = "multi-channel-playback", + [USECASE_AUDIO_PLAYBACK_OFFLOAD] = "compress-offload-playback", +#ifdef MULTIPLE_OFFLOAD_ENABLED + [USECASE_AUDIO_PLAYBACK_OFFLOAD2] = "compress-offload-playback2", + [USECASE_AUDIO_PLAYBACK_OFFLOAD3] = "compress-offload-playback3", + [USECASE_AUDIO_PLAYBACK_OFFLOAD4] = "compress-offload-playback4", + [USECASE_AUDIO_PLAYBACK_OFFLOAD5] = "compress-offload-playback5", + [USECASE_AUDIO_PLAYBACK_OFFLOAD6] = "compress-offload-playback6", + [USECASE_AUDIO_PLAYBACK_OFFLOAD7] = "compress-offload-playback7", + [USECASE_AUDIO_PLAYBACK_OFFLOAD8] = "compress-offload-playback8", + [USECASE_AUDIO_PLAYBACK_OFFLOAD9] = "compress-offload-playback9", +#endif + [USECASE_AUDIO_RECORD] = "audio-record", + [USECASE_AUDIO_RECORD_COMPRESS] = "audio-record-compress", + [USECASE_AUDIO_RECORD_LOW_LATENCY] = "low-latency-record", + [USECASE_AUDIO_RECORD_FM_VIRTUAL] = "fm-virtual-record", + [USECASE_AUDIO_PLAYBACK_FM] = "play-fm", + [USECASE_AUDIO_HFP_SCO] = "hfp-sco", + [USECASE_AUDIO_HFP_SCO_WB] = "hfp-sco-wb", + [USECASE_VOICE_CALL] = "voice-call", + + [USECASE_VOICE2_CALL] = "voice2-call", + [USECASE_VOLTE_CALL] = "volte-call", + [USECASE_QCHAT_CALL] = "qchat-call", + [USECASE_VOWLAN_CALL] = "vowlan-call", + [USECASE_COMPRESS_VOIP_CALL] = "compress-voip-call", + [USECASE_INCALL_REC_UPLINK] = "incall-rec-uplink", + [USECASE_INCALL_REC_DOWNLINK] = "incall-rec-downlink", + [USECASE_INCALL_REC_UPLINK_AND_DOWNLINK] = "incall-rec-uplink-and-downlink", + [USECASE_INCALL_REC_UPLINK_COMPRESS] = "incall-rec-uplink-compress", + [USECASE_INCALL_REC_DOWNLINK_COMPRESS] = "incall-rec-downlink-compress", + [USECASE_INCALL_REC_UPLINK_AND_DOWNLINK_COMPRESS] = "incall-rec-uplink-and-downlink-compress", + + [USECASE_INCALL_MUSIC_UPLINK] = "incall_music_uplink", + [USECASE_INCALL_MUSIC_UPLINK2] = "incall_music_uplink2", + [USECASE_AUDIO_SPKR_CALIB_RX] = "spkr-rx-calib", + [USECASE_AUDIO_SPKR_CALIB_TX] = "spkr-vi-record", + + [USECASE_AUDIO_PLAYBACK_AFE_PROXY] = "afe-proxy-playback", + [USECASE_AUDIO_RECORD_AFE_PROXY] = "afe-proxy-record", +}; + +static const audio_usecase_t offload_usecases[] = { + USECASE_AUDIO_PLAYBACK_OFFLOAD, +#ifdef MULTIPLE_OFFLOAD_ENABLED + USECASE_AUDIO_PLAYBACK_OFFLOAD2, + USECASE_AUDIO_PLAYBACK_OFFLOAD3, + USECASE_AUDIO_PLAYBACK_OFFLOAD4, + USECASE_AUDIO_PLAYBACK_OFFLOAD5, + USECASE_AUDIO_PLAYBACK_OFFLOAD6, + USECASE_AUDIO_PLAYBACK_OFFLOAD7, + USECASE_AUDIO_PLAYBACK_OFFLOAD8, + USECASE_AUDIO_PLAYBACK_OFFLOAD9, +#endif +}; + +#define STRING_TO_ENUM(string) { #string, string } + +struct string_to_enum { + const char *name; + uint32_t value; +}; + +static const struct string_to_enum out_channels_name_to_enum_table[] = { + STRING_TO_ENUM(AUDIO_CHANNEL_OUT_STEREO), + STRING_TO_ENUM(AUDIO_CHANNEL_OUT_5POINT1), + STRING_TO_ENUM(AUDIO_CHANNEL_OUT_7POINT1), +}; + +static const struct string_to_enum out_formats_name_to_enum_table[] = { + STRING_TO_ENUM(AUDIO_FORMAT_AC3), + STRING_TO_ENUM(AUDIO_FORMAT_E_AC3), + STRING_TO_ENUM(AUDIO_FORMAT_E_AC3_JOC), +}; + +static struct audio_device *adev = NULL; +static pthread_mutex_t adev_init_lock; +static unsigned int audio_device_ref_count; + +static int set_voice_volume_l(struct audio_device *adev, float volume); + +static int check_and_set_gapless_mode(struct audio_device *adev) { + + + char value[PROPERTY_VALUE_MAX] = {0}; + bool gapless_enabled = false; + const char *mixer_ctl_name = "Compress Gapless Playback"; + struct mixer_ctl *ctl; + + ALOGV("%s:", __func__); + property_get("audio.offload.gapless.enabled", value, NULL); + gapless_enabled = atoi(value) || !strncmp("true", value, 4); + + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + return -EINVAL; + } + + if (mixer_ctl_set_value(ctl, 0, gapless_enabled) < 0) { + ALOGE("%s: Could not set gapless mode %d", + __func__, gapless_enabled); + return -EINVAL; + } + return 0; +} + +static bool is_supported_format(audio_format_t format) +{ + if (format == AUDIO_FORMAT_MP3 || + format == AUDIO_FORMAT_AAC_LC || + format == AUDIO_FORMAT_AAC_HE_V1 || + format == AUDIO_FORMAT_AAC_HE_V2 || + format == AUDIO_FORMAT_PCM_16_BIT_OFFLOAD || + format == AUDIO_FORMAT_PCM_24_BIT_OFFLOAD || + format == AUDIO_FORMAT_FLAC) + return true; + + return false; +} + +static int get_snd_codec_id(audio_format_t format) +{ + int id = 0; + + switch (format & AUDIO_FORMAT_MAIN_MASK) { + case AUDIO_FORMAT_MP3: + id = SND_AUDIOCODEC_MP3; + break; + case AUDIO_FORMAT_AAC: + id = SND_AUDIOCODEC_AAC; + break; + case AUDIO_FORMAT_PCM_OFFLOAD: + id = SND_AUDIOCODEC_PCM; + break; + case AUDIO_FORMAT_FLAC: + id = SND_AUDIOCODEC_FLAC; + break; + default: + ALOGE("%s: Unsupported audio format :%x", __func__, format); + } + + return id; +} + +int get_snd_card_state(struct audio_device *adev) +{ + int snd_scard_state; + + if (!adev) + return SND_CARD_STATE_OFFLINE; + + pthread_mutex_lock(&adev->snd_card_status.lock); + snd_scard_state = adev->snd_card_status.state; + pthread_mutex_unlock(&adev->snd_card_status.lock); + + return snd_scard_state; +} + +static int set_snd_card_state(struct audio_device *adev, int snd_scard_state) +{ + if (!adev) + return -ENOSYS; + + pthread_mutex_lock(&adev->snd_card_status.lock); + adev->snd_card_status.state = snd_scard_state; + pthread_mutex_unlock(&adev->snd_card_status.lock); + + return 0; +} + +static int enable_audio_route_for_voice_usecases(struct audio_device *adev, + struct audio_usecase *uc_info) +{ + struct listnode *node; + struct audio_usecase *usecase; + + if (uc_info == NULL) + return -EINVAL; + + /* Re-route all voice usecases on the shared backend other than the + specified usecase to new snd devices */ + list_for_each(node, &adev->usecase_list) { + usecase = node_to_item(node, struct audio_usecase, list); + if ((usecase->type == VOICE_CALL || usecase->type == VOIP_CALL) && + (usecase != uc_info)) + enable_audio_route(adev, usecase); + } + return 0; +} + +int pcm_ioctl(struct pcm *pcm, int request, ...) +{ + va_list ap; + void * arg; + int pcm_fd = *(int*)pcm; + + va_start(ap, request); + arg = va_arg(ap, void *); + va_end(ap); + + return ioctl(pcm_fd, request, arg); +} + +int enable_audio_route(struct audio_device *adev, + struct audio_usecase *usecase) +{ + snd_device_t snd_device; + char mixer_path[MIXER_PATH_MAX_LENGTH]; + + if (usecase == NULL) + return -EINVAL; + + ALOGV("%s: enter: usecase(%d)", __func__, usecase->id); + + if (usecase->type == PCM_CAPTURE) + snd_device = usecase->in_snd_device; + else + snd_device = usecase->out_snd_device; + +#ifdef DS1_DOLBY_DAP_ENABLED + audio_extn_dolby_set_dmid(adev); + audio_extn_dolby_set_endpoint(adev); +#endif + audio_extn_dolby_ds2_set_endpoint(adev); + audio_extn_sound_trigger_update_stream_status(usecase, ST_EVENT_STREAM_BUSY); + audio_extn_listen_update_stream_status(usecase, LISTEN_EVENT_STREAM_BUSY); + audio_extn_utils_send_audio_calibration(adev, usecase); + audio_extn_utils_send_app_type_cfg(usecase); + strcpy(mixer_path, use_case_table[usecase->id]); + platform_add_backend_name(mixer_path, snd_device); + ALOGV("%s: apply mixer and update path: %s", __func__, mixer_path); + audio_route_apply_and_update_path(adev->audio_route, mixer_path); + ALOGV("%s: exit", __func__); + return 0; +} + +int disable_audio_route(struct audio_device *adev, + struct audio_usecase *usecase) +{ + snd_device_t snd_device; + char mixer_path[MIXER_PATH_MAX_LENGTH]; + + if (usecase == NULL || usecase->id == USECASE_INVALID) + return -EINVAL; + + ALOGV("%s: enter: usecase(%d)", __func__, usecase->id); + if (usecase->type == PCM_CAPTURE) + snd_device = usecase->in_snd_device; + else + snd_device = usecase->out_snd_device; + strcpy(mixer_path, use_case_table[usecase->id]); + platform_add_backend_name(mixer_path, snd_device); + ALOGV("%s: reset and update mixer path: %s", __func__, mixer_path); + audio_route_reset_and_update_path(adev->audio_route, mixer_path); + audio_extn_sound_trigger_update_stream_status(usecase, ST_EVENT_STREAM_FREE); + audio_extn_listen_update_stream_status(usecase, LISTEN_EVENT_STREAM_FREE); + ALOGV("%s: exit", __func__); + return 0; +} + +int enable_snd_device(struct audio_device *adev, + snd_device_t snd_device) +{ + char device_name[DEVICE_NAME_MAX_SIZE] = {0}; + + if (snd_device < SND_DEVICE_MIN || + snd_device >= SND_DEVICE_MAX) { + ALOGE("%s: Invalid sound device %d", __func__, snd_device); + return -EINVAL; + } + + adev->snd_dev_ref_cnt[snd_device]++; + + if(platform_get_snd_device_name_extn(adev->platform, snd_device, device_name) < 0 ) { + ALOGE("%s: Invalid sound device returned", __func__); + return -EINVAL; + } + if (adev->snd_dev_ref_cnt[snd_device] > 1) { + ALOGV("%s: snd_device(%d: %s) is already active", + __func__, snd_device, device_name); + return 0; + } + + if (audio_extn_spkr_prot_is_enabled()) + audio_extn_spkr_prot_calib_cancel(adev); + /* start usb playback thread */ + if(SND_DEVICE_OUT_USB_HEADSET == snd_device || + SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET == snd_device) + audio_extn_usb_start_playback(adev); + + /* start usb capture thread */ + if(SND_DEVICE_IN_USB_HEADSET_MIC == snd_device) + audio_extn_usb_start_capture(adev); + + if ((snd_device == SND_DEVICE_OUT_SPEAKER || + snd_device == SND_DEVICE_OUT_VOICE_SPEAKER) && + audio_extn_spkr_prot_is_enabled()) { + if (audio_extn_spkr_prot_get_acdb_id(snd_device) < 0) { + adev->snd_dev_ref_cnt[snd_device]--; + return -EINVAL; + } + if (audio_extn_spkr_prot_start_processing(snd_device)) { + ALOGE("%s: spkr_start_processing failed", __func__); + return -EINVAL; + } + } else { + ALOGV("%s: snd_device(%d: %s)", __func__, + snd_device, device_name); + /* due to the possibility of calibration overwrite between listen + and audio, notify listen hal before audio calibration is sent */ + audio_extn_sound_trigger_update_device_status(snd_device, + ST_EVENT_SND_DEVICE_BUSY); + audio_extn_listen_update_device_status(snd_device, + LISTEN_EVENT_SND_DEVICE_BUSY); + if (platform_get_snd_device_acdb_id(snd_device) < 0) { + adev->snd_dev_ref_cnt[snd_device]--; + audio_extn_sound_trigger_update_device_status(snd_device, + ST_EVENT_SND_DEVICE_FREE); + audio_extn_listen_update_device_status(snd_device, + LISTEN_EVENT_SND_DEVICE_FREE); + return -EINVAL; + } + audio_extn_dev_arbi_acquire(snd_device); + audio_route_apply_and_update_path(adev->audio_route, device_name); + } + return 0; +} + +int disable_snd_device(struct audio_device *adev, + snd_device_t snd_device) +{ + char device_name[DEVICE_NAME_MAX_SIZE] = {0}; + + if (snd_device < SND_DEVICE_MIN || + snd_device >= SND_DEVICE_MAX) { + ALOGE("%s: Invalid sound device %d", __func__, snd_device); + return -EINVAL; + } + if (adev->snd_dev_ref_cnt[snd_device] <= 0) { + ALOGE("%s: device ref cnt is already 0", __func__); + return -EINVAL; + } + + adev->snd_dev_ref_cnt[snd_device]--; + + if(platform_get_snd_device_name_extn(adev->platform, snd_device, device_name) < 0) { + ALOGE("%s: Invalid sound device returned", __func__); + return -EINVAL; + } + + if (adev->snd_dev_ref_cnt[snd_device] == 0) { + ALOGV("%s: snd_device(%d: %s)", __func__, + snd_device, device_name); + /* exit usb play back thread */ + if(SND_DEVICE_OUT_USB_HEADSET == snd_device || + SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET == snd_device) + audio_extn_usb_stop_playback(); + + /* exit usb capture thread */ + if(SND_DEVICE_IN_USB_HEADSET_MIC == snd_device) + audio_extn_usb_stop_capture(); + + if ((snd_device == SND_DEVICE_OUT_SPEAKER || + snd_device == SND_DEVICE_OUT_VOICE_SPEAKER) && + audio_extn_spkr_prot_is_enabled()) { + audio_extn_spkr_prot_stop_processing(snd_device); + } else { + audio_route_reset_and_update_path(adev->audio_route, device_name); + audio_extn_dev_arbi_release(snd_device); + } + + audio_extn_sound_trigger_update_device_status(snd_device, + ST_EVENT_SND_DEVICE_FREE); + audio_extn_listen_update_device_status(snd_device, + LISTEN_EVENT_SND_DEVICE_FREE); + } + + return 0; +} + +static void check_usecases_codec_backend(struct audio_device *adev, + struct audio_usecase *uc_info, + snd_device_t snd_device) +{ + struct listnode *node; + struct audio_usecase *usecase; + bool switch_device[AUDIO_USECASE_MAX]; + int i, num_uc_to_switch = 0; + + /* + * This function is to make sure that all the usecases that are active on + * the hardware codec backend are always routed to any one device that is + * handled by the hardware codec. + * For example, if low-latency and deep-buffer usecases are currently active + * on speaker and out_set_parameters(headset) is received on low-latency + * output, then we have to make sure deep-buffer is also switched to headset, + * because of the limitation that both the devices cannot be enabled + * at the same time as they share the same backend. + */ + /* + * This call is to check if we need to force routing for a particular stream + * If there is a backend configuration change for the device when a + * new stream starts, then ADM needs to be closed and re-opened with the new + * configuraion. This call check if we need to re-route all the streams + * associated with the backend. Touch tone + 24 bit playback. + */ + bool force_routing = platform_check_and_set_codec_backend_cfg(adev, uc_info); + + /* Disable all the usecases on the shared backend other than the + specified usecase */ + for (i = 0; i < AUDIO_USECASE_MAX; i++) + switch_device[i] = false; + + list_for_each(node, &adev->usecase_list) { + usecase = node_to_item(node, struct audio_usecase, list); + if (usecase->type != PCM_CAPTURE && + usecase != uc_info && + (usecase->out_snd_device != snd_device || force_routing) && + usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND) { + ALOGV("%s: Usecase (%s) is active on (%s) - disabling ..", + __func__, use_case_table[usecase->id], + platform_get_snd_device_name(usecase->out_snd_device)); + disable_audio_route(adev, usecase); + switch_device[usecase->id] = true; + num_uc_to_switch++; + } + } + + if (num_uc_to_switch) { + /* All streams have been de-routed. Disable the device */ + + /* Make sure the previous devices to be disabled first and then enable the + selected devices */ + list_for_each(node, &adev->usecase_list) { + usecase = node_to_item(node, struct audio_usecase, list); + if (switch_device[usecase->id]) { + disable_snd_device(adev, usecase->out_snd_device); + } + } + + list_for_each(node, &adev->usecase_list) { + usecase = node_to_item(node, struct audio_usecase, list); + if (switch_device[usecase->id]) { + enable_snd_device(adev, snd_device); + } + } + + /* Re-route all the usecases on the shared backend other than the + specified usecase to new snd devices */ + list_for_each(node, &adev->usecase_list) { + usecase = node_to_item(node, struct audio_usecase, list); + /* Update the out_snd_device only before enabling the audio route */ + if (switch_device[usecase->id] ) { + usecase->out_snd_device = snd_device; + if (usecase->type != VOICE_CALL && usecase->type != VOIP_CALL) + enable_audio_route(adev, usecase); + } + } + } +} + +static void check_and_route_capture_usecases(struct audio_device *adev, + struct audio_usecase *uc_info, + snd_device_t snd_device) +{ + struct listnode *node; + struct audio_usecase *usecase; + bool switch_device[AUDIO_USECASE_MAX]; + int i, num_uc_to_switch = 0; + + /* + * This function is to make sure that all the active capture usecases + * are always routed to the same input sound device. + * For example, if audio-record and voice-call usecases are currently + * active on speaker(rx) and speaker-mic (tx) and out_set_parameters(earpiece) + * is received for voice call then we have to make sure that audio-record + * usecase is also switched to earpiece i.e. voice-dmic-ef, + * because of the limitation that two devices cannot be enabled + * at the same time if they share the same backend. + */ + for (i = 0; i < AUDIO_USECASE_MAX; i++) + switch_device[i] = false; + + list_for_each(node, &adev->usecase_list) { + usecase = node_to_item(node, struct audio_usecase, list); + if (usecase->type != PCM_PLAYBACK && + usecase != uc_info && + usecase->in_snd_device != snd_device) { + ALOGV("%s: Usecase (%s) is active on (%s) - disabling ..", + __func__, use_case_table[usecase->id], + platform_get_snd_device_name(usecase->in_snd_device)); + disable_audio_route(adev, usecase); + switch_device[usecase->id] = true; + num_uc_to_switch++; + } + } + + if (num_uc_to_switch) { + /* All streams have been de-routed. Disable the device */ + + /* Make sure the previous devices to be disabled first and then enable the + selected devices */ + list_for_each(node, &adev->usecase_list) { + usecase = node_to_item(node, struct audio_usecase, list); + if (switch_device[usecase->id]) { + disable_snd_device(adev, usecase->in_snd_device); + } + } + + list_for_each(node, &adev->usecase_list) { + usecase = node_to_item(node, struct audio_usecase, list); + if (switch_device[usecase->id]) { + enable_snd_device(adev, snd_device); + } + } + + /* Re-route all the usecases on the shared backend other than the + specified usecase to new snd devices */ + list_for_each(node, &adev->usecase_list) { + usecase = node_to_item(node, struct audio_usecase, list); + /* Update the in_snd_device only before enabling the audio route */ + if (switch_device[usecase->id] ) { + usecase->in_snd_device = snd_device; + if (usecase->type != VOICE_CALL && usecase->type != VOIP_CALL) + enable_audio_route(adev, usecase); + } + } + } +} + +/* must be called with hw device mutex locked */ +static int read_hdmi_channel_masks(struct stream_out *out) +{ + int ret = 0; + int channels = platform_edid_get_max_channels(out->dev->platform); + + switch (channels) { + /* + * Do not handle stereo output in Multi-channel cases + * Stereo case is handled in normal playback path + */ + case 6: + ALOGV("%s: HDMI supports 5.1", __func__); + out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_5POINT1; + break; + case 8: + ALOGV("%s: HDMI supports 5.1 and 7.1 channels", __func__); + out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_5POINT1; + out->supported_channel_masks[1] = AUDIO_CHANNEL_OUT_7POINT1; + break; + default: + ALOGE("HDMI does not support multi channel playback"); + ret = -ENOSYS; + break; + } + return ret; +} + +static audio_usecase_t get_voice_usecase_id_from_list(struct audio_device *adev) +{ + struct audio_usecase *usecase; + struct listnode *node; + + list_for_each(node, &adev->usecase_list) { + usecase = node_to_item(node, struct audio_usecase, list); + if (usecase->type == VOICE_CALL) { + ALOGV("%s: usecase id %d", __func__, usecase->id); + return usecase->id; + } + } + return USECASE_INVALID; +} + +struct audio_usecase *get_usecase_from_list(struct audio_device *adev, + audio_usecase_t uc_id) +{ + struct audio_usecase *usecase; + struct listnode *node; + + list_for_each(node, &adev->usecase_list) { + usecase = node_to_item(node, struct audio_usecase, list); + if (usecase->id == uc_id) + return usecase; + } + return NULL; +} + +int select_devices(struct audio_device *adev, audio_usecase_t uc_id) +{ + snd_device_t out_snd_device = SND_DEVICE_NONE; + snd_device_t in_snd_device = SND_DEVICE_NONE; + struct audio_usecase *usecase = NULL; + struct audio_usecase *vc_usecase = NULL; + struct audio_usecase *voip_usecase = NULL; + struct audio_usecase *hfp_usecase = NULL; + audio_usecase_t hfp_ucid; + struct listnode *node; + int status = 0; + + usecase = get_usecase_from_list(adev, uc_id); + if (usecase == NULL) { + ALOGE("%s: Could not find the usecase(%d)", __func__, uc_id); + return -EINVAL; + } + + if ((usecase->type == VOICE_CALL) || + (usecase->type == VOIP_CALL) || + (usecase->type == PCM_HFP_CALL)) { + out_snd_device = platform_get_output_snd_device(adev->platform, + usecase->stream.out->devices); + in_snd_device = platform_get_input_snd_device(adev->platform, usecase->stream.out->devices); + usecase->devices = usecase->stream.out->devices; + } else { + /* + * If the voice call is active, use the sound devices of voice call usecase + * so that it would not result any device switch. All the usecases will + * be switched to new device when select_devices() is called for voice call + * usecase. This is to avoid switching devices for voice call when + * check_usecases_codec_backend() is called below. + */ + if (voice_is_in_call(adev) && adev->mode == AUDIO_MODE_IN_CALL) { + vc_usecase = get_usecase_from_list(adev, + get_voice_usecase_id_from_list(adev)); + if ((vc_usecase) && ((vc_usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND) || + (usecase->devices == AUDIO_DEVICE_IN_VOICE_CALL))) { + in_snd_device = vc_usecase->in_snd_device; + out_snd_device = vc_usecase->out_snd_device; + } + } else if (voice_extn_compress_voip_is_active(adev)) { + voip_usecase = get_usecase_from_list(adev, USECASE_COMPRESS_VOIP_CALL); + if ((voip_usecase) && ((voip_usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND) && + (usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND) && + (voip_usecase->stream.out != adev->primary_output))) { + in_snd_device = voip_usecase->in_snd_device; + out_snd_device = voip_usecase->out_snd_device; + } + } else if (audio_extn_hfp_is_active(adev)) { + hfp_ucid = audio_extn_hfp_get_usecase(); + hfp_usecase = get_usecase_from_list(adev, hfp_ucid); + if ((hfp_usecase) && (hfp_usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND)) { + in_snd_device = hfp_usecase->in_snd_device; + out_snd_device = hfp_usecase->out_snd_device; + } + } + if (usecase->type == PCM_PLAYBACK) { + usecase->devices = usecase->stream.out->devices; + in_snd_device = SND_DEVICE_NONE; + if (out_snd_device == SND_DEVICE_NONE) { + out_snd_device = platform_get_output_snd_device(adev->platform, + usecase->stream.out->devices); + if (usecase->stream.out == adev->primary_output && + adev->active_input && + out_snd_device != usecase->out_snd_device) { + select_devices(adev, adev->active_input->usecase); + } + } + } else if (usecase->type == PCM_CAPTURE) { + usecase->devices = usecase->stream.in->device; + out_snd_device = SND_DEVICE_NONE; + if (in_snd_device == SND_DEVICE_NONE) { + audio_devices_t out_device = AUDIO_DEVICE_NONE; + if ((adev->active_input->source == AUDIO_SOURCE_VOICE_COMMUNICATION || + (adev->mode == AUDIO_MODE_IN_COMMUNICATION && + adev->active_input->source == AUDIO_SOURCE_MIC)) && + adev->primary_output && !adev->primary_output->standby) { + out_device = adev->primary_output->devices; + platform_set_echo_reference(adev->platform, false); + } else if (usecase->id == USECASE_AUDIO_RECORD_AFE_PROXY) { + out_device = AUDIO_DEVICE_OUT_TELEPHONY_TX; + } + in_snd_device = platform_get_input_snd_device(adev->platform, out_device); + } + } + } + + if (out_snd_device == usecase->out_snd_device && + in_snd_device == usecase->in_snd_device) { + return 0; + } + + ALOGD("%s: out_snd_device(%d: %s) in_snd_device(%d: %s)", __func__, + out_snd_device, platform_get_snd_device_name(out_snd_device), + in_snd_device, platform_get_snd_device_name(in_snd_device)); + + /* + * Limitation: While in call, to do a device switch we need to disable + * and enable both RX and TX devices though one of them is same as current + * device. + */ + if ((usecase->type == VOICE_CALL) && + (usecase->in_snd_device != SND_DEVICE_NONE) && + (usecase->out_snd_device != SND_DEVICE_NONE)) { + status = platform_switch_voice_call_device_pre(adev->platform); + } + + /* Disable current sound devices */ + if (usecase->out_snd_device != SND_DEVICE_NONE) { + disable_audio_route(adev, usecase); + disable_snd_device(adev, usecase->out_snd_device); + } + + if (usecase->in_snd_device != SND_DEVICE_NONE) { + disable_audio_route(adev, usecase); + disable_snd_device(adev, usecase->in_snd_device); + } + + /* Applicable only on the targets that has external modem. + * New device information should be sent to modem before enabling + * the devices to reduce in-call device switch time. + */ + if ((usecase->type == VOICE_CALL) && + (usecase->in_snd_device != SND_DEVICE_NONE) && + (usecase->out_snd_device != SND_DEVICE_NONE)) { + status = platform_switch_voice_call_enable_device_config(adev->platform, + out_snd_device, + in_snd_device); + } + + /* Enable new sound devices */ + if (out_snd_device != SND_DEVICE_NONE) { + if (usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND) + check_usecases_codec_backend(adev, usecase, out_snd_device); + enable_snd_device(adev, out_snd_device); + } + + if (in_snd_device != SND_DEVICE_NONE) { + check_and_route_capture_usecases(adev, usecase, in_snd_device); + enable_snd_device(adev, in_snd_device); + } + + if (usecase->type == VOICE_CALL || usecase->type == VOIP_CALL) { + status = platform_switch_voice_call_device_post(adev->platform, + out_snd_device, + in_snd_device); + enable_audio_route_for_voice_usecases(adev, usecase); + } + + usecase->in_snd_device = in_snd_device; + usecase->out_snd_device = out_snd_device; + + if (usecase->type == PCM_PLAYBACK) { + audio_extn_utils_update_stream_app_type_cfg(adev->platform, + &adev->streams_output_cfg_list, + usecase->stream.out->devices, + usecase->stream.out->flags, + usecase->stream.out->format, + usecase->stream.out->sample_rate, + usecase->stream.out->bit_width, + &usecase->stream.out->app_type_cfg); + ALOGI("%s Selected apptype: %d", __func__, usecase->stream.out->app_type_cfg.app_type); + } + + enable_audio_route(adev, usecase); + + /* Applicable only on the targets that has external modem. + * Enable device command should be sent to modem only after + * enabling voice call mixer controls + */ + if (usecase->type == VOICE_CALL) + status = platform_switch_voice_call_usecase_route_post(adev->platform, + out_snd_device, + in_snd_device); + ALOGD("%s: done",__func__); + + return status; +} + +static int stop_input_stream(struct stream_in *in) +{ + int i, ret = 0; + struct audio_usecase *uc_info; + struct audio_device *adev = in->dev; + + adev->active_input = NULL; + + ALOGV("%s: enter: usecase(%d: %s)", __func__, + in->usecase, use_case_table[in->usecase]); + uc_info = get_usecase_from_list(adev, in->usecase); + if (uc_info == NULL) { + ALOGE("%s: Could not find the usecase (%d) in the list", + __func__, in->usecase); + return -EINVAL; + } + + /* Close in-call recording streams */ + voice_check_and_stop_incall_rec_usecase(adev, in); + + /* 1. Disable stream specific mixer controls */ + disable_audio_route(adev, uc_info); + + /* 2. Disable the tx device */ + disable_snd_device(adev, uc_info->in_snd_device); + + list_remove(&uc_info->list); + free(uc_info); + + ALOGV("%s: exit: status(%d)", __func__, ret); + return ret; +} + +int start_input_stream(struct stream_in *in) +{ + /* 1. Enable output device and stream routing controls */ + int ret = 0; + struct audio_usecase *uc_info; + struct audio_device *adev = in->dev; + int snd_card_status = get_snd_card_state(adev); + + in->usecase = platform_update_usecase_from_source(in->source,in->usecase); + ALOGD("%s: enter: stream(%p)usecase(%d: %s)", + __func__, &in->stream, in->usecase, use_case_table[in->usecase]); + + + if (SND_CARD_STATE_OFFLINE == snd_card_status) { + ALOGE("%s: sound card is not active/SSR returning error", __func__); + ret = -EIO; + goto error_config; + } + + /* Check if source matches incall recording usecase criteria */ + ret = voice_check_and_set_incall_rec_usecase(adev, in); + if (ret) + goto error_config; + else + ALOGV("%s: usecase(%d)", __func__, in->usecase); + + in->pcm_device_id = platform_get_pcm_device_id(in->usecase, PCM_CAPTURE); + if (in->pcm_device_id < 0) { + ALOGE("%s: Could not find PCM device id for the usecase(%d)", + __func__, in->usecase); + ret = -EINVAL; + goto error_config; + } + + adev->active_input = in; + uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase)); + + if (!uc_info) { + ret = -ENOMEM; + goto error_config; + } + + uc_info->id = in->usecase; + uc_info->type = PCM_CAPTURE; + uc_info->stream.in = in; + uc_info->devices = in->device; + uc_info->in_snd_device = SND_DEVICE_NONE; + uc_info->out_snd_device = SND_DEVICE_NONE; + + list_add_tail(&adev->usecase_list, &uc_info->list); + audio_extn_perf_lock_acquire(); + select_devices(adev, in->usecase); + + ALOGV("%s: Opening PCM device card_id(%d) device_id(%d), channels %d", + __func__, adev->snd_card, in->pcm_device_id, in->config.channels); + + unsigned int flags = PCM_IN; + unsigned int pcm_open_retry_count = 0; + + if (in->usecase == USECASE_AUDIO_RECORD_AFE_PROXY) { + flags |= PCM_MMAP | PCM_NOIRQ; + pcm_open_retry_count = PROXY_OPEN_RETRY_COUNT; + } + + while (1) { + in->pcm = pcm_open(adev->snd_card, in->pcm_device_id, + flags, &in->config); + if (in->pcm == NULL || !pcm_is_ready(in->pcm)) { + ALOGE("%s: %s", __func__, pcm_get_error(in->pcm)); + if (in->pcm != NULL) { + pcm_close(in->pcm); + in->pcm = NULL; + } + if (pcm_open_retry_count-- == 0) { + ret = -EIO; + goto error_open; + } + usleep(PROXY_OPEN_WAIT_TIME * 1000); + continue; + } + break; + } + audio_extn_perf_lock_release(); + + ALOGV("%s: exit", __func__); + return ret; + +error_open: + stop_input_stream(in); + audio_extn_perf_lock_release(); + +error_config: + adev->active_input = NULL; + ALOGD("%s: exit: status(%d)", __func__, ret); + + return ret; +} + +/* 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)); + + if (!cmd) { + ALOGE("failed to allocate mem for command 0x%x", command); + return -ENOMEM; + } + + 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->offload_state = OFFLOAD_STATE_IDLE; + out->playback_started = 0; + 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); + } + } +} + +bool is_offload_usecase(audio_usecase_t uc_id) +{ + unsigned int i; + for (i = 0; i < sizeof(offload_usecases)/sizeof(offload_usecases[0]); i++) { + if (uc_id == offload_usecases[i]) + return true; + } + return false; +} + +static audio_usecase_t get_offload_usecase(struct audio_device *adev) +{ + audio_usecase_t ret = USECASE_AUDIO_PLAYBACK_OFFLOAD; + unsigned int i, num_usecase = sizeof(offload_usecases)/sizeof(offload_usecases[0]); + char value[PROPERTY_VALUE_MAX] = {0}; + + property_get("audio.offload.multiple.enabled", value, NULL); + if (!(atoi(value) || !strncmp("true", value, 4))) + num_usecase = 1; /* If prop is not set, limit the num of offload usecases to 1 */ + + ALOGV("%s: num_usecase: %d", __func__, num_usecase); + for (i = 0; i < num_usecase; i++) { + if (!(adev->offload_usecases_state & (0x1<offload_usecases_state |= 0x1 << i; + ret = offload_usecases[i]; + break; + } + } + ALOGV("%s: offload usecase is %d", __func__, ret); + return ret; +} + +static void free_offload_usecase(struct audio_device *adev, + audio_usecase_t uc_id) +{ + unsigned int i; + for (i = 0; i < sizeof(offload_usecases)/sizeof(offload_usecases[0]); i++) { + if (offload_usecases[i] == uc_id) { + adev->offload_usecases_state &= ~(0x1<lock); + 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: + ALOGD("copl(%p):calling compress_wait", out); + compress_wait(out->compr, -1); + ALOGD("copl(%p):out of compress_wait", out); + send_callback = true; + event = STREAM_CBK_EVENT_WRITE_READY; + break; + case OFFLOAD_CMD_PARTIAL_DRAIN: + ret = compress_next_track(out->compr); + if(ret == 0) { + ALOGD("copl(%p):calling compress_partial_drain", out); + ret = compress_partial_drain(out->compr); + ALOGD("copl(%p):out of compress_partial_drain", out); + if (ret < 0) + ret = -errno; + } + else if (ret == -ETIMEDOUT) + compress_drain(out->compr); + else + ALOGE("%s: Next track returned error %d",__func__, ret); + + if (ret != -ENETRESET) { + send_callback = true; + event = STREAM_CBK_EVENT_DRAIN_READY; + ALOGV("copl(%p):send drain callback, ret %d", out, ret); + } else + ALOGE("%s: Block drain ready event during SSR", __func__); + break; + case OFFLOAD_CMD_DRAIN: + ALOGD("copl(%p):calling compress_drain", out); + compress_drain(out->compr); + ALOGD("copl(%p):calling compress_drain", out); + send_callback = true; + event = STREAM_CBK_EVENT_DRAIN_READY; + break; + default: + ALOGE("%s unknown command received: %d", __func__, cmd->cmd); + break; + } + pthread_mutex_lock(&out->lock); + 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) +{ + pthread_mutex_lock(&out->lock); + stop_compressed_output_l(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 bool allow_hdmi_channel_config(struct audio_device *adev) +{ + struct listnode *node; + struct audio_usecase *usecase; + bool ret = true; + + list_for_each(node, &adev->usecase_list) { + usecase = node_to_item(node, struct audio_usecase, list); + if (usecase->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) { + /* + * If voice call is already existing, do not proceed further to avoid + * disabling/enabling both RX and TX devices, CSD calls, etc. + * Once the voice call done, the HDMI channels can be configured to + * max channels of remaining use cases. + */ + if (usecase->id == USECASE_VOICE_CALL) { + ALOGD("%s: voice call is active, no change in HDMI channels", + __func__); + ret = false; + break; + } else if (usecase->id == USECASE_AUDIO_PLAYBACK_MULTI_CH) { + ALOGD("%s: multi channel playback is active, " + "no change in HDMI channels", __func__); + ret = false; + break; + } else if (is_offload_usecase(usecase->id) && + audio_channel_count_from_out_mask(usecase->stream.out->channel_mask) > 2) { + ALOGD("%s: multi-channel(%x) compress offload playback is active, " + "no change in HDMI channels", __func__, usecase->stream.out->channel_mask); + ret = false; + break; + } + } + } + return ret; +} + +static int check_and_set_hdmi_channels(struct audio_device *adev, + unsigned int channels) +{ + struct listnode *node; + struct audio_usecase *usecase; + + /* Check if change in HDMI channel config is allowed */ + if (!allow_hdmi_channel_config(adev)) + return 0; + + if (channels == adev->cur_hdmi_channels) { + ALOGD("%s: Requested channels are same as current channels(%d)", __func__, channels); + return 0; + } + + platform_set_hdmi_channels(adev->platform, channels); + adev->cur_hdmi_channels = channels; + + /* + * Deroute all the playback streams routed to HDMI so that + * the back end is deactivated. Note that backend will not + * be deactivated if any one stream is connected to it. + */ + list_for_each(node, &adev->usecase_list) { + usecase = node_to_item(node, struct audio_usecase, list); + if (usecase->type == PCM_PLAYBACK && + usecase->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) { + disable_audio_route(adev, usecase); + } + } + + /* + * Enable all the streams disabled above. Now the HDMI backend + * will be activated with new channel configuration + */ + list_for_each(node, &adev->usecase_list) { + usecase = node_to_item(node, struct audio_usecase, list); + if (usecase->type == PCM_PLAYBACK && + usecase->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) { + enable_audio_route(adev, usecase); + } + } + + return 0; +} + +static int stop_output_stream(struct stream_out *out) +{ + int i, ret = 0; + struct audio_usecase *uc_info; + struct audio_device *adev = out->dev; + + ALOGV("%s: enter: usecase(%d: %s)", __func__, + out->usecase, use_case_table[out->usecase]); + uc_info = get_usecase_from_list(adev, out->usecase); + if (uc_info == NULL) { + ALOGE("%s: Could not find the usecase (%d) in the list", + __func__, out->usecase); + return -EINVAL; + } + + if (is_offload_usecase(out->usecase)) { + if (adev->visualizer_stop_output != NULL) + adev->visualizer_stop_output(out->handle, out->pcm_device_id); + if (adev->offload_effects_stop_output != NULL) + adev->offload_effects_stop_output(out->handle, out->pcm_device_id); + } + + /* 1. Get and set stream specific mixer controls */ + disable_audio_route(adev, uc_info); + + /* 2. Disable the rx device */ + disable_snd_device(adev, uc_info->out_snd_device); + + list_remove(&uc_info->list); + free(uc_info); + + /* Must be called after removing the usecase from list */ + if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) + check_and_set_hdmi_channels(adev, DEFAULT_HDMI_OUT_CHANNELS); + + ALOGV("%s: exit: status(%d)", __func__, ret); + return ret; +} + +int start_output_stream(struct stream_out *out) +{ + int ret = 0; + int sink_channels = 0; + char prop_value[PROPERTY_VALUE_MAX] = {0}; + struct audio_usecase *uc_info; + struct audio_device *adev = out->dev; + int snd_card_status = get_snd_card_state(adev); + + if ((out->usecase < 0) || (out->usecase >= AUDIO_USECASE_MAX)) { + ret = -EINVAL; + goto error_config; + } + + ALOGD("%s: enter: stream(%p)usecase(%d: %s) devices(%#x)", + __func__, &out->stream, out->usecase, use_case_table[out->usecase], + out->devices); + + if (SND_CARD_STATE_OFFLINE == snd_card_status) { + ALOGE("%s: sound card is not active/SSR returning error", __func__); + ret = -EIO; + goto error_config; + } + + out->pcm_device_id = platform_get_pcm_device_id(out->usecase, PCM_PLAYBACK); + if (out->pcm_device_id < 0) { + ALOGE("%s: Invalid PCM device id(%d) for the usecase(%d)", + __func__, out->pcm_device_id, out->usecase); + ret = -EINVAL; + goto error_config; + } + + uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase)); + + if (!uc_info) { + ret = -ENOMEM; + goto error_config; + } + + uc_info->id = out->usecase; + uc_info->type = PCM_PLAYBACK; + uc_info->stream.out = out; + uc_info->devices = out->devices; + uc_info->in_snd_device = SND_DEVICE_NONE; + uc_info->out_snd_device = SND_DEVICE_NONE; + + /* This must be called before adding this usecase to the list */ + if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) { + property_get("audio.use.hdmi.sink.cap", prop_value, NULL); + if (!strncmp("true", prop_value, 4)) { + sink_channels = platform_edid_get_max_channels(out->dev->platform); + ALOGD("%s: set HDMI channel count[%d] based on sink capability", __func__, sink_channels); + check_and_set_hdmi_channels(adev, sink_channels); + } else { + if (is_offload_usecase(out->usecase)) + check_and_set_hdmi_channels(adev, out->compr_config.codec->ch_in); + else + check_and_set_hdmi_channels(adev, out->config.channels); + } + } + + list_add_tail(&adev->usecase_list, &uc_info->list); + + select_devices(adev, out->usecase); + + ALOGV("%s: Opening PCM device card_id(%d) device_id(%d) format(%#x)", + __func__, adev->snd_card, out->pcm_device_id, out->config.format); + if (!is_offload_usecase(out->usecase)) { + unsigned int flags = PCM_OUT; + unsigned int pcm_open_retry_count = 0; + if (out->usecase == USECASE_AUDIO_PLAYBACK_AFE_PROXY) { + flags |= PCM_MMAP | PCM_NOIRQ; + pcm_open_retry_count = PROXY_OPEN_RETRY_COUNT; + } else + flags |= PCM_MONOTONIC; + + while (1) { + out->pcm = pcm_open(adev->snd_card, out->pcm_device_id, + flags, &out->config); + if (out->pcm == NULL || !pcm_is_ready(out->pcm)) { + ALOGE("%s: %s", __func__, pcm_get_error(out->pcm)); + if (out->pcm != NULL) { + pcm_close(out->pcm); + out->pcm = NULL; + } + if (pcm_open_retry_count-- == 0) { + ret = -EIO; + goto error_open; + } + usleep(PROXY_OPEN_WAIT_TIME * 1000); + continue; + } + break; + } + } else { + out->pcm = NULL; + out->compr = compress_open(adev->snd_card, + out->pcm_device_id, + COMPRESS_IN, &out->compr_config); + if (out->compr && !is_compress_ready(out->compr)) { + ALOGE("%s: %s", __func__, compress_get_error(out->compr)); + compress_close(out->compr); + out->compr = NULL; + ret = -EIO; + goto error_open; + } + if (out->offload_callback) + compress_nonblock(out->compr, out->non_blocking); + +#ifdef DS1_DOLBY_DDP_ENABLED + if (audio_extn_is_dolby_format(out->format)) + audio_extn_dolby_send_ddp_endp_params(adev); +#endif + + if (adev->visualizer_start_output != NULL) + adev->visualizer_start_output(out->handle, out->pcm_device_id); + if (adev->offload_effects_start_output != NULL) + adev->offload_effects_start_output(out->handle, out->pcm_device_id); + } + ALOGV("%s: exit", __func__); + return 0; +error_open: + stop_output_stream(out); +error_config: + return ret; +} + +static int check_input_parameters(uint32_t sample_rate, + audio_format_t format, + int channel_count) +{ + int ret = 0; + + if ((format != AUDIO_FORMAT_PCM_16_BIT) && + !voice_extn_compress_voip_is_format_supported(format) && + !audio_extn_compr_cap_format_supported(format)) ret = -EINVAL; + + switch (channel_count) { + case 1: + case 2: + case 6: + break; + default: + ret = -EINVAL; + } + + switch (sample_rate) { + case 8000: + case 11025: + case 12000: + case 16000: + case 22050: + case 24000: + case 32000: + case 44100: + case 48000: + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static size_t get_input_buffer_size(uint32_t sample_rate, + audio_format_t format, + int channel_count, + bool is_low_latency) +{ + size_t size = 0; + + if (check_input_parameters(sample_rate, format, channel_count) != 0) + return 0; + + size = (sample_rate * AUDIO_CAPTURE_PERIOD_DURATION_MSEC) / 1000; + if (is_low_latency) + size = configured_low_latency_capture_period_size; + /* ToDo: should use frame_size computed based on the format and + channel_count here. */ + size *= sizeof(short) * channel_count; + + /* make sure the size is multiple of 32 bytes + * At 48 kHz mono 16-bit PCM: + * 5.000 ms = 240 frames = 15*16*1*2 = 480, a whole multiple of 32 (15) + * 3.333 ms = 160 frames = 10*16*1*2 = 320, a whole multiple of 32 (10) + */ + size += 0x1f; + size &= ~0x1f; + + return size; +} + +static uint32_t out_get_sample_rate(const struct audio_stream *stream) +{ + struct stream_out *out = (struct stream_out *)stream; + + return out->sample_rate; +} + +static int out_set_sample_rate(struct audio_stream *stream __unused, + uint32_t rate __unused) +{ + return -ENOSYS; +} + +static size_t out_get_buffer_size(const struct audio_stream *stream) +{ + struct stream_out *out = (struct stream_out *)stream; + + if (is_offload_usecase(out->usecase)) + return out->compr_config.fragment_size; + else if(out->usecase == USECASE_COMPRESS_VOIP_CALL) + return voice_extn_compress_voip_out_get_buffer_size(out); + + return out->config.period_size * + audio_stream_out_frame_size((const struct audio_stream_out *)stream); +} + +static uint32_t out_get_channels(const struct audio_stream *stream) +{ + struct stream_out *out = (struct stream_out *)stream; + + return out->channel_mask; +} + +static audio_format_t out_get_format(const struct audio_stream *stream) +{ + struct stream_out *out = (struct stream_out *)stream; + + return out->format; +} + +static int out_set_format(struct audio_stream *stream __unused, + audio_format_t format __unused) +{ + return -ENOSYS; +} + +static int out_standby(struct audio_stream *stream) +{ + struct stream_out *out = (struct stream_out *)stream; + struct audio_device *adev = out->dev; + + ALOGD("%s: enter: stream (%p) usecase(%d: %s)", __func__, + stream, out->usecase, use_case_table[out->usecase]); + if (out->usecase == USECASE_COMPRESS_VOIP_CALL) { + /* Ignore standby in case of voip call because the voip output + * stream is closed in adev_close_output_stream() + */ + ALOGD("%s: Ignore Standby in VOIP call", __func__); + return 0; + } + + pthread_mutex_lock(&out->lock); + if (!out->standby) { + pthread_mutex_lock(&adev->lock); + out->standby = true; + if (!is_offload_usecase(out->usecase)) { + if (out->pcm) { + pcm_close(out->pcm); + out->pcm = NULL; + } + } else { + ALOGD("copl(%p):standby", out); + stop_compressed_output_l(out); + out->gapless_mdata.encoder_delay = 0; + out->gapless_mdata.encoder_padding = 0; + if (out->compr != NULL) { + compress_close(out->compr); + out->compr = NULL; + } + } + stop_output_stream(out); + pthread_mutex_unlock(&adev->lock); + } + pthread_mutex_unlock(&out->lock); + ALOGV("%s: exit", __func__); + return 0; +} + +static int out_dump(const struct audio_stream *stream __unused, + int fd __unused) +{ + return 0; +} + +static int parse_compress_metadata(struct stream_out *out, struct str_parms *parms) +{ + int ret = 0; + char value[32]; + bool is_meta_data_params = false; + struct compr_gapless_mdata tmp_mdata; + tmp_mdata.encoder_delay = 0; + tmp_mdata.encoder_padding = 0; + + if (!out || !parms) { + ALOGE("%s: return invalid ",__func__); + return -EINVAL; + } + + ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_FORMAT, value, sizeof(value)); + if (ret >= 0) { + if (atoi(value) == SND_AUDIOSTREAMFORMAT_MP4ADTS) { + out->compr_config.codec->format = SND_AUDIOSTREAMFORMAT_MP4ADTS; + ALOGV("ADTS format is set in offload mode"); + } + out->send_new_metadata = 1; + } + + if (out->format == AUDIO_FORMAT_FLAC) { + ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_FLAC_MIN_BLK_SIZE, value, sizeof(value)); + if (ret >= 0) { + out->compr_config.codec->options.flac_dec.min_blk_size = atoi(value); + out->send_new_metadata = 1; + } + ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_FLAC_MAX_BLK_SIZE, value, sizeof(value)); + if (ret >= 0) { + out->compr_config.codec->options.flac_dec.max_blk_size = atoi(value); + out->send_new_metadata = 1; + } + ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_FLAC_MIN_FRAME_SIZE, value, sizeof(value)); + if (ret >= 0) { + out->compr_config.codec->options.flac_dec.min_frame_size = atoi(value); + out->send_new_metadata = 1; + } + ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_FLAC_MAX_FRAME_SIZE, value, sizeof(value)); + if (ret >= 0) { + out->compr_config.codec->options.flac_dec.max_frame_size = atoi(value); + out->send_new_metadata = 1; + } + } + + ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_SAMPLE_RATE, value, sizeof(value)); + if(ret >= 0) + is_meta_data_params = true; + ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_NUM_CHANNEL, value, sizeof(value)); + if(ret >= 0 ) + is_meta_data_params = true; + ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_AVG_BIT_RATE, value, sizeof(value)); + if(ret >= 0 ) + is_meta_data_params = true; + ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES, value, sizeof(value)); + if (ret >= 0) { + is_meta_data_params = true; + tmp_mdata.encoder_delay = atoi(value); //whats a good limit check? + } + ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES, value, sizeof(value)); + if (ret >= 0) { + is_meta_data_params = true; + tmp_mdata.encoder_padding = atoi(value); + } + + if(!is_meta_data_params) { + ALOGV("%s: Not gapless meta data params", __func__); + return 0; + } + 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 bool output_drives_call(struct audio_device *adev, struct stream_out *out) +{ + return out == adev->primary_output || out == adev->voice_tx_output; +} + +static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) +{ + struct stream_out *out = (struct stream_out *)stream; + struct audio_device *adev = out->dev; + struct audio_usecase *usecase; + struct listnode *node; + struct str_parms *parms; + char value[32]; + int ret = 0, val = 0, err; + bool select_new_device = false; + + ALOGD("%s: enter: usecase(%d: %s) kvpairs: %s", + __func__, out->usecase, use_case_table[out->usecase], kvpairs); + parms = str_parms_create_str(kvpairs); + if (!parms) + goto error; + err = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value)); + if (err >= 0) { + val = atoi(value); + pthread_mutex_lock(&out->lock); + pthread_mutex_lock(&adev->lock); + + /* + * When HDMI cable is unplugged/usb hs is disconnected the + * music playback is paused and the policy manager sends routing=0 + * But the audioflingercontinues to write data until standby time + * (3sec). As the HDMI core is turned off, the write gets blocked. + * Avoid this by routing audio to speaker until standby. + */ + if ((out->devices == AUDIO_DEVICE_OUT_AUX_DIGITAL || + out->devices == AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET) && + val == AUDIO_DEVICE_NONE) { + val = AUDIO_DEVICE_OUT_SPEAKER; + } + + /* + * select_devices() call below switches all the usecases on the same + * backend to the new device. Refer to check_usecases_codec_backend() in + * the select_devices(). But how do we undo this? + * + * For example, music playback is active on headset (deep-buffer usecase) + * and if we go to ringtones and select a ringtone, low-latency usecase + * will be started on headset+speaker. As we can't enable headset+speaker + * and headset devices at the same time, select_devices() switches the music + * playback to headset+speaker while starting low-lateny usecase for ringtone. + * So when the ringtone playback is completed, how do we undo the same? + * + * We are relying on the out_set_parameters() call on deep-buffer output, + * once the ringtone playback is ended. + * NOTE: We should not check if the current devices are same as new devices. + * Because select_devices() must be called to switch back the music + * playback to headset. + */ + if (val != 0) { + out->devices = val; + + if (!out->standby) + select_devices(adev, out->usecase); + + if ((adev->mode == AUDIO_MODE_IN_CALL) && + output_drives_call(adev, out)) { + adev->current_call_output = out; + if (!voice_is_in_call(adev)) + ret = voice_start_call(adev); + else + voice_update_devices_for_all_voice_usecases(adev); + } + } + + pthread_mutex_unlock(&adev->lock); + pthread_mutex_unlock(&out->lock); + } + + if (out == adev->primary_output) { + pthread_mutex_lock(&adev->lock); + audio_extn_set_parameters(adev, parms); + pthread_mutex_unlock(&adev->lock); + } + if (is_offload_usecase(out->usecase)) { + pthread_mutex_lock(&out->lock); + parse_compress_metadata(out, parms); + pthread_mutex_unlock(&out->lock); + } + + str_parms_destroy(parms); +error: + ALOGV("%s: exit: code(%d)", __func__, ret); + return ret; +} + +static char* out_get_parameters(const struct audio_stream *stream, const char *keys) +{ + struct stream_out *out = (struct stream_out *)stream; + struct str_parms *query = str_parms_create_str(keys); + char *str; + char value[256]; + struct str_parms *reply = str_parms_create(); + size_t i, j; + int ret; + bool first = true; + + if (!query || !reply) { + ALOGE("out_get_parameters: failed to allocate mem for query or reply"); + return NULL; + } + + ALOGV("%s: enter: keys - %s", __func__, keys); + ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_SUP_CHANNELS, value, sizeof(value)); + if (ret >= 0) { + value[0] = '\0'; + i = 0; + while (out->supported_channel_masks[i] != 0) { + for (j = 0; j < ARRAY_SIZE(out_channels_name_to_enum_table); j++) { + if (out_channels_name_to_enum_table[j].value == out->supported_channel_masks[i]) { + if (!first) { + strcat(value, "|"); + } + strcat(value, out_channels_name_to_enum_table[j].name); + first = false; + break; + } + } + i++; + } + str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_CHANNELS, value); + str = str_parms_to_str(reply); + } else { + voice_extn_out_get_parameters(out, query, reply); + str = str_parms_to_str(reply); + if (!strncmp(str, "", sizeof(""))) { + free(str); + str = strdup(keys); + } + } + str_parms_destroy(query); + str_parms_destroy(reply); + ALOGV("%s: exit: returns - %s", __func__, str); + return str; +} + +static uint32_t out_get_latency(const struct audio_stream_out *stream) +{ + struct stream_out *out = (struct stream_out *)stream; + + if (is_offload_usecase(out->usecase)) + return COMPRESS_OFFLOAD_PLAYBACK_LATENCY; + + return (out->config.period_count * out->config.period_size * 1000) / + (out->config.rate); +} + +static int out_set_volume(struct audio_stream_out *stream, float left, + float right) +{ + struct stream_out *out = (struct stream_out *)stream; + int volume[2]; + + 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 (is_offload_usecase(out->usecase)) { + char mixer_ctl_name[128]; + struct audio_device *adev = out->dev; + struct mixer_ctl *ctl; + int pcm_device_id = platform_get_pcm_device_id(out->usecase, + PCM_PLAYBACK); + + snprintf(mixer_ctl_name, sizeof(mixer_ctl_name), + "Compress Playback %d Volume", pcm_device_id); + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + return -EINVAL; + } + volume[0] = (int)(left * COMPRESS_PLAYBACK_VOLUME_MAX); + volume[1] = (int)(right * COMPRESS_PLAYBACK_VOLUME_MAX); + mixer_ctl_set_array(ctl, volume, sizeof(volume)/sizeof(volume[0])); + return 0; + } + + return -ENOSYS; +} + +static ssize_t out_write(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; + int snd_scard_state = get_snd_card_state(adev); + ssize_t ret = 0; + + pthread_mutex_lock(&out->lock); + + if (SND_CARD_STATE_OFFLINE == snd_scard_state) { + // increase written size during SSR to avoid mismatch + // with the written frames count in AF + if (!is_offload_usecase(out->usecase)) + out->written += bytes / (out->config.channels * sizeof(short)); + + if (out->pcm) { + ALOGD(" %s: sound card is not active/SSR state", __func__); + ret= -EIO; + goto exit; + } else if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD) { + //during SSR for compress usecase we should return error to flinger + ALOGD(" copl %s: sound card is not active/SSR state", __func__); + pthread_mutex_unlock(&out->lock); + return -ENETRESET; + } + } + + if (out->standby) { + out->standby = false; + pthread_mutex_lock(&adev->lock); + if (out->usecase == USECASE_COMPRESS_VOIP_CALL) + ret = voice_extn_compress_voip_start_output_stream(out); + else + ret = start_output_stream(out); + pthread_mutex_unlock(&adev->lock); + /* ToDo: If use case is compress offload should return 0 */ + if (ret != 0) { + out->standby = true; + goto exit; + } + } + + if (is_offload_usecase(out->usecase)) { + ALOGD("copl(%p): writing buffer (%zu bytes) to compress device", out, bytes); + if (out->send_new_metadata) { + ALOGD("copl(%p):send new gapless metadata", out); + compress_set_gapless_metadata(out->compr, &out->gapless_mdata); + out->send_new_metadata = 0; + } + + ret = compress_write(out->compr, buffer, bytes); + if (ret < 0) + ret = -errno; + ALOGVV("%s: writing buffer (%d bytes) to compress device returned %d", __func__, bytes, ret); + if (ret >= 0 && ret < (ssize_t)bytes) { + ALOGD("No space available in compress driver, post msg to cb thread"); + send_offload_cmd_l(out, OFFLOAD_CMD_WAIT_FOR_BUFFER); + } else if (-ENETRESET == ret) { + ALOGE("copl %s: received sound card offline state on compress write", __func__); + set_snd_card_state(adev,SND_CARD_STATE_OFFLINE); + pthread_mutex_unlock(&out->lock); + out_standby(&out->stream.common); + return ret; + } + if (!out->playback_started && ret >= 0) { + compress_start(out->compr); + out->playback_started = 1; + out->offload_state = OFFLOAD_STATE_PLAYING; + } + pthread_mutex_unlock(&out->lock); + return ret; + } else { + if (out->pcm) { + if (out->muted) + memset((void *)buffer, 0, bytes); + ALOGVV("%s: writing buffer (%d bytes) to pcm device", __func__, bytes); + if (out->usecase == USECASE_AUDIO_PLAYBACK_AFE_PROXY) + ret = pcm_mmap_write(out->pcm, (void *)buffer, bytes); + else + ret = pcm_write(out->pcm, (void *)buffer, bytes); + if (ret < 0) + ret = -errno; + else if (ret == 0) + out->written += bytes / (out->config.channels * sizeof(short)); + } + } + +exit: + /* ToDo: There may be a corner case when SSR happens back to back during + start/stop. Need to post different error to handle that. */ + if (-ENETRESET == ret) { + set_snd_card_state(adev,SND_CARD_STATE_OFFLINE); + } + + pthread_mutex_unlock(&out->lock); + + if (ret != 0) { + if (out->pcm) + ALOGE("%s: error %ld - %s", __func__, ret, pcm_get_error(out->pcm)); + if (out->usecase == USECASE_COMPRESS_VOIP_CALL) { + pthread_mutex_lock(&adev->lock); + voice_extn_compress_voip_close_output_stream(&out->stream.common); + pthread_mutex_unlock(&adev->lock); + out->standby = true; + } + out_standby(&out->stream.common); + usleep(bytes * 1000000 / audio_stream_out_frame_size(stream) / + out_get_sample_rate(&out->stream.common)); + + } + return bytes; +} + +static int out_get_render_position(const struct audio_stream_out *stream, + uint32_t *dsp_frames) +{ + struct stream_out *out = (struct stream_out *)stream; + struct audio_device *adev = out->dev; + + if (dsp_frames == NULL) + return -EINVAL; + + *dsp_frames = 0; + if (is_offload_usecase(out->usecase)) { + ssize_t ret = 0; + pthread_mutex_lock(&out->lock); + if (out->compr != NULL) { + ret = compress_get_tstamp(out->compr, (unsigned long *)dsp_frames, + &out->sample_rate); + if (ret < 0) + ret = -errno; + ALOGVV("%s rendered frames %d sample_rate %d", + __func__, *dsp_frames, out->sample_rate); + } + pthread_mutex_unlock(&out->lock); + if (-ENETRESET == ret) { + ALOGE(" ERROR: sound card not active Unable to get time stamp from compress driver"); + set_snd_card_state(adev,SND_CARD_STATE_OFFLINE); + return -EINVAL; + } else if(ret < 0) { + ALOGE(" ERROR: Unable to get time stamp from compress driver"); + return -EINVAL; + } else if (get_snd_card_state(adev) == SND_CARD_STATE_OFFLINE){ + /* + * Handle corner case where compress session is closed during SSR + * and timestamp is queried + */ + ALOGE(" ERROR: sound card not active, return error"); + return -EINVAL; + } else { + return 0; + } + } else if (audio_is_linear_pcm(out->format)) { + *dsp_frames = out->written; + return 0; + } else + return -EINVAL; +} + +static int out_add_audio_effect(const struct audio_stream *stream __unused, + effect_handle_t effect __unused) +{ + return 0; +} + +static int out_remove_audio_effect(const struct audio_stream *stream __unused, + effect_handle_t effect __unused) +{ + return 0; +} + +static int out_get_next_write_timestamp(const struct audio_stream_out *stream __unused, + int64_t *timestamp __unused) +{ + return -EINVAL; +} + +static int out_get_presentation_position(const struct audio_stream_out *stream, + uint64_t *frames, struct timespec *timestamp) +{ + struct stream_out *out = (struct stream_out *)stream; + int ret = -1; + unsigned long dsp_frames; + + pthread_mutex_lock(&out->lock); + + if (is_offload_usecase(out->usecase)) { + if (out->compr != NULL) { + ret = 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; + if (ret < 0) + ret = -errno; + if (-ENETRESET == ret) { + ALOGE(" ERROR: sound card not active Unable to get time stamp from compress driver"); + set_snd_card_state(adev,SND_CARD_STATE_OFFLINE); + ret = -EINVAL; + } else + ret = 0; + + /* this is the best we can do */ + clock_gettime(CLOCK_MONOTONIC, timestamp); + } + } else { + if (out->pcm) { + unsigned int avail; + if (pcm_get_htimestamp(out->pcm, &avail, timestamp) == 0) { + size_t kernel_buffer_size = out->config.period_size * out->config.period_count; + int64_t signed_frames = out->written - kernel_buffer_size + avail; + // This adjustment accounts for buffering after app processor. + // It is based on estimated DSP latency per use case, rather than exact. + signed_frames -= + (platform_render_latency(out->usecase) * out->sample_rate / 1000000LL); + + // It would be unusual for this value to be negative, but check just in case ... + if (signed_frames >= 0) { + *frames = signed_frames; + ret = 0; + } + } + } + } + + pthread_mutex_unlock(&out->lock); + + return ret; +} + +static int out_set_callback(struct audio_stream_out *stream, + stream_callback_t callback, void *cookie) +{ + struct stream_out *out = (struct stream_out *)stream; + + ALOGV("%s", __func__); + pthread_mutex_lock(&out->lock); + out->offload_callback = callback; + out->offload_cookie = cookie; + pthread_mutex_unlock(&out->lock); + return 0; +} + +static int out_pause(struct audio_stream_out* stream) +{ + struct stream_out *out = (struct stream_out *)stream; + int status = -ENOSYS; + ALOGV("%s", __func__); + if (is_offload_usecase(out->usecase)) { + ALOGD("copl(%p):pause compress driver", out); + pthread_mutex_lock(&out->lock); + if (out->compr != NULL && out->offload_state == OFFLOAD_STATE_PLAYING) { + struct audio_device *adev = out->dev; + int snd_scard_state = get_snd_card_state(adev); + + if (SND_CARD_STATE_ONLINE == snd_scard_state) + status = compress_pause(out->compr); + + out->offload_state = OFFLOAD_STATE_PAUSED; + } + pthread_mutex_unlock(&out->lock); + } + return status; +} + +static int out_resume(struct audio_stream_out* stream) +{ + struct stream_out *out = (struct stream_out *)stream; + int status = -ENOSYS; + ALOGV("%s", __func__); + if (is_offload_usecase(out->usecase)) { + ALOGD("copl(%p):resume compress driver", out); + status = 0; + pthread_mutex_lock(&out->lock); + if (out->compr != NULL && out->offload_state == OFFLOAD_STATE_PAUSED) { + struct audio_device *adev = out->dev; + int snd_scard_state = get_snd_card_state(adev); + + if (SND_CARD_STATE_ONLINE == snd_scard_state) + status = compress_resume(out->compr); + + out->offload_state = OFFLOAD_STATE_PLAYING; + } + pthread_mutex_unlock(&out->lock); + } + return status; +} + +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 (is_offload_usecase(out->usecase)) { + pthread_mutex_lock(&out->lock); + 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; +} + +static int out_flush(struct audio_stream_out* stream) +{ + struct stream_out *out = (struct stream_out *)stream; + ALOGV("%s", __func__); + if (is_offload_usecase(out->usecase)) { + ALOGD("copl(%p):calling compress flush", out); + pthread_mutex_lock(&out->lock); + stop_compressed_output_l(out); + pthread_mutex_unlock(&out->lock); + ALOGD("copl(%p):out of compress flush", out); + return 0; + } + return -ENOSYS; +} + +/** audio_stream_in implementation **/ +static uint32_t in_get_sample_rate(const struct audio_stream *stream) +{ + struct stream_in *in = (struct stream_in *)stream; + + return in->config.rate; +} + +static int in_set_sample_rate(struct audio_stream *stream __unused, + uint32_t rate __unused) +{ + return -ENOSYS; +} + +static size_t in_get_buffer_size(const struct audio_stream *stream) +{ + struct stream_in *in = (struct stream_in *)stream; + + if(in->usecase == USECASE_COMPRESS_VOIP_CALL) + return voice_extn_compress_voip_in_get_buffer_size(in); + else if(audio_extn_compr_cap_usecase_supported(in->usecase)) + return audio_extn_compr_cap_get_buffer_size(in->config.format); + + return in->config.period_size * + audio_stream_in_frame_size((const struct audio_stream_in *)stream); +} + +static uint32_t in_get_channels(const struct audio_stream *stream) +{ + struct stream_in *in = (struct stream_in *)stream; + + return in->channel_mask; +} + +static audio_format_t in_get_format(const struct audio_stream *stream) +{ + struct stream_in *in = (struct stream_in *)stream; + + return in->format; +} + +static int in_set_format(struct audio_stream *stream __unused, + audio_format_t format __unused) +{ + return -ENOSYS; +} + +static int in_standby(struct audio_stream *stream) +{ + struct stream_in *in = (struct stream_in *)stream; + struct audio_device *adev = in->dev; + int status = 0; + ALOGD("%s: enter: stream (%p) usecase(%d: %s)", __func__, + stream, in->usecase, use_case_table[in->usecase]); + + if (in->usecase == USECASE_COMPRESS_VOIP_CALL) { + /* Ignore standby in case of voip call because the voip input + * stream is closed in adev_close_input_stream() + */ + ALOGV("%s: Ignore Standby in VOIP call", __func__); + return status; + } + + pthread_mutex_lock(&in->lock); + if (!in->standby && in->is_st_session) { + ALOGD("%s: sound trigger pcm stop lab", __func__); + audio_extn_sound_trigger_stop_lab(in); + in->standby = 1; + } + + if (!in->standby) { + pthread_mutex_lock(&adev->lock); + in->standby = true; + if (in->pcm) { + pcm_close(in->pcm); + in->pcm = NULL; + } + status = stop_input_stream(in); + pthread_mutex_unlock(&adev->lock); + } + pthread_mutex_unlock(&in->lock); + ALOGV("%s: exit: status(%d)", __func__, status); + return status; +} + +static int in_dump(const struct audio_stream *stream __unused, + int fd __unused) +{ + return 0; +} + +static int in_set_parameters(struct audio_stream *stream, const char *kvpairs) +{ + struct stream_in *in = (struct stream_in *)stream; + struct audio_device *adev = in->dev; + struct str_parms *parms; + char *str; + char value[32]; + int ret = 0, val = 0, err; + + ALOGD("%s: enter: kvpairs=%s", __func__, kvpairs); + parms = str_parms_create_str(kvpairs); + + if (!parms) + goto error; + pthread_mutex_lock(&in->lock); + pthread_mutex_lock(&adev->lock); + + err = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_INPUT_SOURCE, value, sizeof(value)); + if (err >= 0) { + val = atoi(value); + /* no audio source uses val == 0 */ + if ((in->source != val) && (val != 0)) { + in->source = val; + if ((in->source == AUDIO_SOURCE_VOICE_COMMUNICATION) && + (in->dev->mode == AUDIO_MODE_IN_COMMUNICATION) && + (voice_extn_compress_voip_is_format_supported(in->format)) && + (in->config.rate == 8000 || in->config.rate == 16000) && + (audio_channel_count_from_in_mask(in->channel_mask) == 1)) { + err = voice_extn_compress_voip_open_input_stream(in); + if (err != 0) { + ALOGE("%s: Compress voip input cannot be opened, error:%d", + __func__, err); + } + } + } + } + + err = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value)); + if (err >= 0) { + val = atoi(value); + if (((int)in->device != val) && (val != 0)) { + in->device = val; + /* If recording is in progress, change the tx device to new device */ + if (!in->standby && !in->is_st_session) + ret = select_devices(adev, in->usecase); + } + } + +done: + pthread_mutex_unlock(&adev->lock); + pthread_mutex_unlock(&in->lock); + + str_parms_destroy(parms); +error: + ALOGV("%s: exit: status(%d)", __func__, ret); + return ret; +} + +static char* in_get_parameters(const struct audio_stream *stream, + const char *keys) +{ + struct stream_in *in = (struct stream_in *)stream; + struct str_parms *query = str_parms_create_str(keys); + char *str; + char value[256]; + struct str_parms *reply = str_parms_create(); + + if (!query || !reply) { + ALOGE("in_get_parameters: failed to create query or reply"); + return NULL; + } + + ALOGV("%s: enter: keys - %s", __func__, keys); + + voice_extn_in_get_parameters(in, query, reply); + + str = str_parms_to_str(reply); + str_parms_destroy(query); + str_parms_destroy(reply); + + ALOGV("%s: exit: returns - %s", __func__, str); + return str; +} + +static int in_set_gain(struct audio_stream_in *stream __unused, + float gain __unused) +{ + return 0; +} + +static ssize_t in_read(struct audio_stream_in *stream, void *buffer, + size_t bytes) +{ + struct stream_in *in = (struct stream_in *)stream; + struct audio_device *adev = in->dev; + int i, ret = -1; + int snd_scard_state = get_snd_card_state(adev); + + pthread_mutex_lock(&in->lock); + + if (in->pcm) { + if(SND_CARD_STATE_OFFLINE == snd_scard_state) { + ALOGD(" %s: sound card is not active/SSR state", __func__); + ret= -EIO;; + goto exit; + } + } + + if (in->standby) { + if (!in->is_st_session) { + pthread_mutex_lock(&adev->lock); + if (in->usecase == USECASE_COMPRESS_VOIP_CALL) + ret = voice_extn_compress_voip_start_input_stream(in); + else + ret = start_input_stream(in); + pthread_mutex_unlock(&adev->lock); + if (ret != 0) { + goto exit; + } + } + in->standby = 0; + } + + if (in->pcm) { + if (audio_extn_ssr_get_enabled() && + audio_channel_count_from_in_mask(in->channel_mask) == 6) + ret = audio_extn_ssr_read(stream, buffer, bytes); + else if (audio_extn_compr_cap_usecase_supported(in->usecase)) + ret = audio_extn_compr_cap_read(in, buffer, bytes); + else if (in->usecase == USECASE_AUDIO_RECORD_AFE_PROXY) + ret = pcm_mmap_read(in->pcm, buffer, bytes); + else + ret = pcm_read(in->pcm, buffer, bytes); + if (ret < 0) + ret = -errno; + } + + /* + * Instead of writing zeroes here, we could trust the hardware + * to always provide zeroes when muted. + */ + if (ret == 0 && voice_get_mic_mute(adev) && !voice_is_in_call_rec_stream(in)) + memset(buffer, 0, bytes); + +exit: + /* ToDo: There may be a corner case when SSR happens back to back during + start/stop. Need to post different error to handle that. */ + if (-ENETRESET == ret) { + set_snd_card_state(adev,SND_CARD_STATE_OFFLINE); + } + pthread_mutex_unlock(&in->lock); + + if (ret != 0) { + if (in->usecase == USECASE_COMPRESS_VOIP_CALL) { + pthread_mutex_lock(&adev->lock); + voice_extn_compress_voip_close_input_stream(&in->stream.common); + pthread_mutex_unlock(&adev->lock); + in->standby = true; + } + memset(buffer, 0, bytes); + in_standby(&in->stream.common); + ALOGV("%s: read failed status %d- sleeping for buffer duration", __func__, ret); + usleep(bytes * 1000000 / audio_stream_in_frame_size(stream) / + in_get_sample_rate(&in->stream.common)); + } + return bytes; +} + +static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream __unused) +{ + return 0; +} + +static int add_remove_audio_effect(const struct audio_stream *stream, + effect_handle_t effect, + bool enable) +{ + struct stream_in *in = (struct stream_in *)stream; + int status = 0; + effect_descriptor_t desc; + + status = (*effect)->get_descriptor(effect, &desc); + if (status != 0) + return status; + + pthread_mutex_lock(&in->lock); + pthread_mutex_lock(&in->dev->lock); + if ((in->source == AUDIO_SOURCE_VOICE_COMMUNICATION) && + in->enable_aec != enable && + (memcmp(&desc.type, FX_IID_AEC, sizeof(effect_uuid_t)) == 0)) { + in->enable_aec = enable; + if (!in->standby) + select_devices(in->dev, in->usecase); + } + if (in->enable_ns != enable && + (memcmp(&desc.type, FX_IID_NS, sizeof(effect_uuid_t)) == 0)) { + in->enable_ns = enable; + if (!in->standby) + select_devices(in->dev, in->usecase); + } + pthread_mutex_unlock(&in->dev->lock); + pthread_mutex_unlock(&in->lock); + + return 0; +} + +static int in_add_audio_effect(const struct audio_stream *stream, + effect_handle_t effect) +{ + ALOGV("%s: effect %p", __func__, effect); + return add_remove_audio_effect(stream, effect, true); +} + +static int in_remove_audio_effect(const struct audio_stream *stream, + effect_handle_t effect) +{ + ALOGV("%s: effect %p", __func__, effect); + return add_remove_audio_effect(stream, effect, false); +} + +static int adev_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 __unused) +{ + struct audio_device *adev = (struct audio_device *)dev; + struct stream_out *out; + int i, ret = 0; + audio_format_t format; + + *stream_out = NULL; + + if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) && + (SND_CARD_STATE_OFFLINE == get_snd_card_state(adev))) { + ALOGE(" sound card is not active rejecting compress output open request"); + return -EINVAL; + } + + out = (struct stream_out *)calloc(1, sizeof(struct stream_out)); + + ALOGD("%s: enter: sample_rate(%d) channel_mask(%#x) devices(%#x) flags(%#x)\ + stream_handle(%p)",__func__, config->sample_rate, config->channel_mask, + devices, flags, &out->stream); + + + if (!out) { + return -ENOMEM; + } + + if (devices == AUDIO_DEVICE_NONE) + devices = AUDIO_DEVICE_OUT_SPEAKER; + + out->flags = flags; + out->devices = devices; + out->dev = adev; + format = out->format = config->format; + out->sample_rate = config->sample_rate; + out->channel_mask = AUDIO_CHANNEL_OUT_STEREO; + out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_STEREO; + out->handle = handle; + out->bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH; + + /* Init use case and pcm_config */ + if ((out->flags == AUDIO_OUTPUT_FLAG_DIRECT) && + (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL || + out->devices & AUDIO_DEVICE_OUT_PROXY)) { + + pthread_mutex_lock(&adev->lock); + if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) + ret = read_hdmi_channel_masks(out); + + if (out->devices & AUDIO_DEVICE_OUT_PROXY) + ret = audio_extn_read_afe_proxy_channel_masks(out); + pthread_mutex_unlock(&adev->lock); + if (ret != 0) + goto error_open; + + if (config->sample_rate == 0) + config->sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE; + if (config->channel_mask == 0) + config->channel_mask = AUDIO_CHANNEL_OUT_5POINT1; + + out->channel_mask = config->channel_mask; + out->sample_rate = config->sample_rate; + out->usecase = USECASE_AUDIO_PLAYBACK_MULTI_CH; + out->config = pcm_config_hdmi_multi; + out->config.rate = config->sample_rate; + out->config.channels = audio_channel_count_from_out_mask(out->channel_mask); + out->config.period_size = HDMI_MULTI_PERIOD_BYTES / (out->config.channels * 2); + } else if ((out->dev->mode == AUDIO_MODE_IN_COMMUNICATION) && + (out->flags == (AUDIO_OUTPUT_FLAG_DIRECT | AUDIO_OUTPUT_FLAG_VOIP_RX)) && + (voice_extn_compress_voip_is_config_supported(config))) { + ret = voice_extn_compress_voip_open_output_stream(out); + if (ret != 0) { + ALOGE("%s: Compress voip output cannot be opened, error:%d", + __func__, ret); + goto error_open; + } + } else if (out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) { + if (config->offload_info.version != AUDIO_INFO_INITIALIZER.version || + config->offload_info.size != AUDIO_INFO_INITIALIZER.size) { + ALOGE("%s: Unsupported Offload information", __func__); + ret = -EINVAL; + goto error_open; + } + if (!is_supported_format(config->offload_info.format) && + !audio_extn_is_dolby_format(config->offload_info.format)) { + ALOGE("%s: Unsupported audio format", __func__); + ret = -EINVAL; + goto error_open; + } + + out->compr_config.codec = (struct snd_codec *) + calloc(1, sizeof(struct snd_codec)); + + if (!out->compr_config.codec) { + ret = -ENOMEM; + goto error_open; + } + + out->usecase = get_offload_usecase(adev); + if (config->offload_info.channel_mask) + out->channel_mask = config->offload_info.channel_mask; + else if (config->channel_mask) { + out->channel_mask = config->channel_mask; + config->offload_info.channel_mask = config->channel_mask; + } + format = out->format = config->offload_info.format; + out->sample_rate = config->offload_info.sample_rate; + + out->stream.set_callback = out_set_callback; + out->stream.pause = out_pause; + out->stream.resume = out_resume; + out->stream.drain = out_drain; + out->stream.flush = out_flush; + out->bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH; + + if (audio_extn_is_dolby_format(config->offload_info.format)) + out->compr_config.codec->id = + audio_extn_dolby_get_snd_codec_id(adev, out, + config->offload_info.format); + else + out->compr_config.codec->id = + get_snd_codec_id(config->offload_info.format); + if (audio_is_offload_pcm(config->offload_info.format)) { + out->compr_config.fragment_size = + platform_get_pcm_offload_buffer_size(&config->offload_info); + } else { + out->compr_config.fragment_size = + platform_get_compress_offload_buffer_size(&config->offload_info); + } + out->compr_config.fragments = COMPRESS_OFFLOAD_NUM_FRAGMENTS; + out->compr_config.codec->sample_rate = + compress_get_alsa_rate(config->offload_info.sample_rate); + out->compr_config.codec->bit_rate = + config->offload_info.bit_rate; + out->compr_config.codec->ch_in = + audio_channel_count_from_out_mask(config->channel_mask); + out->compr_config.codec->ch_out = out->compr_config.codec->ch_in; + out->bit_width = PCM_OUTPUT_BIT_WIDTH; + + if (config->offload_info.format == AUDIO_FORMAT_AAC) + out->compr_config.codec->format = SND_AUDIOSTREAMFORMAT_RAW; + if (config->offload_info.format == AUDIO_FORMAT_PCM_16_BIT_OFFLOAD) + out->compr_config.codec->format = SNDRV_PCM_FORMAT_S16_LE; + if(config->offload_info.format == AUDIO_FORMAT_PCM_24_BIT_OFFLOAD) + out->compr_config.codec->format = SNDRV_PCM_FORMAT_S24_LE; + + if (out->bit_width == 24) { + out->compr_config.codec->format = SNDRV_PCM_FORMAT_S24_LE; + } + + if (config->offload_info.format == AUDIO_FORMAT_FLAC) + out->compr_config.codec->options.flac_dec.sample_size = PCM_OUTPUT_BIT_WIDTH; + + if (flags & AUDIO_OUTPUT_FLAG_NON_BLOCKING) + out->non_blocking = 1; + + out->send_new_metadata = 1; + out->offload_state = OFFLOAD_STATE_IDLE; + out->playback_started = 0; + + create_offload_callback_thread(out); + ALOGV("%s: offloaded output offload_info version %04x bit rate %d", + __func__, config->offload_info.version, + config->offload_info.bit_rate); + //Decide if we need to use gapless mode by default + check_and_set_gapless_mode(adev); + + } else if (out->flags & AUDIO_OUTPUT_FLAG_INCALL_MUSIC) { + ret = voice_check_and_set_incall_music_usecase(adev, out); + if (ret != 0) { + ALOGE("%s: Incall music delivery usecase cannot be set error:%d", + __func__, ret); + goto error_open; + } + } else if (out->devices == AUDIO_DEVICE_OUT_TELEPHONY_TX) { + if (config->sample_rate == 0) + config->sample_rate = AFE_PROXY_SAMPLING_RATE; + if (config->sample_rate != 48000 && config->sample_rate != 16000 && + config->sample_rate != 8000) { + config->sample_rate = AFE_PROXY_SAMPLING_RATE; + ret = -EINVAL; + goto error_open; + } + out->sample_rate = config->sample_rate; + out->config.rate = config->sample_rate; + if (config->format == AUDIO_FORMAT_DEFAULT) + config->format = AUDIO_FORMAT_PCM_16_BIT; + if (config->format != AUDIO_FORMAT_PCM_16_BIT) { + config->format = AUDIO_FORMAT_PCM_16_BIT; + ret = -EINVAL; + goto error_open; + } + out->format = config->format; + out->usecase = USECASE_AUDIO_PLAYBACK_AFE_PROXY; + out->config = pcm_config_afe_proxy_playback; + adev->voice_tx_output = out; + } else if (out->flags & AUDIO_OUTPUT_FLAG_FAST) { + format = AUDIO_FORMAT_PCM_16_BIT; + out->usecase = USECASE_AUDIO_PLAYBACK_LOW_LATENCY; + out->config = pcm_config_low_latency; + out->sample_rate = out->config.rate; + } else { + /* primary path is the default path selected if no other outputs are available/suitable */ + format = AUDIO_FORMAT_PCM_16_BIT; + out->usecase = USECASE_AUDIO_PLAYBACK_PRIMARY; + out->config = pcm_config_deep_buffer; + out->sample_rate = out->config.rate; + } + + ALOGV("%s devices %d,flags %x, format %x, out->sample_rate %d, out->bit_width %d", + __func__, devices, flags, format, out->sample_rate, out->bit_width); + audio_extn_utils_update_stream_app_type_cfg(adev->platform, + &adev->streams_output_cfg_list, + devices, flags, format, out->sample_rate, + out->bit_width, &out->app_type_cfg); + if ((out->usecase == USECASE_AUDIO_PLAYBACK_PRIMARY) || + (flags & AUDIO_OUTPUT_FLAG_PRIMARY)) { + /* Ensure the default output is not selected twice */ + if(adev->primary_output == NULL) + adev->primary_output = out; + else { + ALOGE("%s: Primary output is already opened", __func__); + ret = -EEXIST; + goto error_open; + } + } + + /* Check if this usecase is already existing */ + pthread_mutex_lock(&adev->lock); + if ((get_usecase_from_list(adev, out->usecase) != NULL) && + (out->usecase != USECASE_COMPRESS_VOIP_CALL)) { + ALOGE("%s: Usecase (%d) is already present", __func__, out->usecase); + pthread_mutex_unlock(&adev->lock); + ret = -EEXIST; + goto error_open; + } + pthread_mutex_unlock(&adev->lock); + + out->stream.common.get_sample_rate = out_get_sample_rate; + out->stream.common.set_sample_rate = out_set_sample_rate; + out->stream.common.get_buffer_size = out_get_buffer_size; + out->stream.common.get_channels = out_get_channels; + out->stream.common.get_format = out_get_format; + out->stream.common.set_format = out_set_format; + out->stream.common.standby = out_standby; + out->stream.common.dump = out_dump; + out->stream.common.set_parameters = out_set_parameters; + out->stream.common.get_parameters = out_get_parameters; + out->stream.common.add_audio_effect = out_add_audio_effect; + out->stream.common.remove_audio_effect = out_remove_audio_effect; + out->stream.get_latency = out_get_latency; + out->stream.set_volume = out_set_volume; + out->stream.write = out_write; + out->stream.get_render_position = out_get_render_position; + out->stream.get_next_write_timestamp = out_get_next_write_timestamp; + out->stream.get_presentation_position = out_get_presentation_position; + + out->standby = 1; + /* out->muted = false; by calloc() */ + /* out->written = 0; by calloc() */ + + pthread_mutex_init(&out->lock, (const pthread_mutexattr_t *) NULL); + pthread_cond_init(&out->cond, (const pthread_condattr_t *) NULL); + + config->format = out->stream.common.get_format(&out->stream.common); + config->channel_mask = out->stream.common.get_channels(&out->stream.common); + config->sample_rate = out->stream.common.get_sample_rate(&out->stream.common); + + *stream_out = &out->stream; + ALOGD("%s: Stream (%p) picks up usecase (%s)", __func__, &out->stream, + use_case_table[out->usecase]); + ALOGV("%s: exit", __func__); + return 0; + +error_open: + free(out); + *stream_out = NULL; + ALOGD("%s: exit: ret %d", __func__, ret); + return ret; +} + +static void adev_close_output_stream(struct audio_hw_device *dev __unused, + struct audio_stream_out *stream) +{ + struct stream_out *out = (struct stream_out *)stream; + struct audio_device *adev = out->dev; + int ret = 0; + + ALOGD("%s: enter:stream_handle(%p)",__func__, out); + + if (out->usecase == USECASE_COMPRESS_VOIP_CALL) { + pthread_mutex_lock(&adev->lock); + ret = voice_extn_compress_voip_close_output_stream(&stream->common); + pthread_mutex_unlock(&adev->lock); + if(ret != 0) + ALOGE("%s: Compress voip output cannot be closed, error:%d", + __func__, ret); + } else + out_standby(&stream->common); + + if (is_offload_usecase(out->usecase)) { + destroy_offload_callback_thread(out); + free_offload_usecase(adev, out->usecase); + if (out->compr_config.codec != NULL) + free(out->compr_config.codec); + } + + if (adev->voice_tx_output == out) + adev->voice_tx_output = NULL; + + pthread_cond_destroy(&out->cond); + pthread_mutex_destroy(&out->lock); + free(stream); + ALOGV("%s: exit", __func__); +} + +static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs) +{ + struct audio_device *adev = (struct audio_device *)dev; + struct str_parms *parms; + char *str; + char value[32]; + int val; + int ret; + int status = 0; + + ALOGD("%s: enter: %s", __func__, kvpairs); + parms = str_parms_create_str(kvpairs); + + if (!parms) + goto error; + ret = str_parms_get_str(parms, "SND_CARD_STATUS", value, sizeof(value)); + if (ret >= 0) { + char *snd_card_status = value+2; + if (strstr(snd_card_status, "OFFLINE")) { + struct listnode *node; + struct audio_usecase *usecase; + + ALOGD("Received sound card OFFLINE status"); + set_snd_card_state(adev,SND_CARD_STATE_OFFLINE); + + pthread_mutex_lock(&adev->lock); + //close compress session on OFFLINE status + usecase = get_usecase_from_list(adev,USECASE_AUDIO_PLAYBACK_OFFLOAD); + if (usecase && usecase->stream.out) { + ALOGD(" %s closing compress session on OFFLINE state", __func__); + + struct stream_out *out = usecase->stream.out; + + pthread_mutex_unlock(&adev->lock); + out_standby(&out->stream.common); + } else + pthread_mutex_unlock(&adev->lock); + } else if (strstr(snd_card_status, "ONLINE")) { + ALOGD("Received sound card ONLINE status"); + set_snd_card_state(adev,SND_CARD_STATE_ONLINE); + } + } + + pthread_mutex_lock(&adev->lock); + status = voice_set_parameters(adev, parms); + if (status != 0) + goto done; + + status = platform_set_parameters(adev->platform, parms); + if (status != 0) + goto done; + + ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_BT_NREC, value, sizeof(value)); + if (ret >= 0) { + /* When set to false, HAL should disable EC and NS */ + if (strcmp(value, AUDIO_PARAMETER_VALUE_ON) == 0) + adev->bluetooth_nrec = true; + else + adev->bluetooth_nrec = false; + } + + ret = str_parms_get_str(parms, "screen_state", value, sizeof(value)); + if (ret >= 0) { + if (strcmp(value, AUDIO_PARAMETER_VALUE_ON) == 0) + adev->screen_off = false; + else + adev->screen_off = true; + } + + ret = str_parms_get_int(parms, "rotation", &val); + if (ret >= 0) { + bool reverse_speakers = false; + switch(val) { + // FIXME: note that the code below assumes that the speakers are in the correct placement + // relative to the user when the device is rotated 90deg from its default rotation. This + // assumption is device-specific, not platform-specific like this code. + case 270: + reverse_speakers = true; + break; + case 0: + case 90: + case 180: + break; + default: + ALOGE("%s: unexpected rotation of %d", __func__, val); + status = -EINVAL; + } + if (status == 0) { + if (adev->speaker_lr_swap != reverse_speakers) { + adev->speaker_lr_swap = reverse_speakers; + // only update the selected device if there is active pcm playback + struct audio_usecase *usecase; + struct listnode *node; + list_for_each(node, &adev->usecase_list) { + usecase = node_to_item(node, struct audio_usecase, list); + if (usecase->type == PCM_PLAYBACK) { + select_devices(adev, usecase->id); + break; + } + } + } + } + } + + ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_BT_SCO_WB, value, sizeof(value)); + if (ret >= 0) { + if (strcmp(value, AUDIO_PARAMETER_VALUE_ON) == 0) + adev->bt_wb_speech_enabled = true; + else + adev->bt_wb_speech_enabled = false; + } + + audio_extn_set_parameters(adev, parms); + +done: + str_parms_destroy(parms); + pthread_mutex_unlock(&adev->lock); +error: + ALOGV("%s: exit with code(%d)", __func__, status); + return status; +} + +static char* adev_get_parameters(const struct audio_hw_device *dev, + const char *keys) +{ + struct audio_device *adev = (struct audio_device *)dev; + struct str_parms *reply = str_parms_create(); + struct str_parms *query = str_parms_create_str(keys); + char *str; + char value[256] = {0}; + int ret = 0; + + if (!query || !reply) { + ALOGE("adev_get_parameters: failed to create query or reply"); + return NULL; + } + + ret = str_parms_get_str(query, "SND_CARD_STATUS", value, + sizeof(value)); + if (ret >=0) { + int val = 1; + pthread_mutex_lock(&adev->snd_card_status.lock); + if (SND_CARD_STATE_OFFLINE == adev->snd_card_status.state) + val = 0; + pthread_mutex_unlock(&adev->snd_card_status.lock); + str_parms_add_int(reply, "SND_CARD_STATUS", val); + goto exit; + } + + pthread_mutex_lock(&adev->lock); + audio_extn_get_parameters(adev, query, reply); + voice_get_parameters(adev, query, reply); + platform_get_parameters(adev->platform, query, reply); + pthread_mutex_unlock(&adev->lock); + +exit: + str = str_parms_to_str(reply); + str_parms_destroy(query); + str_parms_destroy(reply); + + ALOGV("%s: exit: returns - %s", __func__, str); + return str; +} + +static int adev_init_check(const struct audio_hw_device *dev __unused) +{ + return 0; +} + +static int adev_set_voice_volume(struct audio_hw_device *dev, float volume) +{ + int ret; + struct audio_device *adev = (struct audio_device *)dev; + pthread_mutex_lock(&adev->lock); + /* cache volume */ + ret = voice_set_volume(adev, volume); + pthread_mutex_unlock(&adev->lock); + return ret; +} + +static int adev_set_master_volume(struct audio_hw_device *dev __unused, + float volume __unused) +{ + return -ENOSYS; +} + +static int adev_get_master_volume(struct audio_hw_device *dev __unused, + float *volume __unused) +{ + return -ENOSYS; +} + +static int adev_set_master_mute(struct audio_hw_device *dev __unused, + bool muted __unused) +{ + return -ENOSYS; +} + +static int adev_get_master_mute(struct audio_hw_device *dev __unused, + bool *muted __unused) +{ + return -ENOSYS; +} + +static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t mode) +{ + struct audio_device *adev = (struct audio_device *)dev; + + pthread_mutex_lock(&adev->lock); + if (adev->mode != mode) { + ALOGD("%s: mode %d\n", __func__, mode); + adev->mode = mode; + if ((mode == AUDIO_MODE_NORMAL || mode == AUDIO_MODE_IN_COMMUNICATION) && + voice_is_in_call(adev)) { + voice_stop_call(adev); + adev->current_call_output = NULL; + } + } + pthread_mutex_unlock(&adev->lock); + return 0; +} + +static int adev_set_mic_mute(struct audio_hw_device *dev, bool state) +{ + int ret; + + pthread_mutex_lock(&adev->lock); + ALOGD("%s state %d\n", __func__, state); + ret = voice_set_mic_mute((struct audio_device *)dev, state); + pthread_mutex_unlock(&adev->lock); + + return ret; +} + +static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state) +{ + *state = voice_get_mic_mute((struct audio_device *)dev); + return 0; +} + +static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev __unused, + const struct audio_config *config) +{ + int channel_count = audio_channel_count_from_in_mask(config->channel_mask); + + return get_input_buffer_size(config->sample_rate, config->format, channel_count, + false /* is_low_latency: since we don't know, be conservative */); +} + +static int adev_open_input_stream(struct audio_hw_device *dev, + audio_io_handle_t handle __unused, + audio_devices_t devices, + struct audio_config *config, + struct audio_stream_in **stream_in, + audio_input_flags_t flags __unused, + const char *address __unused, + audio_source_t source __unused) +{ + struct audio_device *adev = (struct audio_device *)dev; + struct stream_in *in; + int ret = 0, buffer_size, frame_size; + int channel_count = audio_channel_count_from_in_mask(config->channel_mask); + bool is_low_latency = false; + + *stream_in = NULL; + if (check_input_parameters(config->sample_rate, config->format, channel_count) != 0) + return -EINVAL; + + in = (struct stream_in *)calloc(1, sizeof(struct stream_in)); + + if (!in) { + ALOGE("failed to allocate input stream"); + return -ENOMEM; + } + + ALOGD("%s: enter: sample_rate(%d) channel_mask(%#x) devices(%#x)\ + stream_handle(%p) io_handle(%d)",__func__, config->sample_rate, config->channel_mask, + devices, &in->stream, handle); + + pthread_mutex_init(&in->lock, (const pthread_mutexattr_t *) NULL); + + in->stream.common.get_sample_rate = in_get_sample_rate; + in->stream.common.set_sample_rate = in_set_sample_rate; + in->stream.common.get_buffer_size = in_get_buffer_size; + in->stream.common.get_channels = in_get_channels; + in->stream.common.get_format = in_get_format; + in->stream.common.set_format = in_set_format; + in->stream.common.standby = in_standby; + in->stream.common.dump = in_dump; + in->stream.common.set_parameters = in_set_parameters; + in->stream.common.get_parameters = in_get_parameters; + in->stream.common.add_audio_effect = in_add_audio_effect; + in->stream.common.remove_audio_effect = in_remove_audio_effect; + in->stream.set_gain = in_set_gain; + in->stream.read = in_read; + in->stream.get_input_frames_lost = in_get_input_frames_lost; + + in->device = devices; + in->source = AUDIO_SOURCE_DEFAULT; + in->dev = adev; + in->standby = 1; + in->channel_mask = config->channel_mask; + in->capture_handle = handle; + + /* Update config params with the requested sample rate and channels */ + in->usecase = USECASE_AUDIO_RECORD; + if (config->sample_rate == LOW_LATENCY_CAPTURE_SAMPLE_RATE && + (flags & AUDIO_INPUT_FLAG_FAST) != 0) { + is_low_latency = true; +#if LOW_LATENCY_CAPTURE_USE_CASE + in->usecase = USECASE_AUDIO_RECORD_LOW_LATENCY; +#endif + } + in->config = pcm_config_audio_capture; + in->config.rate = config->sample_rate; + in->format = config->format; + + if (in->device == AUDIO_DEVICE_IN_TELEPHONY_RX) { + if (config->sample_rate == 0) + config->sample_rate = AFE_PROXY_SAMPLING_RATE; + if (config->sample_rate != 48000 && config->sample_rate != 16000 && + config->sample_rate != 8000) { + config->sample_rate = AFE_PROXY_SAMPLING_RATE; + ret = -EINVAL; + goto err_open; + } + if (config->format == AUDIO_FORMAT_DEFAULT) + config->format = AUDIO_FORMAT_PCM_16_BIT; + if (config->format != AUDIO_FORMAT_PCM_16_BIT) { + config->format = AUDIO_FORMAT_PCM_16_BIT; + ret = -EINVAL; + goto err_open; + } + + in->usecase = USECASE_AUDIO_RECORD_AFE_PROXY; + in->config = pcm_config_afe_proxy_record; + in->config.channels = channel_count; + in->config.rate = config->sample_rate; + } else if (channel_count == 6) { + if(audio_extn_ssr_get_enabled()) { + if(audio_extn_ssr_init(in)) { + ALOGE("%s: audio_extn_ssr_init failed", __func__); + ret = -EINVAL; + goto err_open; + } + } else { + ALOGW("%s: surround sound recording is not supported", __func__); + } + } else if (audio_extn_compr_cap_enabled() && + audio_extn_compr_cap_format_supported(config->format) && + (in->dev->mode != AUDIO_MODE_IN_COMMUNICATION)) { + audio_extn_compr_cap_init(in); + } else { + in->config.channels = channel_count; + frame_size = audio_stream_in_frame_size(&in->stream); + buffer_size = get_input_buffer_size(config->sample_rate, + config->format, + channel_count, + is_low_latency); + in->config.period_size = buffer_size / frame_size; + } + + /* This stream could be for sound trigger lab, + get sound trigger pcm if present */ + audio_extn_sound_trigger_check_and_get_session(in); + audio_extn_perf_lock_init(); + + *stream_in = &in->stream; + ALOGV("%s: exit", __func__); + return ret; + +err_open: + free(in); + *stream_in = NULL; + return ret; +} + +static void adev_close_input_stream(struct audio_hw_device *dev, + struct audio_stream_in *stream) +{ + int ret; + struct stream_in *in = (struct stream_in *)stream; + struct audio_device *adev = (struct audio_device *)dev; + + ALOGD("%s: enter:stream_handle(%p)",__func__, in); + + /* Disable echo reference while closing input stream */ + platform_set_echo_reference(adev->platform, false); + + if (in->usecase == USECASE_COMPRESS_VOIP_CALL) { + pthread_mutex_lock(&adev->lock); + ret = voice_extn_compress_voip_close_input_stream(&stream->common); + pthread_mutex_unlock(&adev->lock); + if (ret != 0) + ALOGE("%s: Compress voip input cannot be closed, error:%d", + __func__, ret); + } else + in_standby(&stream->common); + + if (audio_extn_ssr_get_enabled() && + (audio_channel_count_from_in_mask(in->channel_mask) == 6)) { + audio_extn_ssr_deinit(); + } + + if(audio_extn_compr_cap_enabled() && + audio_extn_compr_cap_format_supported(in->config.format)) + audio_extn_compr_cap_deinit(); + + free(stream); + return; +} + +static int adev_dump(const audio_hw_device_t *device __unused, + int fd __unused) +{ + return 0; +} + +static int adev_close(hw_device_t *device) +{ + struct audio_device *adev = (struct audio_device *)device; + + if (!adev) + return 0; + + pthread_mutex_lock(&adev_init_lock); + + if ((--audio_device_ref_count) == 0) { + audio_extn_sound_trigger_deinit(adev); + audio_extn_listen_deinit(adev); + audio_extn_utils_release_streams_output_cfg_list(&adev->streams_output_cfg_list); + audio_route_free(adev->audio_route); + free(adev->snd_dev_ref_cnt); + platform_deinit(adev->platform); + free(device); + adev = NULL; + } + pthread_mutex_unlock(&adev_init_lock); + return 0; +} + +/* This returns 1 if the input parameter looks at all plausible as a low latency period size, + * or 0 otherwise. A return value of 1 doesn't mean the value is guaranteed to work, + * just that it _might_ work. + */ +static int period_size_is_plausible_for_low_latency(int period_size) +{ + switch (period_size) { + case 160: + case 240: + case 320: + case 480: + return 1; + default: + return 0; + } +} + +static int adev_open(const hw_module_t *module, const char *name, + hw_device_t **device) +{ + int i, ret; + + ALOGD("%s: enter", __func__); + if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) return -EINVAL; + + pthread_mutex_lock(&adev_init_lock); + if (audio_device_ref_count != 0){ + *device = &adev->device.common; + audio_device_ref_count++; + ALOGD("%s: returning existing instance of adev", __func__); + ALOGD("%s: exit", __func__); + pthread_mutex_unlock(&adev_init_lock); + return 0; + } + + adev = calloc(1, sizeof(struct audio_device)); + + if (!adev) { + pthread_mutex_unlock(&adev_init_lock); + return -ENOMEM; + } + + pthread_mutex_init(&adev->lock, (const pthread_mutexattr_t *) NULL); + + adev->device.common.tag = HARDWARE_DEVICE_TAG; + adev->device.common.version = AUDIO_DEVICE_API_VERSION_2_0; + adev->device.common.module = (struct hw_module_t *)module; + adev->device.common.close = adev_close; + + adev->device.init_check = adev_init_check; + adev->device.set_voice_volume = adev_set_voice_volume; + adev->device.set_master_volume = adev_set_master_volume; + adev->device.get_master_volume = adev_get_master_volume; + adev->device.set_master_mute = adev_set_master_mute; + adev->device.get_master_mute = adev_get_master_mute; + adev->device.set_mode = adev_set_mode; + adev->device.set_mic_mute = adev_set_mic_mute; + adev->device.get_mic_mute = adev_get_mic_mute; + adev->device.set_parameters = adev_set_parameters; + adev->device.get_parameters = adev_get_parameters; + adev->device.get_input_buffer_size = adev_get_input_buffer_size; + adev->device.open_output_stream = adev_open_output_stream; + adev->device.close_output_stream = adev_close_output_stream; + adev->device.open_input_stream = adev_open_input_stream; + adev->device.close_input_stream = adev_close_input_stream; + adev->device.dump = adev_dump; + + /* Set the default route before the PCM stream is opened */ + adev->mode = AUDIO_MODE_NORMAL; + adev->active_input = NULL; + adev->primary_output = NULL; + adev->out_device = AUDIO_DEVICE_NONE; + adev->bluetooth_nrec = true; + adev->acdb_settings = TTY_MODE_OFF; + /* adev->cur_hdmi_channels = 0; by calloc() */ + adev->cur_codec_backend_samplerate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE; + adev->cur_codec_backend_bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH; + adev->snd_dev_ref_cnt = calloc(SND_DEVICE_MAX, sizeof(int)); + voice_init(adev); + list_init(&adev->usecase_list); + adev->cur_wfd_channels = 2; + adev->offload_usecases_state = 0; + + pthread_mutex_init(&adev->snd_card_status.lock, (const pthread_mutexattr_t *) NULL); + adev->snd_card_status.state = SND_CARD_STATE_OFFLINE; + /* Loads platform specific libraries dynamically */ + adev->platform = platform_init(adev); + if (!adev->platform) { + free(adev->snd_dev_ref_cnt); + free(adev); + ALOGE("%s: Failed to init platform data, aborting.", __func__); + *device = NULL; + pthread_mutex_unlock(&adev_init_lock); + return -EINVAL; + } + + adev->snd_card_status.state = SND_CARD_STATE_ONLINE; + + if (access(VISUALIZER_LIBRARY_PATH, R_OK) == 0) { + adev->visualizer_lib = dlopen(VISUALIZER_LIBRARY_PATH, RTLD_NOW); + if (adev->visualizer_lib == NULL) { + ALOGE("%s: DLOPEN failed for %s", __func__, VISUALIZER_LIBRARY_PATH); + } else { + ALOGV("%s: DLOPEN successful for %s", __func__, VISUALIZER_LIBRARY_PATH); + adev->visualizer_start_output = + (int (*)(audio_io_handle_t, int))dlsym(adev->visualizer_lib, + "visualizer_hal_start_output"); + adev->visualizer_stop_output = + (int (*)(audio_io_handle_t, int))dlsym(adev->visualizer_lib, + "visualizer_hal_stop_output"); + } + } + audio_extn_listen_init(adev, adev->snd_card); + audio_extn_sound_trigger_init(adev); + + if (access(OFFLOAD_EFFECTS_BUNDLE_LIBRARY_PATH, R_OK) == 0) { + adev->offload_effects_lib = dlopen(OFFLOAD_EFFECTS_BUNDLE_LIBRARY_PATH, RTLD_NOW); + if (adev->offload_effects_lib == NULL) { + ALOGE("%s: DLOPEN failed for %s", __func__, + OFFLOAD_EFFECTS_BUNDLE_LIBRARY_PATH); + } else { + ALOGV("%s: DLOPEN successful for %s", __func__, + OFFLOAD_EFFECTS_BUNDLE_LIBRARY_PATH); + adev->offload_effects_start_output = + (int (*)(audio_io_handle_t, int))dlsym(adev->offload_effects_lib, + "offload_effects_bundle_hal_start_output"); + adev->offload_effects_stop_output = + (int (*)(audio_io_handle_t, int))dlsym(adev->offload_effects_lib, + "offload_effects_bundle_hal_stop_output"); + } + } + + adev->bt_wb_speech_enabled = false; + + audio_extn_ds2_enable(adev); + *device = &adev->device.common; + + audio_extn_utils_update_streams_output_cfg_list(adev->platform, adev->mixer, + &adev->streams_output_cfg_list); + + audio_device_ref_count++; + + char value[PROPERTY_VALUE_MAX]; + int trial; + if (property_get("audio_hal.period_size", value, NULL) > 0) { + trial = atoi(value); + if (period_size_is_plausible_for_low_latency(trial)) { + pcm_config_low_latency.period_size = trial; + pcm_config_low_latency.start_threshold = trial / 4; + pcm_config_low_latency.avail_min = trial / 4; + configured_low_latency_capture_period_size = trial; + } + } + if (property_get("audio_hal.in_period_size", value, NULL) > 0) { + trial = atoi(value); + if (period_size_is_plausible_for_low_latency(trial)) { + configured_low_latency_capture_period_size = trial; + } + } + + pthread_mutex_unlock(&adev_init_lock); + + ALOGV("%s: exit", __func__); + return 0; +} + +static struct hw_module_methods_t hal_module_methods = { + .open = adev_open, +}; + +struct audio_module HAL_MODULE_INFO_SYM = { + .common = { + .tag = HARDWARE_MODULE_TAG, + .module_api_version = AUDIO_MODULE_API_VERSION_0_1, + .hal_api_version = HARDWARE_HAL_API_VERSION, + .id = AUDIO_HARDWARE_MODULE_ID, + .name = "QCOM Audio HAL", + .author = "The Linux Foundation", + .methods = &hal_module_methods, + }, +}; diff --git a/audio/hal/audio_hw.h b/audio/hal/audio_hw.h new file mode 100644 index 0000000..d05f743 --- /dev/null +++ b/audio/hal/audio_hw.h @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * Not a contribution. + * + * Copyright (C) 2013 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. + */ + +#ifndef QCOM_AUDIO_HW_H +#define QCOM_AUDIO_HW_H + +#include +#include +#include +#include + +#include +#include "audio_defs.h" +#include "voice.h" + +#define VISUALIZER_LIBRARY_PATH "/system/lib/soundfx/libqcomvisualizer.so" +#define OFFLOAD_EFFECTS_BUNDLE_LIBRARY_PATH "/system/lib/soundfx/libqcompostprocbundle.so" + +/* Flags used to initialize acdb_settings variable that goes to ACDB library */ +#define NONE_FLAG 0x00000000 +#define ANC_FLAG 0x00000001 +#define DMIC_FLAG 0x00000002 +#define QMIC_FLAG 0x00000004 +#define TTY_MODE_OFF 0x00000010 +#define TTY_MODE_FULL 0x00000020 +#define TTY_MODE_VCO 0x00000040 +#define TTY_MODE_HCO 0x00000080 +#define TTY_MODE_CLEAR 0xFFFFFF0F +#define FLUENCE_MODE_CLEAR 0xFFFFFFF0 + +#define ACDB_DEV_TYPE_OUT 1 +#define ACDB_DEV_TYPE_IN 2 + +#define MAX_SUPPORTED_CHANNEL_MASKS 2 +#define DEFAULT_HDMI_OUT_CHANNELS 2 + +#define SND_CARD_STATE_OFFLINE 0 +#define SND_CARD_STATE_ONLINE 1 +typedef int snd_device_t; + +/* These are the supported use cases by the hardware. + * Each usecase is mapped to a specific PCM device. + * Refer to pcm_device_table[]. + */ +enum { + USECASE_INVALID = -1, + /* Playback usecases */ + USECASE_AUDIO_PLAYBACK_DEEP_BUFFER = 0, + USECASE_AUDIO_PLAYBACK_LOW_LATENCY, + USECASE_AUDIO_PLAYBACK_MULTI_CH, + USECASE_AUDIO_PLAYBACK_OFFLOAD, +#ifdef MULTIPLE_OFFLOAD_ENABLED + USECASE_AUDIO_PLAYBACK_OFFLOAD2, + USECASE_AUDIO_PLAYBACK_OFFLOAD3, + USECASE_AUDIO_PLAYBACK_OFFLOAD4, + USECASE_AUDIO_PLAYBACK_OFFLOAD5, + USECASE_AUDIO_PLAYBACK_OFFLOAD6, + USECASE_AUDIO_PLAYBACK_OFFLOAD7, + USECASE_AUDIO_PLAYBACK_OFFLOAD8, + USECASE_AUDIO_PLAYBACK_OFFLOAD9, +#endif + + /* FM usecase */ + USECASE_AUDIO_PLAYBACK_FM, + + /* HFP Use case*/ + USECASE_AUDIO_HFP_SCO, + USECASE_AUDIO_HFP_SCO_WB, + + /* Capture usecases */ + USECASE_AUDIO_RECORD, + USECASE_AUDIO_RECORD_COMPRESS, + USECASE_AUDIO_RECORD_LOW_LATENCY, + USECASE_AUDIO_RECORD_FM_VIRTUAL, + + /* Voice usecase */ + USECASE_VOICE_CALL, + + /* Voice extension usecases */ + USECASE_VOICE2_CALL, + USECASE_VOLTE_CALL, + USECASE_QCHAT_CALL, + USECASE_VOWLAN_CALL, + USECASE_COMPRESS_VOIP_CALL, + + USECASE_INCALL_REC_UPLINK, + USECASE_INCALL_REC_DOWNLINK, + USECASE_INCALL_REC_UPLINK_AND_DOWNLINK, + USECASE_INCALL_REC_UPLINK_COMPRESS, + USECASE_INCALL_REC_DOWNLINK_COMPRESS, + USECASE_INCALL_REC_UPLINK_AND_DOWNLINK_COMPRESS, + + USECASE_INCALL_MUSIC_UPLINK, + USECASE_INCALL_MUSIC_UPLINK2, + + USECASE_AUDIO_SPKR_CALIB_RX, + USECASE_AUDIO_SPKR_CALIB_TX, + + USECASE_AUDIO_PLAYBACK_AFE_PROXY, + USECASE_AUDIO_RECORD_AFE_PROXY, + + AUDIO_USECASE_MAX +}; + +const char * const use_case_table[AUDIO_USECASE_MAX]; + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +/* + * tinyAlsa library interprets period size as number of frames + * one frame = channel_count * sizeof (pcm sample) + * so if format = 16-bit PCM and channels = Stereo, frame size = 2 ch * 2 = 4 bytes + * DEEP_BUFFER_OUTPUT_PERIOD_SIZE = 1024 means 1024 * 4 = 4096 bytes + * We should take care of returning proper size when AudioFlinger queries for + * the buffer size of an input/output stream + */ + +enum { + OFFLOAD_CMD_EXIT, /* exit compress offload thread loop*/ + OFFLOAD_CMD_DRAIN, /* send a full drain request to DSP */ + OFFLOAD_CMD_PARTIAL_DRAIN, /* send a partial drain request to DSP */ + OFFLOAD_CMD_WAIT_FOR_BUFFER, /* wait for buffer released by DSP */ +}; + +enum { + OFFLOAD_STATE_IDLE, + OFFLOAD_STATE_PLAYING, + OFFLOAD_STATE_PAUSED, +}; + +struct offload_cmd { + struct listnode node; + int cmd; + int data[]; +}; + +struct stream_app_type_cfg { + int sample_rate; + uint32_t bit_width; + int app_type; +}; + +struct stream_out { + struct audio_stream_out stream; + pthread_mutex_t lock; /* see note below on mutex acquisition order */ + pthread_cond_t cond; + struct pcm_config config; + struct compr_config compr_config; + struct pcm *pcm; + struct compress *compr; + int standby; + int pcm_device_id; + unsigned int sample_rate; + audio_channel_mask_t channel_mask; + audio_format_t format; + audio_devices_t devices; + audio_output_flags_t flags; + audio_usecase_t usecase; + /* Array of supported channel mask configurations. +1 so that the last entry is always 0 */ + audio_channel_mask_t supported_channel_masks[MAX_SUPPORTED_CHANNEL_MASKS + 1]; + bool muted; + uint64_t written; /* total frames written, not cleared when entering standby */ + audio_io_handle_t handle; + struct stream_app_type_cfg app_type_cfg; + + int non_blocking; + int playback_started; + int offload_state; + pthread_cond_t offload_cond; + pthread_t offload_thread; + struct listnode offload_cmd_list; + bool offload_thread_blocked; + + stream_callback_t offload_callback; + void *offload_cookie; + struct compr_gapless_mdata gapless_mdata; + int send_new_metadata; + unsigned int bit_width; + + struct audio_device *dev; +}; + +struct stream_in { + struct audio_stream_in stream; + pthread_mutex_t lock; /* see note below on mutex acquisition order */ + struct pcm_config config; + struct pcm *pcm; + int standby; + int source; + int pcm_device_id; + audio_devices_t device; + audio_channel_mask_t channel_mask; + audio_usecase_t usecase; + bool enable_aec; + bool enable_ns; + audio_format_t format; + audio_io_handle_t capture_handle; + bool is_st_session; + + struct audio_device *dev; +}; + +typedef enum { + PCM_PLAYBACK, + PCM_CAPTURE, + VOICE_CALL, + VOIP_CALL, + PCM_HFP_CALL +} usecase_type_t; + +union stream_ptr { + struct stream_in *in; + struct stream_out *out; +}; + +struct audio_usecase { + struct listnode list; + audio_usecase_t id; + usecase_type_t type; + audio_devices_t devices; + snd_device_t out_snd_device; + snd_device_t in_snd_device; + union stream_ptr stream; +}; + +struct sound_card_status { + pthread_mutex_t lock; + int state; +}; + +struct stream_format { + struct listnode list; + audio_format_t format; +}; + +struct stream_sample_rate { + struct listnode list; + uint32_t sample_rate; +}; + +struct streams_output_cfg { + struct listnode list; + audio_output_flags_t flags; + struct listnode format_list; + struct listnode sample_rate_list; + struct stream_app_type_cfg app_type_cfg; +}; + +struct audio_device { + struct audio_hw_device device; + pthread_mutex_t lock; /* see note below on mutex acquisition order */ + struct mixer *mixer; + audio_mode_t mode; + audio_devices_t out_device; + struct stream_in *active_input; + struct stream_out *primary_output; + struct stream_out *voice_tx_output; + struct stream_out *current_call_output; + bool bluetooth_nrec; + bool screen_off; + int *snd_dev_ref_cnt; + struct listnode usecase_list; + struct listnode streams_output_cfg_list; + struct audio_route *audio_route; + int acdb_settings; + bool speaker_lr_swap; + struct voice voice; + unsigned int cur_hdmi_channels; + unsigned int cur_wfd_channels; + bool bt_wb_speech_enabled; + + int snd_card; + unsigned int cur_codec_backend_samplerate; + unsigned int cur_codec_backend_bit_width; + void *platform; + unsigned int offload_usecases_state; + void *visualizer_lib; + int (*visualizer_start_output)(audio_io_handle_t, int); + int (*visualizer_stop_output)(audio_io_handle_t, int); + void *offload_effects_lib; + int (*offload_effects_start_output)(audio_io_handle_t, int); + int (*offload_effects_stop_output)(audio_io_handle_t, int); + + struct sound_card_status snd_card_status; +}; + +int select_devices(struct audio_device *adev, + audio_usecase_t uc_id); +int disable_audio_route(struct audio_device *adev, + struct audio_usecase *usecase); +int disable_snd_device(struct audio_device *adev, + snd_device_t snd_device); +int enable_snd_device(struct audio_device *adev, + snd_device_t snd_device); + +int enable_audio_route(struct audio_device *adev, + struct audio_usecase *usecase); + +struct audio_usecase *get_usecase_from_list(struct audio_device *adev, + audio_usecase_t uc_id); + +bool is_offload_usecase(audio_usecase_t uc_id); + +int pcm_ioctl(struct pcm *pcm, int request, ...); + +int get_snd_card_state(struct audio_device *adev); + +#define LITERAL_TO_STRING(x) #x +#define CHECK(condition) LOG_ALWAYS_FATAL_IF(!(condition), "%s",\ + __FILE__ ":" LITERAL_TO_STRING(__LINE__)\ + " ASSERT_FATAL(" #condition ") failed.") + +/* + * NOTE: when multiple mutexes have to be acquired, always take the + * stream_in or stream_out mutex first, followed by the audio_device mutex. + */ + +#endif // QCOM_AUDIO_HW_H diff --git a/audio/hal/msm8916/hw_info.c b/audio/hal/msm8916/hw_info.c new file mode 100644 index 0000000..697478f --- /dev/null +++ b/audio/hal/msm8916/hw_info.c @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "hardware_info" +/*#define LOG_NDEBUG 0*/ +#define LOG_NDDEBUG 0 + +#include +#include +#include +#include +#include "audio_hw.h" +#include "platform.h" +#include "platform_api.h" + + +struct hardware_info { + char name[HW_INFO_ARRAY_MAX_SIZE]; + char type[HW_INFO_ARRAY_MAX_SIZE]; + /* variables for handling target variants */ + uint32_t num_snd_devices; + char dev_extn[HW_INFO_ARRAY_MAX_SIZE]; + snd_device_t *snd_devices; +}; + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +static const snd_device_t taiko_fluid_variant_devices[] = { + SND_DEVICE_OUT_SPEAKER, + SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES, + SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET, +}; + +static const snd_device_t taiko_CDP_variant_devices[] = { + SND_DEVICE_OUT_SPEAKER, + SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES, + SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET, + SND_DEVICE_IN_QUAD_MIC, +}; + +static const snd_device_t taiko_apq8084_CDP_variant_devices[] = { + SND_DEVICE_IN_HANDSET_MIC, +}; + +static const snd_device_t taiko_liquid_variant_devices[] = { + SND_DEVICE_OUT_SPEAKER, + SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES, + SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET, + SND_DEVICE_IN_SPEAKER_MIC, + SND_DEVICE_IN_HEADSET_MIC, + SND_DEVICE_IN_VOICE_DMIC, + SND_DEVICE_IN_VOICE_SPEAKER_DMIC, + SND_DEVICE_IN_VOICE_REC_DMIC_STEREO, + SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE, + SND_DEVICE_IN_QUAD_MIC, + SND_DEVICE_IN_HANDSET_STEREO_DMIC, + SND_DEVICE_IN_SPEAKER_STEREO_DMIC, +}; + +static const snd_device_t taiko_DB_variant_devices[] = { + SND_DEVICE_OUT_SPEAKER, + SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES, + SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET, + SND_DEVICE_IN_SPEAKER_MIC, + SND_DEVICE_IN_HEADSET_MIC, + SND_DEVICE_IN_QUAD_MIC, +}; + +static const snd_device_t tapan_lite_variant_devices[] = { + SND_DEVICE_OUT_SPEAKER, + SND_DEVICE_OUT_HEADPHONES, + SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES, + SND_DEVICE_OUT_VOICE_HEADPHONES, + SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES, + SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES, +}; + +static const snd_device_t tapan_skuf_variant_devices[] = { + SND_DEVICE_OUT_SPEAKER, + SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES, + SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET, + /*SND_DEVICE_OUT_SPEAKER_AND_ANC_FB_HEADSET,*/ +}; + +static const snd_device_t tapan_lite_skuf_variant_devices[] = { + SND_DEVICE_OUT_SPEAKER, + SND_DEVICE_OUT_HEADPHONES, + SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES, + SND_DEVICE_OUT_VOICE_HEADPHONES, + SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES, + SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES, +}; + +static const snd_device_t helicon_skuab_variant_devices[] = { + SND_DEVICE_OUT_SPEAKER, + SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES, + SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET, +}; + +static void update_hardware_info_8x16(struct hardware_info *hw_info, const char *snd_card_name) +{ + if (!strcmp(snd_card_name, "msm8x16-snd-card")) { + strlcpy(hw_info->type, "", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8x16", sizeof(hw_info->name)); + hw_info->snd_devices = NULL; + hw_info->num_snd_devices = 0; + strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn)); + } else if (!strcmp(snd_card_name, "msm8x16-snd-card-mtp")) { + strlcpy(hw_info->type, "", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8x16", sizeof(hw_info->name)); + hw_info->snd_devices = NULL; + hw_info->num_snd_devices = 0; + strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn)); + } else if (!strcmp(snd_card_name, "msm8x16-snd-card-sbc")) { + strlcpy(hw_info->type, "sbc", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8x16", sizeof(hw_info->name)); + hw_info->snd_devices = NULL; + hw_info->num_snd_devices = 0; + } else if (!strcmp(snd_card_name, "msm8x16-skuh-snd-card")) { + strlcpy(hw_info->type, "skuh", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8x16", sizeof(hw_info->name)); + hw_info->snd_devices = NULL; + hw_info->num_snd_devices = 0; + strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn)); + } else if (!strcmp(snd_card_name, "msm8x16-skui-snd-card")) { + strlcpy(hw_info->type, "skui", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8x16", sizeof(hw_info->name)); + hw_info->snd_devices = NULL; + hw_info->num_snd_devices = 0; + strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn)); + } else if (!strcmp(snd_card_name, "msm8x16-skuhf-snd-card")) { + strlcpy(hw_info->type, "skuhf", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8x16", sizeof(hw_info->name)); + hw_info->snd_devices = NULL; + hw_info->num_snd_devices = 0; + strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn)); + } else if (!strcmp(snd_card_name, "msm8939-snd-card")) { + strlcpy(hw_info->type, "", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8939", sizeof(hw_info->name)); + hw_info->snd_devices = NULL; + hw_info->num_snd_devices = 0; + strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn)); + } else if (!strcmp(snd_card_name, "msm8939-snd-card-mtp")) { + strlcpy(hw_info->type, "", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8939", sizeof(hw_info->name)); + hw_info->snd_devices = NULL; + hw_info->num_snd_devices = 0; + strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn)); + } else if (!strcmp(snd_card_name, "msm8939-snd-card-skuk")) { + strlcpy(hw_info->type, "skuk", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8939", sizeof(hw_info->name)); + hw_info->snd_devices = NULL; + hw_info->num_snd_devices = 0; + strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn)); + } else if (!strcmp(snd_card_name, "msm8939-tapan-snd-card") || + !strcmp(snd_card_name, "msm8939-tapan9302-snd-card")) { + strlcpy(hw_info->type, "", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8939", sizeof(hw_info->name)); + hw_info->snd_devices = NULL; + hw_info->num_snd_devices = 0; + strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn)); + } else if (!strcmp(snd_card_name, "msm8909-snd-card")) { + strlcpy(hw_info->type, "", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8909", sizeof(hw_info->name)); + hw_info->snd_devices = NULL; + hw_info->num_snd_devices = 0; + strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn)); + } else if (!strcmp(snd_card_name, "msm8909-skua-snd-card")) { + strlcpy(hw_info->type, "skua", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8909", sizeof(hw_info->name)); + hw_info->snd_devices = NULL; + hw_info->num_snd_devices = 0; + strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn)); + } else if (!strcmp(snd_card_name, "msm8909-skuc-snd-card")) { + strlcpy(hw_info->type, "skuc", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8909", sizeof(hw_info->name)); + hw_info->snd_devices = NULL; + hw_info->num_snd_devices = 0; + strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn)); + } else if (!strcmp(snd_card_name, "msm8909-pm8916-snd-card")) { + strlcpy(hw_info->type, "", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8909", sizeof(hw_info->name)); + hw_info->snd_devices = NULL; + hw_info->num_snd_devices = 0; + strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn)); + } else if (!strcmp(snd_card_name, "msm8909-skue-snd-card")) { + strlcpy(hw_info->type, "skue", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8909", sizeof(hw_info->name)); + hw_info->snd_devices = NULL; + hw_info->num_snd_devices = 0; + strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn)); + } else if (!strcmp(snd_card_name, "msm8939-snd-card-skul")) { + strlcpy(hw_info->type, "skul", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8939", sizeof(hw_info->name)); + hw_info->snd_devices = NULL; + hw_info->num_snd_devices = 0; + strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn)); + } else { + ALOGW("%s: Not an 8x16/8939/8909 device", __func__); + } +} + +void *hw_info_init(const char *snd_card_name) +{ + struct hardware_info *hw_info; + + hw_info = malloc(sizeof(struct hardware_info)); + if (!hw_info) { + ALOGE("failed to allocate mem for hardware info"); + return NULL; + } + + if (strstr(snd_card_name, "msm8x16") || strstr(snd_card_name, "msm8939") || + strstr(snd_card_name, "msm8909")) { + ALOGV("8x16 - variant soundcard"); + update_hardware_info_8x16(hw_info, snd_card_name); + } else { + ALOGE("%s: Unsupported target %s:",__func__, snd_card_name); + free(hw_info); + hw_info = NULL; + } + + return hw_info; +} + +void hw_info_deinit(void *hw_info) +{ + struct hardware_info *my_data = (struct hardware_info*) hw_info; + + if(!my_data) + free(my_data); +} + +void hw_info_append_hw_type(void *hw_info, snd_device_t snd_device, + char *device_name) +{ + struct hardware_info *my_data = (struct hardware_info*) hw_info; + uint32_t i = 0; + + snd_device_t *snd_devices = + (snd_device_t *) my_data->snd_devices; + + if(snd_devices != NULL) { + for (i = 0; i < my_data->num_snd_devices; i++) { + if (snd_device == (snd_device_t)snd_devices[i]) { + ALOGV("extract dev_extn device %d, extn = %s", + (snd_device_t)snd_devices[i], my_data->dev_extn); + CHECK(strlcat(device_name, my_data->dev_extn, + DEVICE_NAME_MAX_SIZE) < DEVICE_NAME_MAX_SIZE); + break; + } + } + } + ALOGD("%s : device_name = %s", __func__,device_name); +} diff --git a/audio/hal/msm8916/platform.c b/audio/hal/msm8916/platform.c new file mode 100644 index 0000000..fa6bc8f --- /dev/null +++ b/audio/hal/msm8916/platform.c @@ -0,0 +1,2735 @@ +/* + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2013 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 "msm8916_platform" +/*#define LOG_NDEBUG 0*/ +#define LOG_NDDEBUG 0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "platform.h" +#include "audio_extn.h" +#include "voice_extn.h" +#include "sound/msmcal-hwdep.h" +#define SOUND_TRIGGER_DEVICE_HANDSET_MONO_LOW_POWER_ACDB_ID (100) + +#define MIXER_XML_PATH "/system/etc/mixer_paths.xml" +#define MIXER_XML_PATH_MTP "/system/etc/mixer_paths_mtp.xml" +#define MIXER_XML_PATH_SBC "/system/etc/mixer_paths_sbc.xml" +#define MIXER_XML_PATH_MSM8909_PM8916 "/system/etc/mixer_paths_msm8909_pm8916.xml" +#define MIXER_XML_PATH_QRD_SKUH "/system/etc/mixer_paths_qrd_skuh.xml" +#define MIXER_XML_PATH_QRD_SKUI "/system/etc/mixer_paths_qrd_skui.xml" +#define MIXER_XML_PATH_QRD_SKUHF "/system/etc/mixer_paths_qrd_skuhf.xml" +#define MIXER_XML_PATH_SKUK "/system/etc/mixer_paths_skuk.xml" +#define MIXER_XML_PATH_SKUA "/system/etc/mixer_paths_skua.xml" +#define MIXER_XML_PATH_SKUC "/system/etc/mixer_paths_skuc.xml" +#define MIXER_XML_PATH_SKUE "/system/etc/mixer_paths_skue.xml" +#define MIXER_XML_PATH_SKUL "/system/etc/mixer_paths_skul.xml" +#define MIXER_XML_PATH_AUXPCM "/system/etc/mixer_paths_auxpcm.xml" +#define MIXER_XML_PATH_AUXPCM "/system/etc/mixer_paths_auxpcm.xml" +#define MIXER_XML_PATH_WCD9306 "/system/etc/mixer_paths_wcd9306.xml" +#define MIXER_XML_PATH_WCD9330 "/system/etc/mixer_paths_wcd9330.xml" +#define PLATFORM_INFO_XML_PATH "/system/etc/audio_platform_info.xml" +#define LIB_ACDB_LOADER "libacdbloader.so" +#define AUDIO_DATA_BLOCK_MIXER_CTL "HDMI EDID" +#define CVD_VERSION_MIXER_CTL "CVD Version" + +#define MAX_COMPRESS_OFFLOAD_FRAGMENT_SIZE (256 * 1024) +#define MIN_COMPRESS_OFFLOAD_FRAGMENT_SIZE (2 * 1024) +#define COMPRESS_OFFLOAD_FRAGMENT_SIZE_FOR_AV_STREAMING (2 * 1024) +#define COMPRESS_OFFLOAD_FRAGMENT_SIZE (32 * 1024) +/* Used in calculating fragment size for pcm offload */ +#define PCM_OFFLOAD_BUFFER_DURATION_FOR_AV 2000 /* 2 secs */ +#define PCM_OFFLOAD_BUFFER_DURATION_FOR_AV_STREAMING 100 /* 100 millisecs */ + +/* MAX PCM fragment size cannot be increased further due + * to flinger's cblk size of 1mb,and it has to be a multiple of + * 24 - lcm of channels supported by DSP + */ +#define MAX_PCM_OFFLOAD_FRAGMENT_SIZE (240 * 1024) +#define MIN_PCM_OFFLOAD_FRAGMENT_SIZE (32 * 1024) + +#define ALIGN( num, to ) (((num) + (to-1)) & (~(to-1))) +/* + * This file will have a maximum of 38 bytes: + * + * 4 bytes: number of audio blocks + * 4 bytes: total length of Short Audio Descriptor (SAD) blocks + * Maximum 10 * 3 bytes: SAD blocks + */ +#define MAX_SAD_BLOCKS 10 +#define SAD_BLOCK_SIZE 3 +#define MAX_CVD_VERSION_STRING_SIZE 100 + +/* EDID format ID for LPCM audio */ +#define EDID_FORMAT_LPCM 1 + +/* fallback app type if the default app type from acdb loader fails */ +#define DEFAULT_APP_TYPE 0x11130 + +/* Retry for delay in FW loading*/ +#define RETRY_NUMBER 20 +#define RETRY_US 500000 +#define MAX_SND_CARD 8 + +#define SAMPLE_RATE_8KHZ 8000 +#define SAMPLE_RATE_16KHZ 16000 + +#define AUDIO_PARAMETER_KEY_FLUENCE_TYPE "fluence" +#define AUDIO_PARAMETER_KEY_SLOWTALK "st_enable" +#define AUDIO_PARAMETER_KEY_HD_VOICE "hd_voice" +#define AUDIO_PARAMETER_KEY_VOLUME_BOOST "volume_boost" +#define MAX_CAL_NAME 20 +#define APP_TYPE_SYSTEM_SOUNDS 0x00011131 +#define APP_TYPE_GENERAL_RECORDING 0x00011132 + +char cal_name_info[WCD9XXX_MAX_CAL][MAX_CAL_NAME] = { + [WCD9XXX_ANC_CAL] = "anc_cal", + [WCD9XXX_MBHC_CAL] = "mbhc_cal", + [WCD9XXX_MAD_CAL] = "mad_cal", +}; + +#define AUDIO_PARAMETER_KEY_REC_PLAY_CONC "rec_play_conc_on" + +enum { + VOICE_FEATURE_SET_DEFAULT, + VOICE_FEATURE_SET_VOLUME_BOOST +}; + +struct audio_block_header +{ + int reserved; + int length; +}; + +/* Audio calibration related functions */ +typedef void (*acdb_deallocate_t)(); +typedef int (*acdb_init_t)(char *, char *, int); +typedef void (*acdb_send_audio_cal_t)(int, int, int, int); +typedef void (*acdb_send_voice_cal_t)(int, int); +typedef int (*acdb_reload_vocvoltable_t)(int); +typedef int (*acdb_get_default_app_type_t)(void); +typedef int (*acdb_loader_get_calibration_t)(char *attr, int size, void *data); +acdb_loader_get_calibration_t acdb_loader_get_calibration; + +struct platform_data { + struct audio_device *adev; + bool fluence_in_spkr_mode; + bool fluence_in_voice_call; + bool fluence_in_voice_rec; + bool fluence_in_audio_rec; + int fluence_type; + char fluence_cap[PROPERTY_VALUE_MAX]; + int fluence_mode; + bool slowtalk; + bool hd_voice; + bool ec_ref_enabled; + /* Audio calibration related functions */ + void *acdb_handle; + int voice_feature_set; + acdb_init_t acdb_init; + acdb_deallocate_t acdb_deallocate; + acdb_send_audio_cal_t acdb_send_audio_cal; + acdb_send_voice_cal_t acdb_send_voice_cal; + acdb_reload_vocvoltable_t acdb_reload_vocvoltable; + acdb_get_default_app_type_t acdb_get_default_app_type; +#ifdef RECORD_PLAY_CONCURRENCY + bool rec_play_conc_set; +#endif + void *hw_info; + struct csd_data *csd; +}; + +static bool is_external_codec = false; +static const int pcm_device_table_of_ext_codec[AUDIO_USECASE_MAX][2] = { + [USECASE_QCHAT_CALL] = {QCHAT_CALL_PCM_DEVICE_OF_EXT_CODEC, QCHAT_CALL_PCM_DEVICE_OF_EXT_CODEC} +}; + +/* List of use cases that has different PCM device ID's for internal and external codecs */ +static const int misc_usecase[AUDIO_USECASE_MAX] = { USECASE_QCHAT_CALL }; + +static const int pcm_device_table[AUDIO_USECASE_MAX][2] = { + [USECASE_AUDIO_PLAYBACK_DEEP_BUFFER] = {DEEP_BUFFER_PCM_DEVICE, + DEEP_BUFFER_PCM_DEVICE}, + [USECASE_AUDIO_PLAYBACK_LOW_LATENCY] = {LOWLATENCY_PCM_DEVICE, + LOWLATENCY_PCM_DEVICE}, + [USECASE_AUDIO_PLAYBACK_MULTI_CH] = {MULTIMEDIA2_PCM_DEVICE, + MULTIMEDIA2_PCM_DEVICE}, + [USECASE_AUDIO_PLAYBACK_OFFLOAD] = + {PLAYBACK_OFFLOAD_DEVICE, PLAYBACK_OFFLOAD_DEVICE}, + [USECASE_AUDIO_RECORD] = {AUDIO_RECORD_PCM_DEVICE, AUDIO_RECORD_PCM_DEVICE}, + [USECASE_AUDIO_RECORD_COMPRESS] = {COMPRESS_CAPTURE_DEVICE, COMPRESS_CAPTURE_DEVICE}, + [USECASE_AUDIO_RECORD_LOW_LATENCY] = {LOWLATENCY_PCM_DEVICE, + LOWLATENCY_PCM_DEVICE}, + [USECASE_AUDIO_RECORD_FM_VIRTUAL] = {MULTIMEDIA2_PCM_DEVICE, + MULTIMEDIA2_PCM_DEVICE}, + [USECASE_AUDIO_PLAYBACK_FM] = {FM_PLAYBACK_PCM_DEVICE, FM_CAPTURE_PCM_DEVICE}, + [USECASE_AUDIO_HFP_SCO] = {HFP_PCM_RX, HFP_SCO_RX}, + [USECASE_AUDIO_HFP_SCO_WB] = {HFP_PCM_RX, HFP_SCO_RX}, + [USECASE_VOICE_CALL] = {VOICE_CALL_PCM_DEVICE, VOICE_CALL_PCM_DEVICE}, + [USECASE_VOICE2_CALL] = {VOICE2_CALL_PCM_DEVICE, VOICE2_CALL_PCM_DEVICE}, + [USECASE_VOLTE_CALL] = {VOLTE_CALL_PCM_DEVICE, VOLTE_CALL_PCM_DEVICE}, + [USECASE_QCHAT_CALL] = {QCHAT_CALL_PCM_DEVICE, QCHAT_CALL_PCM_DEVICE}, + [USECASE_VOWLAN_CALL] = {VOWLAN_CALL_PCM_DEVICE, VOWLAN_CALL_PCM_DEVICE}, + [USECASE_COMPRESS_VOIP_CALL] = {COMPRESS_VOIP_CALL_PCM_DEVICE, COMPRESS_VOIP_CALL_PCM_DEVICE}, + [USECASE_INCALL_REC_UPLINK] = {AUDIO_RECORD_PCM_DEVICE, + AUDIO_RECORD_PCM_DEVICE}, + [USECASE_INCALL_REC_DOWNLINK] = {AUDIO_RECORD_PCM_DEVICE, + AUDIO_RECORD_PCM_DEVICE}, + [USECASE_INCALL_REC_UPLINK_AND_DOWNLINK] = {AUDIO_RECORD_PCM_DEVICE, + AUDIO_RECORD_PCM_DEVICE}, + [USECASE_INCALL_REC_UPLINK_COMPRESS] = {COMPRESS_CAPTURE_DEVICE, + COMPRESS_CAPTURE_DEVICE}, + [USECASE_INCALL_REC_DOWNLINK_COMPRESS] = {COMPRESS_CAPTURE_DEVICE, + COMPRESS_CAPTURE_DEVICE}, + [USECASE_INCALL_REC_UPLINK_AND_DOWNLINK_COMPRESS] = {COMPRESS_CAPTURE_DEVICE, + COMPRESS_CAPTURE_DEVICE}, + [USECASE_INCALL_MUSIC_UPLINK] = {INCALL_MUSIC_UPLINK_PCM_DEVICE, + INCALL_MUSIC_UPLINK_PCM_DEVICE}, + [USECASE_INCALL_MUSIC_UPLINK2] = {INCALL_MUSIC_UPLINK2_PCM_DEVICE, + INCALL_MUSIC_UPLINK2_PCM_DEVICE}, + [USECASE_AUDIO_SPKR_CALIB_RX] = {SPKR_PROT_CALIB_RX_PCM_DEVICE, -1}, + [USECASE_AUDIO_SPKR_CALIB_TX] = {-1, SPKR_PROT_CALIB_TX_PCM_DEVICE}, +}; + +/* Array to store sound devices */ +static const char * const device_table[SND_DEVICE_MAX] = { + [SND_DEVICE_NONE] = "none", + /* Playback sound devices */ + [SND_DEVICE_OUT_HANDSET] = "handset", + [SND_DEVICE_OUT_SPEAKER] = "speaker", + [SND_DEVICE_OUT_SPEAKER_REVERSE] = "speaker-reverse", + [SND_DEVICE_OUT_HEADPHONES] = "headphones", + [SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES] = "speaker-and-headphones", + [SND_DEVICE_OUT_VOICE_HANDSET] = "voice-handset", + [SND_DEVICE_OUT_VOICE_SPEAKER] = "voice-speaker", + [SND_DEVICE_OUT_VOICE_HEADPHONES] = "voice-headphones", + [SND_DEVICE_OUT_HDMI] = "hdmi", + [SND_DEVICE_OUT_SPEAKER_AND_HDMI] = "speaker-and-hdmi", + [SND_DEVICE_OUT_BT_SCO] = "bt-sco-headset", + [SND_DEVICE_OUT_BT_SCO_WB] = "bt-sco-headset-wb", + [SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES] = "voice-tty-full-headphones", + [SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = "voice-tty-vco-headphones", + [SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET] = "voice-tty-hco-handset", + [SND_DEVICE_OUT_AFE_PROXY] = "afe-proxy", + [SND_DEVICE_OUT_USB_HEADSET] = "usb-headphones", + [SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET] = "speaker-and-usb-headphones", + [SND_DEVICE_OUT_TRANSMISSION_FM] = "transmission-fm", + [SND_DEVICE_OUT_ANC_HEADSET] = "anc-headphones", + [SND_DEVICE_OUT_ANC_FB_HEADSET] = "anc-fb-headphones", + [SND_DEVICE_OUT_VOICE_ANC_HEADSET] = "voice-anc-headphones", + [SND_DEVICE_OUT_VOICE_ANC_FB_HEADSET] = "voice-anc-fb-headphones", + [SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET] = "speaker-and-anc-headphones", + [SND_DEVICE_OUT_ANC_HANDSET] = "anc-handset", + [SND_DEVICE_OUT_SPEAKER_PROTECTED] = "speaker-protected", +#ifdef RECORD_PLAY_CONCURRENCY + [SND_DEVICE_OUT_VOIP_HANDSET] = "voip-handset", + [SND_DEVICE_OUT_VOIP_SPEAKER] = "voip-speaker", + [SND_DEVICE_OUT_VOIP_HEADPHONES] = "voip-headphones", +#endif + + /* Capture sound devices */ + [SND_DEVICE_IN_HANDSET_MIC] = "handset-mic", + [SND_DEVICE_IN_HANDSET_MIC_AEC] = "handset-mic", + [SND_DEVICE_IN_HANDSET_MIC_NS] = "handset-mic", + [SND_DEVICE_IN_HANDSET_MIC_AEC_NS] = "handset-mic", + [SND_DEVICE_IN_HANDSET_DMIC] = "dmic-endfire", + [SND_DEVICE_IN_HANDSET_DMIC_AEC] = "dmic-endfire", + [SND_DEVICE_IN_HANDSET_DMIC_NS] = "dmic-endfire", + [SND_DEVICE_IN_HANDSET_DMIC_AEC_NS] = "dmic-endfire", + [SND_DEVICE_IN_SPEAKER_MIC] = "speaker-mic", + [SND_DEVICE_IN_SPEAKER_MIC_AEC] = "speaker-mic", + [SND_DEVICE_IN_SPEAKER_MIC_NS] = "speaker-mic", + [SND_DEVICE_IN_SPEAKER_MIC_AEC_NS] = "speaker-mic", + [SND_DEVICE_IN_SPEAKER_DMIC] = "speaker-dmic-endfire", + [SND_DEVICE_IN_SPEAKER_DMIC_AEC] = "speaker-dmic-endfire", + [SND_DEVICE_IN_SPEAKER_DMIC_NS] = "speaker-dmic-endfire", + [SND_DEVICE_IN_SPEAKER_DMIC_AEC_NS] = "speaker-dmic-endfire", + [SND_DEVICE_IN_HEADSET_MIC] = "headset-mic", + [SND_DEVICE_IN_HEADSET_MIC_FLUENCE] = "headset-mic", + [SND_DEVICE_IN_VOICE_SPEAKER_MIC] = "voice-speaker-mic", + [SND_DEVICE_IN_VOICE_HEADSET_MIC] = "voice-headset-mic", + [SND_DEVICE_IN_HDMI_MIC] = "hdmi-mic", + [SND_DEVICE_IN_BT_SCO_MIC] = "bt-sco-mic", + [SND_DEVICE_IN_BT_SCO_MIC_NREC] = "bt-sco-mic", + [SND_DEVICE_IN_BT_SCO_MIC_WB] = "bt-sco-mic-wb", + [SND_DEVICE_IN_BT_SCO_MIC_WB_NREC] = "bt-sco-mic-wb", + [SND_DEVICE_IN_CAMCORDER_MIC] = "camcorder-mic", + [SND_DEVICE_IN_VOICE_DMIC] = "voice-dmic-ef", + [SND_DEVICE_IN_VOICE_SPEAKER_DMIC] = "voice-speaker-dmic-ef", + [SND_DEVICE_IN_VOICE_SPEAKER_QMIC] = "voice-speaker-qmic", + [SND_DEVICE_IN_VOICE_TTY_FULL_HEADSET_MIC] = "voice-tty-full-headset-mic", + [SND_DEVICE_IN_VOICE_TTY_VCO_HANDSET_MIC] = "voice-tty-vco-handset-mic", + [SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC] = "voice-tty-hco-headset-mic", + [SND_DEVICE_IN_VOICE_REC_MIC] = "voice-rec-mic", + [SND_DEVICE_IN_VOICE_REC_MIC_NS] = "voice-rec-mic", + [SND_DEVICE_IN_VOICE_REC_DMIC_STEREO] = "voice-rec-dmic-ef", + [SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE] = "voice-rec-dmic-ef-fluence", + [SND_DEVICE_IN_USB_HEADSET_MIC] = "usb-headset-mic", + [SND_DEVICE_IN_CAPTURE_FM] = "capture-fm", + [SND_DEVICE_IN_AANC_HANDSET_MIC] = "aanc-handset-mic", + [SND_DEVICE_IN_QUAD_MIC] = "quad-mic", + [SND_DEVICE_IN_HANDSET_STEREO_DMIC] = "handset-stereo-dmic-ef", + [SND_DEVICE_IN_SPEAKER_STEREO_DMIC] = "speaker-stereo-dmic-ef", + [SND_DEVICE_IN_CAPTURE_VI_FEEDBACK] = "vi-feedback", + [SND_DEVICE_IN_VOICE_SPEAKER_DMIC_BROADSIDE] = "voice-speaker-dmic-broadside", + [SND_DEVICE_IN_SPEAKER_DMIC_BROADSIDE] = "speaker-dmic-broadside", + [SND_DEVICE_IN_SPEAKER_DMIC_AEC_BROADSIDE] = "speaker-dmic-broadside", + [SND_DEVICE_IN_SPEAKER_DMIC_NS_BROADSIDE] = "speaker-dmic-broadside", + [SND_DEVICE_IN_SPEAKER_DMIC_AEC_NS_BROADSIDE] = "speaker-dmic-broadside", + [SND_DEVICE_IN_VOICE_FLUENCE_DMIC_AANC] = "aanc-fluence-dmic-handset", +}; + +/* ACDB IDs (audio DSP path configuration IDs) for each sound device */ +static int acdb_device_table[SND_DEVICE_MAX] = { + [SND_DEVICE_NONE] = -1, + [SND_DEVICE_OUT_HANDSET] = 7, + [SND_DEVICE_OUT_SPEAKER] = 14, + [SND_DEVICE_OUT_SPEAKER_REVERSE] = 14, + [SND_DEVICE_OUT_HEADPHONES] = 10, + [SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES] = 10, + [SND_DEVICE_OUT_VOICE_HANDSET] = 7, + [SND_DEVICE_OUT_VOICE_SPEAKER] = 14, + [SND_DEVICE_OUT_VOICE_HEADPHONES] = 10, + [SND_DEVICE_OUT_HDMI] = 18, + [SND_DEVICE_OUT_SPEAKER_AND_HDMI] = 14, + [SND_DEVICE_OUT_BT_SCO] = 22, + [SND_DEVICE_OUT_BT_SCO_WB] = 39, + [SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES] = 17, + [SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = 17, + [SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET] = 37, + [SND_DEVICE_OUT_AFE_PROXY] = 0, + [SND_DEVICE_OUT_USB_HEADSET] = 45, + [SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET] = 14, + [SND_DEVICE_OUT_TRANSMISSION_FM] = 0, + [SND_DEVICE_OUT_ANC_HEADSET] = 26, + [SND_DEVICE_OUT_ANC_FB_HEADSET] = 27, + [SND_DEVICE_OUT_VOICE_ANC_HEADSET] = 26, + [SND_DEVICE_OUT_VOICE_ANC_FB_HEADSET] = 27, + [SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET] = 26, + [SND_DEVICE_OUT_ANC_HANDSET] = 103, + [SND_DEVICE_OUT_SPEAKER_PROTECTED] = 101, +#ifdef RECORD_PLAY_CONCURRENCY + [SND_DEVICE_OUT_VOIP_HANDSET] = 133, + [SND_DEVICE_OUT_VOIP_SPEAKER] = 132, + [SND_DEVICE_OUT_VOIP_HEADPHONES] = 134, +#endif + + [SND_DEVICE_IN_HANDSET_MIC] = 4, + [SND_DEVICE_IN_HANDSET_MIC_AEC] = 106, + [SND_DEVICE_IN_HANDSET_MIC_NS] = 107, + [SND_DEVICE_IN_HANDSET_MIC_AEC_NS] = 108, + [SND_DEVICE_IN_HANDSET_DMIC] = 41, + [SND_DEVICE_IN_HANDSET_DMIC_AEC] = 109, + [SND_DEVICE_IN_HANDSET_DMIC_NS] = 110, + [SND_DEVICE_IN_HANDSET_DMIC_AEC_NS] = 111, + [SND_DEVICE_IN_SPEAKER_MIC] = 11, + [SND_DEVICE_IN_SPEAKER_MIC_AEC] = 112, + [SND_DEVICE_IN_SPEAKER_MIC_NS] = 113, + [SND_DEVICE_IN_SPEAKER_MIC_AEC_NS] = 114, + [SND_DEVICE_IN_SPEAKER_DMIC] = 43, + [SND_DEVICE_IN_SPEAKER_DMIC_AEC] = 115, + [SND_DEVICE_IN_SPEAKER_DMIC_NS] = 116, + [SND_DEVICE_IN_SPEAKER_DMIC_AEC_NS] = 117, + [SND_DEVICE_IN_HEADSET_MIC] = 8, + [SND_DEVICE_IN_HEADSET_MIC_FLUENCE] = 47, + [SND_DEVICE_IN_VOICE_SPEAKER_MIC] = 11, + [SND_DEVICE_IN_VOICE_HEADSET_MIC] = 8, + [SND_DEVICE_IN_HDMI_MIC] = 4, + [SND_DEVICE_IN_BT_SCO_MIC] = 21, + [SND_DEVICE_IN_BT_SCO_MIC_NREC] = 122, + [SND_DEVICE_IN_BT_SCO_MIC_WB] = 38, + [SND_DEVICE_IN_BT_SCO_MIC_WB_NREC] = 123, + [SND_DEVICE_IN_CAMCORDER_MIC] = 4, + [SND_DEVICE_IN_VOICE_DMIC] = 41, + [SND_DEVICE_IN_VOICE_SPEAKER_DMIC] = 43, + [SND_DEVICE_IN_VOICE_SPEAKER_QMIC] = 19, + [SND_DEVICE_IN_VOICE_TTY_FULL_HEADSET_MIC] = 16, + [SND_DEVICE_IN_VOICE_TTY_VCO_HANDSET_MIC] = 36, + [SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC] = 16, + [SND_DEVICE_IN_VOICE_REC_MIC] = 4, + [SND_DEVICE_IN_VOICE_REC_MIC_NS] = 107, + [SND_DEVICE_IN_VOICE_REC_DMIC_STEREO] = 34, + [SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE] = 41, + [SND_DEVICE_IN_USB_HEADSET_MIC] = 44, + [SND_DEVICE_IN_CAPTURE_FM] = 0, + [SND_DEVICE_IN_AANC_HANDSET_MIC] = 104, + [SND_DEVICE_IN_QUAD_MIC] = 46, + [SND_DEVICE_IN_HANDSET_STEREO_DMIC] = 34, + [SND_DEVICE_IN_SPEAKER_STEREO_DMIC] = 35, + [SND_DEVICE_IN_CAPTURE_VI_FEEDBACK] = 102, + [SND_DEVICE_IN_VOICE_SPEAKER_DMIC_BROADSIDE] = 12, + [SND_DEVICE_IN_SPEAKER_DMIC_BROADSIDE] = 12, + [SND_DEVICE_IN_SPEAKER_DMIC_AEC_BROADSIDE] = 119, + [SND_DEVICE_IN_SPEAKER_DMIC_NS_BROADSIDE] = 121, + [SND_DEVICE_IN_SPEAKER_DMIC_AEC_NS_BROADSIDE] = 120, + [SND_DEVICE_IN_VOICE_FLUENCE_DMIC_AANC] = 135, +}; + +struct snd_device_index { + char name[100]; + unsigned int index; +}; + +#define TO_NAME_INDEX(X) #X, X + +/* Used to get index from parsed sting */ +struct snd_device_index snd_device_name_index[SND_DEVICE_MAX] = { + {TO_NAME_INDEX(SND_DEVICE_OUT_HANDSET)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_REVERSE)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_HEADPHONES)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_HANDSET)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_SPEAKER)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_HEADPHONES)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_HDMI)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_HDMI)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_BT_SCO)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_BT_SCO_WB)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_AFE_PROXY)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_USB_HEADSET)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_TRANSMISSION_FM)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_ANC_HEADSET)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_ANC_FB_HEADSET)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_ANC_HEADSET)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_ANC_FB_HEADSET)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_ANC_HANDSET)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_PROTECTED)}, +#ifdef RECORD_PLAY_CONCURRENCY + {TO_NAME_INDEX(SND_DEVICE_OUT_VOIP_HANDSET)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_VOIP_SPEAKER)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_VOIP_HEADPHONES)}, +#endif + {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_MIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_MIC_AEC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_MIC_NS)}, + {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_MIC_AEC_NS)}, + {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_DMIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_DMIC_AEC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_DMIC_NS)}, + {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_DMIC_AEC_NS)}, + {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_MIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_MIC_AEC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_MIC_NS)}, + {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_MIC_AEC_NS)}, + {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_DMIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_DMIC_AEC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_DMIC_NS)}, + {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_DMIC_AEC_NS)}, + {TO_NAME_INDEX(SND_DEVICE_IN_HEADSET_MIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_HEADSET_MIC_FLUENCE)}, + {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_SPEAKER_MIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_HEADSET_MIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_HDMI_MIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_BT_SCO_MIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_BT_SCO_MIC_NREC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_BT_SCO_MIC_WB)}, + {TO_NAME_INDEX(SND_DEVICE_IN_BT_SCO_MIC_WB_NREC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_CAMCORDER_MIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_DMIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_SPEAKER_DMIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_SPEAKER_QMIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_TTY_FULL_HEADSET_MIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_TTY_VCO_HANDSET_MIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_REC_MIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_REC_MIC_NS)}, + {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_REC_DMIC_STEREO)}, + {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE)}, + {TO_NAME_INDEX(SND_DEVICE_IN_USB_HEADSET_MIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_CAPTURE_FM)}, + {TO_NAME_INDEX(SND_DEVICE_IN_AANC_HANDSET_MIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_QUAD_MIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_STEREO_DMIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_STEREO_DMIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_CAPTURE_VI_FEEDBACK)}, + {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_FLUENCE_DMIC_AANC)}, +}; + +#define NO_COLS 2 +static int msm_be_id_array_len; +static int (*msm_device_to_be_id)[]; + +/* Below table lists output device to BE_ID mapping*/ +/* Update the table based on the board configuration*/ + +static int msm_device_to_be_id_internal_codec [][NO_COLS] = { + {AUDIO_DEVICE_OUT_EARPIECE , 34}, + {AUDIO_DEVICE_OUT_SPEAKER , 34}, + {AUDIO_DEVICE_OUT_WIRED_HEADSET , 34}, + {AUDIO_DEVICE_OUT_WIRED_HEADPHONE , 34}, + {AUDIO_DEVICE_OUT_BLUETOOTH_SCO , 11}, + {AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET , 11}, + {AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT , 11}, + {AUDIO_DEVICE_OUT_BLUETOOTH_A2DP , -1}, + {AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES , -1}, + {AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER , -1}, + {AUDIO_DEVICE_OUT_AUX_DIGITAL , 4}, + {AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET , 9}, + {AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET , 9}, + {AUDIO_DEVICE_OUT_USB_ACCESSORY , -1}, + {AUDIO_DEVICE_OUT_USB_DEVICE , -1}, + {AUDIO_DEVICE_OUT_REMOTE_SUBMIX , 9}, + {AUDIO_DEVICE_OUT_PROXY , 9}, + {AUDIO_DEVICE_OUT_FM , 7}, + {AUDIO_DEVICE_OUT_FM_TX , 8}, + {AUDIO_DEVICE_OUT_ALL , -1}, + {AUDIO_DEVICE_NONE , -1}, + {AUDIO_DEVICE_OUT_DEFAULT , -1}, +}; + +static int msm_device_to_be_id_external_codec [][NO_COLS] = { + {AUDIO_DEVICE_OUT_EARPIECE , 2}, + {AUDIO_DEVICE_OUT_SPEAKER , 2}, + {AUDIO_DEVICE_OUT_WIRED_HEADSET , 2}, + {AUDIO_DEVICE_OUT_WIRED_HEADPHONE , 2}, + {AUDIO_DEVICE_OUT_BLUETOOTH_SCO , 11}, + {AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET , 11}, + {AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT , 11}, + {AUDIO_DEVICE_OUT_BLUETOOTH_A2DP , -1}, + {AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES , -1}, + {AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER , -1}, + {AUDIO_DEVICE_OUT_AUX_DIGITAL , 4}, + {AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET , 9}, + {AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET , 9}, + {AUDIO_DEVICE_OUT_USB_ACCESSORY , -1}, + {AUDIO_DEVICE_OUT_USB_DEVICE , -1}, + {AUDIO_DEVICE_OUT_REMOTE_SUBMIX , 9}, + {AUDIO_DEVICE_OUT_PROXY , 9}, + {AUDIO_DEVICE_OUT_FM , 7}, + {AUDIO_DEVICE_OUT_FM_TX , 8}, + {AUDIO_DEVICE_OUT_ALL , -1}, + {AUDIO_DEVICE_NONE , -1}, + {AUDIO_DEVICE_OUT_DEFAULT , -1}, +}; + + +#define DEEP_BUFFER_PLATFORM_DELAY (29*1000LL) +#define LOW_LATENCY_PLATFORM_DELAY (13*1000LL) + +static bool is_misc_usecase(audio_usecase_t usecase) { + bool ret = false; + int i; + + for (i = 0; i < AUDIO_USECASE_MAX; i++) { + if(usecase == misc_usecase[i]) { + ret = true; + break; + } + } + return ret; +} + + +static void update_codec_type(const char *snd_card_name) { + + if (!strncmp(snd_card_name, "msm8939-tapan-snd-card", + sizeof("msm8939-tapan-snd-card")) || + !strncmp(snd_card_name, "msm8939-tapan9302-snd-card", + sizeof("msm8939-tapan9302-snd-card"))|| + !strncmp(snd_card_name, "msm8939-tomtom9330-snd-card", + sizeof("msm8939-tomtom9330-snd-card"))) { + ALOGI("%s: snd_card_name: %s",__func__,snd_card_name); + is_external_codec = true; + } +} +static void query_platform(const char *snd_card_name, + char *mixer_xml_path) +{ + if (!strncmp(snd_card_name, "msm8x16-snd-card-mtp", + sizeof("msm8x16-snd-card-mtp"))) { + strlcpy(mixer_xml_path, MIXER_XML_PATH_MTP, + sizeof(MIXER_XML_PATH_MTP)); + + msm_device_to_be_id = msm_device_to_be_id_internal_codec; + msm_be_id_array_len = + sizeof(msm_device_to_be_id_internal_codec) / sizeof(msm_device_to_be_id_internal_codec[0]); + + } else if (!strncmp(snd_card_name, "msm8x16-snd-card-sbc", + sizeof("msm8x16-snd-card-sbc"))) { + strlcpy(mixer_xml_path, MIXER_XML_PATH_SBC, + sizeof(MIXER_XML_PATH_SBC)); + + msm_device_to_be_id = msm_device_to_be_id_internal_codec; + msm_be_id_array_len = + sizeof(msm_device_to_be_id_internal_codec) / sizeof(msm_device_to_be_id_internal_codec[0]); + + } else if (!strncmp(snd_card_name, "msm8x16-skuh-snd-card", + sizeof("msm8x16-skuh-snd-card"))) { + strlcpy(mixer_xml_path, MIXER_XML_PATH_QRD_SKUH, + sizeof(MIXER_XML_PATH_QRD_SKUH)); + + msm_device_to_be_id = msm_device_to_be_id_internal_codec; + msm_be_id_array_len = + sizeof(msm_device_to_be_id_internal_codec) / sizeof(msm_device_to_be_id_internal_codec[0]); + + } else if (!strncmp(snd_card_name, "msm8x16-skui-snd-card", + sizeof("msm8x16-skui-snd-card"))) { + strlcpy(mixer_xml_path, MIXER_XML_PATH_QRD_SKUI, + sizeof(MIXER_XML_PATH_QRD_SKUI)); + + msm_device_to_be_id = msm_device_to_be_id_internal_codec; + msm_be_id_array_len = + sizeof(msm_device_to_be_id_internal_codec) / sizeof(msm_device_to_be_id_internal_codec[0]); + + } else if (!strncmp(snd_card_name, "msm8x16-skuhf-snd-card", + sizeof("msm8x16-skuhf-snd-card"))) { + strlcpy(mixer_xml_path, MIXER_XML_PATH_QRD_SKUHF, + sizeof(MIXER_XML_PATH_QRD_SKUHF)); + + msm_device_to_be_id = msm_device_to_be_id_internal_codec; + msm_be_id_array_len = + sizeof(msm_device_to_be_id_internal_codec) / sizeof(msm_device_to_be_id_internal_codec[0]); + + } else if (!strncmp(snd_card_name, "msm8939-snd-card-mtp", + sizeof("msm8939-snd-card-mtp"))) { + strlcpy(mixer_xml_path, MIXER_XML_PATH_MTP, + sizeof(MIXER_XML_PATH_MTP)); + + msm_device_to_be_id = msm_device_to_be_id_internal_codec; + msm_be_id_array_len = + sizeof(msm_device_to_be_id_internal_codec) / sizeof(msm_device_to_be_id_internal_codec[0]); + + } else if (!strncmp(snd_card_name, "msm8939-snd-card-skuk", + sizeof("msm8939-snd-card-skuk"))) { + strlcpy(mixer_xml_path, MIXER_XML_PATH_SKUK, + sizeof(MIXER_XML_PATH_SKUK)); + msm_device_to_be_id = msm_device_to_be_id_internal_codec; + msm_be_id_array_len = + sizeof(msm_device_to_be_id_internal_codec) / sizeof(msm_device_to_be_id_internal_codec[0]); + + } else if (!strncmp(snd_card_name, "msm8939-tapan-snd-card", + sizeof("msm8939-tapan-snd-card"))) { + strlcpy(mixer_xml_path, MIXER_XML_PATH_WCD9306, + sizeof(MIXER_XML_PATH_WCD9306)); + msm_device_to_be_id = msm_device_to_be_id_external_codec; + msm_be_id_array_len = + sizeof(msm_device_to_be_id_external_codec) / sizeof(msm_device_to_be_id_external_codec[0]); + + } else if (!strncmp(snd_card_name, "msm8939-tapan9302-snd-card", + sizeof("msm8939-tapan9302-snd-card"))) { + strlcpy(mixer_xml_path, MIXER_XML_PATH_WCD9306, + sizeof(MIXER_XML_PATH_WCD9306)); + + msm_device_to_be_id = msm_device_to_be_id_external_codec; + msm_be_id_array_len = + sizeof(msm_device_to_be_id_external_codec) / sizeof(msm_device_to_be_id_external_codec[0]); + + } else if (!strncmp(snd_card_name, "msm8939-tomtom9330-snd-card", + sizeof("msm8939-tomtom9330-snd-card"))) { + strlcpy(mixer_xml_path, MIXER_XML_PATH_WCD9330, + sizeof(MIXER_XML_PATH_WCD9330)); + msm_device_to_be_id = msm_device_to_be_id_external_codec; + msm_be_id_array_len = + sizeof(msm_device_to_be_id_external_codec) / sizeof(msm_device_to_be_id_external_codec[0]); + + } else if (!strncmp(snd_card_name, "msm8909-skua-snd-card", + sizeof("msm8909-skua-snd-card"))) { + strlcpy(mixer_xml_path, MIXER_XML_PATH_SKUA, + sizeof(MIXER_XML_PATH_SKUA)); + msm_device_to_be_id = msm_device_to_be_id_internal_codec; + msm_be_id_array_len = + sizeof(msm_device_to_be_id_internal_codec) / sizeof(msm_device_to_be_id_internal_codec[0]); + + } else if (!strncmp(snd_card_name, "msm8909-skuc-snd-card", + sizeof("msm8909-skuc-snd-card"))) { + strlcpy(mixer_xml_path, MIXER_XML_PATH_SKUC, + sizeof(MIXER_XML_PATH_SKUC)); + msm_device_to_be_id = msm_device_to_be_id_internal_codec; + msm_be_id_array_len = + sizeof(msm_device_to_be_id_internal_codec) / sizeof(msm_device_to_be_id_internal_codec[0]); + + } else if (!strncmp(snd_card_name, "msm8909-pm8916-snd-card", + sizeof("msm8909-pm8916-snd-card"))) { + strlcpy(mixer_xml_path, MIXER_XML_PATH_MSM8909_PM8916, + sizeof(MIXER_XML_PATH_MSM8909_PM8916)); + msm_device_to_be_id = msm_device_to_be_id_internal_codec; + msm_be_id_array_len = + sizeof(msm_device_to_be_id_internal_codec) / sizeof(msm_device_to_be_id_internal_codec[0]); + + } else if (!strncmp(snd_card_name, "msm8909-skue-snd-card", + sizeof("msm8909-skue-snd-card"))) { + strlcpy(mixer_xml_path, MIXER_XML_PATH_SKUE, + sizeof(MIXER_XML_PATH_SKUE)); + msm_device_to_be_id = msm_device_to_be_id_internal_codec; + msm_be_id_array_len = + sizeof(msm_device_to_be_id_internal_codec) / sizeof(msm_device_to_be_id_internal_codec[0]); + + } else if (!strncmp(snd_card_name, "msm8939-snd-card-skul", + sizeof("msm8939-snd-card-skul"))) { + strlcpy(mixer_xml_path, MIXER_XML_PATH_SKUL, + sizeof(MIXER_XML_PATH_SKUL)); + msm_device_to_be_id = msm_device_to_be_id_internal_codec; + msm_be_id_array_len = + sizeof(msm_device_to_be_id_external_codec) / sizeof(msm_device_to_be_id_internal_codec[0]); + } else { + strlcpy(mixer_xml_path, MIXER_XML_PATH, + sizeof(MIXER_XML_PATH)); + + msm_device_to_be_id = msm_device_to_be_id_internal_codec; + msm_be_id_array_len = + sizeof(msm_device_to_be_id_internal_codec) / sizeof(msm_device_to_be_id_internal_codec[0]); + + } +} + +void platform_set_echo_reference(void *platform, bool enable) +{ + struct platform_data *my_data = (struct platform_data *)platform; + struct audio_device *adev = my_data->adev; + + if (my_data->ec_ref_enabled) { + my_data->ec_ref_enabled = false; + ALOGD("%s: disabling echo-reference", __func__); + audio_route_reset_and_update_path(adev->audio_route, "echo-reference"); + } + + if (enable) { + my_data->ec_ref_enabled = true; + ALOGD("%s: enabling echo-reference", __func__); + audio_route_apply_and_update_path(adev->audio_route, "echo-reference"); + } + +} + +static struct csd_data *open_csd_client() +{ + struct csd_data *csd = calloc(1, sizeof(struct csd_data)); + if (!csd) { + ALOGE("failed to allocate csd_data mem"); + return NULL; + } + + csd->csd_client = dlopen(LIB_CSD_CLIENT, RTLD_NOW); + if (csd->csd_client == NULL) { + ALOGE("%s: DLOPEN failed for %s", __func__, LIB_CSD_CLIENT); + goto error; + } else { + ALOGV("%s: DLOPEN successful for %s", __func__, LIB_CSD_CLIENT); + + csd->deinit = (deinit_t)dlsym(csd->csd_client, + "csd_client_deinit"); + if (csd->deinit == NULL) { + ALOGE("%s: dlsym error %s for csd_client_deinit", __func__, + dlerror()); + goto error; + } + csd->disable_device = (disable_device_t)dlsym(csd->csd_client, + "csd_client_disable_device"); + if (csd->disable_device == NULL) { + ALOGE("%s: dlsym error %s for csd_client_disable_device", + __func__, dlerror()); + goto error; + } + csd->enable_device_config = (enable_device_config_t)dlsym(csd->csd_client, + "csd_client_enable_device_config"); + if (csd->enable_device_config == NULL) { + ALOGE("%s: dlsym error %s for csd_client_enable_device_config", + __func__, dlerror()); + goto error; + } + csd->enable_device = (enable_device_t)dlsym(csd->csd_client, + "csd_client_enable_device"); + if (csd->enable_device == NULL) { + ALOGE("%s: dlsym error %s for csd_client_enable_device", + __func__, dlerror()); + goto error; + } + csd->start_voice = (start_voice_t)dlsym(csd->csd_client, + "csd_client_start_voice"); + if (csd->start_voice == NULL) { + ALOGE("%s: dlsym error %s for csd_client_start_voice", + __func__, dlerror()); + goto error; + } + csd->stop_voice = (stop_voice_t)dlsym(csd->csd_client, + "csd_client_stop_voice"); + if (csd->stop_voice == NULL) { + ALOGE("%s: dlsym error %s for csd_client_stop_voice", + __func__, dlerror()); + goto error; + } + csd->volume = (volume_t)dlsym(csd->csd_client, + "csd_client_volume"); + if (csd->volume == NULL) { + ALOGE("%s: dlsym error %s for csd_client_volume", + __func__, dlerror()); + goto error; + } + csd->mic_mute = (mic_mute_t)dlsym(csd->csd_client, + "csd_client_mic_mute"); + if (csd->mic_mute == NULL) { + ALOGE("%s: dlsym error %s for csd_client_mic_mute", + __func__, dlerror()); + goto error; + } + csd->slow_talk = (slow_talk_t)dlsym(csd->csd_client, + "csd_client_slow_talk"); + if (csd->slow_talk == NULL) { + ALOGE("%s: dlsym error %s for csd_client_slow_talk", + __func__, dlerror()); + goto error; + } + csd->start_playback = (start_playback_t)dlsym(csd->csd_client, + "csd_client_start_playback"); + if (csd->start_playback == NULL) { + ALOGE("%s: dlsym error %s for csd_client_start_playback", + __func__, dlerror()); + goto error; + } + csd->stop_playback = (stop_playback_t)dlsym(csd->csd_client, + "csd_client_stop_playback"); + if (csd->stop_playback == NULL) { + ALOGE("%s: dlsym error %s for csd_client_stop_playback", + __func__, dlerror()); + goto error; + } + csd->set_lch = (set_lch_t)dlsym(csd->csd_client, "csd_client_set_lch"); + if (csd->set_lch == NULL) { + ALOGE("%s: dlsym error %s for csd_client_set_lch", + __func__, dlerror()); + /* Ignore the error as this is not mandatory function for + * basic voice call to work. + */ + } + csd->start_record = (start_record_t)dlsym(csd->csd_client, + "csd_client_start_record"); + if (csd->start_record == NULL) { + ALOGE("%s: dlsym error %s for csd_client_start_record", + __func__, dlerror()); + goto error; + } + csd->stop_record = (stop_record_t)dlsym(csd->csd_client, + "csd_client_stop_record"); + if (csd->stop_record == NULL) { + ALOGE("%s: dlsym error %s for csd_client_stop_record", + __func__, dlerror()); + goto error; + } + csd->init = (init_t)dlsym(csd->csd_client, "csd_client_init"); + + if (csd->init == NULL) { + ALOGE("%s: dlsym error %s for csd_client_init", + __func__, dlerror()); + goto error; + } else { + csd->init(); + } + } + return csd; + +error: + free(csd); + csd = NULL; + return csd; +} + +void close_csd_client(struct csd_data *csd) +{ + if (csd != NULL) { + csd->deinit(); + dlclose(csd->csd_client); + free(csd); + csd = NULL; + } +} + +void get_cvd_version(char *cvd_version, struct audio_device *adev) +{ + struct mixer_ctl *ctl; + int count; + int ret = 0; + + ctl = mixer_get_ctl_by_name(adev->mixer, CVD_VERSION_MIXER_CTL); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", __func__, CVD_VERSION_MIXER_CTL); + goto done; + } + mixer_ctl_update(ctl); + + count = mixer_ctl_get_num_values(ctl); + if (count > MAX_CVD_VERSION_STRING_SIZE) + count = MAX_CVD_VERSION_STRING_SIZE; + + ret = mixer_ctl_get_array(ctl, cvd_version, count); + if (ret != 0) { + ALOGE("%s: ERROR! mixer_ctl_get_array() failed to get CVD Version", __func__); + goto done; + } + +done: + return; +} + +static int hw_util_open(int card_no) +{ + int fd = -1; + char dev_name[256]; + + snprintf(dev_name, sizeof(dev_name), "/dev/snd/hwC%uD%u", + card_no, WCD9XXX_CODEC_HWDEP_NODE); + ALOGD("%s Opening device %s\n", __func__, dev_name); + fd = open(dev_name, O_WRONLY); + if (fd < 0) { + ALOGE("%s: cannot open device '%s'\n", __func__, dev_name); + return fd; + } + ALOGD("%s success", __func__); + return fd; +} + +struct param_data { + int use_case; + int acdb_id; + int get_size; + int buff_size; + int data_size; + void *buff; +}; + +static int send_codec_cal(acdb_loader_get_calibration_t acdb_loader_get_calibration, int fd) +{ + int ret = 0, type; + + for (type = WCD9XXX_ANC_CAL; type < WCD9XXX_MAX_CAL; type++) { + struct wcdcal_ioctl_buffer codec_buffer; + struct param_data calib; + + if (!strcmp(cal_name_info[type], "mad_cal")) + calib.acdb_id = SOUND_TRIGGER_DEVICE_HANDSET_MONO_LOW_POWER_ACDB_ID; + calib.get_size = 1; + ret = acdb_loader_get_calibration(cal_name_info[type], sizeof(struct param_data), + &calib); + if (ret < 0) { + ALOGE("%s get_calibration failed\n", __func__); + return ret; + } + calib.get_size = 0; + calib.buff = malloc(calib.buff_size); + if(calib.buff == NULL) { + ALOGE("%s mem allocation for %d bytes for %s failed\n" + , __func__, calib.buff_size, cal_name_info[type]); + return -1; + } + ret = acdb_loader_get_calibration(cal_name_info[type], + sizeof(struct param_data), &calib); + if (ret < 0) { + ALOGE("%s get_calibration failed type=%s calib.size=%d\n" + , __func__, cal_name_info[type], codec_buffer.size); + free(calib.buff); + return ret; + } + codec_buffer.buffer = calib.buff; + codec_buffer.size = calib.data_size; + codec_buffer.cal_type = type; + if (ioctl(fd, SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE, &codec_buffer) < 0) + ALOGE("Failed to call ioctl for %s err=%d calib.size=%d", + cal_name_info[type], errno, codec_buffer.size); + ALOGD("%s cal sent for %s calib.size=%d" + , __func__, cal_name_info[type], codec_buffer.size); + free(calib.buff); + } + return ret; +} + +static void audio_hwdep_send_cal(struct platform_data *plat_data) +{ + int fd; + + fd = hw_util_open(plat_data->adev->snd_card); + if (fd == -1) { + ALOGE("%s error open\n", __func__); + return; + } + + acdb_loader_get_calibration = (acdb_loader_get_calibration_t) + dlsym(plat_data->acdb_handle, "acdb_loader_get_calibration"); + + if (acdb_loader_get_calibration == NULL) { + ALOGE("%s: ERROR. dlsym Error:%s acdb_loader_get_calibration", __func__, + dlerror()); + return; + } + if (send_codec_cal(acdb_loader_get_calibration, fd) < 0) + ALOGE("%s: Could not send anc cal", __FUNCTION__); +} + +void *platform_init(struct audio_device *adev) +{ + char platform[PROPERTY_VALUE_MAX]; + char baseband[PROPERTY_VALUE_MAX]; + char value[PROPERTY_VALUE_MAX]; + struct platform_data *my_data = NULL; + int retry_num = 0, snd_card_num = 0, key = 0; + const char *snd_card_name; + char mixer_xml_path[100],ffspEnable[PROPERTY_VALUE_MAX]; + char *cvd_version = NULL; + + my_data = calloc(1, sizeof(struct platform_data)); + if (!my_data) { + ALOGE("failed to allocate platform data"); + return NULL; + } + + while (snd_card_num < MAX_SND_CARD) { + adev->mixer = mixer_open(snd_card_num); + + while (!adev->mixer && retry_num < RETRY_NUMBER) { + usleep(RETRY_US); + adev->mixer = mixer_open(snd_card_num); + retry_num++; + } + + if (!adev->mixer) { + ALOGE("%s: Unable to open the mixer card: %d", __func__, + snd_card_num); + retry_num = 0; + snd_card_num++; + continue; + } + + snd_card_name = mixer_get_name(adev->mixer); + ALOGV("%s: snd_card_name: %s", __func__, snd_card_name); + + my_data->hw_info = hw_info_init(snd_card_name); + if (!my_data->hw_info) { + ALOGE("%s: Failed to init hardware info", __func__); + } else { + query_platform(snd_card_name, mixer_xml_path); + ALOGD("%s: mixer path file is %s", __func__, + mixer_xml_path); + if (audio_extn_read_xml(adev, snd_card_num, mixer_xml_path, + MIXER_XML_PATH_AUXPCM) == -ENOSYS) { + adev->audio_route = audio_route_init(snd_card_num, + mixer_xml_path); + } + if (!adev->audio_route) { + ALOGE("%s: Failed to init audio route controls, aborting.", + __func__); + free(my_data); + return NULL; + } + adev->snd_card = snd_card_num; + update_codec_type(snd_card_name); + ALOGD("%s: Opened sound card:%d", __func__, snd_card_num); + break; + } + retry_num = 0; + snd_card_num++; + } + + if (snd_card_num >= MAX_SND_CARD) { + ALOGE("%s: Unable to find correct sound card, aborting.", __func__); + free(my_data); + return NULL; + } + + my_data->adev = adev; + my_data->fluence_in_spkr_mode = false; + my_data->fluence_in_voice_call = false; + my_data->fluence_in_voice_rec = false; + my_data->fluence_in_audio_rec = false; + my_data->fluence_type = FLUENCE_NONE; + my_data->fluence_mode = FLUENCE_ENDFIRE; + my_data->slowtalk = false; + my_data->hd_voice = false; + + property_get("ro.qc.sdk.audio.fluencetype", my_data->fluence_cap, ""); + if (!strncmp("fluencepro", my_data->fluence_cap, sizeof("fluencepro"))) { + my_data->fluence_type = FLUENCE_QUAD_MIC | FLUENCE_DUAL_MIC; + } else if (!strncmp("fluence", my_data->fluence_cap, sizeof("fluence"))) { + my_data->fluence_type = FLUENCE_DUAL_MIC; + } else { + my_data->fluence_type = FLUENCE_NONE; + } + + if (my_data->fluence_type != FLUENCE_NONE) { + property_get("persist.audio.fluence.voicecall",value,""); + if (!strncmp("true", value, sizeof("true"))) { + my_data->fluence_in_voice_call = true; + } + + property_get("persist.audio.fluence.voicerec",value,""); + if (!strncmp("true", value, sizeof("true"))) { + my_data->fluence_in_voice_rec = true; + } + + property_get("persist.audio.fluence.audiorec",value,""); + if (!strncmp("true", value, sizeof("true"))) { + my_data->fluence_in_audio_rec = true; + } + + property_get("persist.audio.fluence.speaker",value,""); + if (!strncmp("true", value, sizeof("true"))) { + my_data->fluence_in_spkr_mode = true; + } + + property_get("persist.audio.fluence.mode",value,""); + if (!strncmp("broadside", value, sizeof("broadside"))) { + my_data->fluence_mode = FLUENCE_BROADSIDE; + } + } + property_get("persist.audio.FFSP.enable", ffspEnable, ""); + if (!strncmp("true", ffspEnable, sizeof("true"))) { + acdb_device_table[SND_DEVICE_OUT_SPEAKER] = 131; + acdb_device_table[SND_DEVICE_OUT_SPEAKER_REVERSE] = 131; + acdb_device_table[SND_DEVICE_OUT_SPEAKER_AND_HDMI] = 131; + acdb_device_table[SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET] = 131; + } + property_get("audio.ds1.metainfo.key",value,"0"); + key = atoi(value); + + my_data->voice_feature_set = VOICE_FEATURE_SET_DEFAULT; + my_data->acdb_handle = dlopen(LIB_ACDB_LOADER, RTLD_NOW); + if (my_data->acdb_handle == NULL) { + ALOGE("%s: DLOPEN failed for %s", __func__, LIB_ACDB_LOADER); + } else { + ALOGV("%s: DLOPEN successful for %s", __func__, LIB_ACDB_LOADER); + my_data->acdb_deallocate = (acdb_deallocate_t)dlsym(my_data->acdb_handle, + "acdb_loader_deallocate_ACDB"); + if (!my_data->acdb_deallocate) + ALOGE("%s: Could not find the symbol acdb_loader_deallocate_ACDB from %s", + __func__, LIB_ACDB_LOADER); + + my_data->acdb_send_audio_cal = (acdb_send_audio_cal_t)dlsym(my_data->acdb_handle, + "acdb_loader_send_audio_cal_v2"); + if (!my_data->acdb_send_audio_cal) + ALOGE("%s: Could not find the symbol acdb_send_audio_cal from %s", + __func__, LIB_ACDB_LOADER); + + my_data->acdb_send_voice_cal = (acdb_send_voice_cal_t)dlsym(my_data->acdb_handle, + "acdb_loader_send_voice_cal"); + if (!my_data->acdb_send_voice_cal) + ALOGE("%s: Could not find the symbol acdb_loader_send_voice_cal from %s", + __func__, LIB_ACDB_LOADER); + + my_data->acdb_reload_vocvoltable = (acdb_reload_vocvoltable_t)dlsym(my_data->acdb_handle, + "acdb_loader_reload_vocvoltable"); + if (!my_data->acdb_reload_vocvoltable) + ALOGE("%s: Could not find the symbol acdb_loader_reload_vocvoltable from %s", + __func__, LIB_ACDB_LOADER); + + my_data->acdb_get_default_app_type = (acdb_get_default_app_type_t)dlsym( + my_data->acdb_handle, + "acdb_loader_get_default_app_type"); + if (!my_data->acdb_get_default_app_type) + ALOGE("%s: Could not find the symbol acdb_get_default_app_type from %s", + __func__, LIB_ACDB_LOADER); + + my_data->acdb_init = (acdb_init_t)dlsym(my_data->acdb_handle, + "acdb_loader_init_v2"); + if (my_data->acdb_init == NULL) { + ALOGE("%s: dlsym error %s for acdb_loader_init_v2", __func__, dlerror()); + goto acdb_init_fail; + } + + cvd_version = calloc(1, MAX_CVD_VERSION_STRING_SIZE); + if (!cvd_version) + ALOGE("Failed to allocate cvd version"); + else + get_cvd_version(cvd_version, adev); + + my_data->acdb_init(snd_card_name, cvd_version, key); + if (cvd_version) + free(cvd_version); + } + audio_extn_pm_vote(); + +acdb_init_fail: + /* Initialize ACDB ID's */ + platform_info_init(PLATFORM_INFO_XML_PATH); + + /* init usb */ + audio_extn_usb_init(adev); + /* update sound cards appropriately */ + audio_extn_usb_set_proxy_sound_card(adev->snd_card); + + /* Read one time ssr property */ + audio_extn_ssr_update_enabled(); + audio_extn_spkr_prot_init(adev); + + /* init dap hal */ + audio_extn_dap_hal_init(adev->snd_card); + + audio_extn_dolby_set_license(adev); + audio_hwdep_send_cal(my_data); + + return my_data; +} + +void platform_deinit(void *platform) +{ + struct platform_data *my_data = (struct platform_data *)platform; + + hw_info_deinit(my_data->hw_info); + close_csd_client(my_data->csd); + + free(platform); + /* deinit usb */ + audio_extn_usb_deinit(); + audio_extn_dap_hal_deinit(); +} + +const char *platform_get_snd_device_name(snd_device_t snd_device) +{ + if (snd_device >= SND_DEVICE_MIN && snd_device < SND_DEVICE_MAX) + return device_table[snd_device]; + else + return ""; +} + +int platform_get_snd_device_name_extn(void *platform, snd_device_t snd_device, + char *device_name) +{ + struct platform_data *my_data = (struct platform_data *)platform; + + if (snd_device >= SND_DEVICE_MIN && snd_device < SND_DEVICE_MAX) { + strlcpy(device_name, device_table[snd_device], DEVICE_NAME_MAX_SIZE); + hw_info_append_hw_type(my_data->hw_info, snd_device, device_name); + } else { + strlcpy(device_name, "", DEVICE_NAME_MAX_SIZE); + return -EINVAL; + } + + return 0; +} + +void platform_add_backend_name(char *mixer_path, snd_device_t snd_device) +{ + if ((snd_device == SND_DEVICE_IN_BT_SCO_MIC) || + (snd_device == SND_DEVICE_IN_BT_SCO_MIC_NREC)) + strlcat(mixer_path, " bt-sco", MIXER_PATH_MAX_LENGTH); + else if ((snd_device == SND_DEVICE_IN_BT_SCO_MIC_WB) || + (snd_device == SND_DEVICE_IN_BT_SCO_MIC_WB_NREC)) + strlcat(mixer_path, " bt-sco-wb", MIXER_PATH_MAX_LENGTH); + else if(snd_device == SND_DEVICE_OUT_BT_SCO) + strlcat(mixer_path, " bt-sco", MIXER_PATH_MAX_LENGTH); + else if(snd_device == SND_DEVICE_OUT_BT_SCO_WB) + strlcat(mixer_path, " bt-sco-wb", MIXER_PATH_MAX_LENGTH); + else if (snd_device == SND_DEVICE_OUT_HDMI) + strlcat(mixer_path, " hdmi", MIXER_PATH_MAX_LENGTH); + else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_HDMI) + strlcat(mixer_path, " speaker-and-hdmi", MIXER_PATH_MAX_LENGTH); + else if (snd_device == SND_DEVICE_OUT_AFE_PROXY) + strlcat(mixer_path, " afe-proxy", MIXER_PATH_MAX_LENGTH); + else if (snd_device == SND_DEVICE_OUT_USB_HEADSET) + strlcat(mixer_path, " usb-headphones", MIXER_PATH_MAX_LENGTH); + else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET) + strlcat(mixer_path, " speaker-and-usb-headphones", + MIXER_PATH_MAX_LENGTH); + else if (snd_device == SND_DEVICE_IN_USB_HEADSET_MIC) + strlcat(mixer_path, " usb-headset-mic", MIXER_PATH_MAX_LENGTH); + else if (snd_device == SND_DEVICE_IN_CAPTURE_FM) + strlcat(mixer_path, " capture-fm", MIXER_PATH_MAX_LENGTH); + else if (snd_device == SND_DEVICE_OUT_TRANSMISSION_FM) + strlcat(mixer_path, " transmission-fm", MIXER_PATH_MAX_LENGTH); +} + +int platform_get_pcm_device_id(audio_usecase_t usecase, int device_type) +{ + int device_id = -1; + + if (is_external_codec && is_misc_usecase(usecase)) { + if (device_type == PCM_PLAYBACK) + device_id = pcm_device_table_of_ext_codec[usecase][0]; + else + device_id = pcm_device_table_of_ext_codec[usecase][1]; + } else { + if (device_type == PCM_PLAYBACK) + device_id = pcm_device_table[usecase][0]; + else + device_id = pcm_device_table[usecase][1]; + } + return device_id; +} + +int platform_get_snd_device_index(char *snd_device_index_name) +{ + int ret = 0; + int i; + + if (snd_device_index_name == NULL) { + ALOGE("%s: snd_device_index_name is NULL", __func__); + ret = -ENODEV; + goto done; + } + + for (i=0; i < SND_DEVICE_MAX; i++) { + if(strcmp(snd_device_name_index[i].name, snd_device_index_name) == 0) { + ret = snd_device_name_index[i].index; + goto done; + } + } + ALOGE("%s: Could not find index for snd_device_index_name = %s", + __func__, snd_device_index_name); + ret = -ENODEV; +done: + return ret; +} + +int platform_set_fluence_type(void *platform, char *value) +{ + int ret = 0; + int fluence_type = FLUENCE_NONE; + int fluence_flag = NONE_FLAG; + struct platform_data *my_data = (struct platform_data *)platform; + struct audio_device *adev = my_data->adev; + + ALOGV("%s: fluence type:%d", __func__, my_data->fluence_type); + + /* only dual mic turn on and off is supported as of now through setparameters */ + if (!strncmp(AUDIO_PARAMETER_VALUE_DUALMIC,value, sizeof(AUDIO_PARAMETER_VALUE_DUALMIC))) { + if (!strncmp("fluencepro", my_data->fluence_cap, sizeof("fluencepro")) || + !strncmp("fluence", my_data->fluence_cap, sizeof("fluence"))) { + ALOGV("fluence dualmic feature enabled \n"); + fluence_type = FLUENCE_DUAL_MIC; + fluence_flag = DMIC_FLAG; + } else { + ALOGE("%s: Failed to set DUALMIC", __func__); + ret = -1; + goto done; + } + } else if (!strncmp(AUDIO_PARAMETER_KEY_NO_FLUENCE, value, sizeof(AUDIO_PARAMETER_KEY_NO_FLUENCE))) { + ALOGV("fluence disabled"); + fluence_type = FLUENCE_NONE; + } else { + ALOGE("Invalid fluence value : %s",value); + ret = -1; + goto done; + } + + if (fluence_type != my_data->fluence_type) { + ALOGV("%s: Updating fluence_type to :%d", __func__, fluence_type); + my_data->fluence_type = fluence_type; + adev->acdb_settings = (adev->acdb_settings & FLUENCE_MODE_CLEAR) | fluence_flag; + } +done: + return ret; +} + +int platform_get_fluence_type(void *platform, char *value, uint32_t len) +{ + int ret = 0; + struct platform_data *my_data = (struct platform_data *)platform; + + if (my_data->fluence_type == FLUENCE_QUAD_MIC) { + strlcpy(value, "quadmic", len); + } else if (my_data->fluence_type == FLUENCE_DUAL_MIC) { + strlcpy(value, "dualmic", len); + } else if (my_data->fluence_type == FLUENCE_NONE) { + strlcpy(value, "none", len); + } else + ret = -1; + + return ret; +} + +int platform_set_snd_device_acdb_id(snd_device_t snd_device, unsigned int acdb_id) +{ + int ret = 0; + + if ((snd_device < SND_DEVICE_MIN) || (snd_device >= SND_DEVICE_MAX)) { + ALOGE("%s: Invalid snd_device = %d", + __func__, snd_device); + ret = -EINVAL; + goto done; + } + + acdb_device_table[snd_device] = acdb_id; +done: + return ret; +} + +int platform_get_default_app_type(void *platform) +{ + struct platform_data *my_data = (struct platform_data *)platform; + + if (my_data->acdb_get_default_app_type) + return my_data->acdb_get_default_app_type(); + else + return DEFAULT_APP_TYPE; +} + +int platform_get_snd_device_acdb_id(snd_device_t snd_device) +{ + if ((snd_device < SND_DEVICE_MIN) || (snd_device >= SND_DEVICE_MAX)) { + ALOGE("%s: Invalid snd_device = %d", __func__, snd_device); + return -EINVAL; + } + return acdb_device_table[snd_device]; +} + +int platform_send_audio_calibration(void *platform, struct audio_usecase *usecase, + int app_type, int sample_rate) +{ + struct platform_data *my_data = (struct platform_data *)platform; + int acdb_dev_id, acdb_dev_type; + struct audio_device *adev = my_data->adev; + int snd_device = SND_DEVICE_OUT_SPEAKER; + + if (usecase->type == PCM_PLAYBACK) { + snd_device = usecase->out_snd_device; + if(usecase->id != USECASE_AUDIO_PLAYBACK_OFFLOAD) + app_type = APP_TYPE_SYSTEM_SOUNDS; + } else if ((usecase->type == PCM_HFP_CALL) || (usecase->type == PCM_CAPTURE)) { + snd_device = usecase->in_snd_device; + app_type = APP_TYPE_GENERAL_RECORDING; + } + + acdb_dev_id = acdb_device_table[snd_device]; + if (acdb_dev_id < 0) { + ALOGE("%s: Could not find acdb id for device(%d)", + __func__, snd_device); + return -EINVAL; + } + if (my_data->acdb_send_audio_cal) { + ALOGV("%s: sending audio calibration for snd_device(%d) acdb_id(%d)", + __func__, snd_device, acdb_dev_id); + if (snd_device >= SND_DEVICE_OUT_BEGIN && + snd_device < SND_DEVICE_OUT_END) + acdb_dev_type = ACDB_DEV_TYPE_OUT; + else + acdb_dev_type = ACDB_DEV_TYPE_IN; + my_data->acdb_send_audio_cal(acdb_dev_id, acdb_dev_type, app_type, + sample_rate); + } + return 0; +} + +int platform_switch_voice_call_device_pre(void *platform) +{ + struct platform_data *my_data = (struct platform_data *)platform; + int ret = 0; + + if (my_data->csd != NULL && + my_data->adev->mode == AUDIO_MODE_IN_CALL) { + /* This must be called before disabling mixer controls on APQ side */ + ret = my_data->csd->disable_device(); + if (ret < 0) { + ALOGE("%s: csd_client_disable_device, failed, error %d", + __func__, ret); + } + } + return ret; +} +int platform_switch_voice_call_enable_device_config(void *platform, + snd_device_t out_snd_device, + snd_device_t in_snd_device) +{ + struct platform_data *my_data = (struct platform_data *)platform; + int acdb_rx_id, acdb_tx_id; + int ret = 0; + + acdb_rx_id = acdb_device_table[out_snd_device]; + acdb_tx_id = acdb_device_table[in_snd_device]; + + if (my_data->csd != NULL) { + if (acdb_rx_id > 0 && acdb_tx_id > 0) { + ret = my_data->csd->enable_device_config(acdb_rx_id, acdb_tx_id); + if (ret < 0) { + ALOGE("%s: csd_enable_device_config, failed, error %d", + __func__, ret); + } + } else { + ALOGE("%s: Incorrect ACDB IDs (rx: %d tx: %d)", __func__, + acdb_rx_id, acdb_tx_id); + } + } + return ret; +} + + +int platform_switch_voice_call_device_post(void *platform, + snd_device_t out_snd_device, + snd_device_t in_snd_device) +{ + struct platform_data *my_data = (struct platform_data *)platform; + int acdb_rx_id, acdb_tx_id; + + if (my_data->acdb_send_voice_cal == NULL) { + ALOGE("%s: dlsym error for acdb_send_voice_call", __func__); + } else { + acdb_rx_id = acdb_device_table[out_snd_device]; + acdb_tx_id = acdb_device_table[in_snd_device]; + + if (acdb_rx_id > 0 && acdb_tx_id > 0) + my_data->acdb_send_voice_cal(acdb_rx_id, acdb_tx_id); + else + ALOGE("%s: Incorrect ACDB IDs (rx: %d tx: %d)", __func__, + acdb_rx_id, acdb_tx_id); + } + + return 0; +} + +int platform_switch_voice_call_usecase_route_post(void *platform, + snd_device_t out_snd_device, + snd_device_t in_snd_device) +{ + struct platform_data *my_data = (struct platform_data *)platform; + int acdb_rx_id, acdb_tx_id; + int ret = 0; + + acdb_rx_id = acdb_device_table[out_snd_device]; + acdb_tx_id = acdb_device_table[in_snd_device]; + + if (my_data->csd != NULL) { + if (acdb_rx_id > 0 && acdb_tx_id > 0) { + ret = my_data->csd->enable_device(acdb_rx_id, acdb_tx_id, + my_data->adev->acdb_settings); + if (ret < 0) { + ALOGE("%s: csd_enable_device, failed, error %d", + __func__, ret); + } + } else { + ALOGE("%s: Incorrect ACDB IDs (rx: %d tx: %d)", __func__, + acdb_rx_id, acdb_tx_id); + } + } + return ret; +} + +int platform_start_voice_call(void *platform, uint32_t vsid) +{ + struct platform_data *my_data = (struct platform_data *)platform; + int ret = 0; + + if (my_data->csd != NULL) { + ret = my_data->csd->start_voice(vsid); + if (ret < 0) { + ALOGE("%s: csd_start_voice error %d\n", __func__, ret); + } + } + return ret; +} + +int platform_stop_voice_call(void *platform, uint32_t vsid) +{ + struct platform_data *my_data = (struct platform_data *)platform; + int ret = 0; + + if (my_data->csd != NULL) { + ret = my_data->csd->stop_voice(vsid); + if (ret < 0) { + ALOGE("%s: csd_stop_voice error %d\n", __func__, ret); + } + } + return ret; +} + +int platform_get_sample_rate(void *platform __unused, uint32_t *rate __unused) +{ + return 0; +} + +int platform_set_voice_volume(void *platform, int volume) +{ + struct platform_data *my_data = (struct platform_data *)platform; + struct audio_device *adev = my_data->adev; + struct mixer_ctl *ctl; + const char *mixer_ctl_name = "Voice Rx Gain"; + int vol_index = 0, ret = 0; + uint32_t set_values[ ] = {0, + ALL_SESSION_VSID, + DEFAULT_VOLUME_RAMP_DURATION_MS}; + + // Voice volume levels are mapped to adsp volume levels as follows. + // 100 -> 5, 80 -> 4, 60 -> 3, 40 -> 2, 20 -> 1 0 -> 0 + // But this values don't changed in kernel. So, below change is need. + vol_index = (int)percent_to_index(volume, MIN_VOL_INDEX, MAX_VOL_INDEX); + set_values[0] = vol_index; + + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + return -EINVAL; + } + ALOGV("Setting voice volume index: %d", set_values[0]); + mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values)); + + if (my_data->csd != NULL) { + ret = my_data->csd->volume(ALL_SESSION_VSID, volume); + if (ret < 0) { + ALOGE("%s: csd_volume error %d", __func__, ret); + } + } + return ret; +} + +int platform_set_mic_mute(void *platform, bool state) +{ + struct platform_data *my_data = (struct platform_data *)platform; + struct audio_device *adev = my_data->adev; + struct mixer_ctl *ctl; + const char *mixer_ctl_name = "Voice Tx Mute"; + int ret = 0; + uint32_t set_values[ ] = {0, + ALL_SESSION_VSID, + DEFAULT_VOLUME_RAMP_DURATION_MS}; + + set_values[0] = state; + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + return -EINVAL; + } + ALOGV("Setting voice mute state: %d", state); + mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values)); + + if (my_data->csd != NULL) { + ret = my_data->csd->mic_mute(ALL_SESSION_VSID, state); + if (ret < 0) { + ALOGE("%s: csd_mic_mute error %d", __func__, ret); + } + } + return ret; +} + +int platform_set_device_mute(void *platform, bool state, char *dir) +{ + struct platform_data *my_data = (struct platform_data *)platform; + struct audio_device *adev = my_data->adev; + struct mixer_ctl *ctl; + char *mixer_ctl_name = NULL; + int ret = 0; + uint32_t set_values[ ] = {0, + ALL_SESSION_VSID, + 0}; + if(dir == NULL) { + ALOGE("%s: Invalid direction:%s", __func__, dir); + return -EINVAL; + } + + if (!strncmp("rx", dir, sizeof("rx"))) { + mixer_ctl_name = "Voice Rx Device Mute"; + } else if (!strncmp("tx", dir, sizeof("tx"))) { + mixer_ctl_name = "Voice Tx Device Mute"; + } else { + return -EINVAL; + } + + set_values[0] = state; + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + return -EINVAL; + } + + ALOGV("%s: Setting device mute state: %d, mixer ctrl:%s", + __func__,state, mixer_ctl_name); + mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values)); + + return ret; +} + +snd_device_t platform_get_output_snd_device(void *platform, audio_devices_t devices) +{ + struct platform_data *my_data = (struct platform_data *)platform; + struct audio_device *adev = my_data->adev; + audio_mode_t mode = adev->mode; + snd_device_t snd_device = SND_DEVICE_NONE; +#ifdef RECORD_PLAY_CONCURRENCY + bool use_voip_out_devices = false; + bool prop_rec_play_enabled = false; + char recConcPropValue[PROPERTY_VALUE_MAX]; + + if (property_get("rec.playback.conc.disabled", recConcPropValue, NULL)) { + prop_rec_play_enabled = atoi(recConcPropValue) || !strncmp("true", recConcPropValue, 4); + } + use_voip_out_devices = prop_rec_play_enabled && + (my_data->rec_play_conc_set || adev->mode == AUDIO_MODE_IN_COMMUNICATION); + ALOGV("platform_get_output_snd_device use_voip_out_devices : %d",use_voip_out_devices); +#endif + + audio_channel_mask_t channel_mask = (adev->active_input == NULL) ? + AUDIO_CHANNEL_IN_MONO : adev->active_input->channel_mask; + int channel_count = popcount(channel_mask); + + ALOGV("%s: enter: output devices(%#x)", __func__, devices); + if (devices == AUDIO_DEVICE_NONE || + devices & AUDIO_DEVICE_BIT_IN) { + ALOGV("%s: Invalid output devices (%#x)", __func__, devices); + goto exit; + } + + if (popcount(devices) == 2) { + if (devices == (AUDIO_DEVICE_OUT_WIRED_HEADPHONE | + AUDIO_DEVICE_OUT_SPEAKER)) { + snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES; + } else if (devices == (AUDIO_DEVICE_OUT_WIRED_HEADSET | + AUDIO_DEVICE_OUT_SPEAKER)) { + if (audio_extn_get_anc_enabled()) + snd_device = SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET; + else + snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES; + } else if (devices == (AUDIO_DEVICE_OUT_AUX_DIGITAL | + AUDIO_DEVICE_OUT_SPEAKER)) { + snd_device = SND_DEVICE_OUT_SPEAKER_AND_HDMI; + } else if (devices == (AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET | + AUDIO_DEVICE_OUT_SPEAKER)) { + snd_device = SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET; + } else { + ALOGE("%s: Invalid combo device(%#x)", __func__, devices); + goto exit; + } + if (snd_device != SND_DEVICE_NONE) { + goto exit; + } + } + + if (popcount(devices) != 1) { + ALOGE("%s: Invalid output devices(%#x)", __func__, devices); + goto exit; + } + + if ((mode == AUDIO_MODE_IN_CALL) || + voice_extn_compress_voip_is_active(adev)) { + if (devices & AUDIO_DEVICE_OUT_WIRED_HEADPHONE || + devices & AUDIO_DEVICE_OUT_WIRED_HEADSET) { + if ((adev->voice.tty_mode != TTY_MODE_OFF) && + !voice_extn_compress_voip_is_active(adev)) { + switch (adev->voice.tty_mode) { + case TTY_MODE_FULL: + snd_device = SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES; + break; + case TTY_MODE_VCO: + snd_device = SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES; + break; + case TTY_MODE_HCO: + snd_device = SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET; + break; + default: + ALOGE("%s: Invalid TTY mode (%#x)", + __func__, adev->voice.tty_mode); + } + } else if (audio_extn_get_anc_enabled()) { + if (audio_extn_should_use_fb_anc()) + snd_device = SND_DEVICE_OUT_VOICE_ANC_FB_HEADSET; + else + snd_device = SND_DEVICE_OUT_VOICE_ANC_HEADSET; + } else { + snd_device = SND_DEVICE_OUT_VOICE_HEADPHONES; + } + } else if (devices & AUDIO_DEVICE_OUT_ALL_SCO) { + if (adev->bt_wb_speech_enabled) + snd_device = SND_DEVICE_OUT_BT_SCO_WB; + else + snd_device = SND_DEVICE_OUT_BT_SCO; + } else if (devices & AUDIO_DEVICE_OUT_SPEAKER) { + snd_device = SND_DEVICE_OUT_VOICE_SPEAKER; + } else if (devices & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET || + devices & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET) { + snd_device = SND_DEVICE_OUT_USB_HEADSET; + } else if (devices & AUDIO_DEVICE_OUT_FM_TX) { + snd_device = SND_DEVICE_OUT_TRANSMISSION_FM; + } else if (devices & AUDIO_DEVICE_OUT_EARPIECE) { + if (audio_extn_should_use_handset_anc(channel_count)) + snd_device = SND_DEVICE_OUT_ANC_HANDSET; + else + snd_device = SND_DEVICE_OUT_VOICE_HANDSET; + } + if (snd_device != SND_DEVICE_NONE) { + goto exit; + } + } + + if (devices & AUDIO_DEVICE_OUT_WIRED_HEADPHONE || + devices & AUDIO_DEVICE_OUT_WIRED_HEADSET) { + if (devices & AUDIO_DEVICE_OUT_WIRED_HEADSET + && audio_extn_get_anc_enabled()) { +#ifdef RECORD_PLAY_CONCURRENCY + if (use_voip_out_devices) { + // ANC should be disabled for voip concurrency + snd_device = SND_DEVICE_OUT_VOIP_HEADPHONES; + } else +#endif + { + if (audio_extn_should_use_fb_anc()) + snd_device = SND_DEVICE_OUT_ANC_FB_HEADSET; + else + snd_device = SND_DEVICE_OUT_ANC_HEADSET; + } + } else { +#ifdef RECORD_PLAY_CONCURRENCY + if (use_voip_out_devices) + snd_device = SND_DEVICE_OUT_VOIP_HEADPHONES; + else +#endif + snd_device = SND_DEVICE_OUT_HEADPHONES; + } + } else if (devices & AUDIO_DEVICE_OUT_SPEAKER) { +#ifdef RECORD_PLAY_CONCURRENCY + if (use_voip_out_devices) { + snd_device = SND_DEVICE_OUT_VOIP_SPEAKER; + } else +#endif + { + if (adev->speaker_lr_swap) + snd_device = SND_DEVICE_OUT_SPEAKER_REVERSE; + else + snd_device = SND_DEVICE_OUT_SPEAKER; + } + } else if (devices & AUDIO_DEVICE_OUT_ALL_SCO) { + if (adev->bt_wb_speech_enabled) + snd_device = SND_DEVICE_OUT_BT_SCO_WB; + else + snd_device = SND_DEVICE_OUT_BT_SCO; + } else if (devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) { + snd_device = SND_DEVICE_OUT_HDMI ; + } else if (devices & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET || + devices & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET) { + ALOGD("%s: setting USB hadset channel capability(2) for Proxy", __func__); + audio_extn_set_afe_proxy_channel_mixer(adev, 2); + snd_device = SND_DEVICE_OUT_USB_HEADSET; + } else if (devices & AUDIO_DEVICE_OUT_FM_TX) { + snd_device = SND_DEVICE_OUT_TRANSMISSION_FM; + } else if (devices & AUDIO_DEVICE_OUT_EARPIECE) { +#ifdef RECORD_PLAY_CONCURRENCY + if (use_voip_out_devices) + snd_device = SND_DEVICE_OUT_VOIP_HANDSET; + else +#endif + snd_device = SND_DEVICE_OUT_HANDSET; + } else if (devices & AUDIO_DEVICE_OUT_PROXY) { + channel_count = audio_extn_get_afe_proxy_channel_count(); + ALOGD("%s: setting sink capability(%d) for Proxy", __func__, channel_count); + audio_extn_set_afe_proxy_channel_mixer(adev, channel_count); + snd_device = SND_DEVICE_OUT_AFE_PROXY; + } else { + ALOGE("%s: Unknown device(s) %#x", __func__, devices); + } +exit: + ALOGV("%s: exit: snd_device(%s)", __func__, device_table[snd_device]); + return snd_device; +} + +snd_device_t platform_get_input_snd_device(void *platform, audio_devices_t out_device) +{ + struct platform_data *my_data = (struct platform_data *)platform; + struct audio_device *adev = my_data->adev; + audio_source_t source = (adev->active_input == NULL) ? + AUDIO_SOURCE_DEFAULT : adev->active_input->source; + + audio_mode_t mode = adev->mode; + audio_devices_t in_device = ((adev->active_input == NULL) ? + AUDIO_DEVICE_NONE : adev->active_input->device) + & ~AUDIO_DEVICE_BIT_IN; + audio_channel_mask_t channel_mask = (adev->active_input == NULL) ? + AUDIO_CHANNEL_IN_MONO : adev->active_input->channel_mask; + snd_device_t snd_device = SND_DEVICE_NONE; + int channel_count = popcount(channel_mask); + + ALOGV("%s: enter: out_device(%#x) in_device(%#x)", + __func__, out_device, in_device); + if ((out_device != AUDIO_DEVICE_NONE) && ((mode == AUDIO_MODE_IN_CALL) || + voice_extn_compress_voip_is_active(adev) || audio_extn_hfp_is_active(adev))) { + if ((adev->voice.tty_mode != TTY_MODE_OFF) && + !voice_extn_compress_voip_is_active(adev)) { + if (out_device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE || + out_device & AUDIO_DEVICE_OUT_WIRED_HEADSET) { + switch (adev->voice.tty_mode) { + case TTY_MODE_FULL: + snd_device = SND_DEVICE_IN_VOICE_TTY_FULL_HEADSET_MIC; + break; + case TTY_MODE_VCO: + snd_device = SND_DEVICE_IN_VOICE_TTY_VCO_HANDSET_MIC; + break; + case TTY_MODE_HCO: + snd_device = SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC; + break; + default: + ALOGE("%s: Invalid TTY mode (%#x)", + __func__, adev->voice.tty_mode); + } + goto exit; + } + } + if (out_device & AUDIO_DEVICE_OUT_EARPIECE || + out_device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE) { + if (out_device & AUDIO_DEVICE_OUT_EARPIECE && + audio_extn_should_use_handset_anc(channel_count) && + my_data->fluence_type != FLUENCE_NONE) { + snd_device = SND_DEVICE_IN_VOICE_FLUENCE_DMIC_AANC; + adev->acdb_settings |= DMIC_FLAG; + ALOGD("Selecting AANC, Fluence combo device"); + } else if (out_device & AUDIO_DEVICE_OUT_EARPIECE && + audio_extn_should_use_handset_anc(channel_count)) { + snd_device = SND_DEVICE_IN_AANC_HANDSET_MIC; + } else if (my_data->fluence_type == FLUENCE_NONE || + my_data->fluence_in_voice_call == false) { + snd_device = SND_DEVICE_IN_HANDSET_MIC; + if (audio_extn_hfp_is_active(adev)) + platform_set_echo_reference(adev->platform, true); + } else { + snd_device = SND_DEVICE_IN_VOICE_DMIC; + adev->acdb_settings |= DMIC_FLAG; + } + } else if (out_device & AUDIO_DEVICE_OUT_WIRED_HEADSET) { + snd_device = SND_DEVICE_IN_VOICE_HEADSET_MIC; + if (audio_extn_hfp_is_active(adev)) + platform_set_echo_reference(adev->platform, true); + } else if (out_device & AUDIO_DEVICE_OUT_ALL_SCO) { + if (adev->bt_wb_speech_enabled) { + if (adev->bluetooth_nrec) + snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB_NREC; + else + snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB; + } else { + if (adev->bluetooth_nrec) + snd_device = SND_DEVICE_IN_BT_SCO_MIC_NREC; + else + snd_device = SND_DEVICE_IN_BT_SCO_MIC; + } + } else if (out_device & AUDIO_DEVICE_OUT_SPEAKER) { + if (my_data->fluence_type != FLUENCE_NONE && + my_data->fluence_in_voice_call && + my_data->fluence_in_spkr_mode) { + if(my_data->fluence_type & FLUENCE_QUAD_MIC) { + adev->acdb_settings |= QMIC_FLAG; + snd_device = SND_DEVICE_IN_VOICE_SPEAKER_QMIC; + } else { + adev->acdb_settings |= DMIC_FLAG; + if (my_data->fluence_mode == FLUENCE_BROADSIDE) + snd_device = SND_DEVICE_IN_VOICE_SPEAKER_DMIC_BROADSIDE; + else + snd_device = SND_DEVICE_IN_VOICE_SPEAKER_DMIC; + } + } else { + snd_device = SND_DEVICE_IN_VOICE_SPEAKER_MIC; + if (audio_extn_hfp_is_active(adev)) + platform_set_echo_reference(adev->platform, true); + } + } + } else if (source == AUDIO_SOURCE_CAMCORDER) { + if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC || + in_device & AUDIO_DEVICE_IN_BACK_MIC) { + if (my_data->fluence_type & FLUENCE_DUAL_MIC && + channel_count == 2) + snd_device = SND_DEVICE_IN_HANDSET_STEREO_DMIC; + else + snd_device = SND_DEVICE_IN_CAMCORDER_MIC; + } + } else if (source == AUDIO_SOURCE_VOICE_RECOGNITION) { + if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC) { + if (channel_count == 2) { + snd_device = SND_DEVICE_IN_VOICE_REC_DMIC_STEREO; + adev->acdb_settings |= DMIC_FLAG; + } else if (adev->active_input->enable_ns) + snd_device = SND_DEVICE_IN_VOICE_REC_MIC_NS; + else if (my_data->fluence_type != FLUENCE_NONE && + my_data->fluence_in_voice_rec) { + snd_device = SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE; + adev->acdb_settings |= DMIC_FLAG; + } else { + snd_device = SND_DEVICE_IN_VOICE_REC_MIC; + } + } + } else if ((source == AUDIO_SOURCE_VOICE_COMMUNICATION) || + (mode == AUDIO_MODE_IN_COMMUNICATION)) { + if (out_device & AUDIO_DEVICE_OUT_SPEAKER) + in_device = AUDIO_DEVICE_IN_BACK_MIC; + if (adev->active_input) { + if (adev->active_input->enable_aec && + adev->active_input->enable_ns) { + if (in_device & AUDIO_DEVICE_IN_BACK_MIC) { + if (my_data->fluence_type & FLUENCE_DUAL_MIC && + my_data->fluence_in_spkr_mode) { + if (my_data->fluence_mode == FLUENCE_BROADSIDE) + snd_device = SND_DEVICE_IN_SPEAKER_DMIC_AEC_NS_BROADSIDE; + else + snd_device = SND_DEVICE_IN_SPEAKER_DMIC_AEC_NS; + adev->acdb_settings |= DMIC_FLAG; + } else + snd_device = SND_DEVICE_IN_SPEAKER_MIC_AEC_NS; + } else if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC) { + if (my_data->fluence_type & FLUENCE_DUAL_MIC) { + snd_device = SND_DEVICE_IN_HANDSET_DMIC_AEC_NS; + adev->acdb_settings |= DMIC_FLAG; + } else + snd_device = SND_DEVICE_IN_HANDSET_MIC_AEC_NS; + } else if (in_device & AUDIO_DEVICE_IN_WIRED_HEADSET) { + snd_device = SND_DEVICE_IN_HEADSET_MIC_FLUENCE; + } + platform_set_echo_reference(adev->platform, true); + } else if (adev->active_input->enable_aec) { + if (in_device & AUDIO_DEVICE_IN_BACK_MIC) { + if (my_data->fluence_type & FLUENCE_DUAL_MIC && + my_data->fluence_in_spkr_mode) { + if (my_data->fluence_mode == FLUENCE_BROADSIDE) + snd_device = SND_DEVICE_IN_SPEAKER_DMIC_AEC_BROADSIDE; + else + snd_device = SND_DEVICE_IN_SPEAKER_DMIC_AEC; + adev->acdb_settings |= DMIC_FLAG; + } else + snd_device = SND_DEVICE_IN_SPEAKER_MIC_AEC; + } else if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC) { + if (my_data->fluence_type & FLUENCE_DUAL_MIC) { + snd_device = SND_DEVICE_IN_HANDSET_DMIC_AEC; + adev->acdb_settings |= DMIC_FLAG; + } else + snd_device = SND_DEVICE_IN_HANDSET_MIC_AEC; + } else if (in_device & AUDIO_DEVICE_IN_WIRED_HEADSET) { + snd_device = SND_DEVICE_IN_HEADSET_MIC_FLUENCE; + } + platform_set_echo_reference(adev->platform, true); + } else if (adev->active_input->enable_ns) { + if (in_device & AUDIO_DEVICE_IN_BACK_MIC) { + if (my_data->fluence_type & FLUENCE_DUAL_MIC && + my_data->fluence_in_spkr_mode) { + if (my_data->fluence_mode == FLUENCE_BROADSIDE) + snd_device = SND_DEVICE_IN_SPEAKER_DMIC_NS_BROADSIDE; + else + snd_device = SND_DEVICE_IN_SPEAKER_DMIC_NS; + adev->acdb_settings |= DMIC_FLAG; + } else + snd_device = SND_DEVICE_IN_SPEAKER_MIC_NS; + } else if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC) { + if (my_data->fluence_type & FLUENCE_DUAL_MIC) { + snd_device = SND_DEVICE_IN_HANDSET_DMIC_NS; + adev->acdb_settings |= DMIC_FLAG; + } else + snd_device = SND_DEVICE_IN_HANDSET_MIC_NS; + } else if (in_device & AUDIO_DEVICE_IN_WIRED_HEADSET) { + snd_device = SND_DEVICE_IN_HEADSET_MIC_FLUENCE; + } + platform_set_echo_reference(adev->platform,false); + } else + platform_set_echo_reference(adev->platform, false); + } + } else if (source == AUDIO_SOURCE_MIC) { + if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC && + channel_count == 1 ) { + if(my_data->fluence_type & FLUENCE_DUAL_MIC && + my_data->fluence_in_audio_rec) + snd_device = SND_DEVICE_IN_HANDSET_DMIC; + } + } else if (source == AUDIO_SOURCE_FM_RX || + source == AUDIO_SOURCE_FM_RX_A2DP) { + snd_device = SND_DEVICE_IN_CAPTURE_FM; + } else if (source == AUDIO_SOURCE_DEFAULT) { + goto exit; + } + + + if (snd_device != SND_DEVICE_NONE) { + goto exit; + } + + if (in_device != AUDIO_DEVICE_NONE && + !(in_device & AUDIO_DEVICE_IN_VOICE_CALL) && + !(in_device & AUDIO_DEVICE_IN_COMMUNICATION)) { + if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC) { + if (audio_extn_ssr_get_enabled() && channel_count == 6) + snd_device = SND_DEVICE_IN_QUAD_MIC; + else if (channel_count == 2) + snd_device = SND_DEVICE_IN_HANDSET_STEREO_DMIC; + else + snd_device = SND_DEVICE_IN_HANDSET_MIC; + } else if (in_device & AUDIO_DEVICE_IN_BACK_MIC) { + snd_device = SND_DEVICE_IN_SPEAKER_MIC; + } else if (in_device & AUDIO_DEVICE_IN_WIRED_HEADSET) { + snd_device = SND_DEVICE_IN_HEADSET_MIC; + } else if (in_device & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) { + if (adev->bt_wb_speech_enabled) { + if (adev->bluetooth_nrec) + snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB_NREC; + else + snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB; + } else { + if (adev->bluetooth_nrec) + snd_device = SND_DEVICE_IN_BT_SCO_MIC_NREC; + else + snd_device = SND_DEVICE_IN_BT_SCO_MIC; + } + } else if (in_device & AUDIO_DEVICE_IN_AUX_DIGITAL) { + snd_device = SND_DEVICE_IN_HDMI_MIC; + } else if (in_device & AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET || + in_device & AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET) { + snd_device = SND_DEVICE_IN_USB_HEADSET_MIC; + } else if (in_device & AUDIO_DEVICE_IN_FM_RX) { + snd_device = SND_DEVICE_IN_CAPTURE_FM; + } else { + ALOGE("%s: Unknown input device(s) %#x", __func__, in_device); + ALOGW("%s: Using default handset-mic", __func__); + snd_device = SND_DEVICE_IN_HANDSET_MIC; + } + } else { + if (out_device & AUDIO_DEVICE_OUT_EARPIECE) { + snd_device = SND_DEVICE_IN_HANDSET_MIC; + } else if (out_device & AUDIO_DEVICE_OUT_WIRED_HEADSET) { + snd_device = SND_DEVICE_IN_HEADSET_MIC; + } else if (out_device & AUDIO_DEVICE_OUT_SPEAKER) { + if (channel_count > 1) + snd_device = SND_DEVICE_IN_SPEAKER_STEREO_DMIC; + else + snd_device = SND_DEVICE_IN_SPEAKER_MIC; + } else if (out_device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE) { + snd_device = SND_DEVICE_IN_HANDSET_MIC; + } else if (out_device & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET) { + if (adev->bt_wb_speech_enabled) { + if (adev->bluetooth_nrec) + snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB_NREC; + else + snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB; + } else { + if (adev->bluetooth_nrec) + snd_device = SND_DEVICE_IN_BT_SCO_MIC_NREC; + else + snd_device = SND_DEVICE_IN_BT_SCO_MIC; + } + } else if (out_device & AUDIO_DEVICE_OUT_AUX_DIGITAL) { + snd_device = SND_DEVICE_IN_HDMI_MIC; + } else if (out_device & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET || + out_device & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET) { + snd_device = SND_DEVICE_IN_USB_HEADSET_MIC; + } else { + ALOGE("%s: Unknown output device(s) %#x", __func__, out_device); + ALOGW("%s: Using default handset-mic", __func__); + snd_device = SND_DEVICE_IN_HANDSET_MIC; + } + } +exit: + ALOGV("%s: exit: in_snd_device(%s)", __func__, device_table[snd_device]); + return snd_device; +} + +int platform_set_hdmi_channels(void *platform, int channel_count) +{ + struct platform_data *my_data = (struct platform_data *)platform; + struct audio_device *adev = my_data->adev; + struct mixer_ctl *ctl; + const char *channel_cnt_str = NULL; + const char *mixer_ctl_name = "HDMI_RX Channels"; + switch (channel_count) { + case 8: + channel_cnt_str = "Eight"; break; + case 7: + channel_cnt_str = "Seven"; break; + case 6: + channel_cnt_str = "Six"; break; + case 5: + channel_cnt_str = "Five"; break; + case 4: + channel_cnt_str = "Four"; break; + case 3: + channel_cnt_str = "Three"; break; + default: + channel_cnt_str = "Two"; break; + } + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + return -EINVAL; + } + ALOGV("HDMI channel count: %s", channel_cnt_str); + mixer_ctl_set_enum_by_string(ctl, channel_cnt_str); + return 0; +} + +int platform_edid_get_max_channels(void *platform) +{ + struct platform_data *my_data = (struct platform_data *)platform; + struct audio_device *adev = my_data->adev; + char block[MAX_SAD_BLOCKS * SAD_BLOCK_SIZE]; + char *sad = block; + int num_audio_blocks; + int channel_count; + int max_channels = 0; + int i, ret, count; + + struct mixer_ctl *ctl; + + ctl = mixer_get_ctl_by_name(adev->mixer, AUDIO_DATA_BLOCK_MIXER_CTL); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, AUDIO_DATA_BLOCK_MIXER_CTL); + return 0; + } + + mixer_ctl_update(ctl); + + count = mixer_ctl_get_num_values(ctl); + + /* Read SAD blocks, clamping the maximum size for safety */ + if (count > (int)sizeof(block)) + count = (int)sizeof(block); + + ret = mixer_ctl_get_array(ctl, block, count); + if (ret != 0) { + ALOGE("%s: mixer_ctl_get_array() failed to get EDID info", __func__); + return 0; + } + + /* Calculate the number of SAD blocks */ + num_audio_blocks = count / SAD_BLOCK_SIZE; + + for (i = 0; i < num_audio_blocks; i++) { + /* Only consider LPCM blocks */ + if ((sad[0] >> 3) != EDID_FORMAT_LPCM) { + sad += 3; + continue; + } + + channel_count = (sad[0] & 0x7) + 1; + if (channel_count > max_channels) + max_channels = channel_count; + + /* Advance to next block */ + sad += 3; + } + + return max_channels; +} + +static int platform_set_slowtalk(struct platform_data *my_data, bool state) +{ + int ret = 0; + struct audio_device *adev = my_data->adev; + struct mixer_ctl *ctl; + const char *mixer_ctl_name = "Slowtalk Enable"; + uint32_t set_values[ ] = {0, + ALL_SESSION_VSID}; + + set_values[0] = state; + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + ret = -EINVAL; + } else { + ALOGV("Setting slowtalk state: %d", state); + ret = mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values)); + my_data->slowtalk = state; + } + + if (my_data->csd != NULL) { + ret = my_data->csd->slow_talk(ALL_SESSION_VSID, state); + if (ret < 0) { + ALOGE("%s: csd_client_disable_device, failed, error %d", + __func__, ret); + } + } + return ret; +} + +static int set_hd_voice(struct platform_data *my_data, bool state) +{ + struct audio_device *adev = my_data->adev; + struct mixer_ctl *ctl; + const char *mixer_ctl_name = "HD Voice Enable"; + int ret = 0; + uint32_t set_values[ ] = {0, + ALL_SESSION_VSID}; + + set_values[0] = state; + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + ret = -EINVAL; + } else { + ALOGV("Setting HD Voice state: %d", state); + ret = mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values)); + my_data->hd_voice = state; + } + + return ret; +} + +int platform_set_parameters(void *platform, struct str_parms *parms) +{ + struct platform_data *my_data = (struct platform_data *)platform; + char *str; + char value[256] = {0}; + int val; + int ret = 0, err; + char *kv_pairs = NULL; + + kv_pairs = str_parms_to_str(parms); + ALOGV("%s: enter: - %s", __func__, kv_pairs); + free(kv_pairs); + + err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_SLOWTALK, value, sizeof(value)); + if (err >= 0) { + bool state = false; + if (!strncmp("true", value, sizeof("true"))) { + state = true; + } + + str_parms_del(parms, AUDIO_PARAMETER_KEY_SLOWTALK); + ret = platform_set_slowtalk(my_data, state); + if (ret) + ALOGE("%s: Failed to set slow talk err: %d", __func__, ret); + } + + err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_HD_VOICE, value, sizeof(value)); + if (err >= 0) { + bool state = false; + if (!strncmp("true", value, sizeof("true"))) { + state = true; + } + + str_parms_del(parms, AUDIO_PARAMETER_KEY_HD_VOICE); + if (my_data->hd_voice != state) { + ret = set_hd_voice(my_data, state); + if (ret) + ALOGE("%s: Failed to set HD voice err: %d", __func__, ret); + } else { + ALOGV("%s: HD Voice already set to %d", __func__, state); + } + } + + err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_VOLUME_BOOST, + value, sizeof(value)); + if (err >= 0) { + str_parms_del(parms, AUDIO_PARAMETER_KEY_VOLUME_BOOST); + + if (my_data->acdb_reload_vocvoltable == NULL) { + ALOGE("%s: acdb_reload_vocvoltable is NULL", __func__); + } else if (!strcmp(value, "on")) { + if (!my_data->acdb_reload_vocvoltable(VOICE_FEATURE_SET_VOLUME_BOOST)) { + my_data->voice_feature_set = 1; + } + } else { + if (!my_data->acdb_reload_vocvoltable(VOICE_FEATURE_SET_DEFAULT)) { + my_data->voice_feature_set = 0; + } + } + } + +#ifdef RECORD_PLAY_CONCURRENCY + err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_REC_PLAY_CONC, value, sizeof(value)); + if (err >= 0) { + if (!strncmp("true", value, sizeof("true"))) { + ALOGD("setting record playback concurrency to true"); + my_data->rec_play_conc_set = true; + } else { + ALOGD("setting record playback concurrency to false"); + my_data->rec_play_conc_set = false; + } + } +#endif + ALOGV("%s: exit with code(%d)", __func__, ret); + return ret; +} + +int platform_set_incall_recording_session_id(void *platform, + uint32_t session_id, int rec_mode) +{ + int ret = 0; + struct platform_data *my_data = (struct platform_data *)platform; + struct audio_device *adev = my_data->adev; + struct mixer_ctl *ctl; + const char *mixer_ctl_name = "Voc VSID"; + int num_ctl_values; + int i; + + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + ret = -EINVAL; + } else { + num_ctl_values = mixer_ctl_get_num_values(ctl); + for (i = 0; i < num_ctl_values; i++) { + if (mixer_ctl_set_value(ctl, i, session_id)) { + ALOGV("Error: invalid session_id: %x", session_id); + ret = -EINVAL; + break; + } + } + } + + if (my_data->csd != NULL) { + ret = my_data->csd->start_record(ALL_SESSION_VSID, rec_mode); + if (ret < 0) { + ALOGE("%s: csd_client_start_record failed, error %d", + __func__, ret); + } + } + + return ret; +} + +int platform_stop_incall_recording_usecase(void *platform) +{ + int ret = 0; + struct platform_data *my_data = (struct platform_data *)platform; + + if (my_data->csd != NULL) { + ret = my_data->csd->stop_record(ALL_SESSION_VSID); + if (ret < 0) { + ALOGE("%s: csd_client_stop_record failed, error %d", + __func__, ret); + } + } + + return ret; +} + +int platform_start_incall_music_usecase(void *platform) +{ + int ret = 0; + struct platform_data *my_data = (struct platform_data *)platform; + + if (my_data->csd != NULL) { + ret = my_data->csd->start_playback(ALL_SESSION_VSID); + if (ret < 0) { + ALOGE("%s: csd_client_start_playback failed, error %d", + __func__, ret); + } + } + + return ret; +} + +int platform_stop_incall_music_usecase(void *platform) +{ + int ret = 0; + struct platform_data *my_data = (struct platform_data *)platform; + + if (my_data->csd != NULL) { + ret = my_data->csd->stop_playback(ALL_SESSION_VSID); + if (ret < 0) { + ALOGE("%s: csd_client_stop_playback failed, error %d", + __func__, ret); + } + } + + return ret; +} + +int platform_update_lch(void *platform, struct voice_session *session, + enum voice_lch_mode lch_mode) +{ + int ret = 0; + struct platform_data *my_data = (struct platform_data *)platform; + + if ((my_data->csd != NULL) && (my_data->csd->set_lch != NULL)) + ret = my_data->csd->set_lch(session->vsid, lch_mode); + else + ret = pcm_ioctl(session->pcm_tx, SNDRV_VOICE_IOCTL_LCH, &lch_mode); + + return ret; +} + +void platform_get_parameters(void *platform, + struct str_parms *query, + struct str_parms *reply) +{ + struct platform_data *my_data = (struct platform_data *)platform; + char *str = NULL; + char value[256] = {0}; + int ret; + char *kv_pairs = NULL; + + ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_SLOWTALK, + value, sizeof(value)); + if (ret >= 0) { + str_parms_add_str(reply, AUDIO_PARAMETER_KEY_SLOWTALK, + my_data->slowtalk?"true":"false"); + } + + ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_HD_VOICE, + value, sizeof(value)); + if (ret >= 0) { + str_parms_add_str(reply, AUDIO_PARAMETER_KEY_HD_VOICE, + my_data->hd_voice?"true":"false"); + } + + ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_VOLUME_BOOST, + value, sizeof(value)); + if (ret >= 0) { + if (my_data->voice_feature_set == VOICE_FEATURE_SET_VOLUME_BOOST) { + strlcpy(value, "on", sizeof(value)); + } else { + strlcpy(value, "off", sizeof(value)); + } + + str_parms_add_str(reply, AUDIO_PARAMETER_KEY_VOLUME_BOOST, value); + } + + kv_pairs = str_parms_to_str(reply); + ALOGV("%s: exit: returns - %s", __func__, kv_pairs); + free(kv_pairs); +} + +/* Delay in Us */ +int64_t platform_render_latency(audio_usecase_t usecase) +{ + switch (usecase) { + case USECASE_AUDIO_PLAYBACK_DEEP_BUFFER: + return DEEP_BUFFER_PLATFORM_DELAY; + case USECASE_AUDIO_PLAYBACK_LOW_LATENCY: + return LOW_LATENCY_PLATFORM_DELAY; + default: + return 0; + } +} + +int platform_update_usecase_from_source(int source, int usecase) +{ + ALOGV("%s: input source :%d", __func__, source); + if(source == AUDIO_SOURCE_FM_RX_A2DP) + usecase = USECASE_AUDIO_RECORD_FM_VIRTUAL; + return usecase; +} + +bool platform_listen_device_needs_event(snd_device_t snd_device) +{ + bool needs_event = false; + + if ((snd_device >= SND_DEVICE_IN_BEGIN) && + (snd_device < SND_DEVICE_IN_END) && + (snd_device != SND_DEVICE_IN_CAPTURE_FM) && + (snd_device != SND_DEVICE_IN_CAPTURE_VI_FEEDBACK)) + needs_event = true; + + return needs_event; +} + +bool platform_listen_usecase_needs_event(audio_usecase_t uc_id) +{ + bool needs_event = false; + + switch(uc_id){ + /* concurrent playback usecases needs event */ + case USECASE_AUDIO_PLAYBACK_DEEP_BUFFER: + case USECASE_AUDIO_PLAYBACK_MULTI_CH: + case USECASE_AUDIO_PLAYBACK_OFFLOAD: + needs_event = true; + break; + /* concurrent playback in low latency allowed */ + case USECASE_AUDIO_PLAYBACK_LOW_LATENCY: + break; + /* concurrent playback FM needs event */ + case USECASE_AUDIO_PLAYBACK_FM: + needs_event = true; + break; + + /* concurrent capture usecases, no event, capture handled by device + * USECASE_AUDIO_RECORD: + * USECASE_AUDIO_RECORD_COMPRESS: + * USECASE_AUDIO_RECORD_LOW_LATENCY: + + * USECASE_VOICE_CALL: + * USECASE_VOICE2_CALL: + * USECASE_VOLTE_CALL: + * USECASE_QCHAT_CALL: + * USECASE_VOWLAN_CALL: + * USECASE_COMPRESS_VOIP_CALL: + * USECASE_AUDIO_RECORD_FM_VIRTUAL: + * USECASE_INCALL_REC_UPLINK: + * USECASE_INCALL_REC_DOWNLINK: + * USECASE_INCALL_REC_UPLINK_AND_DOWNLINK: + * USECASE_INCALL_REC_UPLINK_COMPRESS: + * USECASE_INCALL_REC_DOWNLINK_COMPRESS: + * USECASE_INCALL_REC_UPLINK_AND_DOWNLINK_COMPRESS: + * USECASE_INCALL_MUSIC_UPLINK: + * USECASE_INCALL_MUSIC_UPLINK2: + * USECASE_AUDIO_SPKR_CALIB_RX: + * USECASE_AUDIO_SPKR_CALIB_TX: + */ + default: + ALOGV("%s:usecase_id[%d} no need to raise event.", __func__, uc_id); + } + return needs_event; +} + +bool platform_sound_trigger_device_needs_event(snd_device_t snd_device) +{ + bool needs_event = false; + + if ((snd_device >= SND_DEVICE_IN_BEGIN) && + (snd_device < SND_DEVICE_IN_END) && + (snd_device != SND_DEVICE_IN_CAPTURE_FM) && + (snd_device != SND_DEVICE_IN_CAPTURE_VI_FEEDBACK)) + needs_event = true; + + return needs_event; +} + +bool platform_sound_trigger_usecase_needs_event(audio_usecase_t uc_id) +{ + bool needs_event = false; + + switch(uc_id){ + /* concurrent playback usecases needs event */ + case USECASE_AUDIO_PLAYBACK_DEEP_BUFFER: + case USECASE_AUDIO_PLAYBACK_MULTI_CH: + case USECASE_AUDIO_PLAYBACK_OFFLOAD: + needs_event = true; + break; + /* concurrent playback in low latency allowed */ + case USECASE_AUDIO_PLAYBACK_LOW_LATENCY: + break; + /* concurrent playback FM needs event */ + case USECASE_AUDIO_PLAYBACK_FM: + needs_event = true; + break; + + /* concurrent capture usecases, no event, capture handled by device + * USECASE_AUDIO_RECORD: + * USECASE_AUDIO_RECORD_COMPRESS: + * USECASE_AUDIO_RECORD_LOW_LATENCY: + + * USECASE_VOICE_CALL: + * USECASE_VOICE2_CALL: + * USECASE_VOLTE_CALL: + * USECASE_QCHAT_CALL: + * USECASE_VOWLAN_CALL: + * USECASE_COMPRESS_VOIP_CALL: + * USECASE_AUDIO_RECORD_FM_VIRTUAL: + * USECASE_INCALL_REC_UPLINK: + * USECASE_INCALL_REC_DOWNLINK: + * USECASE_INCALL_REC_UPLINK_AND_DOWNLINK: + * USECASE_INCALL_REC_UPLINK_COMPRESS: + * USECASE_INCALL_REC_DOWNLINK_COMPRESS: + * USECASE_INCALL_REC_UPLINK_AND_DOWNLINK_COMPRESS: + * USECASE_INCALL_MUSIC_UPLINK: + * USECASE_INCALL_MUSIC_UPLINK2: + * USECASE_AUDIO_SPKR_CALIB_RX: + * USECASE_AUDIO_SPKR_CALIB_TX: + */ + default: + ALOGV("%s:usecase_id[%d] no need to raise event.", __func__, uc_id); + } + return needs_event; +} + +/* Read offload buffer size from a property. + * If value is not power of 2 round it to + * power of 2. + */ +uint32_t platform_get_compress_offload_buffer_size(audio_offload_info_t* info) +{ + char value[PROPERTY_VALUE_MAX] = {0}; + uint32_t fragment_size = COMPRESS_OFFLOAD_FRAGMENT_SIZE; + if((property_get("audio.offload.buffer.size.kb", value, "")) && + atoi(value)) { + fragment_size = atoi(value) * 1024; + } + + if (info != NULL && info->has_video && info->is_streaming) { + fragment_size = COMPRESS_OFFLOAD_FRAGMENT_SIZE_FOR_AV_STREAMING; + ALOGV("%s: offload fragment size reduced for AV streaming to %d", + __func__, fragment_size); + } + + fragment_size = ALIGN( fragment_size, 1024); + + if(fragment_size < MIN_COMPRESS_OFFLOAD_FRAGMENT_SIZE) + fragment_size = MIN_COMPRESS_OFFLOAD_FRAGMENT_SIZE; + else if(fragment_size > MAX_COMPRESS_OFFLOAD_FRAGMENT_SIZE) + fragment_size = MAX_COMPRESS_OFFLOAD_FRAGMENT_SIZE; + ALOGV("%s: fragment_size %d", __func__, fragment_size); + return fragment_size; +} + +uint32_t platform_get_pcm_offload_buffer_size(audio_offload_info_t* info) +{ + uint32_t fragment_size = MIN_PCM_OFFLOAD_FRAGMENT_SIZE; + uint32_t bits_per_sample = 16; + + if (info->format == AUDIO_FORMAT_PCM_24_BIT_OFFLOAD) { + bits_per_sample = 32; + } + + if (!info->has_video) { + fragment_size = MAX_PCM_OFFLOAD_FRAGMENT_SIZE; + + } else if (info->has_video && info->is_streaming) { + fragment_size = (PCM_OFFLOAD_BUFFER_DURATION_FOR_AV_STREAMING + * info->sample_rate + * bits_per_sample + * popcount(info->channel_mask))/1000; + + } else if (info->has_video) { + fragment_size = (PCM_OFFLOAD_BUFFER_DURATION_FOR_AV + * info->sample_rate + * bits_per_sample + * popcount(info->channel_mask))/1000; + } + + fragment_size = ALIGN( fragment_size, 1024); + + if(fragment_size < MIN_PCM_OFFLOAD_FRAGMENT_SIZE) + fragment_size = MIN_PCM_OFFLOAD_FRAGMENT_SIZE; + else if(fragment_size > MAX_PCM_OFFLOAD_FRAGMENT_SIZE) + fragment_size = MAX_PCM_OFFLOAD_FRAGMENT_SIZE; + + ALOGV("%s: fragment_size %d", __func__, fragment_size); + return fragment_size; +} + +void platform_get_device_to_be_id_map(int **device_to_be_id, int *length) +{ + *device_to_be_id = msm_device_to_be_id; + *length = msm_be_id_array_len; +} + +bool platform_check_24_bit_support() { + return false; +} + +bool platform_check_and_set_codec_backend_cfg(struct audio_device* adev __unused, + struct audio_usecase *usecase __unused) +{ + return false; +} + +int platform_get_usecase_index(const char * usecase __unused) +{ + return -ENOSYS; +} + +int platform_set_usecase_pcm_id(audio_usecase_t usecase __unused, int32_t type __unused, + int32_t pcm_id __unused) +{ + return -ENOSYS; +} + +int platform_set_snd_device_backend(snd_device_t snd_device __unused, + const char * backend __unused) +{ + return -ENOSYS; +} + +int platform_get_subsys_image_name(char *buf) +{ + strlcpy(buf, PLATFORM_IMAGE_NAME, sizeof(PLATFORM_IMAGE_NAME)); + return 0; +} diff --git a/audio/hal/msm8916/platform.h b/audio/hal/msm8916/platform.h new file mode 100644 index 0000000..b4e4d7a --- /dev/null +++ b/audio/hal/msm8916/platform.h @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2013 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. + */ + +#ifndef QCOM_AUDIO_PLATFORM_H +#define QCOM_AUDIO_PLATFORM_H +#include + +enum { + FLUENCE_NONE, + FLUENCE_DUAL_MIC = 0x1, + FLUENCE_QUAD_MIC = 0x2, +}; + +enum { + FLUENCE_ENDFIRE = 0x1, + FLUENCE_BROADSIDE = 0x2, +}; + +#define PLATFORM_IMAGE_NAME "modem" + +/* + * Below are the devices for which is back end is same, SLIMBUS_0_RX. + * All these devices are handled by the internal HW codec. We can + * enable any one of these devices at any time + */ +#define AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND \ + (AUDIO_DEVICE_OUT_EARPIECE | AUDIO_DEVICE_OUT_SPEAKER | \ + AUDIO_DEVICE_OUT_WIRED_HEADSET | AUDIO_DEVICE_OUT_WIRED_HEADPHONE) + +/* Sound devices specific to the platform + * The DEVICE_OUT_* and DEVICE_IN_* should be mapped to these sound + * devices to enable corresponding mixer paths + */ +enum { + SND_DEVICE_NONE = 0, + + /* Playback devices */ + SND_DEVICE_MIN, + SND_DEVICE_OUT_BEGIN = SND_DEVICE_MIN, + SND_DEVICE_OUT_HANDSET = SND_DEVICE_OUT_BEGIN, + SND_DEVICE_OUT_SPEAKER, + SND_DEVICE_OUT_SPEAKER_REVERSE, + SND_DEVICE_OUT_HEADPHONES, + SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES, + SND_DEVICE_OUT_VOICE_HANDSET, + SND_DEVICE_OUT_VOICE_SPEAKER, + SND_DEVICE_OUT_VOICE_HEADPHONES, + SND_DEVICE_OUT_HDMI, + SND_DEVICE_OUT_SPEAKER_AND_HDMI, + SND_DEVICE_OUT_BT_SCO, + SND_DEVICE_OUT_BT_SCO_WB, + SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES, + SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES, + SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET, + SND_DEVICE_OUT_AFE_PROXY, + SND_DEVICE_OUT_USB_HEADSET, + SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET, + SND_DEVICE_OUT_TRANSMISSION_FM, + SND_DEVICE_OUT_ANC_HEADSET, + SND_DEVICE_OUT_ANC_FB_HEADSET, + SND_DEVICE_OUT_VOICE_ANC_HEADSET, + SND_DEVICE_OUT_VOICE_ANC_FB_HEADSET, + SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET, + SND_DEVICE_OUT_ANC_HANDSET, + SND_DEVICE_OUT_SPEAKER_PROTECTED, +#ifdef RECORD_PLAY_CONCURRENCY + SND_DEVICE_OUT_VOIP_HANDSET, + SND_DEVICE_OUT_VOIP_SPEAKER, + SND_DEVICE_OUT_VOIP_HEADPHONES, +#endif + SND_DEVICE_OUT_END, + + /* + * Note: IN_BEGIN should be same as OUT_END because total number of devices + * SND_DEVICES_MAX should not exceed MAX_RX + MAX_TX devices. + */ + /* Capture devices */ + SND_DEVICE_IN_BEGIN = SND_DEVICE_OUT_END, + SND_DEVICE_IN_HANDSET_MIC = SND_DEVICE_IN_BEGIN, + SND_DEVICE_IN_HANDSET_MIC_AEC, + SND_DEVICE_IN_HANDSET_MIC_NS, + SND_DEVICE_IN_HANDSET_MIC_AEC_NS, + SND_DEVICE_IN_HANDSET_DMIC, + SND_DEVICE_IN_HANDSET_DMIC_AEC, + SND_DEVICE_IN_HANDSET_DMIC_NS, + SND_DEVICE_IN_HANDSET_DMIC_AEC_NS, + SND_DEVICE_IN_SPEAKER_MIC, + SND_DEVICE_IN_SPEAKER_MIC_AEC, + SND_DEVICE_IN_SPEAKER_MIC_NS, + SND_DEVICE_IN_SPEAKER_MIC_AEC_NS, + SND_DEVICE_IN_SPEAKER_DMIC, + SND_DEVICE_IN_SPEAKER_DMIC_AEC, + SND_DEVICE_IN_SPEAKER_DMIC_NS, + SND_DEVICE_IN_SPEAKER_DMIC_AEC_NS, + SND_DEVICE_IN_HEADSET_MIC, + SND_DEVICE_IN_HEADSET_MIC_FLUENCE, + SND_DEVICE_IN_VOICE_SPEAKER_MIC, + SND_DEVICE_IN_VOICE_HEADSET_MIC, + SND_DEVICE_IN_HDMI_MIC, + SND_DEVICE_IN_BT_SCO_MIC, + SND_DEVICE_IN_BT_SCO_MIC_NREC, + SND_DEVICE_IN_BT_SCO_MIC_WB, + SND_DEVICE_IN_BT_SCO_MIC_WB_NREC, + SND_DEVICE_IN_CAMCORDER_MIC, + SND_DEVICE_IN_VOICE_DMIC, + SND_DEVICE_IN_VOICE_SPEAKER_DMIC, + SND_DEVICE_IN_VOICE_SPEAKER_QMIC, + SND_DEVICE_IN_VOICE_TTY_FULL_HEADSET_MIC, + SND_DEVICE_IN_VOICE_TTY_VCO_HANDSET_MIC, + SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC, + SND_DEVICE_IN_VOICE_REC_MIC, + SND_DEVICE_IN_VOICE_REC_MIC_NS, + SND_DEVICE_IN_VOICE_REC_DMIC_STEREO, + SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE, + SND_DEVICE_IN_USB_HEADSET_MIC, + SND_DEVICE_IN_CAPTURE_FM, + SND_DEVICE_IN_AANC_HANDSET_MIC, + SND_DEVICE_IN_QUAD_MIC, + SND_DEVICE_IN_HANDSET_STEREO_DMIC, + SND_DEVICE_IN_SPEAKER_STEREO_DMIC, + SND_DEVICE_IN_CAPTURE_VI_FEEDBACK, + SND_DEVICE_IN_VOICE_SPEAKER_DMIC_BROADSIDE, + SND_DEVICE_IN_SPEAKER_DMIC_BROADSIDE, + SND_DEVICE_IN_SPEAKER_DMIC_AEC_BROADSIDE, + SND_DEVICE_IN_SPEAKER_DMIC_NS_BROADSIDE, + SND_DEVICE_IN_SPEAKER_DMIC_AEC_NS_BROADSIDE, + SND_DEVICE_IN_VOICE_FLUENCE_DMIC_AANC, + SND_DEVICE_IN_END, + + SND_DEVICE_MAX = SND_DEVICE_IN_END, + +}; + +#define DEFAULT_OUTPUT_SAMPLING_RATE 48000 + +#define ALL_SESSION_VSID 0xFFFFFFFF +#define DEFAULT_MUTE_RAMP_DURATION 500 +#define DEFAULT_VOLUME_RAMP_DURATION_MS 20 +#define MIXER_PATH_MAX_LENGTH 100 + +#define MAX_VOL_INDEX 5 +#define MIN_VOL_INDEX 0 +#define percent_to_index(val, min, max) \ + ((val) * ((max) - (min)) * 0.01 + (min) + .5) + +/* + * tinyAlsa library interprets period size as number of frames + * one frame = channel_count * sizeof (pcm sample) + * so if format = 16-bit PCM and channels = Stereo, frame size = 2 ch * 2 = 4 bytes + * DEEP_BUFFER_OUTPUT_PERIOD_SIZE = 1024 means 1024 * 4 = 4096 bytes + * We should take care of returning proper size when AudioFlinger queries for + * the buffer size of an input/output stream + */ +#define DEEP_BUFFER_OUTPUT_PERIOD_SIZE 960 +#define DEEP_BUFFER_OUTPUT_PERIOD_COUNT 4 +#define LOW_LATENCY_OUTPUT_PERIOD_SIZE 240 +#define LOW_LATENCY_OUTPUT_PERIOD_COUNT 2 + +#define LOW_LATENCY_CAPTURE_SAMPLE_RATE 48000 +#define LOW_LATENCY_CAPTURE_PERIOD_SIZE 240 +#define LOW_LATENCY_CAPTURE_USE_CASE 1 + +#define HDMI_MULTI_PERIOD_SIZE 336 +#define HDMI_MULTI_PERIOD_COUNT 8 +#define HDMI_MULTI_DEFAULT_CHANNEL_COUNT 6 +#define HDMI_MULTI_PERIOD_BYTES (HDMI_MULTI_PERIOD_SIZE * HDMI_MULTI_DEFAULT_CHANNEL_COUNT * 2) + +#define AUDIO_CAPTURE_PERIOD_DURATION_MSEC 20 +#define AUDIO_CAPTURE_PERIOD_COUNT 2 + +#define DEVICE_NAME_MAX_SIZE 128 +#define HW_INFO_ARRAY_MAX_SIZE 32 + +#define DEEP_BUFFER_PCM_DEVICE 0 +#define AUDIO_RECORD_PCM_DEVICE 0 +#define MULTIMEDIA2_PCM_DEVICE 1 +#define FM_PLAYBACK_PCM_DEVICE 5 +#define FM_CAPTURE_PCM_DEVICE 6 +#define HFP_PCM_RX 5 +#define HFP_SCO_RX 17 +#define HFP_ASM_RX_TX 18 + +#define INCALL_MUSIC_UPLINK_PCM_DEVICE 1 +#define INCALL_MUSIC_UPLINK2_PCM_DEVICE 16 +#define SPKR_PROT_CALIB_RX_PCM_DEVICE 5 +#define SPKR_PROT_CALIB_TX_PCM_DEVICE 22 +#define PLAYBACK_OFFLOAD_DEVICE 9 +#define COMPRESS_VOIP_CALL_PCM_DEVICE 3 + +/* Define macro for Internal FM volume mixer */ +#define FM_RX_VOLUME "Internal FM RX Volume" + +#define LOWLATENCY_PCM_DEVICE 12 +#define EC_REF_RX "I2S_RX" +#define COMPRESS_CAPTURE_DEVICE 19 + +#define VOICE_CALL_PCM_DEVICE 2 +#define VOICE2_CALL_PCM_DEVICE 13 +#define VOLTE_CALL_PCM_DEVICE 15 +#define QCHAT_CALL_PCM_DEVICE 26 +#define QCHAT_CALL_PCM_DEVICE_OF_EXT_CODEC 28 +#define VOWLAN_CALL_PCM_DEVICE 16 + +#define LIB_CSD_CLIENT "libcsd-client.so" +/* CSD-CLIENT related functions */ +typedef int (*init_t)(); +typedef int (*deinit_t)(); +typedef int (*disable_device_t)(); +typedef int (*enable_device_config_t)(int, int); +typedef int (*enable_device_t)(int, int, uint32_t); +typedef int (*volume_t)(uint32_t, int); +typedef int (*mic_mute_t)(uint32_t, int); +typedef int (*slow_talk_t)(uint32_t, uint8_t); +typedef int (*start_voice_t)(uint32_t); +typedef int (*stop_voice_t)(uint32_t); +typedef int (*start_playback_t)(uint32_t); +typedef int (*stop_playback_t)(uint32_t); +typedef int (*set_lch_t)(uint32_t, enum voice_lch_mode); +typedef int (*start_record_t)(uint32_t, int); +typedef int (*stop_record_t)(uint32_t); +/* CSD Client structure */ +struct csd_data { + void *csd_client; + init_t init; + deinit_t deinit; + disable_device_t disable_device; + enable_device_config_t enable_device_config; + enable_device_t enable_device; + volume_t volume; + mic_mute_t mic_mute; + slow_talk_t slow_talk; + start_voice_t start_voice; + stop_voice_t stop_voice; + start_playback_t start_playback; + stop_playback_t stop_playback; + set_lch_t set_lch; + start_record_t start_record; + stop_record_t stop_record; +}; + +int platform_get_subsys_image_name (char *buf); + +#endif // QCOM_AUDIO_PLATFORM_H diff --git a/audio/hal/msm8960/platform.c b/audio/hal/msm8960/platform.c new file mode 100644 index 0000000..593b66a --- /dev/null +++ b/audio/hal/msm8960/platform.c @@ -0,0 +1,1088 @@ +/* + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * Not a contribution. + * + * Copyright (C) 2013 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 "msm8960_platform" +/*#define LOG_NDEBUG 0*/ +#define LOG_NDDEBUG 0 + +#include +#include +#include +#include +#include +#include +#include "platform.h" + +#define LIB_ACDB_LOADER "libacdbloader.so" +#define LIB_CSD_CLIENT "libcsd-client.so" + +/* + * This is the sysfs path for the HDMI audio data block + */ +#define AUDIO_DATA_BLOCK_PATH "/sys/class/graphics/fb1/audio_data_block" +#define MIXER_XML_PATH "/system/etc/mixer_paths.xml" + +/* + * This file will have a maximum of 38 bytes: + * + * 4 bytes: number of audio blocks + * 4 bytes: total length of Short Audio Descriptor (SAD) blocks + * Maximum 10 * 3 bytes: SAD blocks + */ +#define MAX_SAD_BLOCKS 10 +#define SAD_BLOCK_SIZE 3 + +/* EDID format ID for LPCM audio */ +#define EDID_FORMAT_LPCM 1 + +struct audio_block_header +{ + int reserved; + int length; +}; + + +typedef void (*acdb_deallocate_t)(); +typedef int (*acdb_init_t)(); +typedef void (*acdb_send_audio_cal_t)(int, int); +typedef void (*acdb_send_voice_cal_t)(int, int); + +typedef int (*csd_client_init_t)(); +typedef int (*csd_client_deinit_t)(); +typedef int (*csd_disable_device_t)(); +typedef int (*csd_enable_device_t)(int, int, uint32_t); +typedef int (*csd_volume_t)(int); +typedef int (*csd_mic_mute_t)(int); +typedef int (*csd_start_voice_t)(); +typedef int (*csd_stop_voice_t)(); + + +struct platform_data { + struct audio_device *adev; + bool fluence_in_spkr_mode; + bool fluence_in_voice_call; + bool fluence_in_voice_rec; + int fluence_type; + int dualmic_config; + bool ec_ref_enabled; + + /* Audio calibration related functions */ + void *acdb_handle; + acdb_init_t acdb_init; + acdb_deallocate_t acdb_deallocate; + acdb_send_audio_cal_t acdb_send_audio_cal; + acdb_send_voice_cal_t acdb_send_voice_cal; + + /* CSD Client related functions for voice call */ + void *csd_client; + csd_client_init_t csd_client_init; + csd_client_deinit_t csd_client_deinit; + csd_disable_device_t csd_disable_device; + csd_enable_device_t csd_enable_device; + csd_volume_t csd_volume; + csd_mic_mute_t csd_mic_mute; + csd_start_voice_t csd_start_voice; + csd_stop_voice_t csd_stop_voice; +}; + +static const int pcm_device_table[AUDIO_USECASE_MAX][2] = { + [USECASE_AUDIO_PLAYBACK_DEEP_BUFFER] = {0, 0}, + [USECASE_AUDIO_PLAYBACK_LOW_LATENCY] = {14, 14}, + [USECASE_AUDIO_PLAYBACK_MULTI_CH] = {1, 1}, + [USECASE_AUDIO_RECORD] = {0, 0}, + [USECASE_AUDIO_RECORD_LOW_LATENCY] = {14, 14}, + [USECASE_VOICE_CALL] = {12, 12}, +}; + +/* Array to store sound devices */ +static const char * const device_table[SND_DEVICE_MAX] = { + [SND_DEVICE_NONE] = "none", + /* Playback sound devices */ + [SND_DEVICE_OUT_HANDSET] = "handset", + [SND_DEVICE_OUT_SPEAKER] = "speaker", + [SND_DEVICE_OUT_SPEAKER_REVERSE] = "speaker-reverse", + [SND_DEVICE_OUT_HEADPHONES] = "headphones", + [SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES] = "speaker-and-headphones", + [SND_DEVICE_OUT_VOICE_SPEAKER] = "voice-speaker", + [SND_DEVICE_OUT_VOICE_HEADPHONES] = "voice-headphones", + [SND_DEVICE_OUT_HDMI] = "hdmi", + [SND_DEVICE_OUT_SPEAKER_AND_HDMI] = "speaker-and-hdmi", + [SND_DEVICE_OUT_BT_SCO] = "bt-sco-headset", + [SND_DEVICE_OUT_BT_SCO_WB] = "bt-sco-headset-wb", + [SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES] = "voice-tty-full-headphones", + [SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = "voice-tty-vco-headphones", + [SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET] = "voice-tty-hco-handset", + [SND_DEVICE_OUT_USB_HEADSET] = "usb-headphones", + [SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET] = "speaker-and-usb-headphones", + + /* Capture sound devices */ + [SND_DEVICE_IN_HANDSET_MIC] = "handset-mic", + [SND_DEVICE_IN_SPEAKER_MIC] = "speaker-mic", + [SND_DEVICE_IN_HEADSET_MIC] = "headset-mic", + [SND_DEVICE_IN_HANDSET_MIC_AEC] = "handset-mic", + [SND_DEVICE_IN_SPEAKER_MIC_AEC] = "voice-speaker-mic", + [SND_DEVICE_IN_HEADSET_MIC_AEC] = "headset-mic", + [SND_DEVICE_IN_VOICE_SPEAKER_MIC] = "voice-speaker-mic", + [SND_DEVICE_IN_VOICE_HEADSET_MIC] = "voice-headset-mic", + [SND_DEVICE_IN_HDMI_MIC] = "hdmi-mic", + [SND_DEVICE_IN_BT_SCO_MIC] = "bt-sco-mic", + [SND_DEVICE_IN_BT_SCO_MIC_WB] = "bt-sco-mic-wb", + [SND_DEVICE_IN_CAMCORDER_MIC] = "camcorder-mic", + [SND_DEVICE_IN_VOICE_DMIC] = "voice-dmic-ef", + [SND_DEVICE_IN_VOICE_SPEAKER_DMIC] = "voice-speaker-dmic-ef", + [SND_DEVICE_IN_VOICE_TTY_FULL_HEADSET_MIC] = "voice-tty-full-headset-mic", + [SND_DEVICE_IN_VOICE_TTY_VCO_HANDSET_MIC] = "voice-tty-vco-handset-mic", + [SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC] = "voice-tty-hco-headset-mic", + [SND_DEVICE_IN_VOICE_REC_MIC] = "voice-rec-mic", + [SND_DEVICE_IN_VOICE_REC_DMIC] = "voice-rec-dmic-ef", + [SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE] = "voice-rec-dmic-ef-fluence", + [SND_DEVICE_IN_USB_HEADSET_MIC] = "usb-headset-mic", +}; + +/* ACDB IDs (audio DSP path configuration IDs) for each sound device */ +static const int acdb_device_table[SND_DEVICE_MAX] = { + [SND_DEVICE_NONE] = -1, + [SND_DEVICE_OUT_HANDSET] = 7, + [SND_DEVICE_OUT_SPEAKER] = 14, + [SND_DEVICE_OUT_SPEAKER_REVERSE] = 14, + [SND_DEVICE_OUT_HEADPHONES] = 10, + [SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES] = 10, + [SND_DEVICE_OUT_VOICE_SPEAKER] = 14, + [SND_DEVICE_OUT_VOICE_HEADPHONES] = 10, + [SND_DEVICE_OUT_HDMI] = 18, + [SND_DEVICE_OUT_SPEAKER_AND_HDMI] = 14, + [SND_DEVICE_OUT_BT_SCO] = 22, + [SND_DEVICE_OUT_BT_SCO_WB] = 39, + [SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES] = 17, + [SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = 17, + [SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET] = 37, + [SND_DEVICE_OUT_USB_HEADSET] = 45, + [SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET] = 14, + + [SND_DEVICE_IN_HANDSET_MIC] = 4, + [SND_DEVICE_IN_SPEAKER_MIC] = 4, + [SND_DEVICE_IN_HEADSET_MIC] = 8, + [SND_DEVICE_IN_HANDSET_MIC_AEC] = 40, + [SND_DEVICE_IN_SPEAKER_MIC_AEC] = 42, + [SND_DEVICE_IN_HEADSET_MIC_AEC] = 47, + [SND_DEVICE_IN_VOICE_SPEAKER_MIC] = 11, + [SND_DEVICE_IN_VOICE_HEADSET_MIC] = 8, + [SND_DEVICE_IN_HDMI_MIC] = 4, + [SND_DEVICE_IN_BT_SCO_MIC] = 21, + [SND_DEVICE_IN_BT_SCO_MIC_WB] = 38, + [SND_DEVICE_IN_CAMCORDER_MIC] = 61, + [SND_DEVICE_IN_VOICE_DMIC] = 6, + [SND_DEVICE_IN_VOICE_SPEAKER_DMIC] = 13, + [SND_DEVICE_IN_VOICE_TTY_FULL_HEADSET_MIC] = 16, + [SND_DEVICE_IN_VOICE_TTY_VCO_HANDSET_MIC] = 36, + [SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC] = 16, + [SND_DEVICE_IN_VOICE_REC_MIC] = 62, + [SND_DEVICE_IN_USB_HEADSET_MIC] = 44, + /* TODO: Update with proper acdb ids */ + [SND_DEVICE_IN_VOICE_REC_DMIC] = 62, + [SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE] = 6, +}; + +#define DEEP_BUFFER_PLATFORM_DELAY (29*1000LL) +#define LOW_LATENCY_PLATFORM_DELAY (13*1000LL) + +void platform_set_echo_reference(void *platform, bool enable) +{ + struct platform_data *my_data = (struct platform_data *)platform; + struct audio_device *adev = my_data->adev; + + if (enable) { + my_data->ec_ref_enabled = enable; + audio_route_apply_and_update_path(adev->audio_route, "echo-reference"); + } else { + if (my_data->ec_ref_enabled) { + audio_route_reset_and_update_path(adev->audio_route, "echo-reference"); + my_data->ec_ref_enabled = enable; + } else { + ALOGV("EC Reference is already disabled: %d", my_data->ec_ref_enabled); + } + } + + ALOGV("Setting EC Reference: %d", enable); +} + +void *platform_init(struct audio_device *adev) +{ + char platform[PROPERTY_VALUE_MAX]; + char baseband[PROPERTY_VALUE_MAX]; + char value[PROPERTY_VALUE_MAX]; + struct platform_data *my_data; + const char *snd_card_name; + + adev->mixer = mixer_open(MIXER_CARD); + + if (!adev->mixer) { + ALOGE("Unable to open the mixer, aborting."); + return NULL; + } + + adev->audio_route = audio_route_init(MIXER_CARD, MIXER_XML_PATH); + if (!adev->audio_route) { + ALOGE("%s: Failed to init audio route controls, aborting.", __func__); + return NULL; + } + + my_data = calloc(1, sizeof(struct platform_data)); + + snd_card_name = mixer_get_name(adev->mixer); + + my_data->adev = adev; + my_data->fluence_in_spkr_mode = false; + my_data->fluence_in_voice_call = false; + my_data->fluence_in_voice_rec = false; + my_data->fluence_type = FLUENCE_NONE; + + property_get("ro.qc.sdk.audio.fluencetype", value, ""); + if (!strncmp("fluencepro", value, sizeof("fluencepro"))) { + my_data->fluence_type = FLUENCE_QUAD_MIC; + } else if (!strncmp("fluence", value, sizeof("fluence"))) { + my_data->fluence_type = FLUENCE_DUAL_MIC; + } else { + my_data->fluence_type = FLUENCE_NONE; + } + + if (my_data->fluence_type != FLUENCE_NONE) { + property_get("persist.audio.fluence.voicecall",value,""); + if (!strncmp("true", value, sizeof("true"))) { + my_data->fluence_in_voice_call = true; + } + + property_get("persist.audio.fluence.voicerec",value,""); + if (!strncmp("true", value, sizeof("true"))) { + my_data->fluence_in_voice_rec = true; + } + + property_get("persist.audio.fluence.speaker",value,""); + if (!strncmp("true", value, sizeof("true"))) { + my_data->fluence_in_spkr_mode = true; + } + } + + my_data->acdb_handle = dlopen(LIB_ACDB_LOADER, RTLD_NOW); + if (my_data->acdb_handle == NULL) { + ALOGE("%s: DLOPEN failed for %s", __func__, LIB_ACDB_LOADER); + } else { + ALOGV("%s: DLOPEN successful for %s", __func__, LIB_ACDB_LOADER); + my_data->acdb_deallocate = (acdb_deallocate_t)dlsym(my_data->acdb_handle, + "acdb_loader_deallocate_ACDB"); + my_data->acdb_send_audio_cal = (acdb_send_audio_cal_t)dlsym(my_data->acdb_handle, + "acdb_loader_send_audio_cal"); + if (!my_data->acdb_send_audio_cal) + ALOGW("%s: Could not find the symbol acdb_send_audio_cal from %s", + __func__, LIB_ACDB_LOADER); + my_data->acdb_send_voice_cal = (acdb_send_voice_cal_t)dlsym(my_data->acdb_handle, + "acdb_loader_send_voice_cal"); + my_data->acdb_init = (acdb_init_t)dlsym(my_data->acdb_handle, + "acdb_loader_init_ACDB"); + if (my_data->acdb_init == NULL) + ALOGE("%s: dlsym error %s for acdb_loader_init_ACDB", __func__, dlerror()); + else + my_data->acdb_init(); + } + + /* If platform is Fusion3, load CSD Client specific symbols + * Voice call is handled by MDM and apps processor talks to + * MDM through CSD Client + */ + property_get("ro.board.platform", platform, ""); + property_get("ro.baseband", baseband, ""); + if (!strcmp("msm8960", platform) && !strcmp("mdm", baseband)) { + my_data->csd_client = dlopen(LIB_CSD_CLIENT, RTLD_NOW); + if (my_data->csd_client == NULL) + ALOGE("%s: DLOPEN failed for %s", __func__, LIB_CSD_CLIENT); + } + + if (my_data->csd_client) { + ALOGV("%s: DLOPEN successful for %s", __func__, LIB_CSD_CLIENT); + my_data->csd_client_deinit = (csd_client_deinit_t)dlsym(my_data->csd_client, + "csd_client_deinit"); + my_data->csd_disable_device = (csd_disable_device_t)dlsym(my_data->csd_client, + "csd_client_disable_device"); + my_data->csd_enable_device = (csd_enable_device_t)dlsym(my_data->csd_client, + "csd_client_enable_device"); + my_data->csd_start_voice = (csd_start_voice_t)dlsym(my_data->csd_client, + "csd_client_start_voice"); + my_data->csd_stop_voice = (csd_stop_voice_t)dlsym(my_data->csd_client, + "csd_client_stop_voice"); + my_data->csd_volume = (csd_volume_t)dlsym(my_data->csd_client, + "csd_client_volume"); + my_data->csd_mic_mute = (csd_mic_mute_t)dlsym(my_data->csd_client, + "csd_client_mic_mute"); + my_data->csd_client_init = (csd_client_init_t)dlsym(my_data->csd_client, + "csd_client_init"); + + if (my_data->csd_client_init == NULL) { + ALOGE("%s: dlsym error %s for csd_client_init", __func__, dlerror()); + } else { + my_data->csd_client_init(); + } + } + + return my_data; +} + +void platform_deinit(void *platform) +{ + struct platform_data *my_data = (struct platform_data *)platform; + + free(platform); +} + +const char *platform_get_snd_device_name(snd_device_t snd_device) +{ + if (snd_device >= SND_DEVICE_MIN && snd_device < SND_DEVICE_MAX) + return device_table[snd_device]; + else + return ""; +} + +int platform_get_snd_device_name_extn(void *platform, snd_device_t snd_device, + char *device_name) +{ + struct platform_data *my_data = (struct platform_data *)platform; + + if (snd_device >= SND_DEVICE_MIN && snd_device < SND_DEVICE_MAX) { + strlcpy(device_name, device_table[snd_device], DEVICE_NAME_MAX_SIZE); + } else { + strlcpy(device_name, "", DEVICE_NAME_MAX_SIZE); + return -EINVAL; + } + + return 0; +} + +void platform_add_backend_name(char *mixer_path, snd_device_t snd_device) +{ + if (snd_device == SND_DEVICE_IN_BT_SCO_MIC) + strlcat(mixer_path, " bt-sco", MIXER_PATH_MAX_LENGTH); + else if (snd_device == SND_DEVICE_IN_BT_SCO_MIC_WB) + strlcat(mixer_path, " bt-sco-wb", MIXER_PATH_MAX_LENGTH); + else if(snd_device == SND_DEVICE_OUT_BT_SCO) + strlcat(mixer_path, " bt-sco", MIXER_PATH_MAX_LENGTH); + else if(snd_device == SND_DEVICE_OUT_BT_SCO_WB) + strlcat(mixer_path, " bt-sco-wb", MIXER_PATH_MAX_LENGTH); + else if (snd_device == SND_DEVICE_OUT_HDMI) + strlcat(mixer_path, " hdmi", MIXER_PATH_MAX_LENGTH); + else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_HDMI) + strlcat(mixer_path, " speaker-and-hdmi", MIXER_PATH_MAX_LENGTH); +} + +int platform_get_pcm_device_id(audio_usecase_t usecase, int device_type) +{ + int device_id; + if (device_type == PCM_PLAYBACK) + device_id = pcm_device_table[usecase][0]; + else + device_id = pcm_device_table[usecase][1]; + return device_id; +} + +int platform_get_snd_device_index(char *snd_device_index_name __unused) +{ + return -ENODEV; +} + +int platform_set_snd_device_acdb_id(snd_device_t snd_device __unused, + unsigned int acdb_id __unused) +{ + return -ENODEV; +} + +uint32_t platform_get_compress_offload_buffer_size(audio_offload_info_t* info __unused) +{ + ALOGE("%s: Not implemented", __func__); + return -ENOSYS; +} + +int platform_get_snd_device_acdb_id(snd_device_t snd_device __unused) +{ + ALOGE("%s: Not implemented", __func__); + return -ENOSYS; +} + +int platform_switch_voice_call_enable_device_config(void *platform __unused, + snd_device_t out_snd_device __unused, + snd_device_t in_snd_device __unused) +{ + ALOGE("%s: Not implemented", __func__); + return -ENOSYS; +} + +int platform_switch_voice_call_usecase_route_post(void *platform __unused, + snd_device_t out_snd_device __unused, + snd_device_t in_snd_device __unused) +{ + ALOGE("%s: Not implemented", __func__); + return -ENOSYS; +} + +int platform_set_incall_recording_session_id(void *platform __unused, + uint32_t session_id __unused, + int rec_mode __unused) +{ + ALOGE("%s: Not implemented", __func__); + return -ENOSYS; +} + +int platform_stop_incall_recording_usecase(void *platform __unused) +{ + ALOGE("%s: Not implemented", __func__); + return -ENOSYS; +} + +int platform_get_sample_rate(void *platform __unused, uint32_t *rate __unused) +{ + ALOGE("%s: Not implemented", __func__); + return -ENOSYS; +} + +int platform_get_default_app_type(void *platform __unused) +{ + ALOGE("%s: Not implemented", __func__); + return -ENOSYS; +} + +int platform_send_audio_calibration(void *platform, struct audio_usecase *usecase, + int app_type __unused, int sample_rate __unused) +{ + struct platform_data *my_data = (struct platform_data *)platform; + int acdb_dev_id, acdb_dev_type; + struct audio_device *adev = my_data->adev; + int snd_device = SND_DEVICE_OUT_SPEAKER; + + if (usecase->type == PCM_PLAYBACK) + snd_device = platform_get_output_snd_device(adev->platform, + usecase->stream.out->devices); + else if ((usecase->type == PCM_HFP_CALL) || (usecase->type == PCM_CAPTURE)) + snd_device = platform_get_input_snd_device(adev->platform, + adev->primary_output->devices); + acdb_dev_id = acdb_device_table[snd_device]; + if (acdb_dev_id < 0) { + ALOGE("%s: Could not find acdb id for device(%d)", + __func__, snd_device); + return -EINVAL; + } + if (my_data->acdb_send_audio_cal) { + ("%s: sending audio calibration for snd_device(%d) acdb_id(%d)", + __func__, snd_device, acdb_dev_id); + if (snd_device >= SND_DEVICE_OUT_BEGIN && + snd_device < SND_DEVICE_OUT_END) + acdb_dev_type = ACDB_DEV_TYPE_OUT; + else + acdb_dev_type = ACDB_DEV_TYPE_IN; + my_data->acdb_send_audio_cal(acdb_dev_id, acdb_dev_type); + } + return 0; +} + +int platform_switch_voice_call_device_pre(void *platform) +{ + struct platform_data *my_data = (struct platform_data *)platform; + int ret = 0; + + if (my_data->csd_client != NULL) { + /* This must be called before disabling the mixer controls on APQ side */ + if (my_data->csd_disable_device == NULL) { + ALOGE("%s: dlsym error for csd_disable_device", __func__); + } else { + ret = my_data->csd_disable_device(); + if (ret < 0) { + ALOGE("%s: csd_client_disable_device, failed, error %d", + __func__, ret); + } + } + } + return ret; +} + +int platform_switch_voice_call_device_post(void *platform, + snd_device_t out_snd_device, + snd_device_t in_snd_device) +{ + struct platform_data *my_data = (struct platform_data *)platform; + int acdb_rx_id, acdb_tx_id; + int ret = 0; + + if (my_data->csd_client) { + if (my_data->csd_enable_device == NULL) { + ALOGE("%s: dlsym error for csd_enable_device", + __func__); + } else { + acdb_rx_id = acdb_device_table[out_snd_device]; + acdb_tx_id = acdb_device_table[in_snd_device]; + + if (acdb_rx_id > 0 || acdb_tx_id > 0) { + ret = my_data->csd_enable_device(acdb_rx_id, acdb_tx_id, + my_data->adev->acdb_settings); + if (ret < 0) { + ALOGE("%s: csd_enable_device, failed, error %d", + __func__, ret); + } + } else { + ALOGE("%s: Incorrect ACDB IDs (rx: %d tx: %d)", __func__, + acdb_rx_id, acdb_tx_id); + } + } + } + + return ret; +} + +int platform_start_voice_call(void *platform, uint32_t vsid __unused) +{ + struct platform_data *my_data = (struct platform_data *)platform; + int ret = 0; + + if (my_data->csd_client) { + if (my_data->csd_start_voice == NULL) { + ALOGE("dlsym error for csd_client_start_voice"); + ret = -ENOSYS; + } else { + ret = my_data->csd_start_voice(); + if (ret < 0) { + ALOGE("%s: csd_start_voice error %d\n", __func__, ret); + } + } + } + + return ret; +} + +int platform_stop_voice_call(void *platform, uint32_t vsid __unused) +{ + struct platform_data *my_data = (struct platform_data *)platform; + int ret = 0; + + if (my_data->csd_client) { + if (my_data->csd_stop_voice == NULL) { + ALOGE("dlsym error for csd_stop_voice"); + } else { + ret = my_data->csd_stop_voice(); + if (ret < 0) { + ALOGE("%s: csd_stop_voice error %d\n", __func__, ret); + } + } + } + + return ret; +} + +int platform_set_voice_volume(void *platform, int volume) +{ + struct platform_data *my_data = (struct platform_data *)platform; + int ret = 0; + + if (my_data->csd_client) { + if (my_data->csd_volume == NULL) { + ALOGE("%s: dlsym error for csd_volume", __func__); + } else { + ret = my_data->csd_volume(volume); + if (ret < 0) { + ALOGE("%s: csd_volume error %d", __func__, ret); + } + } + } else { + ALOGE("%s: No CSD Client present", __func__); + } + + return ret; +} + +int platform_set_mic_mute(void *platform, bool state) +{ + struct platform_data *my_data = (struct platform_data *)platform; + int ret = 0; + + if (my_data->adev->mode == AUDIO_MODE_IN_CALL) { + if (my_data->csd_client) { + if (my_data->csd_mic_mute == NULL) { + ALOGE("%s: dlsym error for csd_mic_mute", __func__); + } else { + ret = my_data->csd_mic_mute(state); + if (ret < 0) { + ALOGE("%s: csd_mic_mute error %d", __func__, ret); + } + } + } else { + ALOGE("%s: No CSD Client present", __func__); + } + } + + return ret; +} + +int platform_set_device_mute(void *platform __unused, bool state __unused, char *dir __unused) +{ + ALOGE("%s: Not implemented", __func__); + return -ENOSYS; +} + +snd_device_t platform_get_output_snd_device(void *platform, audio_devices_t devices) +{ + struct platform_data *my_data = (struct platform_data *)platform; + struct audio_device *adev = my_data->adev; + audio_mode_t mode = adev->mode; + snd_device_t snd_device = SND_DEVICE_NONE; + + ALOGV("%s: enter: output devices(%#x)", __func__, devices); + if (devices == AUDIO_DEVICE_NONE || + devices & AUDIO_DEVICE_BIT_IN) { + ALOGV("%s: Invalid output devices (%#x)", __func__, devices); + goto exit; + } + + if (mode == AUDIO_MODE_IN_CALL) { + if (devices & AUDIO_DEVICE_OUT_WIRED_HEADPHONE || + devices & AUDIO_DEVICE_OUT_WIRED_HEADSET) { + if (adev->voice.tty_mode == TTY_MODE_FULL) + snd_device = SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES; + else if (adev->voice.tty_mode == TTY_MODE_VCO) + snd_device = SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES; + else if (adev->voice.tty_mode == TTY_MODE_HCO) + snd_device = SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET; + else + snd_device = SND_DEVICE_OUT_VOICE_HEADPHONES; + } else if (devices & AUDIO_DEVICE_OUT_ALL_SCO) { + if (adev->bt_wb_speech_enabled) + snd_device = SND_DEVICE_OUT_BT_SCO_WB; + else + snd_device = SND_DEVICE_OUT_BT_SCO; + } else if (devices & AUDIO_DEVICE_OUT_SPEAKER) { + snd_device = SND_DEVICE_OUT_VOICE_SPEAKER; + } else if (devices & AUDIO_DEVICE_OUT_EARPIECE) { + snd_device = SND_DEVICE_OUT_HANDSET; + } + if (snd_device != SND_DEVICE_NONE) { + goto exit; + } + } + + if (popcount(devices) == 2) { + if (devices == (AUDIO_DEVICE_OUT_WIRED_HEADPHONE | + AUDIO_DEVICE_OUT_SPEAKER)) { + snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES; + } else if (devices == (AUDIO_DEVICE_OUT_WIRED_HEADSET | + AUDIO_DEVICE_OUT_SPEAKER)) { + snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES; + } else if (devices == (AUDIO_DEVICE_OUT_AUX_DIGITAL | + AUDIO_DEVICE_OUT_SPEAKER)) { + snd_device = SND_DEVICE_OUT_SPEAKER_AND_HDMI; + } else { + ALOGE("%s: Invalid combo device(%#x)", __func__, devices); + goto exit; + } + if (snd_device != SND_DEVICE_NONE) { + goto exit; + } + } + + if (popcount(devices) != 1) { + ALOGE("%s: Invalid output devices(%#x)", __func__, devices); + goto exit; + } + + if (devices & AUDIO_DEVICE_OUT_WIRED_HEADPHONE || + devices & AUDIO_DEVICE_OUT_WIRED_HEADSET) { + snd_device = SND_DEVICE_OUT_HEADPHONES; + } else if (devices & AUDIO_DEVICE_OUT_SPEAKER) { + if (adev->speaker_lr_swap) + snd_device = SND_DEVICE_OUT_SPEAKER_REVERSE; + else + snd_device = SND_DEVICE_OUT_SPEAKER; + } else if (devices & AUDIO_DEVICE_OUT_ALL_SCO) { + if (adev->bt_wb_speech_enabled) + snd_device = SND_DEVICE_OUT_BT_SCO_WB; + else + snd_device = SND_DEVICE_OUT_BT_SCO; + } else if (devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) { + snd_device = SND_DEVICE_OUT_HDMI ; + } else if (devices & AUDIO_DEVICE_OUT_EARPIECE) { + snd_device = SND_DEVICE_OUT_HANDSET; + } else { + ALOGE("%s: Unknown device(s) %#x", __func__, devices); + } +exit: + ALOGV("%s: exit: snd_device(%s)", __func__, device_table[snd_device]); + return snd_device; +} + +snd_device_t platform_get_input_snd_device(void *platform, audio_devices_t out_device) +{ + struct platform_data *my_data = (struct platform_data *)platform; + struct audio_device *adev = my_data->adev; + audio_source_t source = (adev->active_input == NULL) ? + AUDIO_SOURCE_DEFAULT : adev->active_input->source; + + audio_mode_t mode = adev->mode; + audio_devices_t in_device = ((adev->active_input == NULL) ? + AUDIO_DEVICE_NONE : adev->active_input->device) + & ~AUDIO_DEVICE_BIT_IN; + audio_channel_mask_t channel_mask = (adev->active_input == NULL) ? + AUDIO_CHANNEL_IN_MONO : adev->active_input->channel_mask; + snd_device_t snd_device = SND_DEVICE_NONE; + + ALOGV("%s: enter: out_device(%#x) in_device(%#x)", + __func__, out_device, in_device); + if (mode == AUDIO_MODE_IN_CALL) { + if (out_device == AUDIO_DEVICE_NONE) { + ALOGE("%s: No output device set for voice call", __func__); + goto exit; + } + if (adev->voice.tty_mode != TTY_MODE_OFF) { + if (out_device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE || + out_device & AUDIO_DEVICE_OUT_WIRED_HEADSET) { + switch (adev->voice.tty_mode) { + case TTY_MODE_FULL: + snd_device = SND_DEVICE_IN_VOICE_TTY_FULL_HEADSET_MIC; + break; + case TTY_MODE_VCO: + snd_device = SND_DEVICE_IN_VOICE_TTY_VCO_HANDSET_MIC; + break; + case TTY_MODE_HCO: + snd_device = SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC; + break; + default: + ALOGE("%s: Invalid TTY mode (%#x)", __func__, adev->voice.tty_mode); + } + goto exit; + } + } + if (out_device & AUDIO_DEVICE_OUT_EARPIECE || + out_device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE) { + if (my_data->fluence_type == FLUENCE_NONE || + my_data->fluence_in_voice_call == false) { + snd_device = SND_DEVICE_IN_HANDSET_MIC; + } else { + snd_device = SND_DEVICE_IN_VOICE_DMIC; + adev->acdb_settings |= DMIC_FLAG; + } + } else if (out_device & AUDIO_DEVICE_OUT_WIRED_HEADSET) { + snd_device = SND_DEVICE_IN_VOICE_HEADSET_MIC; + } else if (out_device & AUDIO_DEVICE_OUT_ALL_SCO) { + if (adev->bt_wb_speech_enabled) + snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB; + else + snd_device = SND_DEVICE_IN_BT_SCO_MIC; + } else if (out_device & AUDIO_DEVICE_OUT_SPEAKER) { + if (my_data->fluence_type != FLUENCE_NONE && + my_data->fluence_in_voice_call && + my_data->fluence_in_spkr_mode) { + if(my_data->fluence_type == FLUENCE_DUAL_MIC) { + adev->acdb_settings |= DMIC_FLAG; + snd_device = SND_DEVICE_IN_VOICE_SPEAKER_DMIC; + } else { + adev->acdb_settings |= QMIC_FLAG; + snd_device = SND_DEVICE_IN_VOICE_SPEAKER_QMIC; + } + } else { + snd_device = SND_DEVICE_IN_VOICE_SPEAKER_MIC; + } + } + } else if (source == AUDIO_SOURCE_CAMCORDER) { + if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC || + in_device & AUDIO_DEVICE_IN_BACK_MIC) { + snd_device = SND_DEVICE_IN_CAMCORDER_MIC; + } + } else if (source == AUDIO_SOURCE_VOICE_RECOGNITION) { + if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC) { + if (channel_mask == AUDIO_CHANNEL_IN_FRONT_BACK) + snd_device = SND_DEVICE_IN_VOICE_REC_DMIC; + else if (my_data->fluence_in_voice_rec) + snd_device = SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE; + + if (snd_device == SND_DEVICE_NONE) + snd_device = SND_DEVICE_IN_VOICE_REC_MIC; + else + adev->acdb_settings |= DMIC_FLAG; + } + } else if (source == AUDIO_SOURCE_VOICE_COMMUNICATION) { + if (out_device & AUDIO_DEVICE_OUT_SPEAKER) + in_device = AUDIO_DEVICE_IN_BACK_MIC; + if (adev->active_input) { + if (adev->active_input->enable_aec) { + if (in_device & AUDIO_DEVICE_IN_BACK_MIC) { + snd_device = SND_DEVICE_IN_SPEAKER_MIC_AEC; + } else if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC) { + snd_device = SND_DEVICE_IN_HANDSET_MIC_AEC; + } else if (in_device & AUDIO_DEVICE_IN_WIRED_HEADSET) { + snd_device = SND_DEVICE_IN_HEADSET_MIC_AEC; + } + platform_set_echo_reference(adev->platform, true); + } else + platform_set_echo_reference(adev->platform, false); + } + } else if (source == AUDIO_SOURCE_DEFAULT) { + goto exit; + } + + + if (snd_device != SND_DEVICE_NONE) { + goto exit; + } + + if (in_device != AUDIO_DEVICE_NONE && + !(in_device & AUDIO_DEVICE_IN_VOICE_CALL) && + !(in_device & AUDIO_DEVICE_IN_COMMUNICATION)) { + if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC) { + snd_device = SND_DEVICE_IN_HANDSET_MIC; + } else if (in_device & AUDIO_DEVICE_IN_BACK_MIC) { + snd_device = SND_DEVICE_IN_SPEAKER_MIC; + } else if (in_device & AUDIO_DEVICE_IN_WIRED_HEADSET) { + snd_device = SND_DEVICE_IN_HEADSET_MIC; + } else if (in_device & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) { + if (adev->bt_wb_speech_enabled) + snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB; + else + snd_device = SND_DEVICE_IN_BT_SCO_MIC; + } else if (in_device & AUDIO_DEVICE_IN_AUX_DIGITAL) { + snd_device = SND_DEVICE_IN_HDMI_MIC; + } else { + ALOGE("%s: Unknown input device(s) %#x", __func__, in_device); + ALOGW("%s: Using default handset-mic", __func__); + snd_device = SND_DEVICE_IN_HANDSET_MIC; + } + } else { + if (out_device & AUDIO_DEVICE_OUT_EARPIECE) { + snd_device = SND_DEVICE_IN_HANDSET_MIC; + } else if (out_device & AUDIO_DEVICE_OUT_WIRED_HEADSET) { + snd_device = SND_DEVICE_IN_HEADSET_MIC; + } else if (out_device & AUDIO_DEVICE_OUT_SPEAKER) { + snd_device = SND_DEVICE_IN_SPEAKER_MIC; + } else if (out_device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE) { + snd_device = SND_DEVICE_IN_HANDSET_MIC; + } else if (out_device & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET) { + if (adev->bt_wb_speech_enabled) + snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB; + else + snd_device = SND_DEVICE_IN_BT_SCO_MIC; + } else if (out_device & AUDIO_DEVICE_OUT_AUX_DIGITAL) { + snd_device = SND_DEVICE_IN_HDMI_MIC; + } else { + ALOGE("%s: Unknown output device(s) %#x", __func__, out_device); + ALOGW("%s: Using default handset-mic", __func__); + snd_device = SND_DEVICE_IN_HANDSET_MIC; + } + } +exit: + ALOGV("%s: exit: in_snd_device(%s)", __func__, device_table[snd_device]); + return snd_device; +} + +int platform_set_hdmi_channels(void *platform, int channel_count) +{ + struct platform_data *my_data = (struct platform_data *)platform; + struct audio_device *adev = my_data->adev; + struct mixer_ctl *ctl; + const char *channel_cnt_str = NULL; + const char *mixer_ctl_name = "HDMI_RX Channels"; + switch (channel_count) { + case 8: + channel_cnt_str = "Eight"; break; + case 7: + channel_cnt_str = "Seven"; break; + case 6: + channel_cnt_str = "Six"; break; + case 5: + channel_cnt_str = "Five"; break; + case 4: + channel_cnt_str = "Four"; break; + case 3: + channel_cnt_str = "Three"; break; + default: + channel_cnt_str = "Two"; break; + } + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + return -EINVAL; + } + ALOGV("HDMI channel count: %s", channel_cnt_str); + mixer_ctl_set_enum_by_string(ctl, channel_cnt_str); + return 0; +} + +int platform_edid_get_max_channels(void *platform __unused) +{ + FILE *file; + struct audio_block_header header; + char block[MAX_SAD_BLOCKS * SAD_BLOCK_SIZE]; + char *sad = block; + int num_audio_blocks; + int channel_count; + int max_channels = 0; + int i; + + file = fopen(AUDIO_DATA_BLOCK_PATH, "rb"); + if (file == NULL) { + ALOGE("Unable to open '%s'", AUDIO_DATA_BLOCK_PATH); + return 0; + } + + /* Read audio block header */ + fread(&header, 1, sizeof(header), file); + + /* Read SAD blocks, clamping the maximum size for safety */ + if (header.length > (int)sizeof(block)) + header.length = (int)sizeof(block); + fread(&block, header.length, 1, file); + + fclose(file); + + /* Calculate the number of SAD blocks */ + num_audio_blocks = header.length / SAD_BLOCK_SIZE; + + for (i = 0; i < num_audio_blocks; i++) { + /* Only consider LPCM blocks */ + if ((sad[0] >> 3) != EDID_FORMAT_LPCM) + continue; + + channel_count = (sad[0] & 0x7) + 1; + if (channel_count > max_channels) + max_channels = channel_count; + + /* Advance to next block */ + sad += 3; + } + + return max_channels; +} + +void platform_get_parameters(void *platform __unused, + struct str_parms *query __unused, + struct str_parms *reply __unused) +{ + ALOGE("%s: Not implemented", __func__); +} + +int platform_set_parameters(void *platform __unused, struct str_parms *parms __unused) +{ + ALOGE("%s: Not implemented", __func__); + return -ENOSYS; +} + +int platform_set_incall_recoding_session_id(void *platform __unused, + uint32_t session_id __unused) +{ + ALOGE("%s: Not implemented", __func__); + return -ENOSYS; +} + +int platform_update_lch(void *platform __unused, + struct voice_session *session __unused, + enum voice_lch_mode lch_mode __unused) +{ + ALOGE("%s: Not implemented", __func__); + return -ENOSYS; +} + +int platform_start_incall_music_usecase(void *platform __unused) +{ + ALOGE("%s: Not implemented", __func__); + return -ENOSYS; +} + +int platform_stop_incall_music_usecase(void *platform __unused) +{ + ALOGE("%s: Not implemented", __func__); + return -ENOSYS; +} + +/* Delay in Us */ +int64_t platform_render_latency(audio_usecase_t usecase) +{ + switch (usecase) { + case USECASE_AUDIO_PLAYBACK_DEEP_BUFFER: + return DEEP_BUFFER_PLATFORM_DELAY; + case USECASE_AUDIO_PLAYBACK_LOW_LATENCY: + return LOW_LATENCY_PLATFORM_DELAY; + default: + return 0; + } +} + +int platform_update_usecase_from_source(int source, int usecase) +{ + ALOGV("%s: input source :%d", __func__, source); + return usecase; +} + +bool platform_listen_device_needs_event(snd_device_t snd_device __unused) +{ + return false; +} + +bool platform_listen_usecase_needs_event(audio_usecase_t uc_id __unused) +{ + return false; +} + +bool platform_check_and_set_codec_backend_cfg(struct audio_device* adev __unused, + struct audio_usecase *usecase __unused) +{ + return false; +} + +int platform_get_usecase_index(const char * usecase __unused) +{ + return -ENOSYS; +} + +int platform_set_usecase_pcm_id(audio_usecase_t usecase __unused, int32_t type __unused, + int32_t pcm_id __unused) +{ + return -ENOSYS; +} + +int platform_set_snd_device_backend(snd_device_t snd_device __unused, + const char * backend __unused) +{ + return -ENOSYS; +} + +bool platform_sound_trigger_device_needs_event(snd_device_t snd_device __unused) +{ + return false; +} + +bool platform_sound_trigger_usecase_needs_event(audio_usecase_t uc_id __unused) +{ + return false; +} + +int platform_set_fluence_type(void *platform __unused, char *value __unused) +{ + return -ENOSYS; +} + +int platform_get_fluence_type(void *platform __unused, char *value __unused, + uint32_t len __unused) +{ + return -ENOSYS; +} + +uint32_t platform_get_pcm_offload_buffer_size(audio_offload_info_t* info __unused) +{ + return 0; +} diff --git a/audio/hal/msm8960/platform.h b/audio/hal/msm8960/platform.h new file mode 100644 index 0000000..950ea84 --- /dev/null +++ b/audio/hal/msm8960/platform.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Not a contribution. + * + * Copyright (C) 2013 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. + */ + +#ifndef QCOM_AUDIO_PLATFORM_H +#define QCOM_AUDIO_PLATFORM_H + +enum { + FLUENCE_NONE, + FLUENCE_DUAL_MIC, + FLUENCE_QUAD_MIC +}; + +/* + * Below are the devices for which is back end is same, SLIMBUS_0_RX. + * All these devices are handled by the internal HW codec. We can + * enable any one of these devices at any time + */ +#define AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND \ + (AUDIO_DEVICE_OUT_EARPIECE | AUDIO_DEVICE_OUT_SPEAKER | \ + AUDIO_DEVICE_OUT_WIRED_HEADSET | AUDIO_DEVICE_OUT_WIRED_HEADPHONE) + +/* Sound devices specific to the platform + * The DEVICE_OUT_* and DEVICE_IN_* should be mapped to these sound + * devices to enable corresponding mixer paths + */ +enum { + SND_DEVICE_NONE = 0, + + /* Playback devices */ + SND_DEVICE_MIN, + SND_DEVICE_OUT_BEGIN = SND_DEVICE_MIN, + SND_DEVICE_OUT_HANDSET = SND_DEVICE_OUT_BEGIN, + SND_DEVICE_OUT_SPEAKER, + SND_DEVICE_OUT_SPEAKER_REVERSE, + SND_DEVICE_OUT_HEADPHONES, + SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES, + SND_DEVICE_OUT_VOICE_SPEAKER, + SND_DEVICE_OUT_VOICE_HEADPHONES, + SND_DEVICE_OUT_HDMI, + SND_DEVICE_OUT_SPEAKER_AND_HDMI, + SND_DEVICE_OUT_BT_SCO, + SND_DEVICE_OUT_BT_SCO_WB, + SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES, + SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES, + SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET, + SND_DEVICE_OUT_USB_HEADSET, + SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET, + SND_DEVICE_OUT_END, + + /* + * Note: IN_BEGIN should be same as OUT_END because total number of devices + * SND_DEVICES_MAX should not exceed MAX_RX + MAX_TX devices. + */ + /* Capture devices */ + SND_DEVICE_IN_BEGIN = SND_DEVICE_OUT_END, + SND_DEVICE_IN_HANDSET_MIC = SND_DEVICE_IN_BEGIN, + SND_DEVICE_IN_SPEAKER_MIC, + SND_DEVICE_IN_HEADSET_MIC, + SND_DEVICE_IN_HANDSET_MIC_AEC, + SND_DEVICE_IN_SPEAKER_MIC_AEC, + SND_DEVICE_IN_HEADSET_MIC_AEC, + SND_DEVICE_IN_VOICE_SPEAKER_MIC, + SND_DEVICE_IN_VOICE_HEADSET_MIC, + SND_DEVICE_IN_HDMI_MIC, + SND_DEVICE_IN_BT_SCO_MIC, + SND_DEVICE_IN_BT_SCO_MIC_WB, + SND_DEVICE_IN_CAMCORDER_MIC, + SND_DEVICE_IN_VOICE_DMIC, + SND_DEVICE_IN_VOICE_SPEAKER_DMIC, + SND_DEVICE_IN_VOICE_SPEAKER_QMIC, + SND_DEVICE_IN_VOICE_TTY_FULL_HEADSET_MIC, + SND_DEVICE_IN_VOICE_TTY_VCO_HANDSET_MIC, + SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC, + SND_DEVICE_IN_VOICE_REC_MIC, + SND_DEVICE_IN_VOICE_REC_DMIC, + SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE, + SND_DEVICE_IN_USB_HEADSET_MIC, + SND_DEVICE_IN_END, + + SND_DEVICE_MAX = SND_DEVICE_IN_END, + +}; + +#define MIXER_CARD 0 +#define SOUND_CARD 0 + +#define DEFAULT_OUTPUT_SAMPLING_RATE 48000 + +#define ALL_SESSION_VSID 0xFFFFFFFF +#define DEFAULT_MUTE_RAMP_DURATION_MS 20 +#define DEFAULT_VOLUME_RAMP_DURATION_MS 20 +#define MIXER_PATH_MAX_LENGTH 100 + +#define MAX_VOL_INDEX 5 +#define MIN_VOL_INDEX 0 +#define percent_to_index(val, min, max) \ + ((val) * ((max) - (min)) * 0.01 + (min) + .5) + +/* + * tinyAlsa library interprets period size as number of frames + * one frame = channel_count * sizeof (pcm sample) + * so if format = 16-bit PCM and channels = Stereo, frame size = 2 ch * 2 = 4 bytes + * DEEP_BUFFER_OUTPUT_PERIOD_SIZE = 1024 means 1024 * 4 = 4096 bytes + * We should take care of returning proper size when AudioFlinger queries for + * the buffer size of an input/output stream + */ +#define DEEP_BUFFER_OUTPUT_PERIOD_SIZE 960 +#define DEEP_BUFFER_OUTPUT_PERIOD_COUNT 4 +#define LOW_LATENCY_OUTPUT_PERIOD_SIZE 240 +#define LOW_LATENCY_OUTPUT_PERIOD_COUNT 2 + +#define HDMI_MULTI_PERIOD_SIZE 336 +#define HDMI_MULTI_PERIOD_COUNT 8 +#define HDMI_MULTI_DEFAULT_CHANNEL_COUNT 6 +#define HDMI_MULTI_PERIOD_BYTES (HDMI_MULTI_PERIOD_SIZE * HDMI_MULTI_DEFAULT_CHANNEL_COUNT * 2) + +#define AUDIO_CAPTURE_PERIOD_DURATION_MSEC 20 +#define AUDIO_CAPTURE_PERIOD_COUNT 2 + +#define DEVICE_NAME_MAX_SIZE 128 + +/* Define macro for Internal FM volume mixer */ +#define FM_RX_VOLUME "Internal FM RX Volume" + +#define LOW_LATENCY_CAPTURE_SAMPLE_RATE 48000 +#define LOW_LATENCY_CAPTURE_PERIOD_SIZE 240 +#define LOW_LATENCY_CAPTURE_USE_CASE 0 + +#define AFE_PROXY_PLAYBACK_PCM_DEVICE 7 +#define AFE_PROXY_RECORD_PCM_DEVICE 8 + +#endif // QCOM_AUDIO_PLATFORM_H diff --git a/audio/hal/msm8974/hw_info.c b/audio/hal/msm8974/hw_info.c new file mode 100644 index 0000000..f7d19f4 --- /dev/null +++ b/audio/hal/msm8974/hw_info.c @@ -0,0 +1,397 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "hardware_info" +/*#define LOG_NDEBUG 0*/ +#define LOG_NDDEBUG 0 + +#include +#include +#include +#include +#include "audio_hw.h" +#include "platform.h" +#include "platform_api.h" + + +struct hardware_info { + char name[HW_INFO_ARRAY_MAX_SIZE]; + char type[HW_INFO_ARRAY_MAX_SIZE]; + /* variables for handling target variants */ + uint32_t num_snd_devices; + char dev_extn[HW_INFO_ARRAY_MAX_SIZE]; + snd_device_t *snd_devices; +}; + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +static const snd_device_t taiko_fluid_variant_devices[] = { + SND_DEVICE_OUT_SPEAKER, + SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES, + SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET, +}; + +static const snd_device_t taiko_CDP_variant_devices[] = { + SND_DEVICE_OUT_SPEAKER, + SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES, + SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET, + SND_DEVICE_IN_QUAD_MIC, +}; + +static const snd_device_t taiko_apq8084_CDP_variant_devices[] = { + SND_DEVICE_IN_HANDSET_MIC, +}; + +static const snd_device_t taiko_liquid_variant_devices[] = { + SND_DEVICE_OUT_SPEAKER, + SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES, + SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET, + SND_DEVICE_IN_SPEAKER_MIC, + SND_DEVICE_IN_HEADSET_MIC, + SND_DEVICE_IN_VOICE_DMIC, + SND_DEVICE_IN_VOICE_SPEAKER_DMIC, + SND_DEVICE_IN_VOICE_REC_DMIC_STEREO, + SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE, + SND_DEVICE_IN_QUAD_MIC, + SND_DEVICE_IN_HANDSET_STEREO_DMIC, + SND_DEVICE_IN_SPEAKER_STEREO_DMIC, +}; + +static const snd_device_t tomtom_msm8994_CDP_variant_devices[] = { + SND_DEVICE_IN_HANDSET_MIC, +}; + +static const snd_device_t tomtom_liquid_variant_devices[] = { + SND_DEVICE_OUT_SPEAKER, + SND_DEVICE_OUT_SPEAKER_EXTERNAL_1, + SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_EXTERNAL_1, + SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES, + SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET, + SND_DEVICE_IN_SPEAKER_MIC, + SND_DEVICE_IN_HEADSET_MIC, + SND_DEVICE_IN_VOICE_DMIC, + SND_DEVICE_IN_VOICE_SPEAKER_DMIC, + SND_DEVICE_IN_VOICE_REC_DMIC_STEREO, + SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE, + SND_DEVICE_IN_QUAD_MIC, + SND_DEVICE_IN_HANDSET_STEREO_DMIC, + SND_DEVICE_IN_SPEAKER_STEREO_DMIC, +}; + +static const snd_device_t tomtom_stp_variant_devices[] = { + SND_DEVICE_OUT_SPEAKER, + SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES, + SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET, +}; + +static const snd_device_t taiko_DB_variant_devices[] = { + SND_DEVICE_OUT_SPEAKER, + SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES, + SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET, + SND_DEVICE_IN_SPEAKER_MIC, + SND_DEVICE_IN_HEADSET_MIC, + SND_DEVICE_IN_QUAD_MIC, +}; + +static const snd_device_t taiko_apq8084_sbc_variant_devices[] = { + SND_DEVICE_IN_HANDSET_MIC, + SND_DEVICE_IN_SPEAKER_MIC, +}; + +static const snd_device_t tapan_lite_variant_devices[] = { + SND_DEVICE_OUT_SPEAKER, + SND_DEVICE_OUT_HEADPHONES, + SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES, + SND_DEVICE_OUT_VOICE_HEADPHONES, + SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES, + SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES, +}; + +static const snd_device_t tapan_skuf_variant_devices[] = { + SND_DEVICE_OUT_SPEAKER, + SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES, + SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET, + /*SND_DEVICE_OUT_SPEAKER_AND_ANC_FB_HEADSET,*/ +}; + +static const snd_device_t tapan_lite_skuf_variant_devices[] = { + SND_DEVICE_OUT_SPEAKER, + SND_DEVICE_OUT_HEADPHONES, + SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES, + SND_DEVICE_OUT_VOICE_HEADPHONES, + SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES, + SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES, +}; + +static const snd_device_t helicon_skuab_variant_devices[] = { + SND_DEVICE_OUT_SPEAKER, + SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES, + SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET, + SND_DEVICE_OUT_VOICE_SPEAKER, +}; + +static void update_hardware_info_8084(struct hardware_info *hw_info, const char *snd_card_name) +{ + if (!strcmp(snd_card_name, "apq8084-taiko-mtp-snd-card") || + !strncmp(snd_card_name, "apq8084-taiko-i2s-mtp-snd-card", + sizeof("apq8084-taiko-i2s-mtp-snd-card")) || + !strncmp(snd_card_name, "apq8084-tomtom-mtp-snd-card", + sizeof("apq8084-tomtom-mtp-snd-card"))) { + strlcpy(hw_info->type, "mtp", sizeof(hw_info->type)); + strlcpy(hw_info->name, "apq8084", sizeof(hw_info->name)); + hw_info->snd_devices = NULL; + hw_info->num_snd_devices = 0; + strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn)); + } else if ((!strcmp(snd_card_name, "apq8084-taiko-cdp-snd-card")) || + !strncmp(snd_card_name, "apq8084-tomtom-cdp-snd-card", + sizeof("apq8084-tomtom-cdp-snd-card"))) { + strlcpy(hw_info->type, " cdp", sizeof(hw_info->type)); + strlcpy(hw_info->name, "apq8084", sizeof(hw_info->name)); + hw_info->snd_devices = (snd_device_t *)taiko_apq8084_CDP_variant_devices; + hw_info->num_snd_devices = ARRAY_SIZE(taiko_apq8084_CDP_variant_devices); + strlcpy(hw_info->dev_extn, "-cdp", sizeof(hw_info->dev_extn)); + } else if (!strncmp(snd_card_name, "apq8084-taiko-i2s-cdp-snd-card", + sizeof("apq8084-taiko-i2s-cdp-snd-card"))) { + strlcpy(hw_info->type, " cdp", sizeof(hw_info->type)); + strlcpy(hw_info->name, "apq8084", sizeof(hw_info->name)); + hw_info->snd_devices = NULL; + hw_info->num_snd_devices = 0; + strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn)); + } else if (!strcmp(snd_card_name, "apq8084-taiko-liquid-snd-card")) { + strlcpy(hw_info->type , " liquid", sizeof(hw_info->type)); + strlcpy(hw_info->name, "apq8084", sizeof(hw_info->type)); + hw_info->snd_devices = (snd_device_t *)taiko_liquid_variant_devices; + hw_info->num_snd_devices = ARRAY_SIZE(taiko_liquid_variant_devices); + strlcpy(hw_info->dev_extn, "-liquid", sizeof(hw_info->dev_extn)); + } else if (!strcmp(snd_card_name, "apq8084-taiko-sbc-snd-card")) { + strlcpy(hw_info->type, " sbc", sizeof(hw_info->type)); + strlcpy(hw_info->name, "apq8084", sizeof(hw_info->name)); + hw_info->snd_devices = (snd_device_t *)taiko_apq8084_sbc_variant_devices; + hw_info->num_snd_devices = ARRAY_SIZE(taiko_apq8084_sbc_variant_devices); + strlcpy(hw_info->dev_extn, "-sbc", sizeof(hw_info->dev_extn)); + } else { + ALOGW("%s: Not an 8084 device", __func__); + } +} + +static void update_hardware_info_8994(struct hardware_info *hw_info, const char *snd_card_name) +{ + if (!strcmp(snd_card_name, "msm8994-tomtom-mtp-snd-card")) { + strlcpy(hw_info->type, " mtp", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8994", sizeof(hw_info->name)); + hw_info->snd_devices = NULL; + hw_info->num_snd_devices = 0; + strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn)); + } else if (!strcmp(snd_card_name, "msm8994-tomtom-cdp-snd-card")) { + strlcpy(hw_info->type, " cdp", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8994", sizeof(hw_info->name)); + hw_info->snd_devices = (snd_device_t *)tomtom_msm8994_CDP_variant_devices; + hw_info->num_snd_devices = ARRAY_SIZE(tomtom_msm8994_CDP_variant_devices); + strlcpy(hw_info->dev_extn, "-cdp", sizeof(hw_info->dev_extn)); + } else if (!strcmp(snd_card_name, "msm8994-tomtom-stp-snd-card")) { + strlcpy(hw_info->type, " stp", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8994", sizeof(hw_info->name)); + hw_info->snd_devices = (snd_device_t *)tomtom_stp_variant_devices; + hw_info->num_snd_devices = ARRAY_SIZE(tomtom_stp_variant_devices); + strlcpy(hw_info->dev_extn, "-stp", sizeof(hw_info->dev_extn)); + } else if (!strcmp(snd_card_name, "msm8994-tomtom-liquid-snd-card")) { + strlcpy(hw_info->type, " liquid", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8994", sizeof(hw_info->name)); + hw_info->snd_devices = (snd_device_t *)tomtom_liquid_variant_devices; + hw_info->num_snd_devices = ARRAY_SIZE(tomtom_liquid_variant_devices); + strlcpy(hw_info->dev_extn, "-liquid", sizeof(hw_info->dev_extn)); + } else { + ALOGW("%s: Not an 8994 device", __func__); + } +} + +static void update_hardware_info_8974(struct hardware_info *hw_info, const char *snd_card_name) +{ + if (!strcmp(snd_card_name, "msm8974-taiko-mtp-snd-card")) { + strlcpy(hw_info->type, " mtp", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8974", sizeof(hw_info->name)); + hw_info->snd_devices = NULL; + hw_info->num_snd_devices = 0; + strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn)); + } else if (!strcmp(snd_card_name, "msm8974-taiko-cdp-snd-card")) { + strlcpy(hw_info->type, " cdp", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8974", sizeof(hw_info->name)); + hw_info->snd_devices = (snd_device_t *)taiko_CDP_variant_devices; + hw_info->num_snd_devices = ARRAY_SIZE(taiko_CDP_variant_devices); + strlcpy(hw_info->dev_extn, "-cdp", sizeof(hw_info->dev_extn)); + } else if (!strcmp(snd_card_name, "msm8974-taiko-fluid-snd-card")) { + strlcpy(hw_info->type, " fluid", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8974", sizeof(hw_info->name)); + hw_info->snd_devices = (snd_device_t *) taiko_fluid_variant_devices; + hw_info->num_snd_devices = ARRAY_SIZE(taiko_fluid_variant_devices); + strlcpy(hw_info->dev_extn, "-fluid", sizeof(hw_info->dev_extn)); + } else if (!strcmp(snd_card_name, "msm8974-taiko-liquid-snd-card")) { + strlcpy(hw_info->type, " liquid", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8974", sizeof(hw_info->name)); + hw_info->snd_devices = (snd_device_t *)taiko_liquid_variant_devices; + hw_info->num_snd_devices = ARRAY_SIZE(taiko_liquid_variant_devices); + strlcpy(hw_info->dev_extn, "-liquid", sizeof(hw_info->dev_extn)); + } else if (!strcmp(snd_card_name, "apq8074-taiko-db-snd-card")) { + strlcpy(hw_info->type, " dragon-board", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8974", sizeof(hw_info->name)); + hw_info->snd_devices = (snd_device_t *)taiko_DB_variant_devices; + hw_info->num_snd_devices = ARRAY_SIZE(taiko_DB_variant_devices); + strlcpy(hw_info->dev_extn, "-DB", sizeof(hw_info->dev_extn)); + } else { + ALOGW("%s: Not an 8974 device", __func__); + } +} + +static void update_hardware_info_8610(struct hardware_info *hw_info, const char *snd_card_name) +{ + if (!strcmp(snd_card_name, "msm8x10-snd-card")) { + strlcpy(hw_info->type, "", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8x10", sizeof(hw_info->name)); + hw_info->snd_devices = NULL; + hw_info->num_snd_devices = 0; + strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn)); + } else if (!strcmp(snd_card_name, "msm8x10-skuab-snd-card")) { + strlcpy(hw_info->type, "skuab", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8x10", sizeof(hw_info->name)); + hw_info->snd_devices = (snd_device_t *)helicon_skuab_variant_devices; + hw_info->num_snd_devices = ARRAY_SIZE(helicon_skuab_variant_devices); + strlcpy(hw_info->dev_extn, "-skuab", sizeof(hw_info->dev_extn)); + } else if (!strcmp(snd_card_name, "msm8x10-skuaa-snd-card")) { + strlcpy(hw_info->type, " skuaa", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8x10", sizeof(hw_info->name)); + hw_info->snd_devices = NULL; + hw_info->num_snd_devices = 0; + strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn)); + } else { + ALOGW("%s: Not an 8x10 device", __func__); + } +} + +static void update_hardware_info_8226(struct hardware_info *hw_info, const char *snd_card_name) +{ + if (!strcmp(snd_card_name, "msm8226-tapan-snd-card")) { + strlcpy(hw_info->type, "", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8226", sizeof(hw_info->name)); + hw_info->snd_devices = NULL; + hw_info->num_snd_devices = 0; + strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn)); + } else if (!strcmp(snd_card_name, "msm8226-tapan9302-snd-card")) { + strlcpy(hw_info->type, "tapan_lite", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8226", sizeof(hw_info->name)); + hw_info->snd_devices = (snd_device_t *)tapan_lite_variant_devices; + hw_info->num_snd_devices = ARRAY_SIZE(tapan_lite_variant_devices); + strlcpy(hw_info->dev_extn, "-lite", sizeof(hw_info->dev_extn)); + } else if (!strcmp(snd_card_name, "msm8226-tapan-skuf-snd-card")) { + strlcpy(hw_info->type, " skuf", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8226", sizeof(hw_info->name)); + hw_info->snd_devices = (snd_device_t *) tapan_skuf_variant_devices; + hw_info->num_snd_devices = ARRAY_SIZE(tapan_skuf_variant_devices); + strlcpy(hw_info->dev_extn, "-skuf", sizeof(hw_info->dev_extn)); + } else if (!strcmp(snd_card_name, "msm8226-tapan9302-skuf-snd-card")) { + strlcpy(hw_info->type, " tapan9302-skuf", sizeof(hw_info->type)); + strlcpy(hw_info->name, "msm8226", sizeof(hw_info->name)); + hw_info->snd_devices = (snd_device_t *)tapan_lite_skuf_variant_devices; + hw_info->num_snd_devices = ARRAY_SIZE(tapan_lite_skuf_variant_devices); + strlcpy(hw_info->dev_extn, "-skuf-lite", sizeof(hw_info->dev_extn)); + } else { + ALOGW("%s: Not an 8x26 device", __func__); + } +} + +void *hw_info_init(const char *snd_card_name) +{ + struct hardware_info *hw_info; + + hw_info = malloc(sizeof(struct hardware_info)); + if (!hw_info) { + ALOGE("failed to allocate mem for hardware info"); + return NULL; + } + + hw_info->snd_devices = NULL; + hw_info->num_snd_devices = 0; + strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn)); + strlcpy(hw_info->type, "", sizeof(hw_info->type)); + strlcpy(hw_info->name, "", sizeof(hw_info->name)); + + if(strstr(snd_card_name, "msm8974") || + strstr(snd_card_name, "apq8074")) { + ALOGV("8974 - variant soundcard"); + update_hardware_info_8974(hw_info, snd_card_name); + } else if(strstr(snd_card_name, "msm8226")) { + ALOGV("8x26 - variant soundcard"); + update_hardware_info_8226(hw_info, snd_card_name); + } else if(strstr(snd_card_name, "msm8x10")) { + ALOGV("8x10 - variant soundcard"); + update_hardware_info_8610(hw_info, snd_card_name); + } else if(strstr(snd_card_name, "apq8084")) { + ALOGV("8084 - variant soundcard"); + update_hardware_info_8084(hw_info, snd_card_name); + } else if(strstr(snd_card_name, "msm8994")) { + ALOGV("8994 - variant soundcard"); + update_hardware_info_8994(hw_info, snd_card_name); + } else { + ALOGE("%s: Unsupported target %s:",__func__, snd_card_name); + free(hw_info); + hw_info = NULL; + } + + return hw_info; +} + +void hw_info_deinit(void *hw_info) +{ + struct hardware_info *my_data = (struct hardware_info*) hw_info; + + if(!my_data) + free(my_data); +} + +void hw_info_append_hw_type(void *hw_info, snd_device_t snd_device, + char *device_name) +{ + struct hardware_info *my_data = (struct hardware_info*) hw_info; + uint32_t i = 0; + + snd_device_t *snd_devices = + (snd_device_t *) my_data->snd_devices; + + if(snd_devices != NULL) { + for (i = 0; i < my_data->num_snd_devices; i++) { + if (snd_device == (snd_device_t)snd_devices[i]) { + ALOGV("extract dev_extn device %d, extn = %s", + (snd_device_t)snd_devices[i], my_data->dev_extn); + CHECK(strlcat(device_name, my_data->dev_extn, + DEVICE_NAME_MAX_SIZE) < DEVICE_NAME_MAX_SIZE); + break; + } + } + } + ALOGD("%s : device_name = %s", __func__,device_name); +} diff --git a/audio/hal/msm8974/platform.c b/audio/hal/msm8974/platform.c new file mode 100644 index 0000000..b59f97d --- /dev/null +++ b/audio/hal/msm8974/platform.c @@ -0,0 +1,2825 @@ +/* + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2013 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 "msm8974_platform" +/*#define LOG_NDEBUG 0*/ +#define LOG_NDDEBUG 0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "platform.h" +#include "audio_extn.h" +#include "voice_extn.h" +#include "sound/compress_params.h" +#include "sound/msmcal-hwdep.h" + +#define SOUND_TRIGGER_DEVICE_HANDSET_MONO_LOW_POWER_ACDB_ID (100) +#define MIXER_XML_PATH "/system/etc/mixer_paths.xml" +#define MIXER_XML_PATH_AUXPCM "/system/etc/mixer_paths_auxpcm.xml" +#define MIXER_XML_PATH_I2S "/system/etc/mixer_paths_i2s.xml" + +#define PLATFORM_INFO_XML_PATH "/system/etc/audio_platform_info.xml" +#define PLATFORM_INFO_XML_PATH_I2S "/system/etc/audio_platform_info_i2s.xml" + +#define LIB_ACDB_LOADER "libacdbloader.so" +#define AUDIO_DATA_BLOCK_MIXER_CTL "HDMI EDID" +#define CVD_VERSION_MIXER_CTL "CVD Version" + +#define MAX_COMPRESS_OFFLOAD_FRAGMENT_SIZE (256 * 1024) +#define MIN_COMPRESS_OFFLOAD_FRAGMENT_SIZE (2 * 1024) +#define COMPRESS_OFFLOAD_FRAGMENT_SIZE_FOR_AV_STREAMING (2 * 1024) +#define COMPRESS_OFFLOAD_FRAGMENT_SIZE (32 * 1024) + +/* Used in calculating fragment size for pcm offload */ +#define PCM_OFFLOAD_BUFFER_DURATION_FOR_AV 1000 /* 1 sec */ +#define PCM_OFFLOAD_BUFFER_DURATION_FOR_AV_STREAMING 80 /* 80 millisecs */ + +/* MAX PCM fragment size cannot be increased further due + * to flinger's cblk size of 1mb,and it has to be a multiple of + * 24 - lcm of channels supported by DSP + */ +#define MAX_PCM_OFFLOAD_FRAGMENT_SIZE (240 * 1024) +#define MIN_PCM_OFFLOAD_FRAGMENT_SIZE (4 * 1024) + +#define ALIGN( num, to ) (((num) + (to-1)) & (~(to-1))) +/* + * This file will have a maximum of 38 bytes: + * + * 4 bytes: number of audio blocks + * 4 bytes: total length of Short Audio Descriptor (SAD) blocks + * Maximum 10 * 3 bytes: SAD blocks + */ +#define MAX_SAD_BLOCKS 10 +#define SAD_BLOCK_SIZE 3 + +#define MAX_CVD_VERSION_STRING_SIZE 100 + +/* EDID format ID for LPCM audio */ +#define EDID_FORMAT_LPCM 1 + +/* fallback app type if the default app type from acdb loader fails */ +#define DEFAULT_APP_TYPE 0x11130 + +/* Retry for delay in FW loading*/ +#define RETRY_NUMBER 10 +#define RETRY_US 500000 +#define MAX_SND_CARD 8 + +#define SAMPLE_RATE_8KHZ 8000 +#define SAMPLE_RATE_16KHZ 16000 + +#define AUDIO_PARAMETER_KEY_FLUENCE_TYPE "fluence" +#define AUDIO_PARAMETER_KEY_SLOWTALK "st_enable" +#define AUDIO_PARAMETER_KEY_HD_VOICE "hd_voice" +#define AUDIO_PARAMETER_KEY_VOLUME_BOOST "volume_boost" +/* Query external audio device connection status */ +#define AUDIO_PARAMETER_KEY_EXT_AUDIO_DEVICE "ext_audio_device" + +#define EVENT_EXTERNAL_SPK_1 "qc_ext_spk_1" +#define EVENT_EXTERNAL_SPK_2 "qc_ext_spk_2" +#define EVENT_EXTERNAL_MIC "qc_ext_mic" +#define MAX_CAL_NAME 20 + +char cal_name_info[WCD9XXX_MAX_CAL][MAX_CAL_NAME] = { + [WCD9XXX_ANC_CAL] = "anc_cal", + [WCD9XXX_MBHC_CAL] = "mbhc_cal", + [WCD9XXX_MAD_CAL] = "mad_cal", +}; + +enum { + VOICE_FEATURE_SET_DEFAULT, + VOICE_FEATURE_SET_VOLUME_BOOST +}; + +struct audio_block_header +{ + int reserved; + int length; +}; + +/* Audio calibration related functions */ +typedef void (*acdb_deallocate_t)(); +typedef int (*acdb_init_t)(const char *, char *, int); +typedef void (*acdb_send_audio_cal_t)(int, int, int , int); +typedef void (*acdb_send_voice_cal_t)(int, int); +typedef int (*acdb_reload_vocvoltable_t)(int); +typedef int (*acdb_get_default_app_type_t)(void); +typedef int (*acdb_loader_get_calibration_t)(char *attr, int size, void *data); +acdb_loader_get_calibration_t acdb_loader_get_calibration; + +struct platform_data { + struct audio_device *adev; + bool fluence_in_spkr_mode; + bool fluence_in_voice_call; + bool fluence_in_voice_rec; + bool fluence_in_audio_rec; + bool external_spk_1; + bool external_spk_2; + bool external_mic; + int fluence_type; + int fluence_mode; + char fluence_cap[PROPERTY_VALUE_MAX]; + bool slowtalk; + bool hd_voice; + bool ec_ref_enabled; + bool is_i2s_ext_modem; + /* Audio calibration related functions */ + void *acdb_handle; + int voice_feature_set; + acdb_init_t acdb_init; + acdb_deallocate_t acdb_deallocate; + acdb_send_audio_cal_t acdb_send_audio_cal; + acdb_send_voice_cal_t acdb_send_voice_cal; + acdb_reload_vocvoltable_t acdb_reload_vocvoltable; + acdb_get_default_app_type_t acdb_get_default_app_type; + + void *hw_info; + struct csd_data *csd; +}; + +static int pcm_device_table[AUDIO_USECASE_MAX][2] = { + [USECASE_AUDIO_PLAYBACK_DEEP_BUFFER] = {DEEP_BUFFER_PCM_DEVICE, + DEEP_BUFFER_PCM_DEVICE}, + [USECASE_AUDIO_PLAYBACK_LOW_LATENCY] = {LOWLATENCY_PCM_DEVICE, + LOWLATENCY_PCM_DEVICE}, + [USECASE_AUDIO_PLAYBACK_MULTI_CH] = {MULTIMEDIA2_PCM_DEVICE, + MULTIMEDIA2_PCM_DEVICE}, + [USECASE_AUDIO_PLAYBACK_OFFLOAD] = + {PLAYBACK_OFFLOAD_DEVICE, PLAYBACK_OFFLOAD_DEVICE}, +#ifdef MULTIPLE_OFFLOAD_ENABLED + [USECASE_AUDIO_PLAYBACK_OFFLOAD2] = + {PLAYBACK_OFFLOAD_DEVICE2, PLAYBACK_OFFLOAD_DEVICE2}, + [USECASE_AUDIO_PLAYBACK_OFFLOAD3] = + {PLAYBACK_OFFLOAD_DEVICE3, PLAYBACK_OFFLOAD_DEVICE3}, + [USECASE_AUDIO_PLAYBACK_OFFLOAD4] = + {PLAYBACK_OFFLOAD_DEVICE4, PLAYBACK_OFFLOAD_DEVICE4}, + [USECASE_AUDIO_PLAYBACK_OFFLOAD5] = + {PLAYBACK_OFFLOAD_DEVICE5, PLAYBACK_OFFLOAD_DEVICE5}, + [USECASE_AUDIO_PLAYBACK_OFFLOAD6] = + {PLAYBACK_OFFLOAD_DEVICE6, PLAYBACK_OFFLOAD_DEVICE6}, + [USECASE_AUDIO_PLAYBACK_OFFLOAD7] = + {PLAYBACK_OFFLOAD_DEVICE7, PLAYBACK_OFFLOAD_DEVICE7}, + [USECASE_AUDIO_PLAYBACK_OFFLOAD8] = + {PLAYBACK_OFFLOAD_DEVICE8, PLAYBACK_OFFLOAD_DEVICE8}, + [USECASE_AUDIO_PLAYBACK_OFFLOAD9] = + {PLAYBACK_OFFLOAD_DEVICE9, PLAYBACK_OFFLOAD_DEVICE9}, +#endif + [USECASE_AUDIO_RECORD] = {AUDIO_RECORD_PCM_DEVICE, AUDIO_RECORD_PCM_DEVICE}, + [USECASE_AUDIO_RECORD_COMPRESS] = {COMPRESS_CAPTURE_DEVICE, COMPRESS_CAPTURE_DEVICE}, + [USECASE_AUDIO_RECORD_LOW_LATENCY] = {LOWLATENCY_PCM_DEVICE, + LOWLATENCY_PCM_DEVICE}, + [USECASE_AUDIO_RECORD_FM_VIRTUAL] = {MULTIMEDIA2_PCM_DEVICE, + MULTIMEDIA2_PCM_DEVICE}, + [USECASE_AUDIO_PLAYBACK_FM] = {FM_PLAYBACK_PCM_DEVICE, FM_CAPTURE_PCM_DEVICE}, + [USECASE_AUDIO_HFP_SCO] = {HFP_PCM_RX, HFP_SCO_RX}, + [USECASE_AUDIO_HFP_SCO_WB] = {HFP_PCM_RX, HFP_SCO_RX}, + [USECASE_VOICE_CALL] = {VOICE_CALL_PCM_DEVICE, VOICE_CALL_PCM_DEVICE}, + [USECASE_VOICE2_CALL] = {VOICE2_CALL_PCM_DEVICE, VOICE2_CALL_PCM_DEVICE}, + [USECASE_VOLTE_CALL] = {VOLTE_CALL_PCM_DEVICE, VOLTE_CALL_PCM_DEVICE}, + [USECASE_QCHAT_CALL] = {QCHAT_CALL_PCM_DEVICE, QCHAT_CALL_PCM_DEVICE}, + [USECASE_VOWLAN_CALL] = {VOWLAN_CALL_PCM_DEVICE, VOWLAN_CALL_PCM_DEVICE}, + [USECASE_COMPRESS_VOIP_CALL] = {COMPRESS_VOIP_CALL_PCM_DEVICE, COMPRESS_VOIP_CALL_PCM_DEVICE}, + [USECASE_INCALL_REC_UPLINK] = {AUDIO_RECORD_PCM_DEVICE, + AUDIO_RECORD_PCM_DEVICE}, + [USECASE_INCALL_REC_DOWNLINK] = {AUDIO_RECORD_PCM_DEVICE, + AUDIO_RECORD_PCM_DEVICE}, + [USECASE_INCALL_REC_UPLINK_AND_DOWNLINK] = {AUDIO_RECORD_PCM_DEVICE, + AUDIO_RECORD_PCM_DEVICE}, + [USECASE_INCALL_REC_UPLINK_COMPRESS] = {COMPRESS_CAPTURE_DEVICE, + COMPRESS_CAPTURE_DEVICE}, + [USECASE_INCALL_REC_DOWNLINK_COMPRESS] = {COMPRESS_CAPTURE_DEVICE, + COMPRESS_CAPTURE_DEVICE}, + [USECASE_INCALL_REC_UPLINK_AND_DOWNLINK_COMPRESS] = {COMPRESS_CAPTURE_DEVICE, + COMPRESS_CAPTURE_DEVICE}, + [USECASE_INCALL_MUSIC_UPLINK] = {INCALL_MUSIC_UPLINK_PCM_DEVICE, + INCALL_MUSIC_UPLINK_PCM_DEVICE}, + [USECASE_INCALL_MUSIC_UPLINK2] = {INCALL_MUSIC_UPLINK2_PCM_DEVICE, + INCALL_MUSIC_UPLINK2_PCM_DEVICE}, + [USECASE_AUDIO_SPKR_CALIB_RX] = {SPKR_PROT_CALIB_RX_PCM_DEVICE, -1}, + [USECASE_AUDIO_SPKR_CALIB_TX] = {-1, SPKR_PROT_CALIB_TX_PCM_DEVICE}, + + [USECASE_AUDIO_PLAYBACK_AFE_PROXY] = {AFE_PROXY_PLAYBACK_PCM_DEVICE, + AFE_PROXY_RECORD_PCM_DEVICE}, + [USECASE_AUDIO_RECORD_AFE_PROXY] = {AFE_PROXY_PLAYBACK_PCM_DEVICE, + AFE_PROXY_RECORD_PCM_DEVICE}, + +}; + +/* Array to store sound devices */ +static const char * const device_table[SND_DEVICE_MAX] = { + [SND_DEVICE_NONE] = "none", + /* Playback sound devices */ + [SND_DEVICE_OUT_HANDSET] = "handset", + [SND_DEVICE_OUT_SPEAKER] = "speaker", + [SND_DEVICE_OUT_SPEAKER_EXTERNAL_1] = "speaker-ext-1", + [SND_DEVICE_OUT_SPEAKER_EXTERNAL_2] = "speaker-ext-2", + [SND_DEVICE_OUT_SPEAKER_REVERSE] = "speaker-reverse", + [SND_DEVICE_OUT_HEADPHONES] = "headphones", + [SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES] = "speaker-and-headphones", + [SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_EXTERNAL_1] = "speaker-and-headphones-ext-1", + [SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_EXTERNAL_2] = "speaker-and-headphones-ext-2", + [SND_DEVICE_OUT_VOICE_HANDSET] = "voice-handset", + [SND_DEVICE_OUT_VOICE_SPEAKER] = "voice-speaker", + [SND_DEVICE_OUT_VOICE_HEADPHONES] = "voice-headphones", + [SND_DEVICE_OUT_HDMI] = "hdmi", + [SND_DEVICE_OUT_SPEAKER_AND_HDMI] = "speaker-and-hdmi", + [SND_DEVICE_OUT_BT_SCO] = "bt-sco-headset", + [SND_DEVICE_OUT_BT_SCO_WB] = "bt-sco-headset-wb", + [SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES] = "voice-tty-full-headphones", + [SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = "voice-tty-vco-headphones", + [SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET] = "voice-tty-hco-handset", + [SND_DEVICE_OUT_VOICE_TX] = "voice-tx", + [SND_DEVICE_OUT_AFE_PROXY] = "afe-proxy", + [SND_DEVICE_OUT_USB_HEADSET] = "usb-headphones", + [SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET] = "speaker-and-usb-headphones", + [SND_DEVICE_OUT_TRANSMISSION_FM] = "transmission-fm", + [SND_DEVICE_OUT_ANC_HEADSET] = "anc-headphones", + [SND_DEVICE_OUT_ANC_FB_HEADSET] = "anc-fb-headphones", + [SND_DEVICE_OUT_VOICE_ANC_HEADSET] = "voice-anc-headphones", + [SND_DEVICE_OUT_VOICE_ANC_FB_HEADSET] = "voice-anc-fb-headphones", + [SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET] = "speaker-and-anc-headphones", + [SND_DEVICE_OUT_ANC_HANDSET] = "anc-handset", + [SND_DEVICE_OUT_SPEAKER_PROTECTED] = "speaker-protected", + [SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED] = "voice-speaker-protected", + + /* Capture sound devices */ + [SND_DEVICE_IN_HANDSET_MIC] = "handset-mic", + [SND_DEVICE_IN_HANDSET_MIC_EXTERNAL] = "handset-mic-ext", + [SND_DEVICE_IN_HANDSET_MIC_AEC] = "handset-mic", + [SND_DEVICE_IN_HANDSET_MIC_NS] = "handset-mic", + [SND_DEVICE_IN_HANDSET_MIC_AEC_NS] = "handset-mic", + [SND_DEVICE_IN_HANDSET_DMIC] = "dmic-endfire", + [SND_DEVICE_IN_HANDSET_DMIC_AEC] = "dmic-endfire", + [SND_DEVICE_IN_HANDSET_DMIC_NS] = "dmic-endfire", + [SND_DEVICE_IN_HANDSET_DMIC_AEC_NS] = "dmic-endfire", + [SND_DEVICE_IN_SPEAKER_MIC] = "speaker-mic", + [SND_DEVICE_IN_SPEAKER_MIC_AEC] = "speaker-mic", + [SND_DEVICE_IN_SPEAKER_MIC_NS] = "speaker-mic", + [SND_DEVICE_IN_SPEAKER_MIC_AEC_NS] = "speaker-mic", + [SND_DEVICE_IN_SPEAKER_DMIC] = "speaker-dmic-endfire", + [SND_DEVICE_IN_SPEAKER_DMIC_AEC] = "speaker-dmic-endfire", + [SND_DEVICE_IN_SPEAKER_DMIC_NS] = "speaker-dmic-endfire", + [SND_DEVICE_IN_SPEAKER_DMIC_AEC_NS] = "speaker-dmic-endfire", + [SND_DEVICE_IN_HEADSET_MIC] = "headset-mic", + [SND_DEVICE_IN_HEADSET_MIC_FLUENCE] = "headset-mic", + [SND_DEVICE_IN_VOICE_SPEAKER_MIC] = "voice-speaker-mic", + [SND_DEVICE_IN_VOICE_HEADSET_MIC] = "voice-headset-mic", + [SND_DEVICE_IN_HDMI_MIC] = "hdmi-mic", + [SND_DEVICE_IN_BT_SCO_MIC] = "bt-sco-mic", + [SND_DEVICE_IN_BT_SCO_MIC_NREC] = "bt-sco-mic", + [SND_DEVICE_IN_BT_SCO_MIC_WB] = "bt-sco-mic-wb", + [SND_DEVICE_IN_BT_SCO_MIC_WB_NREC] = "bt-sco-mic-wb", + [SND_DEVICE_IN_CAMCORDER_MIC] = "camcorder-mic", + [SND_DEVICE_IN_VOICE_DMIC] = "voice-dmic-ef", + [SND_DEVICE_IN_VOICE_SPEAKER_DMIC] = "voice-speaker-dmic-ef", + [SND_DEVICE_IN_VOICE_SPEAKER_QMIC] = "voice-speaker-qmic", + [SND_DEVICE_IN_VOICE_TTY_FULL_HEADSET_MIC] = "voice-tty-full-headset-mic", + [SND_DEVICE_IN_VOICE_TTY_VCO_HANDSET_MIC] = "voice-tty-vco-handset-mic", + [SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC] = "voice-tty-hco-headset-mic", + [SND_DEVICE_IN_VOICE_RX] = "voice-rx", + + [SND_DEVICE_IN_VOICE_REC_MIC] = "voice-rec-mic", + [SND_DEVICE_IN_VOICE_REC_MIC_NS] = "voice-rec-mic", + [SND_DEVICE_IN_VOICE_REC_DMIC_STEREO] = "voice-rec-dmic-ef", + [SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE] = "voice-rec-dmic-ef-fluence", + [SND_DEVICE_IN_USB_HEADSET_MIC] = "usb-headset-mic", + [SND_DEVICE_IN_CAPTURE_FM] = "capture-fm", + [SND_DEVICE_IN_AANC_HANDSET_MIC] = "aanc-handset-mic", + [SND_DEVICE_IN_QUAD_MIC] = "quad-mic", + [SND_DEVICE_IN_HANDSET_STEREO_DMIC] = "handset-stereo-dmic-ef", + [SND_DEVICE_IN_SPEAKER_STEREO_DMIC] = "speaker-stereo-dmic-ef", + [SND_DEVICE_IN_CAPTURE_VI_FEEDBACK] = "vi-feedback", + [SND_DEVICE_IN_VOICE_SPEAKER_DMIC_BROADSIDE] = "voice-speaker-dmic-broadside", + [SND_DEVICE_IN_SPEAKER_DMIC_BROADSIDE] = "speaker-dmic-broadside", + [SND_DEVICE_IN_SPEAKER_DMIC_AEC_BROADSIDE] = "speaker-dmic-broadside", + [SND_DEVICE_IN_SPEAKER_DMIC_NS_BROADSIDE] = "speaker-dmic-broadside", + [SND_DEVICE_IN_SPEAKER_DMIC_AEC_NS_BROADSIDE] = "speaker-dmic-broadside", +}; + +/* ACDB IDs (audio DSP path configuration IDs) for each sound device */ +static int acdb_device_table[SND_DEVICE_MAX] = { + [SND_DEVICE_NONE] = -1, + [SND_DEVICE_OUT_HANDSET] = 7, + [SND_DEVICE_OUT_SPEAKER] = 14, + [SND_DEVICE_OUT_SPEAKER_EXTERNAL_1] = 14, + [SND_DEVICE_OUT_SPEAKER_EXTERNAL_2] = 14, + [SND_DEVICE_OUT_SPEAKER_REVERSE] = 14, + [SND_DEVICE_OUT_HEADPHONES] = 10, + [SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES] = 10, + [SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_EXTERNAL_1] = 10, + [SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_EXTERNAL_2] = 10, + [SND_DEVICE_OUT_VOICE_HANDSET] = 7, + [SND_DEVICE_OUT_VOICE_SPEAKER] = 14, + [SND_DEVICE_OUT_VOICE_HEADPHONES] = 10, + [SND_DEVICE_OUT_HDMI] = 18, + [SND_DEVICE_OUT_SPEAKER_AND_HDMI] = 14, + [SND_DEVICE_OUT_BT_SCO] = 22, + [SND_DEVICE_OUT_BT_SCO_WB] = 39, + [SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES] = 17, + [SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = 17, + [SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET] = 37, + [SND_DEVICE_OUT_VOICE_TX] = 45, + [SND_DEVICE_OUT_AFE_PROXY] = 0, + [SND_DEVICE_OUT_USB_HEADSET] = 45, + [SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET] = 14, + [SND_DEVICE_OUT_TRANSMISSION_FM] = 0, + [SND_DEVICE_OUT_ANC_HEADSET] = 26, + [SND_DEVICE_OUT_ANC_FB_HEADSET] = 27, + [SND_DEVICE_OUT_VOICE_ANC_HEADSET] = 26, + [SND_DEVICE_OUT_VOICE_ANC_FB_HEADSET] = 27, + [SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET] = 26, + [SND_DEVICE_OUT_ANC_HANDSET] = 103, + [SND_DEVICE_OUT_SPEAKER_PROTECTED] = 124, + [SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED] = 101, + + [SND_DEVICE_IN_HANDSET_MIC] = 4, + [SND_DEVICE_IN_HANDSET_MIC_EXTERNAL] = 4, + [SND_DEVICE_IN_HANDSET_MIC_AEC] = 106, + [SND_DEVICE_IN_HANDSET_MIC_NS] = 107, + [SND_DEVICE_IN_HANDSET_MIC_AEC_NS] = 108, + [SND_DEVICE_IN_HANDSET_DMIC] = 41, + [SND_DEVICE_IN_HANDSET_DMIC_AEC] = 109, + [SND_DEVICE_IN_HANDSET_DMIC_NS] = 110, + [SND_DEVICE_IN_HANDSET_DMIC_AEC_NS] = 111, + [SND_DEVICE_IN_SPEAKER_MIC] = 11, + [SND_DEVICE_IN_SPEAKER_MIC_AEC] = 112, + [SND_DEVICE_IN_SPEAKER_MIC_NS] = 113, + [SND_DEVICE_IN_SPEAKER_MIC_AEC_NS] = 114, + [SND_DEVICE_IN_SPEAKER_DMIC] = 43, + [SND_DEVICE_IN_SPEAKER_DMIC_AEC] = 115, + [SND_DEVICE_IN_SPEAKER_DMIC_NS] = 116, + [SND_DEVICE_IN_SPEAKER_DMIC_AEC_NS] = 117, + [SND_DEVICE_IN_HEADSET_MIC] = 8, + [SND_DEVICE_IN_HEADSET_MIC_FLUENCE] = 47, + [SND_DEVICE_IN_VOICE_SPEAKER_MIC] = 11, + [SND_DEVICE_IN_VOICE_HEADSET_MIC] = 8, + [SND_DEVICE_IN_HDMI_MIC] = 4, + [SND_DEVICE_IN_BT_SCO_MIC] = 21, + [SND_DEVICE_IN_BT_SCO_MIC_NREC] = 122, + [SND_DEVICE_IN_BT_SCO_MIC_WB] = 38, + [SND_DEVICE_IN_BT_SCO_MIC_WB_NREC] = 123, + [SND_DEVICE_IN_CAMCORDER_MIC] = 4, + [SND_DEVICE_IN_VOICE_DMIC] = 41, + [SND_DEVICE_IN_VOICE_SPEAKER_DMIC] = 43, + [SND_DEVICE_IN_VOICE_SPEAKER_QMIC] = 19, + [SND_DEVICE_IN_VOICE_TTY_FULL_HEADSET_MIC] = 16, + [SND_DEVICE_IN_VOICE_TTY_VCO_HANDSET_MIC] = 36, + [SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC] = 16, + [SND_DEVICE_IN_VOICE_RX] = 44, + + [SND_DEVICE_IN_VOICE_REC_MIC] = 4, + [SND_DEVICE_IN_VOICE_REC_MIC_NS] = 107, + [SND_DEVICE_IN_VOICE_REC_DMIC_STEREO] = 34, + [SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE] = 41, + [SND_DEVICE_IN_USB_HEADSET_MIC] = 44, + [SND_DEVICE_IN_CAPTURE_FM] = 0, + [SND_DEVICE_IN_AANC_HANDSET_MIC] = 104, + [SND_DEVICE_IN_QUAD_MIC] = 46, + [SND_DEVICE_IN_HANDSET_STEREO_DMIC] = 34, + [SND_DEVICE_IN_SPEAKER_STEREO_DMIC] = 35, + [SND_DEVICE_IN_CAPTURE_VI_FEEDBACK] = 102, + [SND_DEVICE_IN_VOICE_SPEAKER_DMIC_BROADSIDE] = 12, + [SND_DEVICE_IN_SPEAKER_DMIC_BROADSIDE] = 12, + [SND_DEVICE_IN_SPEAKER_DMIC_AEC_BROADSIDE] = 119, + [SND_DEVICE_IN_SPEAKER_DMIC_NS_BROADSIDE] = 121, + [SND_DEVICE_IN_SPEAKER_DMIC_AEC_NS_BROADSIDE] = 120, +}; + +struct name_to_index { + char name[100]; + unsigned int index; +}; + +#define TO_NAME_INDEX(X) #X, X + +/* Used to get index from parsed sting */ +static struct name_to_index snd_device_name_index[SND_DEVICE_MAX] = { + {TO_NAME_INDEX(SND_DEVICE_OUT_HANDSET)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_EXTERNAL_1)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_EXTERNAL_2)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_REVERSE)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_HEADPHONES)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_EXTERNAL_1)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_EXTERNAL_2)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_HANDSET)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_SPEAKER)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_HEADPHONES)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_HDMI)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_HDMI)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_BT_SCO)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_BT_SCO_WB)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_AFE_PROXY)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_USB_HEADSET)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_TRANSMISSION_FM)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_ANC_HEADSET)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_ANC_FB_HEADSET)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_ANC_HEADSET)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_ANC_FB_HEADSET)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_ANC_HANDSET)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_PROTECTED)}, + {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED)}, + {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_MIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_MIC_EXTERNAL)}, + {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_MIC_AEC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_MIC_NS)}, + {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_MIC_AEC_NS)}, + {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_DMIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_DMIC_AEC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_DMIC_NS)}, + {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_DMIC_AEC_NS)}, + {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_MIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_MIC_AEC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_MIC_NS)}, + {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_MIC_AEC_NS)}, + {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_DMIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_DMIC_AEC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_DMIC_NS)}, + {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_DMIC_AEC_NS)}, + {TO_NAME_INDEX(SND_DEVICE_IN_HEADSET_MIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_HEADSET_MIC_FLUENCE)}, + {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_SPEAKER_MIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_HEADSET_MIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_HDMI_MIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_BT_SCO_MIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_BT_SCO_MIC_NREC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_BT_SCO_MIC_WB)}, + {TO_NAME_INDEX(SND_DEVICE_IN_BT_SCO_MIC_WB_NREC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_CAMCORDER_MIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_DMIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_SPEAKER_DMIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_SPEAKER_QMIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_TTY_FULL_HEADSET_MIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_TTY_VCO_HANDSET_MIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_REC_MIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_REC_MIC_NS)}, + {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_REC_DMIC_STEREO)}, + {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE)}, + {TO_NAME_INDEX(SND_DEVICE_IN_USB_HEADSET_MIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_CAPTURE_FM)}, + {TO_NAME_INDEX(SND_DEVICE_IN_AANC_HANDSET_MIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_QUAD_MIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_STEREO_DMIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_STEREO_DMIC)}, + {TO_NAME_INDEX(SND_DEVICE_IN_CAPTURE_VI_FEEDBACK)}, + {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_SPEAKER_DMIC_BROADSIDE)}, + {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_DMIC_BROADSIDE)}, + {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_DMIC_AEC_BROADSIDE)}, + {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_DMIC_NS_BROADSIDE)}, + {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_DMIC_AEC_NS_BROADSIDE)}, +}; + +static char * backend_table[SND_DEVICE_MAX] = {0}; + +static struct name_to_index usecase_name_index[AUDIO_USECASE_MAX] = { + {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_DEEP_BUFFER)}, + {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_LOW_LATENCY)}, + {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_MULTI_CH)}, + {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_OFFLOAD)}, + {TO_NAME_INDEX(USECASE_AUDIO_RECORD)}, + {TO_NAME_INDEX(USECASE_AUDIO_RECORD_LOW_LATENCY)}, + {TO_NAME_INDEX(USECASE_VOICE_CALL)}, + {TO_NAME_INDEX(USECASE_VOICE2_CALL)}, + {TO_NAME_INDEX(USECASE_VOLTE_CALL)}, + {TO_NAME_INDEX(USECASE_QCHAT_CALL)}, + {TO_NAME_INDEX(USECASE_VOWLAN_CALL)}, + {TO_NAME_INDEX(USECASE_INCALL_REC_UPLINK)}, + {TO_NAME_INDEX(USECASE_INCALL_REC_DOWNLINK)}, + {TO_NAME_INDEX(USECASE_INCALL_REC_UPLINK_AND_DOWNLINK)}, + {TO_NAME_INDEX(USECASE_AUDIO_HFP_SCO)}, +}; + +#define NO_COLS 2 +#ifdef PLATFORM_APQ8084 +static int msm_device_to_be_id [][NO_COLS] = { + {AUDIO_DEVICE_OUT_EARPIECE , 2}, + {AUDIO_DEVICE_OUT_SPEAKER , 2}, + {AUDIO_DEVICE_OUT_WIRED_HEADSET , 2}, + {AUDIO_DEVICE_OUT_WIRED_HEADPHONE , 2}, + {AUDIO_DEVICE_OUT_BLUETOOTH_SCO , 11}, + {AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET , 11}, + {AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT , 11}, + {AUDIO_DEVICE_OUT_BLUETOOTH_A2DP , -1}, + {AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES , -1}, + {AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER , -1}, + {AUDIO_DEVICE_OUT_AUX_DIGITAL , 4}, + {AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET , 9}, + {AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET , 9}, + {AUDIO_DEVICE_OUT_USB_ACCESSORY , -1}, + {AUDIO_DEVICE_OUT_USB_DEVICE , -1}, + {AUDIO_DEVICE_OUT_REMOTE_SUBMIX , 9}, + {AUDIO_DEVICE_OUT_PROXY , 9}, + {AUDIO_DEVICE_OUT_FM , 7}, + {AUDIO_DEVICE_OUT_FM_TX , 8}, + {AUDIO_DEVICE_OUT_ALL , -1}, + {AUDIO_DEVICE_NONE , -1}, + {AUDIO_DEVICE_OUT_DEFAULT , -1}, +}; +#elif PLATFORM_MSM8994 +static int msm_device_to_be_id [][NO_COLS] = { + {AUDIO_DEVICE_OUT_EARPIECE , 2}, + {AUDIO_DEVICE_OUT_SPEAKER , 2}, + {AUDIO_DEVICE_OUT_WIRED_HEADSET , 2}, + {AUDIO_DEVICE_OUT_WIRED_HEADPHONE , 2}, + {AUDIO_DEVICE_OUT_BLUETOOTH_SCO , 38}, + {AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET , 38}, + {AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT , 38}, + {AUDIO_DEVICE_OUT_BLUETOOTH_A2DP , -1}, + {AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES , -1}, + {AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER , -1}, + {AUDIO_DEVICE_OUT_AUX_DIGITAL , 4}, + {AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET , 9}, + {AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET , 9}, + {AUDIO_DEVICE_OUT_USB_ACCESSORY , -1}, + {AUDIO_DEVICE_OUT_USB_DEVICE , -1}, + {AUDIO_DEVICE_OUT_REMOTE_SUBMIX , 9}, + {AUDIO_DEVICE_OUT_PROXY , 9}, +/* Add the correct be ids */ + {AUDIO_DEVICE_OUT_FM , 7}, + {AUDIO_DEVICE_OUT_FM_TX , 8}, + {AUDIO_DEVICE_OUT_ALL , -1}, + {AUDIO_DEVICE_NONE , -1}, + {AUDIO_DEVICE_OUT_DEFAULT , -1}, +}; +#else +static int msm_device_to_be_id [][NO_COLS] = { + {AUDIO_DEVICE_NONE, -1}, +}; +#endif +static int msm_be_id_array_len = + sizeof(msm_device_to_be_id) / sizeof(msm_device_to_be_id[0]); + + +#define DEEP_BUFFER_PLATFORM_DELAY (29*1000LL) +#define LOW_LATENCY_PLATFORM_DELAY (13*1000LL) + +void platform_set_echo_reference(void *platform, bool enable) +{ + struct platform_data *my_data = (struct platform_data *)platform; + struct audio_device *adev = my_data->adev; + + if (my_data->ec_ref_enabled) { + my_data->ec_ref_enabled = false; + ALOGV("%s: disabling echo-reference", __func__); + audio_route_reset_and_update_path(adev->audio_route, "echo-reference"); + } + + if (enable) { + my_data->ec_ref_enabled = true; + ALOGD("%s: enabling echo-reference", __func__); + audio_route_apply_and_update_path(adev->audio_route, "echo-reference"); + } + +} + +static struct csd_data *open_csd_client(bool i2s_ext_modem) +{ + struct csd_data *csd = calloc(1, sizeof(struct csd_data)); + + if (!csd) { + ALOGE("failed to allocate csd_data mem"); + return NULL; + } + + csd->csd_client = dlopen(LIB_CSD_CLIENT, RTLD_NOW); + if (csd->csd_client == NULL) { + ALOGE("%s: DLOPEN failed for %s", __func__, LIB_CSD_CLIENT); + goto error; + } else { + ALOGV("%s: DLOPEN successful for %s", __func__, LIB_CSD_CLIENT); + + csd->deinit = (deinit_t)dlsym(csd->csd_client, + "csd_client_deinit"); + if (csd->deinit == NULL) { + ALOGE("%s: dlsym error %s for csd_client_deinit", __func__, + dlerror()); + goto error; + } + csd->disable_device = (disable_device_t)dlsym(csd->csd_client, + "csd_client_disable_device"); + if (csd->disable_device == NULL) { + ALOGE("%s: dlsym error %s for csd_client_disable_device", + __func__, dlerror()); + goto error; + } + csd->enable_device_config = (enable_device_config_t)dlsym(csd->csd_client, + "csd_client_enable_device_config"); + if (csd->enable_device_config == NULL) { + ALOGE("%s: dlsym error %s for csd_client_enable_device_config", + __func__, dlerror()); + goto error; + } + csd->enable_device = (enable_device_t)dlsym(csd->csd_client, + "csd_client_enable_device"); + if (csd->enable_device == NULL) { + ALOGE("%s: dlsym error %s for csd_client_enable_device", + __func__, dlerror()); + goto error; + } + csd->start_voice = (start_voice_t)dlsym(csd->csd_client, + "csd_client_start_voice"); + if (csd->start_voice == NULL) { + ALOGE("%s: dlsym error %s for csd_client_start_voice", + __func__, dlerror()); + goto error; + } + csd->stop_voice = (stop_voice_t)dlsym(csd->csd_client, + "csd_client_stop_voice"); + if (csd->stop_voice == NULL) { + ALOGE("%s: dlsym error %s for csd_client_stop_voice", + __func__, dlerror()); + goto error; + } + csd->volume = (volume_t)dlsym(csd->csd_client, + "csd_client_volume"); + if (csd->volume == NULL) { + ALOGE("%s: dlsym error %s for csd_client_volume", + __func__, dlerror()); + goto error; + } + csd->mic_mute = (mic_mute_t)dlsym(csd->csd_client, + "csd_client_mic_mute"); + if (csd->mic_mute == NULL) { + ALOGE("%s: dlsym error %s for csd_client_mic_mute", + __func__, dlerror()); + goto error; + } + csd->slow_talk = (slow_talk_t)dlsym(csd->csd_client, + "csd_client_slow_talk"); + if (csd->slow_talk == NULL) { + ALOGE("%s: dlsym error %s for csd_client_slow_talk", + __func__, dlerror()); + goto error; + } + csd->start_playback = (start_playback_t)dlsym(csd->csd_client, + "csd_client_start_playback"); + if (csd->start_playback == NULL) { + ALOGE("%s: dlsym error %s for csd_client_start_playback", + __func__, dlerror()); + goto error; + } + csd->stop_playback = (stop_playback_t)dlsym(csd->csd_client, + "csd_client_stop_playback"); + if (csd->stop_playback == NULL) { + ALOGE("%s: dlsym error %s for csd_client_stop_playback", + __func__, dlerror()); + goto error; + } + csd->set_lch = (set_lch_t)dlsym(csd->csd_client, "csd_client_set_lch"); + if (csd->set_lch == NULL) { + ALOGE("%s: dlsym error %s for csd_client_set_lch", + __func__, dlerror()); + /* Ignore the error as this is not mandatory function for + * basic voice call to work. + */ + } + csd->start_record = (start_record_t)dlsym(csd->csd_client, + "csd_client_start_record"); + if (csd->start_record == NULL) { + ALOGE("%s: dlsym error %s for csd_client_start_record", + __func__, dlerror()); + goto error; + } + csd->stop_record = (stop_record_t)dlsym(csd->csd_client, + "csd_client_stop_record"); + if (csd->stop_record == NULL) { + ALOGE("%s: dlsym error %s for csd_client_stop_record", + __func__, dlerror()); + goto error; + } + + csd->get_sample_rate = (get_sample_rate_t)dlsym(csd->csd_client, + "csd_client_get_sample_rate"); + if (csd->get_sample_rate == NULL) { + ALOGE("%s: dlsym error %s for csd_client_get_sample_rate", + __func__, dlerror()); + + goto error; + } + + csd->init = (init_t)dlsym(csd->csd_client, "csd_client_init"); + + if (csd->init == NULL) { + ALOGE("%s: dlsym error %s for csd_client_init", + __func__, dlerror()); + goto error; + } else { + csd->init(i2s_ext_modem); + } + } + return csd; + +error: + free(csd); + csd = NULL; + return csd; +} + +void close_csd_client(struct csd_data *csd) +{ + if (csd != NULL) { + csd->deinit(); + dlclose(csd->csd_client); + free(csd); + csd = NULL; + } +} + +static bool platform_is_i2s_ext_modem(const char *snd_card_name, + struct platform_data *plat_data) +{ + plat_data->is_i2s_ext_modem = false; + + if (!strncmp(snd_card_name, "apq8084-taiko-i2s-mtp-snd-card", + sizeof("apq8084-taiko-i2s-mtp-snd-card")) || + !strncmp(snd_card_name, "apq8084-taiko-i2s-cdp-snd-card", + sizeof("apq8084-taiko-i2s-cdp-snd-card"))) { + plat_data->is_i2s_ext_modem = true; + } + ALOGV("%s, is_i2s_ext_modem:%d",__func__, plat_data->is_i2s_ext_modem); + + return plat_data->is_i2s_ext_modem; +} + +static void set_platform_defaults() +{ + int32_t dev; + for (dev = 0; dev < SND_DEVICE_MAX; dev++) { + backend_table[dev] = NULL; + } + + // TBD - do these go to the platform-info.xml file. + // will help in avoiding strdups here + backend_table[SND_DEVICE_IN_BT_SCO_MIC] = strdup("bt-sco"); + backend_table[SND_DEVICE_IN_BT_SCO_MIC_WB] = strdup("bt-sco-wb"); + backend_table[SND_DEVICE_IN_BT_SCO_MIC_NREC] = strdup("bt-sco"); + backend_table[SND_DEVICE_IN_BT_SCO_MIC_WB_NREC] = strdup("bt-sco-wb"); + backend_table[SND_DEVICE_OUT_BT_SCO] = strdup("bt-sco"); + backend_table[SND_DEVICE_OUT_BT_SCO_WB] = strdup("bt-sco-wb"); + backend_table[SND_DEVICE_OUT_HDMI] = strdup("hdmi"); + backend_table[SND_DEVICE_OUT_SPEAKER_AND_HDMI] = strdup("speaker-and-hdmi"); + backend_table[SND_DEVICE_OUT_VOICE_TX] = strdup("afe-proxy"); + backend_table[SND_DEVICE_IN_VOICE_RX] = strdup("afe-proxy"); + + backend_table[SND_DEVICE_OUT_AFE_PROXY] = strdup("afe-proxy"); + backend_table[SND_DEVICE_OUT_USB_HEADSET] = strdup("usb-headphones"); + backend_table[SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET] = + strdup("speaker-and-usb-headphones"); + backend_table[SND_DEVICE_IN_USB_HEADSET_MIC] = strdup("usb-headset-mic"); + backend_table[SND_DEVICE_IN_CAPTURE_FM] = strdup("capture-fm"); + backend_table[SND_DEVICE_OUT_TRANSMISSION_FM] = strdup("transmission-fm"); +} + +void get_cvd_version(char *cvd_version, struct audio_device *adev) +{ + struct mixer_ctl *ctl; + int count; + int ret = 0; + + ctl = mixer_get_ctl_by_name(adev->mixer, CVD_VERSION_MIXER_CTL); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", __func__, CVD_VERSION_MIXER_CTL); + goto done; + } + mixer_ctl_update(ctl); + + count = mixer_ctl_get_num_values(ctl); + if (count > MAX_CVD_VERSION_STRING_SIZE) + count = MAX_CVD_VERSION_STRING_SIZE; + + ret = mixer_ctl_get_array(ctl, cvd_version, count); + if (ret != 0) { + ALOGE("%s: ERROR! mixer_ctl_get_array() failed to get CVD Version", __func__); + goto done; + } + +done: + return; +} + +static int hw_util_open(int card_no) +{ + int fd = -1; + char dev_name[256]; + + snprintf(dev_name, sizeof(dev_name), "/dev/snd/hwC%uD%u", + card_no, WCD9XXX_CODEC_HWDEP_NODE); + ALOGD("%s Opening device %s\n", __func__, dev_name); + fd = open(dev_name, O_WRONLY); + if (fd < 0) { + ALOGE("%s: cannot open device '%s'\n", __func__, dev_name); + return fd; + } + ALOGD("%s success", __func__); + return fd; +} + +struct param_data { + int use_case; + int acdb_id; + int get_size; + int buff_size; + int data_size; + void *buff; +}; + +static int send_codec_cal(acdb_loader_get_calibration_t acdb_loader_get_calibration, int fd) +{ + int ret = 0, type; + + for (type = WCD9XXX_ANC_CAL; type < WCD9XXX_MAX_CAL; type++) { + struct wcdcal_ioctl_buffer codec_buffer; + struct param_data calib; + + if (!strcmp(cal_name_info[type], "mad_cal")) + calib.acdb_id = SOUND_TRIGGER_DEVICE_HANDSET_MONO_LOW_POWER_ACDB_ID; + calib.get_size = 1; + ret = acdb_loader_get_calibration(cal_name_info[type], sizeof(struct param_data), + &calib); + if (ret < 0) { + ALOGE("%s get_calibration failed\n", __func__); + return ret; + } + calib.get_size = 0; + calib.buff = malloc(calib.buff_size); + ret = acdb_loader_get_calibration(cal_name_info[type], + sizeof(struct param_data), &calib); + if (ret < 0) { + ALOGE("%s get_calibration failed\n", __func__); + free(calib.buff); + return ret; + } + codec_buffer.buffer = calib.buff; + codec_buffer.size = calib.data_size; + codec_buffer.cal_type = type; + if (ioctl(fd, SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE, &codec_buffer) < 0) + ALOGE("Failed to call ioctl for %s err=%d", + cal_name_info[type], errno); + ALOGD("%s cal sent for %s", __func__, cal_name_info[type]); + free(calib.buff); + } + return ret; +} + +static void audio_hwdep_send_cal(struct platform_data *plat_data) +{ + int fd; + + fd = hw_util_open(plat_data->adev->snd_card); + if (fd == -1) { + ALOGE("%s error open\n", __func__); + return; + } + + acdb_loader_get_calibration = (acdb_loader_get_calibration_t) + dlsym(plat_data->acdb_handle, "acdb_loader_get_calibration"); + + if (acdb_loader_get_calibration == NULL) { + ALOGE("%s: ERROR. dlsym Error:%s acdb_loader_get_calibration", __func__, + dlerror()); + return; + } + if (send_codec_cal(acdb_loader_get_calibration, fd) < 0) + ALOGE("%s: Could not send anc cal", __FUNCTION__); +} + +void *platform_init(struct audio_device *adev) +{ + char platform[PROPERTY_VALUE_MAX]; + char baseband[PROPERTY_VALUE_MAX]; + char value[PROPERTY_VALUE_MAX]; + struct platform_data *my_data = NULL; + int retry_num = 0, snd_card_num = 0, key = 0; + const char *snd_card_name; + char *cvd_version = NULL; + + my_data = calloc(1, sizeof(struct platform_data)); + + if (!my_data) { + ALOGE("failed to allocate platform data"); + return NULL; + } + + while (snd_card_num < MAX_SND_CARD) { + adev->mixer = mixer_open(snd_card_num); + + while (!adev->mixer && retry_num < RETRY_NUMBER) { + usleep(RETRY_US); + adev->mixer = mixer_open(snd_card_num); + retry_num++; + } + + if (!adev->mixer) { + ALOGE("%s: Unable to open the mixer card: %d", __func__, + snd_card_num); + retry_num = 0; + snd_card_num++; + continue; + } + + snd_card_name = mixer_get_name(adev->mixer); + ALOGV("%s: snd_card_name: %s", __func__, snd_card_name); + + my_data->hw_info = hw_info_init(snd_card_name); + if (!my_data->hw_info) { + ALOGE("%s: Failed to init hardware info", __func__); + } else { + if (platform_is_i2s_ext_modem(snd_card_name, my_data)) { + ALOGD("%s: Call MIXER_XML_PATH_I2S", __func__); + + adev->audio_route = audio_route_init(snd_card_num, + MIXER_XML_PATH_I2S); + } else if (audio_extn_read_xml(adev, snd_card_num, MIXER_XML_PATH, + MIXER_XML_PATH_AUXPCM) == -ENOSYS) { + adev->audio_route = audio_route_init(snd_card_num, + MIXER_XML_PATH); + } + if (!adev->audio_route) { + ALOGE("%s: Failed to init audio route controls, aborting.", + __func__); + free(my_data); + return NULL; + } + adev->snd_card = snd_card_num; + ALOGD("%s: Opened sound card:%d", __func__, snd_card_num); + break; + } + retry_num = 0; + snd_card_num++; + } + + if (snd_card_num >= MAX_SND_CARD) { + ALOGE("%s: Unable to find correct sound card, aborting.", __func__); + free(my_data); + return NULL; + } + + my_data->adev = adev; + my_data->fluence_in_spkr_mode = false; + my_data->fluence_in_voice_call = false; + my_data->fluence_in_voice_rec = false; + my_data->fluence_in_audio_rec = false; + my_data->external_spk_1 = false; + my_data->external_spk_2 = false; + my_data->external_mic = false; + my_data->fluence_type = FLUENCE_NONE; + my_data->fluence_mode = FLUENCE_ENDFIRE; + my_data->slowtalk = false; + my_data->hd_voice = false; + + property_get("ro.qc.sdk.audio.fluencetype", my_data->fluence_cap, ""); + if (!strncmp("fluencepro", my_data->fluence_cap, sizeof("fluencepro"))) { + my_data->fluence_type = FLUENCE_QUAD_MIC | FLUENCE_DUAL_MIC; + } else if (!strncmp("fluence", my_data->fluence_cap, sizeof("fluence"))) { + my_data->fluence_type = FLUENCE_DUAL_MIC; + } else { + my_data->fluence_type = FLUENCE_NONE; + } + + if (my_data->fluence_type != FLUENCE_NONE) { + property_get("persist.audio.fluence.voicecall",value,""); + if (!strncmp("true", value, sizeof("true"))) { + my_data->fluence_in_voice_call = true; + } + + property_get("persist.audio.fluence.voicerec",value,""); + if (!strncmp("true", value, sizeof("true"))) { + my_data->fluence_in_voice_rec = true; + } + + property_get("persist.audio.fluence.audiorec",value,""); + if (!strncmp("true", value, sizeof("true"))) { + my_data->fluence_in_audio_rec = true; + } + + property_get("persist.audio.fluence.speaker",value,""); + if (!strncmp("true", value, sizeof("true"))) { + my_data->fluence_in_spkr_mode = true; + } + + property_get("persist.audio.fluence.mode",value,""); + if (!strncmp("broadside", value, sizeof("broadside"))) { + my_data->fluence_mode = FLUENCE_BROADSIDE; + } + } + property_get("audio.ds1.metainfo.key",value,"0"); + key = atoi(value); + + my_data->voice_feature_set = VOICE_FEATURE_SET_DEFAULT; + my_data->acdb_handle = dlopen(LIB_ACDB_LOADER, RTLD_NOW); + if (my_data->acdb_handle == NULL) { + ALOGE("%s: DLOPEN failed for %s", __func__, LIB_ACDB_LOADER); + } else { + ALOGV("%s: DLOPEN successful for %s", __func__, LIB_ACDB_LOADER); + my_data->acdb_deallocate = (acdb_deallocate_t)dlsym(my_data->acdb_handle, + "acdb_loader_deallocate_ACDB"); + if (!my_data->acdb_deallocate) + ALOGE("%s: Could not find the symbol acdb_loader_deallocate_ACDB from %s", + __func__, LIB_ACDB_LOADER); + + my_data->acdb_send_audio_cal = (acdb_send_audio_cal_t)dlsym(my_data->acdb_handle, + "acdb_loader_send_audio_cal_v2"); + if (!my_data->acdb_send_audio_cal) + ALOGE("%s: Could not find the symbol acdb_send_audio_cal from %s", + __func__, LIB_ACDB_LOADER); + + my_data->acdb_send_voice_cal = (acdb_send_voice_cal_t)dlsym(my_data->acdb_handle, + "acdb_loader_send_voice_cal"); + if (!my_data->acdb_send_voice_cal) + ALOGE("%s: Could not find the symbol acdb_loader_send_voice_cal from %s", + __func__, LIB_ACDB_LOADER); + + my_data->acdb_reload_vocvoltable = (acdb_reload_vocvoltable_t)dlsym(my_data->acdb_handle, + "acdb_loader_reload_vocvoltable"); + if (!my_data->acdb_reload_vocvoltable) + ALOGE("%s: Could not find the symbol acdb_loader_reload_vocvoltable from %s", + __func__, LIB_ACDB_LOADER); + + my_data->acdb_get_default_app_type = (acdb_get_default_app_type_t)dlsym( + my_data->acdb_handle, + "acdb_loader_get_default_app_type"); + if (!my_data->acdb_get_default_app_type) + ALOGE("%s: Could not find the symbol acdb_get_default_app_type from %s", + __func__, LIB_ACDB_LOADER); + + my_data->acdb_init = (acdb_init_t)dlsym(my_data->acdb_handle, + "acdb_loader_init_v2"); + if (my_data->acdb_init == NULL) { + ALOGE("%s: dlsym error %s for acdb_loader_init_v2", __func__, dlerror()); + goto acdb_init_fail; + } + + cvd_version = calloc(1, MAX_CVD_VERSION_STRING_SIZE); + if (!cvd_version) + ALOGE("failed to allocate cvd_version"); + else + get_cvd_version(cvd_version, adev); + + my_data->acdb_init(snd_card_name, cvd_version, key); + if (cvd_version) + free(cvd_version); + } + +acdb_init_fail: + + set_platform_defaults(); + + /* Initialize ACDB ID's */ + if (my_data->is_i2s_ext_modem) + platform_info_init(PLATFORM_INFO_XML_PATH_I2S); + else + platform_info_init(PLATFORM_INFO_XML_PATH); + + /* If platform is apq8084 and baseband is MDM, load CSD Client specific + * symbols. Voice call is handled by MDM and apps processor talks to + * MDM through CSD Client + */ + property_get("ro.board.platform", platform, ""); + property_get("ro.baseband", baseband, ""); + if (!strncmp("apq8084", platform, sizeof("apq8084")) && + !strncmp("mdm", baseband, (sizeof("mdm")-1))) { + my_data->csd = open_csd_client(my_data->is_i2s_ext_modem); + } else { + my_data->csd = NULL; + } + + /* init usb */ + audio_extn_usb_init(adev); + /* update sound cards appropriately */ + audio_extn_usb_set_proxy_sound_card(adev->snd_card); + + /* init dap hal */ + audio_extn_dap_hal_init(adev->snd_card); + + /* Read one time ssr property */ + audio_extn_ssr_update_enabled(); + audio_extn_spkr_prot_init(adev); + + audio_extn_dolby_set_license(adev); + audio_hwdep_send_cal(my_data); + + /* init audio device arbitration */ + audio_extn_dev_arbi_init(); + + return my_data; +} + +void platform_deinit(void *platform) +{ + struct platform_data *my_data = (struct platform_data *)platform; + + hw_info_deinit(my_data->hw_info); + close_csd_client(my_data->csd); + + int32_t dev; + for (dev = 0; dev < SND_DEVICE_MAX; dev++) { + if (backend_table[dev]) { + free(backend_table[dev]); + backend_table[dev]= NULL; + } + } + + /* deinit audio device arbitration */ + audio_extn_dev_arbi_deinit(); + + free(platform); + /* deinit usb */ + audio_extn_usb_deinit(); + audio_extn_dap_hal_deinit(); +} + +const char *platform_get_snd_device_name(snd_device_t snd_device) +{ + if (snd_device >= SND_DEVICE_MIN && snd_device < SND_DEVICE_MAX) + return device_table[snd_device]; + else + return ""; +} + +int platform_get_snd_device_name_extn(void *platform, snd_device_t snd_device, + char *device_name) +{ + struct platform_data *my_data = (struct platform_data *)platform; + + if (snd_device >= SND_DEVICE_MIN && snd_device < SND_DEVICE_MAX) { + strlcpy(device_name, device_table[snd_device], DEVICE_NAME_MAX_SIZE); + hw_info_append_hw_type(my_data->hw_info, snd_device, device_name); + } else { + strlcpy(device_name, "", DEVICE_NAME_MAX_SIZE); + return -EINVAL; + } + + return 0; +} + +void platform_add_backend_name(char *mixer_path, snd_device_t snd_device) +{ + if ((snd_device < SND_DEVICE_MIN) || (snd_device >= SND_DEVICE_MAX)) { + ALOGE("%s: Invalid snd_device = %d", __func__, snd_device); + return; + } + + const char * suffix = backend_table[snd_device]; + + if (suffix != NULL) { + strlcat(mixer_path, " ", MIXER_PATH_MAX_LENGTH); + strlcat(mixer_path, suffix, MIXER_PATH_MAX_LENGTH); + } +} + +int platform_get_pcm_device_id(audio_usecase_t usecase, int device_type) +{ + int device_id; + if (device_type == PCM_PLAYBACK) + device_id = pcm_device_table[usecase][0]; + else + device_id = pcm_device_table[usecase][1]; + return device_id; +} + +static int find_index(struct name_to_index * table, int32_t len, const char * name) +{ + int ret = 0; + int32_t i; + + if (table == NULL) { + ALOGE("%s: table is NULL", __func__); + ret = -ENODEV; + goto done; + } + + if (name == NULL) { + ALOGE("null key"); + ret = -ENODEV; + goto done; + } + + for (i=0; i < len; i++) { + const char* tn = table[i].name; + size_t len = strlen(tn); + if (strncmp(tn, name, len) == 0) { + if (strlen(name) != len) { + continue; // substring + } + ret = table[i].index; + goto done; + } + } + ALOGE("%s: Could not find index for name = %s", + __func__, name); + ret = -ENODEV; +done: + return ret; +} + +int platform_set_fluence_type(void *platform, char *value) +{ + int ret = 0; + int fluence_type = FLUENCE_NONE; + int fluence_flag = NONE_FLAG; + struct platform_data *my_data = (struct platform_data *)platform; + struct audio_device *adev = my_data->adev; + + ALOGV("%s: fluence type:%d", __func__, my_data->fluence_type); + + /* only dual mic turn on and off is supported as of now through setparameters */ + if (!strncmp(AUDIO_PARAMETER_VALUE_DUALMIC,value, sizeof(AUDIO_PARAMETER_VALUE_DUALMIC))) { + if (!strncmp("fluencepro", my_data->fluence_cap, sizeof("fluencepro")) || + !strncmp("fluence", my_data->fluence_cap, sizeof("fluence"))) { + ALOGV("fluence dualmic feature enabled \n"); + fluence_type = FLUENCE_DUAL_MIC; + fluence_flag = DMIC_FLAG; + } else { + ALOGE("%s: Failed to set DUALMIC", __func__); + ret = -1; + goto done; + } + } else if (!strncmp(AUDIO_PARAMETER_KEY_NO_FLUENCE, value, sizeof(AUDIO_PARAMETER_KEY_NO_FLUENCE))) { + ALOGV("fluence disabled"); + fluence_type = FLUENCE_NONE; + } else { + ALOGE("Invalid fluence value : %s",value); + ret = -1; + goto done; + } + + if (fluence_type != my_data->fluence_type) { + ALOGV("%s: Updating fluence_type to :%d", __func__, fluence_type); + my_data->fluence_type = fluence_type; + adev->acdb_settings = (adev->acdb_settings & FLUENCE_MODE_CLEAR) | fluence_flag; + } +done: + return ret; +} + +int platform_get_fluence_type(void *platform, char *value, uint32_t len) +{ + int ret = 0; + struct platform_data *my_data = (struct platform_data *)platform; + + if (my_data->fluence_type == FLUENCE_QUAD_MIC) { + strlcpy(value, "quadmic", len); + } else if (my_data->fluence_type == FLUENCE_DUAL_MIC) { + strlcpy(value, "dualmic", len); + } else if (my_data->fluence_type == FLUENCE_NONE) { + strlcpy(value, "none", len); + } else + ret = -1; + + return ret; +} + +int platform_get_snd_device_index(char *device_name) +{ + return find_index(snd_device_name_index, SND_DEVICE_MAX, device_name); +} + +int platform_get_usecase_index(const char *usecase_name) +{ + return find_index(usecase_name_index, AUDIO_USECASE_MAX, usecase_name); +} + +int platform_set_snd_device_acdb_id(snd_device_t snd_device, unsigned int acdb_id) +{ + int ret = 0; + + if ((snd_device < SND_DEVICE_MIN) || (snd_device >= SND_DEVICE_MAX)) { + ALOGE("%s: Invalid snd_device = %d", + __func__, snd_device); + ret = -EINVAL; + goto done; + } + + acdb_device_table[snd_device] = acdb_id; +done: + return ret; +} + +int platform_get_default_app_type(void *platform) +{ + struct platform_data *my_data = (struct platform_data *)platform; + + if (my_data->acdb_get_default_app_type) + return my_data->acdb_get_default_app_type(); + else + return DEFAULT_APP_TYPE; +} + +int platform_get_snd_device_acdb_id(snd_device_t snd_device) +{ + if ((snd_device < SND_DEVICE_MIN) || (snd_device >= SND_DEVICE_MAX)) { + ALOGE("%s: Invalid snd_device = %d", __func__, snd_device); + return -EINVAL; + } + return acdb_device_table[snd_device]; +} + +int platform_send_audio_calibration(void *platform, struct audio_usecase *usecase, + int app_type, int sample_rate) +{ + struct platform_data *my_data = (struct platform_data *)platform; + int acdb_dev_id, acdb_dev_type; + struct audio_device *adev = my_data->adev; + int snd_device = SND_DEVICE_OUT_SPEAKER; + + if (usecase->type == PCM_PLAYBACK) + snd_device = platform_get_output_snd_device(adev->platform, + usecase->stream.out->devices); + else if ((usecase->type == PCM_HFP_CALL) || (usecase->type == PCM_CAPTURE)) + snd_device = platform_get_input_snd_device(adev->platform, + adev->primary_output->devices); + acdb_dev_id = acdb_device_table[audio_extn_get_spkr_prot_snd_device(snd_device)]; + if (acdb_dev_id < 0) { + ALOGE("%s: Could not find acdb id for device(%d)", + __func__, snd_device); + return -EINVAL; + } + if (my_data->acdb_send_audio_cal) { + ALOGV("%s: sending audio calibration for snd_device(%d) acdb_id(%d)", + __func__, snd_device, acdb_dev_id); + if (snd_device >= SND_DEVICE_OUT_BEGIN && + snd_device < SND_DEVICE_OUT_END) + acdb_dev_type = ACDB_DEV_TYPE_OUT; + else + acdb_dev_type = ACDB_DEV_TYPE_IN; + my_data->acdb_send_audio_cal(acdb_dev_id, acdb_dev_type, app_type, + sample_rate); + } + return 0; +} + +int platform_switch_voice_call_device_pre(void *platform) +{ + struct platform_data *my_data = (struct platform_data *)platform; + int ret = 0; + + if (my_data->csd != NULL && + my_data->adev->mode == AUDIO_MODE_IN_CALL) { + /* This must be called before disabling mixer controls on APQ side */ + ret = my_data->csd->disable_device(); + if (ret < 0) { + ALOGE("%s: csd_client_disable_device, failed, error %d", + __func__, ret); + } + } + return ret; +} + +int platform_switch_voice_call_enable_device_config(void *platform, + snd_device_t out_snd_device, + snd_device_t in_snd_device) +{ + struct platform_data *my_data = (struct platform_data *)platform; + int acdb_rx_id, acdb_tx_id; + int ret = 0; + + if (my_data->csd == NULL) + return ret; + + if (out_snd_device == SND_DEVICE_OUT_VOICE_SPEAKER && + audio_extn_spkr_prot_is_enabled()) + acdb_rx_id = acdb_device_table[SND_DEVICE_OUT_SPEAKER_PROTECTED]; + else + acdb_rx_id = acdb_device_table[out_snd_device]; + + acdb_tx_id = acdb_device_table[in_snd_device]; + + if (acdb_rx_id > 0 && acdb_tx_id > 0) { + ret = my_data->csd->enable_device_config(acdb_rx_id, acdb_tx_id); + if (ret < 0) { + ALOGE("%s: csd_enable_device_config, failed, error %d", + __func__, ret); + } + } else { + ALOGE("%s: Incorrect ACDB IDs (rx: %d tx: %d)", __func__, + acdb_rx_id, acdb_tx_id); + } + + return ret; +} + +int platform_switch_voice_call_device_post(void *platform, + snd_device_t out_snd_device, + snd_device_t in_snd_device) +{ + struct platform_data *my_data = (struct platform_data *)platform; + int acdb_rx_id, acdb_tx_id; + + if (my_data->acdb_send_voice_cal == NULL) { + ALOGE("%s: dlsym error for acdb_send_voice_call", __func__); + } else { + if (out_snd_device == SND_DEVICE_OUT_VOICE_SPEAKER && + audio_extn_spkr_prot_is_enabled()) + out_snd_device = SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED; + + acdb_rx_id = acdb_device_table[out_snd_device]; + acdb_tx_id = acdb_device_table[in_snd_device]; + + if (acdb_rx_id > 0 && acdb_tx_id > 0) + my_data->acdb_send_voice_cal(acdb_rx_id, acdb_tx_id); + else + ALOGE("%s: Incorrect ACDB IDs (rx: %d tx: %d)", __func__, + acdb_rx_id, acdb_tx_id); + } + + return 0; +} + +int platform_switch_voice_call_usecase_route_post(void *platform, + snd_device_t out_snd_device, + snd_device_t in_snd_device) +{ + struct platform_data *my_data = (struct platform_data *)platform; + int acdb_rx_id, acdb_tx_id; + int ret = 0; + + if (my_data->csd == NULL) + return ret; + + if (out_snd_device == SND_DEVICE_OUT_VOICE_SPEAKER && + audio_extn_spkr_prot_is_enabled()) + acdb_rx_id = acdb_device_table[SND_DEVICE_OUT_SPEAKER_PROTECTED]; + else + acdb_rx_id = acdb_device_table[out_snd_device]; + + acdb_tx_id = acdb_device_table[in_snd_device]; + + if (acdb_rx_id > 0 && acdb_tx_id > 0) { + ret = my_data->csd->enable_device(acdb_rx_id, acdb_tx_id, + my_data->adev->acdb_settings); + if (ret < 0) { + ALOGE("%s: csd_enable_device, failed, error %d", __func__, ret); + } + } else { + ALOGE("%s: Incorrect ACDB IDs (rx: %d tx: %d)", __func__, + acdb_rx_id, acdb_tx_id); + } + + return ret; +} + +int platform_start_voice_call(void *platform, uint32_t vsid) +{ + struct platform_data *my_data = (struct platform_data *)platform; + int ret = 0; + + if (my_data->csd != NULL) { + ret = my_data->csd->start_voice(vsid); + if (ret < 0) { + ALOGE("%s: csd_start_voice error %d\n", __func__, ret); + } + } + return ret; +} + +int platform_stop_voice_call(void *platform, uint32_t vsid) +{ + struct platform_data *my_data = (struct platform_data *)platform; + int ret = 0; + + if (my_data->csd != NULL) { + ret = my_data->csd->stop_voice(vsid); + if (ret < 0) { + ALOGE("%s: csd_stop_voice error %d\n", __func__, ret); + } + } + return ret; +} + +int platform_get_sample_rate(void *platform, uint32_t *rate) +{ + struct platform_data *my_data = (struct platform_data *)platform; + int ret = 0; + + if ((my_data->csd != NULL) && my_data->is_i2s_ext_modem) { + ret = my_data->csd->get_sample_rate(rate); + if (ret < 0) { + ALOGE("%s: csd_get_sample_rate error %d\n", __func__, ret); + } + } + return ret; +} + +int platform_set_voice_volume(void *platform, int volume) +{ + struct platform_data *my_data = (struct platform_data *)platform; + struct audio_device *adev = my_data->adev; + struct mixer_ctl *ctl; + const char *mixer_ctl_name = "Voice Rx Gain"; + int vol_index = 0, ret = 0; + uint32_t set_values[ ] = {0, + ALL_SESSION_VSID, + DEFAULT_VOLUME_RAMP_DURATION_MS}; + + // Voice volume levels are mapped to adsp volume levels as follows. + // 100 -> 5, 80 -> 4, 60 -> 3, 40 -> 2, 20 -> 1 0 -> 0 + // But this values don't changed in kernel. So, below change is need. + vol_index = (int)percent_to_index(volume, MIN_VOL_INDEX, MAX_VOL_INDEX); + set_values[0] = vol_index; + + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + return -EINVAL; + } + ALOGV("Setting voice volume index: %d", set_values[0]); + mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values)); + + if (my_data->csd != NULL) { + ret = my_data->csd->volume(ALL_SESSION_VSID, volume, + DEFAULT_VOLUME_RAMP_DURATION_MS); + if (ret < 0) { + ALOGE("%s: csd_volume error %d", __func__, ret); + } + } + return ret; +} + +int platform_set_mic_mute(void *platform, bool state) +{ + struct platform_data *my_data = (struct platform_data *)platform; + struct audio_device *adev = my_data->adev; + struct mixer_ctl *ctl; + const char *mixer_ctl_name = "Voice Tx Mute"; + int ret = 0; + uint32_t set_values[ ] = {0, + ALL_SESSION_VSID, + DEFAULT_MUTE_RAMP_DURATION_MS}; + + set_values[0] = state; + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + return -EINVAL; + } + ALOGV("Setting voice mute state: %d", state); + mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values)); + + if (my_data->csd != NULL) { + ret = my_data->csd->mic_mute(ALL_SESSION_VSID, state, + DEFAULT_MUTE_RAMP_DURATION_MS); + if (ret < 0) { + ALOGE("%s: csd_mic_mute error %d", __func__, ret); + } + } + return ret; +} + +int platform_set_device_mute(void *platform, bool state, char *dir) +{ + struct platform_data *my_data = (struct platform_data *)platform; + struct audio_device *adev = my_data->adev; + struct mixer_ctl *ctl; + char *mixer_ctl_name = NULL; + int ret = 0; + uint32_t set_values[ ] = {0, + ALL_SESSION_VSID, + 0}; + if(dir == NULL) { + ALOGE("%s: Invalid direction:%s", __func__, dir); + return -EINVAL; + } + + if (!strncmp("rx", dir, sizeof("rx"))) { + mixer_ctl_name = "Voice Rx Device Mute"; + } else if (!strncmp("tx", dir, sizeof("tx"))) { + mixer_ctl_name = "Voice Tx Device Mute"; + } else { + return -EINVAL; + } + + set_values[0] = state; + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + return -EINVAL; + } + + ALOGV("%s: Setting device mute state: %d, mixer ctrl:%s", + __func__,state, mixer_ctl_name); + mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values)); + + return ret; +} + +snd_device_t platform_get_output_snd_device(void *platform, audio_devices_t devices) +{ + struct platform_data *my_data = (struct platform_data *)platform; + struct audio_device *adev = my_data->adev; + audio_mode_t mode = adev->mode; + snd_device_t snd_device = SND_DEVICE_NONE; + + audio_channel_mask_t channel_mask = (adev->active_input == NULL) ? + AUDIO_CHANNEL_IN_MONO : adev->active_input->channel_mask; + int channel_count = popcount(channel_mask); + + ALOGV("%s: enter: output devices(%#x)", __func__, devices); + if (devices == AUDIO_DEVICE_NONE || + devices & AUDIO_DEVICE_BIT_IN) { + ALOGV("%s: Invalid output devices (%#x)", __func__, devices); + goto exit; + } + + if (popcount(devices) == 2) { + if (devices == (AUDIO_DEVICE_OUT_WIRED_HEADPHONE | + AUDIO_DEVICE_OUT_SPEAKER)) { + if (my_data->external_spk_1) + snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_EXTERNAL_1; + else if (my_data->external_spk_2) + snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_EXTERNAL_2; + else + snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES; + } else if (devices == (AUDIO_DEVICE_OUT_WIRED_HEADSET | + AUDIO_DEVICE_OUT_SPEAKER)) { + if (audio_extn_get_anc_enabled()) + snd_device = SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET; + else if (my_data->external_spk_1) + snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_EXTERNAL_1; + else if (my_data->external_spk_2) + snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_EXTERNAL_2; + else + snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES; + } else if (devices == (AUDIO_DEVICE_OUT_AUX_DIGITAL | + AUDIO_DEVICE_OUT_SPEAKER)) { + snd_device = SND_DEVICE_OUT_SPEAKER_AND_HDMI; + } else if (devices == (AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET | + AUDIO_DEVICE_OUT_SPEAKER)) { + snd_device = SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET; + } else { + ALOGE("%s: Invalid combo device(%#x)", __func__, devices); + goto exit; + } + if (snd_device != SND_DEVICE_NONE) { + goto exit; + } + } + + if (popcount(devices) != 1) { + ALOGE("%s: Invalid output devices(%#x)", __func__, devices); + goto exit; + } + + if ((mode == AUDIO_MODE_IN_CALL) || + voice_extn_compress_voip_is_active(adev)) { + if (devices & AUDIO_DEVICE_OUT_WIRED_HEADPHONE || + devices & AUDIO_DEVICE_OUT_WIRED_HEADSET) { + if ((adev->voice.tty_mode != TTY_MODE_OFF) && + !voice_extn_compress_voip_is_active(adev)) { + switch (adev->voice.tty_mode) { + case TTY_MODE_FULL: + snd_device = SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES; + break; + case TTY_MODE_VCO: + snd_device = SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES; + break; + case TTY_MODE_HCO: + snd_device = SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET; + break; + default: + ALOGE("%s: Invalid TTY mode (%#x)", + __func__, adev->voice.tty_mode); + } + } else if (audio_extn_get_anc_enabled()) { + if (audio_extn_should_use_fb_anc()) + snd_device = SND_DEVICE_OUT_VOICE_ANC_FB_HEADSET; + else + snd_device = SND_DEVICE_OUT_VOICE_ANC_HEADSET; + } else { + snd_device = SND_DEVICE_OUT_VOICE_HEADPHONES; + } + } else if (devices & AUDIO_DEVICE_OUT_ALL_SCO) { + if (adev->bt_wb_speech_enabled) + snd_device = SND_DEVICE_OUT_BT_SCO_WB; + else + snd_device = SND_DEVICE_OUT_BT_SCO; + } else if (devices & AUDIO_DEVICE_OUT_SPEAKER) { + snd_device = SND_DEVICE_OUT_VOICE_SPEAKER; + } else if (devices & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET || + devices & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET) { + snd_device = SND_DEVICE_OUT_USB_HEADSET; + } else if (devices & AUDIO_DEVICE_OUT_FM_TX) { + snd_device = SND_DEVICE_OUT_TRANSMISSION_FM; + } else if (devices & AUDIO_DEVICE_OUT_EARPIECE) { + if (audio_extn_should_use_handset_anc(channel_count)) + snd_device = SND_DEVICE_OUT_ANC_HANDSET; + else + snd_device = SND_DEVICE_OUT_VOICE_HANDSET; + } else if (devices & AUDIO_DEVICE_OUT_TELEPHONY_TX) + snd_device = SND_DEVICE_OUT_VOICE_TX; + + if (snd_device != SND_DEVICE_NONE) { + goto exit; + } + } + + if (devices & AUDIO_DEVICE_OUT_WIRED_HEADPHONE || + devices & AUDIO_DEVICE_OUT_WIRED_HEADSET) { + if (devices & AUDIO_DEVICE_OUT_WIRED_HEADSET + && audio_extn_get_anc_enabled()) { + if (audio_extn_should_use_fb_anc()) + snd_device = SND_DEVICE_OUT_ANC_FB_HEADSET; + else + snd_device = SND_DEVICE_OUT_ANC_HEADSET; + } else + snd_device = SND_DEVICE_OUT_HEADPHONES; + } else if (devices & AUDIO_DEVICE_OUT_SPEAKER) { + if (my_data->external_spk_1) + snd_device = SND_DEVICE_OUT_SPEAKER_EXTERNAL_1; + else if (my_data->external_spk_2) + snd_device = SND_DEVICE_OUT_SPEAKER_EXTERNAL_2; + else if (adev->speaker_lr_swap) + snd_device = SND_DEVICE_OUT_SPEAKER_REVERSE; + else + snd_device = SND_DEVICE_OUT_SPEAKER; + } else if (devices & AUDIO_DEVICE_OUT_ALL_SCO) { + if (adev->bt_wb_speech_enabled) + snd_device = SND_DEVICE_OUT_BT_SCO_WB; + else + snd_device = SND_DEVICE_OUT_BT_SCO; + } else if (devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) { + snd_device = SND_DEVICE_OUT_HDMI ; + } else if (devices & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET || + devices & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET) { + ALOGD("%s: setting USB hadset channel capability(2) for Proxy", __func__); + audio_extn_set_afe_proxy_channel_mixer(adev, 2); + snd_device = SND_DEVICE_OUT_USB_HEADSET; + } else if (devices & AUDIO_DEVICE_OUT_FM_TX) { + snd_device = SND_DEVICE_OUT_TRANSMISSION_FM; + } else if (devices & AUDIO_DEVICE_OUT_EARPIECE) { + snd_device = SND_DEVICE_OUT_HANDSET; + } else if (devices & AUDIO_DEVICE_OUT_PROXY) { + channel_count = audio_extn_get_afe_proxy_channel_count(); + ALOGD("%s: setting sink capability(%d) for Proxy", __func__, channel_count); + audio_extn_set_afe_proxy_channel_mixer(adev, channel_count); + snd_device = SND_DEVICE_OUT_AFE_PROXY; + } else { + ALOGE("%s: Unknown device(s) %#x", __func__, devices); + } +exit: + ALOGV("%s: exit: snd_device(%s)", __func__, device_table[snd_device]); + return snd_device; +} + +snd_device_t platform_get_input_snd_device(void *platform, audio_devices_t out_device) +{ + struct platform_data *my_data = (struct platform_data *)platform; + struct audio_device *adev = my_data->adev; + audio_source_t source = (adev->active_input == NULL) ? + AUDIO_SOURCE_DEFAULT : adev->active_input->source; + + audio_mode_t mode = adev->mode; + audio_devices_t in_device = ((adev->active_input == NULL) ? + AUDIO_DEVICE_NONE : adev->active_input->device) + & ~AUDIO_DEVICE_BIT_IN; + audio_channel_mask_t channel_mask = (adev->active_input == NULL) ? + AUDIO_CHANNEL_IN_MONO : adev->active_input->channel_mask; + snd_device_t snd_device = SND_DEVICE_NONE; + int channel_count = popcount(channel_mask); + + ALOGV("%s: enter: out_device(%#x) in_device(%#x)", + __func__, out_device, in_device); + if (my_data->external_mic) { + if (((out_device != AUDIO_DEVICE_NONE) && (mode == AUDIO_MODE_IN_CALL)) || + voice_extn_compress_voip_is_active(adev) || audio_extn_hfp_is_active(adev)) { + if (out_device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE || + out_device & AUDIO_DEVICE_OUT_EARPIECE || + out_device & AUDIO_DEVICE_OUT_SPEAKER ) + snd_device = SND_DEVICE_IN_HANDSET_MIC_EXTERNAL; + } else if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC || + in_device & AUDIO_DEVICE_IN_BACK_MIC) { + snd_device = SND_DEVICE_IN_HANDSET_MIC_EXTERNAL; + } + } + + if (snd_device != AUDIO_DEVICE_NONE) + goto exit; + + if ((out_device != AUDIO_DEVICE_NONE) && ((mode == AUDIO_MODE_IN_CALL) || + voice_extn_compress_voip_is_active(adev) || audio_extn_hfp_is_active(adev))) { + if ((adev->voice.tty_mode != TTY_MODE_OFF) && + !voice_extn_compress_voip_is_active(adev)) { + if (out_device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE || + out_device & AUDIO_DEVICE_OUT_WIRED_HEADSET) { + switch (adev->voice.tty_mode) { + case TTY_MODE_FULL: + snd_device = SND_DEVICE_IN_VOICE_TTY_FULL_HEADSET_MIC; + break; + case TTY_MODE_VCO: + snd_device = SND_DEVICE_IN_VOICE_TTY_VCO_HANDSET_MIC; + break; + case TTY_MODE_HCO: + snd_device = SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC; + break; + default: + ALOGE("%s: Invalid TTY mode (%#x)", __func__, adev->voice.tty_mode); + } + goto exit; + } + } + if (out_device & AUDIO_DEVICE_OUT_EARPIECE || + out_device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE) { + if (out_device & AUDIO_DEVICE_OUT_EARPIECE && + audio_extn_should_use_handset_anc(channel_count)) { + snd_device = SND_DEVICE_IN_AANC_HANDSET_MIC; + adev->acdb_settings |= ANC_FLAG; + } else if (my_data->fluence_type == FLUENCE_NONE || + my_data->fluence_in_voice_call == false) { + snd_device = SND_DEVICE_IN_HANDSET_MIC; + if (audio_extn_hfp_is_active(adev)) + platform_set_echo_reference(adev->platform, true); + } else { + snd_device = SND_DEVICE_IN_VOICE_DMIC; + } + } else if (out_device & AUDIO_DEVICE_OUT_WIRED_HEADSET) { + snd_device = SND_DEVICE_IN_VOICE_HEADSET_MIC; + if (audio_extn_hfp_is_active(adev)) + platform_set_echo_reference(adev->platform, true); + } else if (out_device & AUDIO_DEVICE_OUT_ALL_SCO) { + if (adev->bt_wb_speech_enabled) { + if (adev->bluetooth_nrec) + snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB_NREC; + else + snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB; + } else { + if (adev->bluetooth_nrec) + snd_device = SND_DEVICE_IN_BT_SCO_MIC_NREC; + else + snd_device = SND_DEVICE_IN_BT_SCO_MIC; + } + } else if (out_device & AUDIO_DEVICE_OUT_SPEAKER) { + if (my_data->fluence_type != FLUENCE_NONE && + my_data->fluence_in_voice_call && + my_data->fluence_in_spkr_mode) { + if(my_data->fluence_type & FLUENCE_QUAD_MIC) { + snd_device = SND_DEVICE_IN_VOICE_SPEAKER_QMIC; + } else { + if (my_data->fluence_mode == FLUENCE_BROADSIDE) + snd_device = SND_DEVICE_IN_VOICE_SPEAKER_DMIC_BROADSIDE; + else + snd_device = SND_DEVICE_IN_VOICE_SPEAKER_DMIC; + } + } else { + snd_device = SND_DEVICE_IN_VOICE_SPEAKER_MIC; + if (audio_extn_hfp_is_active(adev)) + platform_set_echo_reference(adev->platform, true); + } + } else if (out_device & AUDIO_DEVICE_OUT_TELEPHONY_TX) + snd_device = SND_DEVICE_IN_VOICE_RX; + } else if (source == AUDIO_SOURCE_CAMCORDER) { + if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC || + in_device & AUDIO_DEVICE_IN_BACK_MIC) { + snd_device = SND_DEVICE_IN_CAMCORDER_MIC; + } + } else if (source == AUDIO_SOURCE_VOICE_RECOGNITION) { + if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC) { + if (channel_count == 2) { + snd_device = SND_DEVICE_IN_VOICE_REC_DMIC_STEREO; + } else if (adev->active_input->enable_ns) + snd_device = SND_DEVICE_IN_VOICE_REC_MIC_NS; + else if (my_data->fluence_type != FLUENCE_NONE && + my_data->fluence_in_voice_rec) { + snd_device = SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE; + } else { + snd_device = SND_DEVICE_IN_VOICE_REC_MIC; + } + } + } else if (source == AUDIO_SOURCE_VOICE_COMMUNICATION) { + if (out_device & AUDIO_DEVICE_OUT_SPEAKER) + in_device = AUDIO_DEVICE_IN_BACK_MIC; + if (adev->active_input) { + if (adev->active_input->enable_aec && + adev->active_input->enable_ns) { + if (in_device & AUDIO_DEVICE_IN_BACK_MIC) { + if (my_data->fluence_type & FLUENCE_DUAL_MIC && + my_data->fluence_in_spkr_mode) { + if (my_data->fluence_mode == FLUENCE_BROADSIDE) + snd_device = SND_DEVICE_IN_SPEAKER_DMIC_AEC_NS_BROADSIDE; + else + snd_device = SND_DEVICE_IN_SPEAKER_DMIC_AEC_NS; + } else + snd_device = SND_DEVICE_IN_SPEAKER_MIC_AEC_NS; + } else if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC) { + if (my_data->fluence_type & FLUENCE_DUAL_MIC) { + snd_device = SND_DEVICE_IN_HANDSET_DMIC_AEC_NS; + } else + snd_device = SND_DEVICE_IN_HANDSET_MIC_AEC_NS; + } else if (in_device & AUDIO_DEVICE_IN_WIRED_HEADSET) { + snd_device = SND_DEVICE_IN_HEADSET_MIC_FLUENCE; + } + platform_set_echo_reference(adev->platform, true); + } else if (adev->active_input->enable_aec) { + if (in_device & AUDIO_DEVICE_IN_BACK_MIC) { + if (my_data->fluence_type & FLUENCE_DUAL_MIC && + my_data->fluence_in_spkr_mode) { + if (my_data->fluence_mode == FLUENCE_BROADSIDE) + snd_device = SND_DEVICE_IN_SPEAKER_DMIC_AEC_BROADSIDE; + else + snd_device = SND_DEVICE_IN_SPEAKER_DMIC_AEC; + } else + snd_device = SND_DEVICE_IN_SPEAKER_MIC_AEC; + } else if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC) { + if (my_data->fluence_type & FLUENCE_DUAL_MIC) { + snd_device = SND_DEVICE_IN_HANDSET_DMIC_AEC; + } else + snd_device = SND_DEVICE_IN_HANDSET_MIC_AEC; + } else if (in_device & AUDIO_DEVICE_IN_WIRED_HEADSET) { + snd_device = SND_DEVICE_IN_HEADSET_MIC_FLUENCE; + } + platform_set_echo_reference(adev->platform, true); + } else if (adev->active_input->enable_ns) { + if (in_device & AUDIO_DEVICE_IN_BACK_MIC) { + if (my_data->fluence_type & FLUENCE_DUAL_MIC && + my_data->fluence_in_spkr_mode) { + if (my_data->fluence_mode == FLUENCE_BROADSIDE) + snd_device = SND_DEVICE_IN_SPEAKER_DMIC_NS_BROADSIDE; + else + snd_device = SND_DEVICE_IN_SPEAKER_DMIC_NS; + } else + snd_device = SND_DEVICE_IN_SPEAKER_MIC_NS; + } else if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC) { + if (my_data->fluence_type & FLUENCE_DUAL_MIC) { + snd_device = SND_DEVICE_IN_HANDSET_DMIC_NS; + } else + snd_device = SND_DEVICE_IN_HANDSET_MIC_NS; + } else if (in_device & AUDIO_DEVICE_IN_WIRED_HEADSET) { + snd_device = SND_DEVICE_IN_HEADSET_MIC_FLUENCE; + } + platform_set_echo_reference(adev->platform, false); + } else + platform_set_echo_reference(adev->platform, false); + } + } else if (source == AUDIO_SOURCE_MIC) { + if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC && + channel_count == 1 ) { + if(my_data->fluence_type & FLUENCE_DUAL_MIC && + my_data->fluence_in_audio_rec) { + snd_device = SND_DEVICE_IN_HANDSET_DMIC; + platform_set_echo_reference(adev->platform, true); + } + } + } else if (source == AUDIO_SOURCE_FM_RX || + source == AUDIO_SOURCE_FM_RX_A2DP) { + snd_device = SND_DEVICE_IN_CAPTURE_FM; + } else if (source == AUDIO_SOURCE_DEFAULT) { + goto exit; + } + + + if (snd_device != SND_DEVICE_NONE) { + goto exit; + } + + if (in_device != AUDIO_DEVICE_NONE && + !(in_device & AUDIO_DEVICE_IN_VOICE_CALL) && + !(in_device & AUDIO_DEVICE_IN_COMMUNICATION)) { + if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC) { + if (audio_extn_ssr_get_enabled() && channel_count == 6) + snd_device = SND_DEVICE_IN_QUAD_MIC; + else if (my_data->fluence_type & (FLUENCE_DUAL_MIC | FLUENCE_QUAD_MIC) && + channel_count == 2) + snd_device = SND_DEVICE_IN_HANDSET_STEREO_DMIC; + else + snd_device = SND_DEVICE_IN_HANDSET_MIC; + } else if (in_device & AUDIO_DEVICE_IN_BACK_MIC) { + snd_device = SND_DEVICE_IN_SPEAKER_MIC; + } else if (in_device & AUDIO_DEVICE_IN_WIRED_HEADSET) { + snd_device = SND_DEVICE_IN_HEADSET_MIC; + } else if (in_device & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) { + if (adev->bt_wb_speech_enabled) { + if (adev->bluetooth_nrec) + snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB_NREC; + else + snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB; + } else { + if (adev->bluetooth_nrec) + snd_device = SND_DEVICE_IN_BT_SCO_MIC_NREC; + else + snd_device = SND_DEVICE_IN_BT_SCO_MIC; + } + } else if (in_device & AUDIO_DEVICE_IN_AUX_DIGITAL) { + snd_device = SND_DEVICE_IN_HDMI_MIC; + } else if (in_device & AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET || + in_device & AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET) { + snd_device = SND_DEVICE_IN_USB_HEADSET_MIC; + } else if (in_device & AUDIO_DEVICE_IN_FM_RX) { + snd_device = SND_DEVICE_IN_CAPTURE_FM; + } else { + ALOGE("%s: Unknown input device(s) %#x", __func__, in_device); + ALOGW("%s: Using default handset-mic", __func__); + snd_device = SND_DEVICE_IN_HANDSET_MIC; + } + } else { + if (out_device & AUDIO_DEVICE_OUT_EARPIECE) { + snd_device = SND_DEVICE_IN_HANDSET_MIC; + } else if (out_device & AUDIO_DEVICE_OUT_WIRED_HEADSET) { + snd_device = SND_DEVICE_IN_HEADSET_MIC; + } else if (out_device & AUDIO_DEVICE_OUT_SPEAKER) { + if (channel_count == 2) + snd_device = SND_DEVICE_IN_SPEAKER_STEREO_DMIC; + else + snd_device = SND_DEVICE_IN_SPEAKER_MIC; + } else if (out_device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE) { + snd_device = SND_DEVICE_IN_HANDSET_MIC; + } else if (out_device & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET) { + if (adev->bt_wb_speech_enabled) { + if (adev->bluetooth_nrec) + snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB_NREC; + else + snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB; + } else { + if (adev->bluetooth_nrec) + snd_device = SND_DEVICE_IN_BT_SCO_MIC_NREC; + else + snd_device = SND_DEVICE_IN_BT_SCO_MIC; + } + } else if (out_device & AUDIO_DEVICE_OUT_AUX_DIGITAL) { + snd_device = SND_DEVICE_IN_HDMI_MIC; + } else if (out_device & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET || + out_device & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET) { + snd_device = SND_DEVICE_IN_USB_HEADSET_MIC; + } else { + ALOGE("%s: Unknown output device(s) %#x", __func__, out_device); + ALOGW("%s: Using default handset-mic", __func__); + snd_device = SND_DEVICE_IN_HANDSET_MIC; + } + } +exit: + ALOGV("%s: exit: in_snd_device(%s)", __func__, device_table[snd_device]); + return snd_device; +} + +int platform_set_hdmi_channels(void *platform, int channel_count) +{ + struct platform_data *my_data = (struct platform_data *)platform; + struct audio_device *adev = my_data->adev; + struct mixer_ctl *ctl; + const char *channel_cnt_str = NULL; + const char *mixer_ctl_name = "HDMI_RX Channels"; + switch (channel_count) { + case 8: + channel_cnt_str = "Eight"; break; + case 7: + channel_cnt_str = "Seven"; break; + case 6: + channel_cnt_str = "Six"; break; + case 5: + channel_cnt_str = "Five"; break; + case 4: + channel_cnt_str = "Four"; break; + case 3: + channel_cnt_str = "Three"; break; + default: + channel_cnt_str = "Two"; break; + } + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + return -EINVAL; + } + ALOGV("HDMI channel count: %s", channel_cnt_str); + mixer_ctl_set_enum_by_string(ctl, channel_cnt_str); + return 0; +} + +int platform_edid_get_max_channels(void *platform) +{ + struct platform_data *my_data = (struct platform_data *)platform; + struct audio_device *adev = my_data->adev; + char block[MAX_SAD_BLOCKS * SAD_BLOCK_SIZE]; + char *sad = block; + int num_audio_blocks; + int channel_count; + int max_channels = 0; + int i, ret, count; + + struct mixer_ctl *ctl; + + ctl = mixer_get_ctl_by_name(adev->mixer, AUDIO_DATA_BLOCK_MIXER_CTL); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, AUDIO_DATA_BLOCK_MIXER_CTL); + return 0; + } + + mixer_ctl_update(ctl); + + count = mixer_ctl_get_num_values(ctl); + + /* Read SAD blocks, clamping the maximum size for safety */ + if (count > (int)sizeof(block)) + count = (int)sizeof(block); + + ret = mixer_ctl_get_array(ctl, block, count); + if (ret != 0) { + ALOGE("%s: mixer_ctl_get_array() failed to get EDID info", __func__); + return 0; + } + + /* Calculate the number of SAD blocks */ + num_audio_blocks = count / SAD_BLOCK_SIZE; + + for (i = 0; i < num_audio_blocks; i++) { + /* Only consider LPCM blocks */ + if ((sad[0] >> 3) != EDID_FORMAT_LPCM) { + sad += 3; + continue; + } + + channel_count = (sad[0] & 0x7) + 1; + if (channel_count > max_channels) + max_channels = channel_count; + + /* Advance to next block */ + sad += 3; + } + + return max_channels; +} + +static int platform_set_slowtalk(struct platform_data *my_data, bool state) +{ + int ret = 0; + struct audio_device *adev = my_data->adev; + struct mixer_ctl *ctl; + const char *mixer_ctl_name = "Slowtalk Enable"; + uint32_t set_values[ ] = {0, + ALL_SESSION_VSID}; + + set_values[0] = state; + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + ret = -EINVAL; + } else { + ALOGV("Setting slowtalk state: %d", state); + ret = mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values)); + my_data->slowtalk = state; + } + + if (my_data->csd != NULL) { + ret = my_data->csd->slow_talk(ALL_SESSION_VSID, state); + if (ret < 0) { + ALOGE("%s: csd_client_disable_device, failed, error %d", + __func__, ret); + } + } + return ret; +} + +static int set_hd_voice(struct platform_data *my_data, bool state) +{ + struct audio_device *adev = my_data->adev; + struct mixer_ctl *ctl; + char *mixer_ctl_name = "HD Voice Enable"; + int ret = 0; + uint32_t set_values[ ] = {0, + ALL_SESSION_VSID}; + + set_values[0] = state; + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + return -EINVAL; + } else { + ALOGV("Setting HD Voice state: %d", state); + ret = mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values)); + my_data->hd_voice = state; + } + + return ret; +} + +static int update_external_device_status(struct platform_data *my_data, + char* event_name, bool status) +{ + int ret = 0; + struct audio_usecase *usecase; + struct listnode *node; + + ALOGD("Recieved external event switch %s", event_name); + + if (!strcmp(event_name, EVENT_EXTERNAL_SPK_1)) + my_data->external_spk_1 = status; + else if (!strcmp(event_name, EVENT_EXTERNAL_SPK_2)) + my_data->external_spk_2 = status; + else if (!strcmp(event_name, EVENT_EXTERNAL_MIC)) + my_data->external_mic = status; + else { + ALOGE("The audio event type is not found"); + return -EINVAL; + } + + list_for_each(node, &my_data->adev->usecase_list) { + usecase = node_to_item(node, struct audio_usecase, list); + select_devices(my_data->adev, usecase->id); + } + + return ret; +} + +int platform_set_parameters(void *platform, struct str_parms *parms) +{ + struct platform_data *my_data = (struct platform_data *)platform; + char *str; + char value[256] = {0}; + int val; + int ret = 0, err; + char *kv_pairs = str_parms_to_str(parms); + + ALOGV_IF(kv_pairs != NULL, "%s: enter: %s", __func__, kv_pairs); + + err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_SLOWTALK, value, sizeof(value)); + if (err >= 0) { + bool state = false; + if (!strncmp("true", value, sizeof("true"))) { + state = true; + } + + str_parms_del(parms, AUDIO_PARAMETER_KEY_SLOWTALK); + ret = platform_set_slowtalk(my_data, state); + if (ret) + ALOGE("%s: Failed to set slow talk err: %d", __func__, ret); + } + + err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_HD_VOICE, value, sizeof(value)); + if (err >= 0) { + bool state = false; + if (!strncmp("true", value, sizeof("true"))) { + state = true; + } + + str_parms_del(parms, AUDIO_PARAMETER_KEY_HD_VOICE); + if (my_data->hd_voice != state) { + ret = set_hd_voice(my_data, state); + if (ret) + ALOGE("%s: Failed to set HD voice err: %d", __func__, ret); + } else { + ALOGV("%s: HD Voice already set to %d", __func__, state); + } + } + + err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_VOLUME_BOOST, + value, sizeof(value)); + if (err >= 0) { + str_parms_del(parms, AUDIO_PARAMETER_KEY_VOLUME_BOOST); + + if (my_data->acdb_reload_vocvoltable == NULL) { + ALOGE("%s: acdb_reload_vocvoltable is NULL", __func__); + } else if (!strcmp(value, "on")) { + if (!my_data->acdb_reload_vocvoltable(VOICE_FEATURE_SET_VOLUME_BOOST)) { + my_data->voice_feature_set = 1; + } + } else { + if (!my_data->acdb_reload_vocvoltable(VOICE_FEATURE_SET_DEFAULT)) { + my_data->voice_feature_set = 0; + } + } + } + + err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_EXT_AUDIO_DEVICE, + value, sizeof(value)); + if (err >= 0) { + char *event_name, *status_str; + bool status = false; + str_parms_del(parms, AUDIO_PARAMETER_KEY_EXT_AUDIO_DEVICE); + event_name = strtok_r(value, ",", &status_str); + ALOGV("%s: recieved update of external audio device %s %s", + __func__, + event_name, status_str); + if (!strncmp(status_str, "ON", sizeof("ON"))) + status = true; + else if (!strncmp(status_str, "OFF", sizeof("OFF"))) + status = false; + update_external_device_status(my_data, event_name, status); + } + + ALOGV("%s: exit with code(%d)", __func__, ret); + free(kv_pairs); + return ret; +} + +int platform_set_incall_recording_session_id(void *platform, + uint32_t session_id, int rec_mode) +{ + int ret = 0; + struct platform_data *my_data = (struct platform_data *)platform; + struct audio_device *adev = my_data->adev; + struct mixer_ctl *ctl; + const char *mixer_ctl_name = "Voc VSID"; + int num_ctl_values; + int i; + + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + ret = -EINVAL; + } else { + num_ctl_values = mixer_ctl_get_num_values(ctl); + for (i = 0; i < num_ctl_values; i++) { + if (mixer_ctl_set_value(ctl, i, session_id)) { + ALOGV("Error: invalid session_id: %x", session_id); + ret = -EINVAL; + break; + } + } + } + + if (my_data->csd != NULL) { + ret = my_data->csd->start_record(ALL_SESSION_VSID, rec_mode); + if (ret < 0) { + ALOGE("%s: csd_client_start_record failed, error %d", + __func__, ret); + } + } + + return ret; +} + +int platform_stop_incall_recording_usecase(void *platform) +{ + int ret = 0; + struct platform_data *my_data = (struct platform_data *)platform; + + if (my_data->csd != NULL) { + ret = my_data->csd->stop_record(ALL_SESSION_VSID); + if (ret < 0) { + ALOGE("%s: csd_client_stop_record failed, error %d", + __func__, ret); + } + } + + return ret; +} + +int platform_start_incall_music_usecase(void *platform) +{ + int ret = 0; + struct platform_data *my_data = (struct platform_data *)platform; + + if (my_data->csd != NULL) { + ret = my_data->csd->start_playback(ALL_SESSION_VSID); + if (ret < 0) { + ALOGE("%s: csd_client_start_playback failed, error %d", + __func__, ret); + } + } + + return ret; +} + +int platform_stop_incall_music_usecase(void *platform) +{ + int ret = 0; + struct platform_data *my_data = (struct platform_data *)platform; + + if (my_data->csd != NULL) { + ret = my_data->csd->stop_playback(ALL_SESSION_VSID); + if (ret < 0) { + ALOGE("%s: csd_client_stop_playback failed, error %d", + __func__, ret); + } + } + + return ret; +} + +int platform_update_lch(void *platform, struct voice_session *session, + enum voice_lch_mode lch_mode) +{ + int ret = 0; + struct platform_data *my_data = (struct platform_data *)platform; + + if ((my_data->csd != NULL) && (my_data->csd->set_lch != NULL)) + ret = my_data->csd->set_lch(session->vsid, lch_mode); + else + ret = pcm_ioctl(session->pcm_tx, SNDRV_VOICE_IOCTL_LCH, &lch_mode); + + return ret; +} + +void platform_get_parameters(void *platform, + struct str_parms *query, + struct str_parms *reply) +{ + struct platform_data *my_data = (struct platform_data *)platform; + char *str = NULL; + char value[256] = {0}; + int ret; + char *kv_pairs = NULL; + + ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_SLOWTALK, + value, sizeof(value)); + if (ret >= 0) { + str_parms_add_str(reply, AUDIO_PARAMETER_KEY_SLOWTALK, + my_data->slowtalk?"true":"false"); + } + + ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_HD_VOICE, + value, sizeof(value)); + if (ret >= 0) { + str_parms_add_str(reply, AUDIO_PARAMETER_KEY_HD_VOICE, + my_data->hd_voice?"true":"false"); + } + + ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_VOLUME_BOOST, + value, sizeof(value)); + if (ret >= 0) { + if (my_data->voice_feature_set == VOICE_FEATURE_SET_VOLUME_BOOST) { + strlcpy(value, "on", sizeof(value)); + } else { + strlcpy(value, "off", sizeof(value)); + } + + str_parms_add_str(reply, AUDIO_PARAMETER_KEY_VOLUME_BOOST, value); + } + + kv_pairs = str_parms_to_str(reply); + ALOGV_IF(kv_pairs != NULL, "%s: exit: returns - %s", __func__, kv_pairs); + free(kv_pairs); +} + +/* Delay in Us */ +int64_t platform_render_latency(audio_usecase_t usecase) +{ + switch (usecase) { + case USECASE_AUDIO_PLAYBACK_DEEP_BUFFER: + return DEEP_BUFFER_PLATFORM_DELAY; + case USECASE_AUDIO_PLAYBACK_LOW_LATENCY: + return LOW_LATENCY_PLATFORM_DELAY; + default: + return 0; + } +} + +int platform_update_usecase_from_source(int source, int usecase) +{ + ALOGV("%s: input source :%d", __func__, source); + if(source == AUDIO_SOURCE_FM_RX_A2DP) + usecase = USECASE_AUDIO_RECORD_FM_VIRTUAL; + return usecase; +} + +bool platform_listen_device_needs_event(snd_device_t snd_device) +{ + bool needs_event = false; + + if ((snd_device >= SND_DEVICE_IN_BEGIN) && + (snd_device < SND_DEVICE_IN_END) && + (snd_device != SND_DEVICE_IN_CAPTURE_FM) && + (snd_device != SND_DEVICE_IN_CAPTURE_VI_FEEDBACK)) + needs_event = true; + + return needs_event; +} + +bool platform_listen_usecase_needs_event(audio_usecase_t uc_id __unused) +{ + return false; +} + +bool platform_sound_trigger_device_needs_event(snd_device_t snd_device) +{ + bool needs_event = false; + + if ((snd_device >= SND_DEVICE_IN_BEGIN) && + (snd_device < SND_DEVICE_IN_END) && + (snd_device != SND_DEVICE_IN_CAPTURE_FM) && + (snd_device != SND_DEVICE_IN_CAPTURE_VI_FEEDBACK)) + needs_event = true; + + return needs_event; +} + +bool platform_sound_trigger_usecase_needs_event(audio_usecase_t uc_id __unused) +{ + return false; +} + +/* Read offload buffer size from a property. + * If value is not power of 2 round it to + * power of 2. + */ +uint32_t platform_get_compress_offload_buffer_size(audio_offload_info_t* info) +{ + char value[PROPERTY_VALUE_MAX] = {0}; + uint32_t fragment_size = COMPRESS_OFFLOAD_FRAGMENT_SIZE; + if((property_get("audio.offload.buffer.size.kb", value, "")) && + atoi(value)) { + fragment_size = atoi(value) * 1024; + } + + // For FLAC use max size since it is loss less, and has sampling rates + // upto 192kHZ + if (info != NULL && !info->has_video && + info->format == AUDIO_FORMAT_FLAC) { + fragment_size = MAX_COMPRESS_OFFLOAD_FRAGMENT_SIZE; + ALOGV("FLAC fragment size %d", fragment_size); + } + + if (info != NULL && info->has_video && info->is_streaming) { + fragment_size = COMPRESS_OFFLOAD_FRAGMENT_SIZE_FOR_AV_STREAMING; + ALOGV("%s: offload fragment size reduced for AV streaming to %d", + __func__, fragment_size); + } + + fragment_size = ALIGN( fragment_size, 1024); + + if(fragment_size < MIN_COMPRESS_OFFLOAD_FRAGMENT_SIZE) + fragment_size = MIN_COMPRESS_OFFLOAD_FRAGMENT_SIZE; + else if(fragment_size > MAX_COMPRESS_OFFLOAD_FRAGMENT_SIZE) + fragment_size = MAX_COMPRESS_OFFLOAD_FRAGMENT_SIZE; + ALOGV("%s: fragment_size %d", __func__, fragment_size); + return fragment_size; +} + +uint32_t platform_get_pcm_offload_buffer_size(audio_offload_info_t* info) +{ + uint32_t fragment_size = MIN_PCM_OFFLOAD_FRAGMENT_SIZE; + uint32_t bits_per_sample = 16; + + if (info->format == AUDIO_FORMAT_PCM_24_BIT_OFFLOAD) { + bits_per_sample = 32; + } + + if (!info->has_video) { + fragment_size = MAX_PCM_OFFLOAD_FRAGMENT_SIZE; + + } else if (info->has_video && info->is_streaming) { + fragment_size = (PCM_OFFLOAD_BUFFER_DURATION_FOR_AV_STREAMING + * info->sample_rate + * (bits_per_sample >> 3) + * popcount(info->channel_mask))/1000; + + } else if (info->has_video) { + fragment_size = (PCM_OFFLOAD_BUFFER_DURATION_FOR_AV + * info->sample_rate + * (bits_per_sample >> 3) + * popcount(info->channel_mask))/1000; + } + + char value[PROPERTY_VALUE_MAX] = {0}; + if((property_get("audio.offload.pcm.buffer.size", value, "")) && + atoi(value)) { + fragment_size = atoi(value) * 1024; + ALOGV("Using buffer size from sys prop %d", fragment_size); + } + + fragment_size = ALIGN( fragment_size, 1024); + + if(fragment_size < MIN_PCM_OFFLOAD_FRAGMENT_SIZE) + fragment_size = MIN_PCM_OFFLOAD_FRAGMENT_SIZE; + else if(fragment_size > MAX_PCM_OFFLOAD_FRAGMENT_SIZE) + fragment_size = MAX_PCM_OFFLOAD_FRAGMENT_SIZE; + + ALOGV("%s: fragment_size %d", __func__, fragment_size); + return fragment_size; +} + +int platform_set_codec_backend_cfg(struct audio_device* adev, + unsigned int bit_width, unsigned int sample_rate) +{ + ALOGV("%s bit width: %d, sample rate: %d", __func__, bit_width, sample_rate); + + int ret = 0; + if (bit_width != adev->cur_codec_backend_bit_width) { + const char * mixer_ctl_name = "SLIM_0_RX Format"; + struct mixer_ctl *ctl; + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer command - %s", + __func__, mixer_ctl_name); + return -EINVAL; + } + + if (bit_width == 24) { + mixer_ctl_set_enum_by_string(ctl, "S24_LE"); + } else { + mixer_ctl_set_enum_by_string(ctl, "S16_LE"); + sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE; + } + adev->cur_codec_backend_bit_width = bit_width; + ALOGE("Backend bit width is set to %d ", bit_width); + } + + /* + * Backend sample rate configuration follows: + * 16 bit playback - 48khz for streams at any valid sample rate + * 24 bit playback - 48khz for stream sample rate less than 48khz + * 24 bit playback - 96khz for sample rate range of 48khz to 96khz + * 24 bit playback - 192khz for sample rate range of 96khz to 192 khz + * Upper limit is inclusive in the sample rate range. + */ + // TODO: This has to be more dynamic based on policy file + if (sample_rate != adev->cur_codec_backend_samplerate) { + char *rate_str = NULL; + const char * mixer_ctl_name = "SLIM_0_RX SampleRate"; + struct mixer_ctl *ctl; + + switch (sample_rate) { + case 8000: + case 11025: + case 16000: + case 22050: + case 32000: + case 44100: + case 48000: + rate_str = "KHZ_48"; + break; + case 64000: + case 88200: + case 96000: + rate_str = "KHZ_96"; + break; + case 176400: + case 192000: + rate_str = "KHZ_192"; + break; + default: + rate_str = "KHZ_48"; + break; + } + + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if(!ctl) { + ALOGE("%s: Could not get ctl for mixer command - %s", + __func__, mixer_ctl_name); + return -EINVAL; + } + + ALOGV("Set sample rate as rate_str = %s", rate_str); + mixer_ctl_set_enum_by_string(ctl, rate_str); + adev->cur_codec_backend_samplerate = sample_rate; + } + + return ret; +} + +bool platform_check_codec_backend_cfg(struct audio_device* adev, + struct audio_usecase* usecase __unused, + unsigned int* new_bit_width, + unsigned int* new_sample_rate) +{ + bool backend_change = false; + struct listnode *node; + struct stream_out *out = NULL; + unsigned int bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH; + unsigned int sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE; + + // For voice calls use default configuration + // force routing is not required here, caller will do it anyway + if (adev->mode == AUDIO_MODE_IN_CALL || + adev->mode == AUDIO_MODE_IN_COMMUNICATION) { + ALOGW("%s:Use default bw and sr for voice/voip calls ",__func__); + *new_bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH; + *new_sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE; + backend_change = true; + } + + /* + * The backend should be configured at highest bit width and/or + * sample rate amongst all playback usecases. + * If the selected sample rate and/or bit width differ with + * current backend sample rate and/or bit width, then, we set the + * backend re-configuration flag. + * + * Exception: 16 bit playbacks is allowed through 16 bit/48 khz backend only + */ + if (!backend_change) { + list_for_each(node, &adev->usecase_list) { + struct audio_usecase *curr_usecase; + curr_usecase = node_to_item(node, struct audio_usecase, list); + if (curr_usecase->type == PCM_PLAYBACK) { + struct stream_out *out = + (struct stream_out*) curr_usecase->stream.out; + if (out != NULL ) { + ALOGV("Offload playback running bw %d sr %d", + out->bit_width, out->sample_rate); + if (bit_width < out->bit_width) + bit_width = out->bit_width; + if (sample_rate < out->sample_rate) + sample_rate = out->sample_rate; + } + } + } + } + + // 24 bit playback on speakers and all 16 bit playbacks is allowed through + // 16 bit/48 khz backend only + if ((16 == bit_width) || + ((24 == bit_width) && + (usecase->stream.out->devices & AUDIO_DEVICE_OUT_SPEAKER))) { + sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE; + } + // Force routing if the expected bitwdith or samplerate + // is not same as current backend comfiguration + if ((bit_width != adev->cur_codec_backend_bit_width) || + (sample_rate != adev->cur_codec_backend_samplerate)) { + *new_bit_width = bit_width; + *new_sample_rate = sample_rate; + backend_change = true; + ALOGI("%s Codec backend needs to be updated. new bit width: %d new sample rate: %d", + __func__, *new_bit_width, *new_sample_rate); + } + + return backend_change; +} + +bool platform_check_and_set_codec_backend_cfg(struct audio_device* adev, struct audio_usecase *usecase) +{ + ALOGV("platform_check_and_set_codec_backend_cfg usecase = %d",usecase->id ); + + unsigned int new_bit_width, old_bit_width; + unsigned int new_sample_rate, old_sample_rate; + + new_bit_width = old_bit_width = adev->cur_codec_backend_bit_width; + new_sample_rate = old_sample_rate = adev->cur_codec_backend_samplerate; + + ALOGW("Codec backend bitwidth %d, samplerate %d", old_bit_width, old_sample_rate); + if (platform_check_codec_backend_cfg(adev, usecase, + &new_bit_width, &new_sample_rate)) { + platform_set_codec_backend_cfg(adev, new_bit_width, new_sample_rate); + return true; + } + + return false; +} + +int platform_set_snd_device_backend(snd_device_t device, const char *backend) +{ + int ret = 0; + + if ((device < SND_DEVICE_MIN) || (device >= SND_DEVICE_MAX)) { + ALOGE("%s: Invalid snd_device = %d", + __func__, device); + ret = -EINVAL; + goto done; + } + + if (backend_table[device]) { + free(backend_table[device]); + } + backend_table[device] = strdup(backend); +done: + return ret; +} + +int platform_set_usecase_pcm_id(audio_usecase_t usecase, int32_t type, int32_t pcm_id) +{ + int ret = 0; + if ((usecase <= USECASE_INVALID) || (usecase >= AUDIO_USECASE_MAX)) { + ALOGE("%s: invalid usecase case idx %d", __func__, usecase); + ret = -EINVAL; + goto done; + } + + if ((type != 0) && (type != 1)) { + ALOGE("%s: invalid usecase type", __func__); + ret = -EINVAL; + } + pcm_device_table[usecase][type] = pcm_id; +done: + return ret; +} + +void platform_get_device_to_be_id_map(int **device_to_be_id, int *length) +{ + *device_to_be_id = msm_device_to_be_id; + *length = msm_be_id_array_len; +} diff --git a/audio/hal/msm8974/platform.h b/audio/hal/msm8974/platform.h new file mode 100644 index 0000000..831ee58 --- /dev/null +++ b/audio/hal/msm8974/platform.h @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2013 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. + */ + +#ifndef QCOM_AUDIO_PLATFORM_H +#define QCOM_AUDIO_PLATFORM_H +#include + +enum { + FLUENCE_NONE, + FLUENCE_DUAL_MIC = 0x1, + FLUENCE_QUAD_MIC = 0x2, +}; + +enum { + FLUENCE_ENDFIRE = 0x1, + FLUENCE_BROADSIDE = 0x2, +}; + +/* + * Below are the devices for which is back end is same, SLIMBUS_0_RX. + * All these devices are handled by the internal HW codec. We can + * enable any one of these devices at any time + */ +#define AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND \ + (AUDIO_DEVICE_OUT_EARPIECE | AUDIO_DEVICE_OUT_SPEAKER | \ + AUDIO_DEVICE_OUT_WIRED_HEADSET | AUDIO_DEVICE_OUT_WIRED_HEADPHONE) + +/* Sound devices specific to the platform + * The DEVICE_OUT_* and DEVICE_IN_* should be mapped to these sound + * devices to enable corresponding mixer paths + */ +enum { + SND_DEVICE_NONE = 0, + + /* Playback devices */ + SND_DEVICE_MIN, + SND_DEVICE_OUT_BEGIN = SND_DEVICE_MIN, + SND_DEVICE_OUT_HANDSET = SND_DEVICE_OUT_BEGIN, + SND_DEVICE_OUT_SPEAKER, + SND_DEVICE_OUT_SPEAKER_EXTERNAL_1, + SND_DEVICE_OUT_SPEAKER_EXTERNAL_2, + SND_DEVICE_OUT_SPEAKER_REVERSE, + SND_DEVICE_OUT_HEADPHONES, + SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES, + SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_EXTERNAL_1, + SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_EXTERNAL_2, + SND_DEVICE_OUT_VOICE_HANDSET, + SND_DEVICE_OUT_VOICE_SPEAKER, + SND_DEVICE_OUT_VOICE_HEADPHONES, + SND_DEVICE_OUT_HDMI, + SND_DEVICE_OUT_SPEAKER_AND_HDMI, + SND_DEVICE_OUT_BT_SCO, + SND_DEVICE_OUT_BT_SCO_WB, + SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES, + SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES, + SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET, + SND_DEVICE_OUT_VOICE_TX, + SND_DEVICE_OUT_AFE_PROXY, + SND_DEVICE_OUT_USB_HEADSET, + SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET, + SND_DEVICE_OUT_TRANSMISSION_FM, + SND_DEVICE_OUT_ANC_HEADSET, + SND_DEVICE_OUT_ANC_FB_HEADSET, + SND_DEVICE_OUT_VOICE_ANC_HEADSET, + SND_DEVICE_OUT_VOICE_ANC_FB_HEADSET, + SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET, + SND_DEVICE_OUT_ANC_HANDSET, + SND_DEVICE_OUT_SPEAKER_PROTECTED, + SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED, + SND_DEVICE_OUT_END, + + /* + * Note: IN_BEGIN should be same as OUT_END because total number of devices + * SND_DEVICES_MAX should not exceed MAX_RX + MAX_TX devices. + */ + /* Capture devices */ + SND_DEVICE_IN_BEGIN = SND_DEVICE_OUT_END, + SND_DEVICE_IN_HANDSET_MIC = SND_DEVICE_IN_BEGIN, + SND_DEVICE_IN_HANDSET_MIC_EXTERNAL, + SND_DEVICE_IN_HANDSET_MIC_AEC, + SND_DEVICE_IN_HANDSET_MIC_NS, + SND_DEVICE_IN_HANDSET_MIC_AEC_NS, + SND_DEVICE_IN_HANDSET_DMIC, + SND_DEVICE_IN_HANDSET_DMIC_AEC, + SND_DEVICE_IN_HANDSET_DMIC_NS, + SND_DEVICE_IN_HANDSET_DMIC_AEC_NS, + SND_DEVICE_IN_SPEAKER_MIC, + SND_DEVICE_IN_SPEAKER_MIC_AEC, + SND_DEVICE_IN_SPEAKER_MIC_NS, + SND_DEVICE_IN_SPEAKER_MIC_AEC_NS, + SND_DEVICE_IN_SPEAKER_DMIC, + SND_DEVICE_IN_SPEAKER_DMIC_AEC, + SND_DEVICE_IN_SPEAKER_DMIC_NS, + SND_DEVICE_IN_SPEAKER_DMIC_AEC_NS, + SND_DEVICE_IN_HEADSET_MIC, + SND_DEVICE_IN_HEADSET_MIC_FLUENCE, + SND_DEVICE_IN_VOICE_SPEAKER_MIC, + SND_DEVICE_IN_VOICE_HEADSET_MIC, + SND_DEVICE_IN_HDMI_MIC, + SND_DEVICE_IN_BT_SCO_MIC, + SND_DEVICE_IN_BT_SCO_MIC_NREC, + SND_DEVICE_IN_BT_SCO_MIC_WB, + SND_DEVICE_IN_BT_SCO_MIC_WB_NREC, + SND_DEVICE_IN_CAMCORDER_MIC, + SND_DEVICE_IN_VOICE_DMIC, + SND_DEVICE_IN_VOICE_SPEAKER_DMIC, + SND_DEVICE_IN_VOICE_SPEAKER_QMIC, + SND_DEVICE_IN_VOICE_TTY_FULL_HEADSET_MIC, + SND_DEVICE_IN_VOICE_TTY_VCO_HANDSET_MIC, + SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC, + SND_DEVICE_IN_VOICE_REC_MIC, + SND_DEVICE_IN_VOICE_REC_MIC_NS, + SND_DEVICE_IN_VOICE_REC_DMIC_STEREO, + SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE, + SND_DEVICE_IN_VOICE_RX, + SND_DEVICE_IN_USB_HEADSET_MIC, + SND_DEVICE_IN_CAPTURE_FM, + SND_DEVICE_IN_AANC_HANDSET_MIC, + SND_DEVICE_IN_QUAD_MIC, + SND_DEVICE_IN_HANDSET_STEREO_DMIC, + SND_DEVICE_IN_SPEAKER_STEREO_DMIC, + SND_DEVICE_IN_CAPTURE_VI_FEEDBACK, + SND_DEVICE_IN_VOICE_SPEAKER_DMIC_BROADSIDE, + SND_DEVICE_IN_SPEAKER_DMIC_BROADSIDE, + SND_DEVICE_IN_SPEAKER_DMIC_AEC_BROADSIDE, + SND_DEVICE_IN_SPEAKER_DMIC_NS_BROADSIDE, + SND_DEVICE_IN_SPEAKER_DMIC_AEC_NS_BROADSIDE, + SND_DEVICE_IN_END, + + SND_DEVICE_MAX = SND_DEVICE_IN_END, + +}; + +#define DEFAULT_OUTPUT_SAMPLING_RATE 48000 + +#define ALL_SESSION_VSID 0xFFFFFFFF +#define DEFAULT_MUTE_RAMP_DURATION_MS 20 +#define DEFAULT_VOLUME_RAMP_DURATION_MS 20 +#define MIXER_PATH_MAX_LENGTH 100 + +#define MAX_VOL_INDEX 5 +#define MIN_VOL_INDEX 0 +#define percent_to_index(val, min, max) \ + ((val) * ((max) - (min)) * 0.01 + (min) + .5) + +/* + * tinyAlsa library interprets period size as number of frames + * one frame = channel_count * sizeof (pcm sample) + * so if format = 16-bit PCM and channels = Stereo, frame size = 2 ch * 2 = 4 bytes + * DEEP_BUFFER_OUTPUT_PERIOD_SIZE = 1024 means 1024 * 4 = 4096 bytes + * We should take care of returning proper size when AudioFlinger queries for + * the buffer size of an input/output stream + */ +#define DEEP_BUFFER_OUTPUT_PERIOD_SIZE 960 +#define DEEP_BUFFER_OUTPUT_PERIOD_COUNT 4 +#define LOW_LATENCY_OUTPUT_PERIOD_SIZE 240 +#define LOW_LATENCY_OUTPUT_PERIOD_COUNT 2 + +#define HDMI_MULTI_PERIOD_SIZE 336 +#define HDMI_MULTI_PERIOD_COUNT 8 +#define HDMI_MULTI_DEFAULT_CHANNEL_COUNT 6 +#define HDMI_MULTI_PERIOD_BYTES (HDMI_MULTI_PERIOD_SIZE * HDMI_MULTI_DEFAULT_CHANNEL_COUNT * 2) + +#define AUDIO_CAPTURE_PERIOD_DURATION_MSEC 20 +#define AUDIO_CAPTURE_PERIOD_COUNT 2 + +#define LOW_LATENCY_CAPTURE_SAMPLE_RATE 48000 +#define LOW_LATENCY_CAPTURE_PERIOD_SIZE 240 +#define LOW_LATENCY_CAPTURE_USE_CASE 1 + +#define DEVICE_NAME_MAX_SIZE 128 +#define HW_INFO_ARRAY_MAX_SIZE 32 + +#define DEEP_BUFFER_PCM_DEVICE 0 +#define AUDIO_RECORD_PCM_DEVICE 0 +#define MULTIMEDIA2_PCM_DEVICE 1 +#define FM_PLAYBACK_PCM_DEVICE 5 +#define FM_CAPTURE_PCM_DEVICE 6 +#define HFP_PCM_RX 5 + +#define INCALL_MUSIC_UPLINK_PCM_DEVICE 1 + +#ifdef PLATFORM_MSM8610 +#define INCALL_MUSIC_UPLINK2_PCM_DEVICE 14 +#elif PLATFORM_MSM8x26 +#define INCALL_MUSIC_UPLINK2_PCM_DEVICE 16 +#elif PLATFORM_APQ8084 +#define INCALL_MUSIC_UPLINK2_PCM_DEVICE 34 +#else +#define INCALL_MUSIC_UPLINK2_PCM_DEVICE 35 +#endif + +#define SPKR_PROT_CALIB_RX_PCM_DEVICE 5 +#ifdef PLATFORM_APQ8084 +#define SPKR_PROT_CALIB_TX_PCM_DEVICE 35 +#else +#define SPKR_PROT_CALIB_TX_PCM_DEVICE 25 +#endif +#define PLAYBACK_OFFLOAD_DEVICE 9 + +#ifdef MULTIPLE_OFFLOAD_ENABLED +#ifdef PLATFORM_APQ8084 +#define PLAYBACK_OFFLOAD_DEVICE2 17 +#define PLAYBACK_OFFLOAD_DEVICE3 18 +#define PLAYBACK_OFFLOAD_DEVICE4 34 +#define PLAYBACK_OFFLOAD_DEVICE5 35 +#define PLAYBACK_OFFLOAD_DEVICE6 36 +#define PLAYBACK_OFFLOAD_DEVICE7 37 +#define PLAYBACK_OFFLOAD_DEVICE8 38 +#define PLAYBACK_OFFLOAD_DEVICE9 39 +#endif +#ifdef PLATFORM_MSM8994 +#define PLAYBACK_OFFLOAD_DEVICE2 17 +#define PLAYBACK_OFFLOAD_DEVICE3 18 +#define PLAYBACK_OFFLOAD_DEVICE4 37 +#define PLAYBACK_OFFLOAD_DEVICE5 38 +#define PLAYBACK_OFFLOAD_DEVICE6 39 +#define PLAYBACK_OFFLOAD_DEVICE7 40 +#define PLAYBACK_OFFLOAD_DEVICE8 41 +#define PLAYBACK_OFFLOAD_DEVICE9 42 +#endif +#endif + +#define COMPRESS_VOIP_CALL_PCM_DEVICE 3 + +#ifdef PLATFORM_MSM8610 +#define LOWLATENCY_PCM_DEVICE 12 +#define EC_REF_RX "SEC_I2S_RX" +#else +#define LOWLATENCY_PCM_DEVICE 15 +#define EC_REF_RX "SLIM_RX" +#endif +#ifdef PLATFORM_MSM8x26 +#define COMPRESS_CAPTURE_DEVICE 20 +#else +#define COMPRESS_CAPTURE_DEVICE 19 +#endif + +#ifdef PLATFORM_MSM8x26 +#define VOICE_CALL_PCM_DEVICE 2 +#define VOICE2_CALL_PCM_DEVICE 14 +#define VOLTE_CALL_PCM_DEVICE 17 +#define QCHAT_CALL_PCM_DEVICE 18 +#define VOWLAN_CALL_PCM_DEVICE 30 +#elif PLATFORM_APQ8084 +#define VOICE_CALL_PCM_DEVICE 20 +#define VOICE2_CALL_PCM_DEVICE 25 +#define VOLTE_CALL_PCM_DEVICE 21 +#define QCHAT_CALL_PCM_DEVICE 33 +#define VOWLAN_CALL_PCM_DEVICE -1 +#elif PLATFORM_MSM8610 +#define VOICE_CALL_PCM_DEVICE 2 +#define VOICE2_CALL_PCM_DEVICE 13 +#define VOLTE_CALL_PCM_DEVICE 15 +#define QCHAT_CALL_PCM_DEVICE 14 +#define VOWLAN_CALL_PCM_DEVICE -1 +#elif PLATFORM_MSM8994 +#define VOICE_CALL_PCM_DEVICE 2 +#define VOICE2_CALL_PCM_DEVICE 22 +#define VOLTE_CALL_PCM_DEVICE 14 +#define QCHAT_CALL_PCM_DEVICE 20 +#define VOWLAN_CALL_PCM_DEVICE 36 +#else +#define VOICE_CALL_PCM_DEVICE 2 +#define VOICE2_CALL_PCM_DEVICE 22 +#define VOLTE_CALL_PCM_DEVICE 14 +#define QCHAT_CALL_PCM_DEVICE 20 +#define VOWLAN_CALL_PCM_DEVICE 36 +#endif + +#define AFE_PROXY_PLAYBACK_PCM_DEVICE 7 +#define AFE_PROXY_RECORD_PCM_DEVICE 8 + +#ifdef PLATFORM_MSM8x26 +#define HFP_SCO_RX 28 +#define HFP_ASM_RX_TX 29 +#else +#define HFP_SCO_RX 23 +#define HFP_ASM_RX_TX 24 +#endif + +#ifdef PLATFORM_APQ8084 +#define FM_RX_VOLUME "Quat MI2S FM RX Volume" +#elif PLATFORM_MSM8994 +#define FM_RX_VOLUME "PRI MI2S LOOPBACK Volume" +#else +#define FM_RX_VOLUME "Internal FM RX Volume" +#endif + +#define LIB_CSD_CLIENT "libcsd-client.so" +/* CSD-CLIENT related functions */ +typedef int (*init_t)(bool); +typedef int (*deinit_t)(); +typedef int (*disable_device_t)(); +typedef int (*enable_device_config_t)(int, int); +typedef int (*enable_device_t)(int, int, uint32_t); +typedef int (*volume_t)(uint32_t, int, uint16_t); +typedef int (*mic_mute_t)(uint32_t, int, uint16_t); +typedef int (*slow_talk_t)(uint32_t, uint8_t); +typedef int (*start_voice_t)(uint32_t); +typedef int (*stop_voice_t)(uint32_t); +typedef int (*start_playback_t)(uint32_t); +typedef int (*stop_playback_t)(uint32_t); +typedef int (*set_lch_t)(uint32_t, enum voice_lch_mode); +typedef int (*start_record_t)(uint32_t, int); +typedef int (*stop_record_t)(uint32_t); +typedef int (*get_sample_rate_t)(uint32_t *); +/* CSD Client structure */ +struct csd_data { + void *csd_client; + init_t init; + deinit_t deinit; + disable_device_t disable_device; + enable_device_config_t enable_device_config; + enable_device_t enable_device; + volume_t volume; + mic_mute_t mic_mute; + slow_talk_t slow_talk; + start_voice_t start_voice; + stop_voice_t stop_voice; + start_playback_t start_playback; + stop_playback_t stop_playback; + set_lch_t set_lch; + start_record_t start_record; + stop_record_t stop_record; + get_sample_rate_t get_sample_rate; +}; + +#endif // QCOM_AUDIO_PLATFORM_H diff --git a/audio/hal/platform_api.h b/audio/hal/platform_api.h new file mode 100644 index 0000000..c7a45bd --- /dev/null +++ b/audio/hal/platform_api.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * Not a contribution. + * + * Copyright (C) 2013 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. + */ + +#ifndef AUDIO_PLATFORM_API_H +#define AUDIO_PLATFORM_API_H +#include + +#define CODEC_BACKEND_DEFAULT_BIT_WIDTH 16 +#define CODEC_BACKEND_DEFAULT_SAMPLE_RATE 48000 + +void *platform_init(struct audio_device *adev); +void platform_deinit(void *platform); +const char *platform_get_snd_device_name(snd_device_t snd_device); +int platform_get_snd_device_name_extn(void *platform, snd_device_t snd_device, + char *device_name); +void platform_add_backend_name(char *mixer_path, snd_device_t snd_device); +int platform_get_pcm_device_id(audio_usecase_t usecase, int device_type); +int platform_get_snd_device_index(char *snd_device_index_name); +int platform_set_fluence_type(void *platform, char *value); +int platform_get_fluence_type(void *platform, char *value, uint32_t len); +int platform_set_snd_device_acdb_id(snd_device_t snd_device, unsigned int acdb_id); +int platform_get_snd_device_acdb_id(snd_device_t snd_device); +int platform_send_audio_calibration(void *platform, struct audio_usecase *usecase, + int app_type, int sample_rate); +int platform_get_default_app_type(void *platform); +int platform_switch_voice_call_device_pre(void *platform); +int platform_switch_voice_call_enable_device_config(void *platform, + snd_device_t out_snd_device, + snd_device_t in_snd_device); +int platform_switch_voice_call_device_post(void *platform, + snd_device_t out_snd_device, + snd_device_t in_snd_device); +int platform_switch_voice_call_usecase_route_post(void *platform, + snd_device_t out_snd_device, + snd_device_t in_snd_device); +int platform_start_voice_call(void *platform, uint32_t vsid); +int platform_stop_voice_call(void *platform, uint32_t vsid); +int platform_set_voice_volume(void *platform, int volume); +int platform_set_mic_mute(void *platform, bool state); +int platform_get_sample_rate(void *platform, uint32_t *rate); +int platform_set_device_mute(void *platform, bool state, char *dir); +snd_device_t platform_get_output_snd_device(void *platform, audio_devices_t devices); +snd_device_t platform_get_input_snd_device(void *platform, audio_devices_t out_device); +int platform_set_hdmi_channels(void *platform, int channel_count); +int platform_edid_get_max_channels(void *platform); +void platform_get_parameters(void *platform, struct str_parms *query, + struct str_parms *reply); +int platform_set_parameters(void *platform, struct str_parms *parms); +int platform_set_incall_recording_session_id(void *platform, uint32_t session_id, + int rec_mode); +int platform_stop_incall_recording_usecase(void *platform); +int platform_start_incall_music_usecase(void *platform); +int platform_stop_incall_music_usecase(void *platform); +int platform_update_lch(void *platform, struct voice_session *session, + enum voice_lch_mode lch_mode); +/* returns the latency for a usecase in Us */ +int64_t platform_render_latency(audio_usecase_t usecase); +int platform_update_usecase_from_source(int source, audio_usecase_t usecase); + +bool platform_listen_device_needs_event(snd_device_t snd_device); +bool platform_listen_usecase_needs_event(audio_usecase_t uc_id); + +bool platform_sound_trigger_device_needs_event(snd_device_t snd_device); +bool platform_sound_trigger_usecase_needs_event(audio_usecase_t uc_id); + +int platform_set_snd_device_backend(snd_device_t snd_device, const char * backend); + +/* From platform_info_parser.c */ +int platform_info_init(const char *filename); + +struct audio_offload_info_t; +uint32_t platform_get_compress_offload_buffer_size(audio_offload_info_t* info); +uint32_t platform_get_pcm_offload_buffer_size(audio_offload_info_t* info); + +bool platform_check_and_set_codec_backend_cfg(struct audio_device* adev, struct audio_usecase *usecase); +int platform_get_usecase_index(const char * usecase); +int platform_set_usecase_pcm_id(audio_usecase_t usecase, int32_t type, int32_t pcm_id); +void platform_set_echo_reference(void *platform, bool enable); +void platform_get_device_to_be_id_map(int **be_id_map, int *length); + +#endif // AUDIO_PLATFORM_API_H diff --git a/audio/hal/platform_info.c b/audio/hal/platform_info.c new file mode 100644 index 0000000..615b9f3 --- /dev/null +++ b/audio/hal/platform_info.c @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "platform_info" +#define LOG_NDDEBUG 0 + +#include +#include +#include +#include +#include +#include "platform_api.h" +#include + +#define BUF_SIZE 1024 + +typedef enum { + ROOT, + ACDB, + PCM_ID, + BACKEND_NAME, +} section_t; + +typedef void (* section_process_fn)(const XML_Char **attr); + +static void process_acdb_id(const XML_Char **attr); +static void process_pcm_id(const XML_Char **attr); +static void process_backend_name(const XML_Char **attr); +static void process_root(const XML_Char **attr); + +static section_process_fn section_table[] = { + [ROOT] = process_root, + [ACDB] = process_acdb_id, + [PCM_ID] = process_pcm_id, + [BACKEND_NAME] = process_backend_name, +}; + +static section_t section; + +/* + * + * + * + * ... + * ... + * + * + * + * ... + * ... + * + * + * + * ... + * ... + * + * + */ + +static void process_root(const XML_Char **attr __unused) +{ +} + +/* mapping from usecase to pcm dev id */ +static void process_pcm_id(const XML_Char **attr) +{ + int index; + + if (strcmp(attr[0], "name") != 0) { + ALOGE("%s: 'name' not found, no ACDB ID set!", __func__); + goto done; + } + + index = platform_get_usecase_index((char *)attr[1]); + if (index < 0) { + ALOGE("%s: usecase %s not found!", + __func__, attr[1]); + goto done; + } + + if (strcmp(attr[2], "type") != 0) { + ALOGE("%s: usecase type not mentioned", __func__); + goto done; + } + + int type = -1; + + if (!strcasecmp((char *)attr[3], "in")) { + type = 1; + } else if (!strcasecmp((char *)attr[3], "out")) { + type = 0; + } else { + ALOGE("%s: type must be IN or OUT", __func__); + goto done; + } + + if (strcmp(attr[4], "id") != 0) { + ALOGE("%s: usecase id not mentioned", __func__); + goto done; + } + + int id = atoi((char *)attr[5]); + + if (platform_set_usecase_pcm_id(index, type, id) < 0) { + ALOGE("%s: usecase %s type %d id %d was not set!", + __func__, attr[1], type, id); + goto done; + } + +done: + return; +} + +/* backend to be used for a device */ +static void process_backend_name(const XML_Char **attr) +{ + int index; + + if (strcmp(attr[0], "name") != 0) { + ALOGE("%s: 'name' not found, no ACDB ID set!", __func__); + goto done; + } + + index = platform_get_snd_device_index((char *)attr[1]); + if (index < 0) { + ALOGE("%s: Device %s not found, no ACDB ID set!", + __func__, attr[1]); + goto done; + } + + if (strcmp(attr[2], "backend") != 0) { + ALOGE("%s: Device %s has no backend set!", + __func__, attr[1]); + goto done; + } + + if (platform_set_snd_device_backend(index, attr[3]) < 0) { + ALOGE("%s: Device %s backend %s was not set!", + __func__, attr[1], attr[3]); + goto done; + } + +done: + return; +} + +static void process_acdb_id(const XML_Char **attr) +{ + int index; + + if (strcmp(attr[0], "name") != 0) { + ALOGE("%s: 'name' not found, no ACDB ID set!", __func__); + goto done; + } + + index = platform_get_snd_device_index((char *)attr[1]); + if (index < 0) { + ALOGE("%s: Device %s in platform info xml not found, no ACDB ID set!", + __func__, attr[1]); + goto done; + } + + if (strcmp(attr[2], "acdb_id") != 0) { + ALOGE("%s: Device %s in platform info xml has no acdb_id, no ACDB ID set!", + __func__, attr[1]); + goto done; + } + + if (platform_set_snd_device_acdb_id(index, atoi((char *)attr[3])) < 0) { + ALOGE("%s: Device %s, ACDB ID %d was not set!", + __func__, attr[1], atoi((char *)attr[3])); + goto done; + } + +done: + return; +} + +static void start_tag(void *userdata __unused, const XML_Char *tag_name, + const XML_Char **attr) +{ + const XML_Char *attr_name = NULL; + const XML_Char *attr_value = NULL; + unsigned int i; + + if (strcmp(tag_name, "acdb_ids") == 0) { + section = ACDB; + } else if (strcmp(tag_name, "pcm_ids") == 0) { + section = PCM_ID; + } else if (strcmp(tag_name, "backend_names") == 0) { + section = BACKEND_NAME; + } else if (strcmp(tag_name, "device") == 0) { + if ((section != ACDB) && (section != BACKEND_NAME)) { + ALOGE("device tag only supported for acdb/backend names"); + return; + } + + /* call into process function for the current section */ + section_process_fn fn = section_table[section]; + fn(attr); + } else if (strcmp(tag_name, "usecase") == 0) { + if (section != PCM_ID) { + ALOGE("usecase tag only supported with PCM_ID section"); + return; + } + + section_process_fn fn = section_table[PCM_ID]; + fn(attr); + } + + return; +} + +static void end_tag(void *userdata __unused, const XML_Char *tag_name) +{ + if (strcmp(tag_name, "acdb_ids") == 0) { + section = ROOT; + } else if (strcmp(tag_name, "pcm_ids") == 0) { + section = ROOT; + } else if (strcmp(tag_name, "backend_names") == 0) { + section = ROOT; + } +} + +int platform_info_init(const char *filename) +{ + XML_Parser parser; + FILE *file; + int ret = 0; + int bytes_read; + void *buf; + + file = fopen(filename, "r"); + section = ROOT; + + if (!file) { + ALOGD("%s: Failed to open %s, using defaults.", + __func__, filename); + ret = -ENODEV; + goto done; + } + + parser = XML_ParserCreate(NULL); + if (!parser) { + ALOGE("%s: Failed to create XML parser!", __func__); + ret = -ENODEV; + goto err_close_file; + } + + XML_SetElementHandler(parser, start_tag, end_tag); + + while (1) { + buf = XML_GetBuffer(parser, BUF_SIZE); + if (buf == NULL) { + ALOGE("%s: XML_GetBuffer failed", __func__); + ret = -ENOMEM; + goto err_free_parser; + } + + bytes_read = fread(buf, 1, BUF_SIZE, file); + if (bytes_read < 0) { + ALOGE("%s: fread failed, bytes read = %d", __func__, bytes_read); + ret = bytes_read; + goto err_free_parser; + } + + if (XML_ParseBuffer(parser, bytes_read, + bytes_read == 0) == XML_STATUS_ERROR) { + ALOGE("%s: XML_ParseBuffer failed, for %s", + __func__, filename); + ret = -EINVAL; + goto err_free_parser; + } + + if (bytes_read == 0) + break; + } + +err_free_parser: + XML_ParserFree(parser); +err_close_file: + fclose(file); +done: + return ret; +} diff --git a/audio/hal/voice.c b/audio/hal/voice.c new file mode 100644 index 0000000..c4fa163 --- /dev/null +++ b/audio/hal/voice.c @@ -0,0 +1,508 @@ +/* + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * Not a contribution. + * + * Copyright (C) 2013 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 "voice" +/*#define LOG_NDEBUG 0*/ +#define LOG_NDDEBUG 0 + +#include +#include +#include +#include + +#include "audio_hw.h" +#include "voice.h" +#include "voice_extn/voice_extn.h" +#include "platform.h" +#include "platform_api.h" +#include "audio_extn.h" + +struct pcm_config pcm_config_voice_call = { + .channels = 1, + .rate = 8000, + .period_size = 160, + .period_count = 2, + .format = PCM_FORMAT_S16_LE, +}; + +static struct voice_session *voice_get_session_from_use_case(struct audio_device *adev, + audio_usecase_t usecase_id) +{ + struct voice_session *session = NULL; + int ret = 0; + + ret = voice_extn_get_session_from_use_case(adev, usecase_id, &session); + if (ret == -ENOSYS) { + session = &adev->voice.session[VOICE_SESS_IDX]; + } + + return session; +} + +int voice_stop_usecase(struct audio_device *adev, audio_usecase_t usecase_id) +{ + int i, ret = 0; + struct audio_usecase *uc_info; + struct voice_session *session = NULL; + + ALOGD("%s: enter usecase:%s", __func__, use_case_table[usecase_id]); + + session = (struct voice_session *)voice_get_session_from_use_case(adev, usecase_id); + if (!session) { + ALOGE("stop_call: couldn't find voice session"); + return -EINVAL; + } + + session->state.current = CALL_INACTIVE; + if (adev->mode == AUDIO_MODE_NORMAL) + adev->voice.is_in_call = false; + + ret = platform_stop_voice_call(adev->platform, session->vsid); + + /* 1. Close the PCM devices */ + if (session->pcm_rx) { + pcm_close(session->pcm_rx); + session->pcm_rx = NULL; + } + if (session->pcm_tx) { + pcm_close(session->pcm_tx); + session->pcm_tx = NULL; + } + + uc_info = get_usecase_from_list(adev, usecase_id); + if (uc_info == NULL) { + ALOGE("%s: Could not find the usecase (%d) in the list", + __func__, usecase_id); + return -EINVAL; + } + + /* 2. Get and set stream specific mixer controls */ + disable_audio_route(adev, uc_info); + + /* 3. Disable the rx and tx devices */ + disable_snd_device(adev, uc_info->out_snd_device); + disable_snd_device(adev, uc_info->in_snd_device); + + list_remove(&uc_info->list); + free(uc_info); + + ALOGD("%s: exit: status(%d)", __func__, ret); + return ret; +} + +int voice_start_usecase(struct audio_device *adev, audio_usecase_t usecase_id) +{ + int i, ret = 0; + struct audio_usecase *uc_info; + int pcm_dev_rx_id, pcm_dev_tx_id; + uint32_t sample_rate = 8000; + struct voice_session *session = NULL; + struct pcm_config voice_config = pcm_config_voice_call; + + ALOGD("%s: enter usecase:%s", __func__, use_case_table[usecase_id]); + + session = (struct voice_session *)voice_get_session_from_use_case(adev, usecase_id); + if (!session) { + ALOGE("start_call: couldn't find voice session"); + return -EINVAL; + } + + uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase)); + if (!uc_info) { + ALOGE("start_call: couldn't allocate mem for audio_usecase"); + return -ENOMEM; + } + + uc_info->id = usecase_id; + uc_info->type = VOICE_CALL; + uc_info->stream.out = adev->current_call_output ; + uc_info->devices = adev->current_call_output ->devices; + uc_info->in_snd_device = SND_DEVICE_NONE; + uc_info->out_snd_device = SND_DEVICE_NONE; + + list_add_tail(&adev->usecase_list, &uc_info->list); + + select_devices(adev, usecase_id); + + pcm_dev_rx_id = platform_get_pcm_device_id(uc_info->id, PCM_PLAYBACK); + pcm_dev_tx_id = platform_get_pcm_device_id(uc_info->id, PCM_CAPTURE); + + if (pcm_dev_rx_id < 0 || pcm_dev_tx_id < 0) { + ALOGE("%s: Invalid PCM devices (rx: %d tx: %d) for the usecase(%d)", + __func__, pcm_dev_rx_id, pcm_dev_tx_id, uc_info->id); + ret = -EIO; + goto error_start_voice; + } + ret = platform_get_sample_rate(adev->platform, &sample_rate); + if (ret < 0) { + ALOGE("platform_get_sample_rate error %d\n", ret); + } else { + voice_config.rate = sample_rate; + } + ALOGD("voice_config.rate %d\n", voice_config.rate); + + ALOGV("%s: Opening PCM playback device card_id(%d) device_id(%d)", + __func__, adev->snd_card, pcm_dev_rx_id); + session->pcm_rx = pcm_open(adev->snd_card, + pcm_dev_rx_id, + PCM_OUT, &voice_config); + if (session->pcm_rx && !pcm_is_ready(session->pcm_rx)) { + ALOGE("%s: %s", __func__, pcm_get_error(session->pcm_rx)); + ret = -EIO; + goto error_start_voice; + } + + ALOGV("%s: Opening PCM capture device card_id(%d) device_id(%d)", + __func__, adev->snd_card, pcm_dev_tx_id); + session->pcm_tx = pcm_open(adev->snd_card, + pcm_dev_tx_id, + PCM_IN, &voice_config); + if (session->pcm_tx && !pcm_is_ready(session->pcm_tx)) { + ALOGE("%s: %s", __func__, pcm_get_error(session->pcm_tx)); + ret = -EIO; + goto error_start_voice; + } + pcm_start(session->pcm_rx); + pcm_start(session->pcm_tx); + + voice_set_volume(adev, adev->voice.volume); + + ret = platform_start_voice_call(adev->platform, session->vsid); + if (ret < 0) { + ALOGE("%s: platform_start_voice_call error %d\n", __func__, ret); + goto error_start_voice; + } + + session->state.current = CALL_ACTIVE; + goto done; + +error_start_voice: + voice_stop_usecase(adev, usecase_id); + +done: + ALOGD("%s: exit: status(%d)", __func__, ret); + return ret; +} + +bool voice_is_call_state_active(struct audio_device *adev) +{ + bool call_state = false; + int ret = 0; + + ret = voice_extn_is_call_state_active(adev, &call_state); + if (ret == -ENOSYS) { + call_state = (adev->voice.session[VOICE_SESS_IDX].state.current == CALL_ACTIVE) ? true : false; + } + + return call_state; +} + +bool voice_is_in_call(struct audio_device *adev) +{ + return adev->voice.in_call; +} + +bool voice_is_in_call_rec_stream(struct stream_in *in) +{ + bool in_call_rec = false; + int ret = 0; + + ret = voice_extn_is_in_call_rec_stream(in, &in_call_rec); + if (ret == -ENOSYS) { + in_call_rec = false; + } + + return in_call_rec; +} + +uint32_t voice_get_active_session_id(struct audio_device *adev) +{ + int ret = 0; + uint32_t session_id; + + ret = voice_extn_get_active_session_id(adev, &session_id); + if (ret == -ENOSYS) { + session_id = VOICE_VSID; + } + return session_id; +} + +int voice_check_and_set_incall_rec_usecase(struct audio_device *adev, + struct stream_in *in) +{ + int ret = 0; + uint32_t session_id; + int usecase_id; + int rec_mode = INCALL_REC_NONE; + + if (voice_is_call_state_active(adev)) { + switch (in->source) { + case AUDIO_SOURCE_VOICE_UPLINK: + if (audio_extn_compr_cap_enabled() && + audio_extn_compr_cap_format_supported(in->config.format)) { + in->usecase = USECASE_INCALL_REC_UPLINK_COMPRESS; + } else + in->usecase = USECASE_INCALL_REC_UPLINK; + rec_mode = INCALL_REC_UPLINK; + break; + case AUDIO_SOURCE_VOICE_DOWNLINK: + if (audio_extn_compr_cap_enabled() && + audio_extn_compr_cap_format_supported(in->config.format)) { + in->usecase = USECASE_INCALL_REC_DOWNLINK_COMPRESS; + } else + in->usecase = USECASE_INCALL_REC_DOWNLINK; + rec_mode = INCALL_REC_DOWNLINK; + break; + case AUDIO_SOURCE_VOICE_CALL: + if (audio_extn_compr_cap_enabled() && + audio_extn_compr_cap_format_supported(in->config.format)) { + in->usecase = USECASE_INCALL_REC_UPLINK_AND_DOWNLINK_COMPRESS; + } else + in->usecase = USECASE_INCALL_REC_UPLINK_AND_DOWNLINK; + rec_mode = INCALL_REC_UPLINK_AND_DOWNLINK; + break; + default: + ALOGV("%s: Source type %d doesnt match incall recording criteria", + __func__, in->source); + return ret; + } + + session_id = voice_get_active_session_id(adev); + ret = platform_set_incall_recording_session_id(adev->platform, + session_id, rec_mode); + ALOGV("%s: Update usecase to %d",__func__, in->usecase); + } else { + ALOGV("%s: voice call not active", __func__); + } + + return ret; +} + +int voice_check_and_stop_incall_rec_usecase(struct audio_device *adev, + struct stream_in *in) +{ + int ret = 0; + + if (in->source == AUDIO_SOURCE_VOICE_UPLINK || + in->source == AUDIO_SOURCE_VOICE_DOWNLINK || + in->source == AUDIO_SOURCE_VOICE_CALL) { + ret = platform_stop_incall_recording_usecase(adev->platform); + ALOGV("%s: Stop In-call recording", __func__); + } + + return ret; +} + +int voice_check_and_set_incall_music_usecase(struct audio_device *adev, + struct stream_out *out) +{ + int ret = 0; + + ret = voice_extn_check_and_set_incall_music_usecase(adev, out); + if (ret == -ENOSYS) { + /* Incall music delivery is used only for LCH call state */ + ret = -EINVAL; + } + + return ret; +} + +int voice_set_mic_mute(struct audio_device *adev, bool state) +{ + int err = 0; + + adev->voice.mic_mute = state; + if (adev->mode == AUDIO_MODE_IN_CALL) + err = platform_set_mic_mute(adev->platform, state); + if (adev->mode == AUDIO_MODE_IN_COMMUNICATION) + err = voice_extn_compress_voip_set_mic_mute(adev, state); + + return err; +} + +bool voice_get_mic_mute(struct audio_device *adev) +{ + return adev->voice.mic_mute; +} + +int voice_set_volume(struct audio_device *adev, float volume) +{ + int vol, err = 0; + + adev->voice.volume = volume; + if (adev->mode == AUDIO_MODE_IN_CALL) { + if (volume < 0.0) { + volume = 0.0; + } else if (volume > 1.0) { + volume = 1.0; + } + + vol = lrint(volume * 100.0); + + // Voice volume levels from android are mapped to driver volume levels as follows. + // 0 -> 5, 20 -> 4, 40 ->3, 60 -> 2, 80 -> 1, 100 -> 0 + // So adjust the volume to get the correct volume index in driver + vol = 100 - vol; + + err = platform_set_voice_volume(adev->platform, vol); + } + if (adev->mode == AUDIO_MODE_IN_COMMUNICATION) + err = voice_extn_compress_voip_set_volume(adev, volume); + + + return err; +} + +int voice_start_call(struct audio_device *adev) +{ + int ret = 0; + + ret = voice_extn_start_call(adev); + if (ret == -ENOSYS) { + ret = voice_start_usecase(adev, USECASE_VOICE_CALL); + } + adev->voice.in_call = true; + + return ret; +} + +int voice_stop_call(struct audio_device *adev) +{ + int ret = 0; + + adev->voice.in_call = false; + ret = voice_extn_stop_call(adev); + if (ret == -ENOSYS) { + ret = voice_stop_usecase(adev, USECASE_VOICE_CALL); + } + + return ret; +} + +void voice_get_parameters(struct audio_device *adev, + struct str_parms *query, + struct str_parms *reply) +{ + voice_extn_get_parameters(adev, query, reply); +} + +int voice_set_parameters(struct audio_device *adev, struct str_parms *parms) +{ + char *str; + char value[32]; + int val; + int ret = 0, err; + char *kv_pairs = str_parms_to_str(parms); + + ALOGV_IF(kv_pairs != NULL, "%s: enter: %s", __func__, kv_pairs); + + ret = voice_extn_set_parameters(adev, parms); + if (ret != 0) { + if (ret == -ENOSYS) + ret = 0; + else + goto done; + } + + ret = voice_extn_compress_voip_set_parameters(adev, parms); + if (ret != 0) { + if (ret == -ENOSYS) + ret = 0; + else + goto done; + } + + err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_TTY_MODE, value, sizeof(value)); + if (err >= 0) { + int tty_mode; + str_parms_del(parms, AUDIO_PARAMETER_KEY_TTY_MODE); + if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_OFF) == 0) + tty_mode = TTY_MODE_OFF; + else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_VCO) == 0) + tty_mode = TTY_MODE_VCO; + else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_HCO) == 0) + tty_mode = TTY_MODE_HCO; + else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_FULL) == 0) + tty_mode = TTY_MODE_FULL; + else { + ret = -EINVAL; + goto done; + } + + if (tty_mode != adev->voice.tty_mode) { + adev->voice.tty_mode = tty_mode; + adev->acdb_settings = (adev->acdb_settings & TTY_MODE_CLEAR) | tty_mode; + if (voice_is_call_state_active(adev)) + voice_update_devices_for_all_voice_usecases(adev); + } + } + + err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_INCALLMUSIC, + value, sizeof(value)); + if (err >= 0) { + str_parms_del(parms, AUDIO_PARAMETER_KEY_INCALLMUSIC); + if (strcmp(value, AUDIO_PARAMETER_VALUE_TRUE) == 0) + platform_start_incall_music_usecase(adev->platform); + else + platform_stop_incall_music_usecase(adev->platform); + } + +done: + ALOGV("%s: exit with code(%d)", __func__, ret); + free(kv_pairs); + return ret; +} + +void voice_init(struct audio_device *adev) +{ + int i = 0; + + memset(&adev->voice, 0, sizeof(adev->voice)); + adev->voice.tty_mode = TTY_MODE_OFF; + adev->voice.volume = 1.0f; + adev->voice.mic_mute = false; + adev->voice.in_call = false; + for (i = 0; i < MAX_VOICE_SESSIONS; i++) { + adev->voice.session[i].pcm_rx = NULL; + adev->voice.session[i].pcm_tx = NULL; + adev->voice.session[i].state.current = CALL_INACTIVE; + adev->voice.session[i].state.new = CALL_INACTIVE; + adev->voice.session[i].vsid = VOICE_VSID; + } + + voice_extn_init(adev); +} + +void voice_update_devices_for_all_voice_usecases(struct audio_device *adev) +{ + struct listnode *node; + struct audio_usecase *usecase; + + list_for_each(node, &adev->usecase_list) { + usecase = node_to_item(node, struct audio_usecase, list); + if (usecase->type == VOICE_CALL) { + ALOGV("%s: updating device for usecase:%s", __func__, + use_case_table[usecase->id]); + usecase->stream.out = adev->current_call_output; + select_devices(adev, usecase->id); + } + } +} + + diff --git a/audio/hal/voice.h b/audio/hal/voice.h new file mode 100644 index 0000000..9be8443 --- /dev/null +++ b/audio/hal/voice.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * Not a contribution. + * + * Copyright (C) 2013 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. + */ + +#ifndef VOICE_H +#define VOICE_H + +#define BASE_SESS_IDX 0 +#define VOICE_SESS_IDX (BASE_SESS_IDX) + +#ifdef MULTI_VOICE_SESSION_ENABLED +#define MAX_VOICE_SESSIONS 5 +#else +#define MAX_VOICE_SESSIONS 1 +#endif + +#define BASE_CALL_STATE 1 +#define CALL_INACTIVE (BASE_CALL_STATE) +#define CALL_ACTIVE (BASE_CALL_STATE + 1) + +#define VOICE_VSID 0x10C01000 + +#define AUDIO_PARAMETER_KEY_INCALLMUSIC "incall_music_enabled" +#define AUDIO_PARAMETER_VALUE_TRUE "true" + +struct audio_device; +struct str_parms; +struct stream_in; +struct stream_out; +typedef int audio_usecase_t; + +struct call_state { + int current; + int new; +}; + +struct voice_session { + struct pcm *pcm_rx; + struct pcm *pcm_tx; + struct call_state state; + uint32_t vsid; +}; + +struct voice { + struct voice_session session[MAX_VOICE_SESSIONS]; + int tty_mode; + bool mic_mute; + float volume; + bool is_in_call; + bool in_call; +}; + +enum { + INCALL_REC_NONE = -1, + INCALL_REC_UPLINK, + INCALL_REC_DOWNLINK, + INCALL_REC_UPLINK_AND_DOWNLINK, +}; + +int voice_start_usecase(struct audio_device *adev, audio_usecase_t usecase_id); +int voice_stop_usecase(struct audio_device *adev, audio_usecase_t usecase_id); + +int voice_start_call(struct audio_device *adev); +int voice_stop_call(struct audio_device *adev); +int voice_set_parameters(struct audio_device *adev, struct str_parms *parms); +void voice_get_parameters(struct audio_device *adev, struct str_parms *query, + struct str_parms *reply); +void voice_init(struct audio_device *adev); +bool voice_is_in_call(struct audio_device *adev); +bool voice_is_in_call_rec_stream(struct stream_in *in); +int voice_set_mic_mute(struct audio_device *dev, bool state); +bool voice_get_mic_mute(struct audio_device *dev); +int voice_set_volume(struct audio_device *adev, float volume); +int voice_check_and_set_incall_rec_usecase(struct audio_device *adev, + struct stream_in *in); +int voice_check_and_set_incall_music_usecase(struct audio_device *adev, + struct stream_out *out); +int voice_check_and_stop_incall_rec_usecase(struct audio_device *adev, + struct stream_in *in); +void voice_update_devices_for_all_voice_usecases(struct audio_device *adev); +#endif //VOICE_H diff --git a/audio/hal/voice_extn/compress_voip.c b/audio/hal/voice_extn/compress_voip.c new file mode 100644 index 0000000..26636db --- /dev/null +++ b/audio/hal/voice_extn/compress_voip.c @@ -0,0 +1,811 @@ +/* + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * Not a contribution. + * + * Copyright (C) 2013 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 "compress_voip" +/*#define LOG_NDEBUG 0*/ +#define LOG_NDDEBUG 0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "audio_hw.h" +#include "platform_api.h" +#include "platform.h" +#include "voice_extn.h" + +#define COMPRESS_VOIP_IO_BUF_SIZE_NB 320 +#define COMPRESS_VOIP_IO_BUF_SIZE_WB 640 + +struct pcm_config pcm_config_voip_nb = { + .channels = 1, + .rate = 8000, /* changed when the stream is opened */ + .period_size = COMPRESS_VOIP_IO_BUF_SIZE_NB/2, + .period_count = 10, + .format = PCM_FORMAT_S16_LE, +}; + +struct pcm_config pcm_config_voip_wb = { + .channels = 1, + .rate = 16000, /* changed when the stream is opened */ + .period_size = COMPRESS_VOIP_IO_BUF_SIZE_WB/2, + .period_count = 10, + .format = PCM_FORMAT_S16_LE, +}; + +struct voip_data { + struct pcm *pcm_rx; + struct pcm *pcm_tx; + struct stream_out *out_stream; + uint32_t out_stream_count; + uint32_t in_stream_count; + uint32_t sample_rate; +}; + +#define MODE_IS127 0x2 +#define MODE_4GV_NB 0x3 +#define MODE_4GV_WB 0x4 +#define MODE_AMR 0x5 +#define MODE_AMR_WB 0xD +#define MODE_PCM 0xC +#define MODE_4GV_NW 0xE + +#define AUDIO_PARAMETER_KEY_VOIP_RATE "voip_rate" +#define AUDIO_PARAMETER_KEY_VOIP_EVRC_RATE_MIN "evrc_rate_min" +#define AUDIO_PARAMETER_KEY_VOIP_EVRC_RATE_MAX "evrc_rate_max" +#define AUDIO_PARAMETER_KEY_VOIP_DTX_MODE "dtx_on" +#define AUDIO_PARAMETER_VALUE_VOIP_TRUE "true" +#define AUDIO_PARAMETER_KEY_VOIP_CHECK "voip_flag" +#define AUDIO_PARAMETER_KEY_VOIP_OUT_STREAM_COUNT "voip_out_stream_count" +#define AUDIO_PARAMETER_KEY_VOIP_SAMPLE_RATE "voip_sample_rate" + +static struct voip_data voip_data = { + .pcm_rx = NULL, + .pcm_tx = NULL, + .out_stream = NULL, + .out_stream_count = 0, + .in_stream_count = 0, + .sample_rate = 0 +}; + +static int voip_set_volume(struct audio_device *adev, int volume); +static int voip_set_mic_mute(struct audio_device *adev, bool state); +static int voip_set_mode(struct audio_device *adev, int format); +static int voip_set_rate(struct audio_device *adev, int rate); +static int voip_set_evrc_min_max_rate(struct audio_device *adev, int min_rate, + int max_rate); +static int voip_set_dtx(struct audio_device *adev, bool enable); +static int voip_stop_call(struct audio_device *adev); +static int voip_start_call(struct audio_device *adev, + struct pcm_config *voip_config); + +static int audio_format_to_voip_mode(int format) +{ + int mode; + + switch(format) { + case AUDIO_FORMAT_PCM_16_BIT: + mode = MODE_PCM; + break; + case AUDIO_FORMAT_AMR_NB: + mode = MODE_AMR; + break; + case AUDIO_FORMAT_AMR_WB: + mode = MODE_AMR_WB; + break; + case AUDIO_FORMAT_EVRC: + mode = MODE_IS127; + break; + case AUDIO_FORMAT_EVRCB: + mode = MODE_4GV_NB; + break; + case AUDIO_FORMAT_EVRCWB: + mode = MODE_4GV_WB; + break; + case AUDIO_FORMAT_EVRCNW: + mode = MODE_4GV_NW; + break; + default: + mode = MODE_PCM; + } + return mode; +} + +static int voip_set_volume(struct audio_device *adev, int volume) +{ + struct mixer_ctl *ctl; + const char *mixer_ctl_name = "Voip Rx Gain"; + int vol_index = 0; + uint32_t set_values[ ] = {0, + DEFAULT_VOLUME_RAMP_DURATION_MS}; + + ALOGV("%s: enter", __func__); + + /* Voice volume levels are mapped to adsp volume levels as follows. + * 100 -> 5, 80 -> 4, 60 -> 3, 40 -> 2, 20 -> 1 0 -> 0 + * But this values don't changed in kernel. So, below change is need. + */ + vol_index = (int)percent_to_index(volume, MIN_VOL_INDEX, MAX_VOL_INDEX); + set_values[0] = vol_index; + + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + return -EINVAL; + } + ALOGV("%s: Setting voip volume index: %d", __func__, set_values[0]); + mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values)); + + ALOGV("%s: exit", __func__); + return 0; +} + +static int voip_set_mic_mute(struct audio_device *adev, bool state) +{ + struct mixer_ctl *ctl; + const char *mixer_ctl_name = "Voip Tx Mute"; + uint32_t set_values[ ] = {0, + DEFAULT_VOLUME_RAMP_DURATION_MS}; + + ALOGV("%s: enter, state=%d", __func__, state); + + if (adev->mode == AUDIO_MODE_IN_COMMUNICATION) { + set_values[0] = state; + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + return -EINVAL; + } + mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values)); + } + + ALOGV("%s: exit", __func__); + return 0; +} + +static int voip_set_mode(struct audio_device *adev, int format) +{ + struct mixer_ctl *ctl; + const char *mixer_ctl_name = "Voip Mode Config"; + uint32_t set_values[ ] = {0}; + int mode; + + ALOGD("%s: enter, format=%d", __func__, format); + + mode = audio_format_to_voip_mode(format); + ALOGD("%s: Derived mode = %d", __func__, mode); + + set_values[0] = mode; + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + return -EINVAL; + } + mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values)); + + ALOGV("%s: exit", __func__); + return 0; +} + +static int voip_set_rate(struct audio_device *adev, int rate) +{ + struct mixer_ctl *ctl; + const char *mixer_ctl_name = "Voip Rate Config"; + uint32_t set_values[ ] = {0}; + + ALOGD("%s: enter, rate=%d", __func__, rate); + + set_values[0] = rate; + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + return -EINVAL; + } + mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values)); + + ALOGV("%s: exit", __func__); + return 0; +} + +static int voip_set_evrc_min_max_rate(struct audio_device *adev, int min_rate, + int max_rate) +{ + struct mixer_ctl *ctl; + const char *mixer_ctl_name = "Voip Evrc Min Max Rate Config"; + uint32_t set_values[ ] = {0, 0}; + + ALOGD("%s: enter, min_rate=%d, max_rate=%d", + __func__, min_rate, max_rate); + + set_values[0] = min_rate; + set_values[1] = max_rate; + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + return -EINVAL; + } + mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values)); + + ALOGV("%s: exit", __func__); + return 0; +} + +static int voip_set_dtx(struct audio_device *adev, bool enable) +{ + struct mixer_ctl *ctl; + const char *mixer_ctl_name = "Voip Dtx Mode"; + uint32_t set_values[ ] = {0}; + + ALOGD("%s: enter, enable=%d", __func__, enable); + + set_values[0] = enable; + ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name); + if (!ctl) { + ALOGE("%s: Could not get ctl for mixer cmd - %s", + __func__, mixer_ctl_name); + return -EINVAL; + } + mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values)); + + ALOGV("%s: exit", __func__); + return 0; +} + +static int voip_stop_call(struct audio_device *adev) +{ + int i, ret = 0; + struct audio_usecase *uc_info; + + ALOGD("%s: enter, out_stream_count=%d, in_stream_count=%d", + __func__, voip_data.out_stream_count, voip_data.in_stream_count); + + if (!voip_data.out_stream_count && !voip_data.in_stream_count) { + voip_data.sample_rate = 0; + uc_info = get_usecase_from_list(adev, USECASE_COMPRESS_VOIP_CALL); + if (uc_info == NULL) { + ALOGE("%s: Could not find the usecase (%d) in the list", + __func__, USECASE_COMPRESS_VOIP_CALL); + return -EINVAL; + } + + /* 1. Close the PCM devices */ + if (voip_data.pcm_rx) { + pcm_close(voip_data.pcm_rx); + voip_data.pcm_rx = NULL; + } + if (voip_data.pcm_tx) { + pcm_close(voip_data.pcm_tx); + voip_data.pcm_tx = NULL; + } + + /* 2. Get and set stream specific mixer controls */ + disable_audio_route(adev, uc_info); + + /* 3. Disable the rx and tx devices */ + disable_snd_device(adev, uc_info->out_snd_device); + disable_snd_device(adev, uc_info->in_snd_device); + + list_remove(&uc_info->list); + free(uc_info); + } else + ALOGV("%s: NO-OP because out_stream_count=%d, in_stream_count=%d", + __func__, voip_data.out_stream_count, voip_data.in_stream_count); + + ALOGV("%s: exit: status(%d)", __func__, ret); + return ret; +} + +static int voip_start_call(struct audio_device *adev, + struct pcm_config *voip_config) +{ + int i, ret = 0; + struct audio_usecase *uc_info; + int pcm_dev_rx_id, pcm_dev_tx_id; + + ALOGD("%s: enter", __func__); + + uc_info = get_usecase_from_list(adev, USECASE_COMPRESS_VOIP_CALL); + if (uc_info == NULL) { + ALOGV("%s: voip usecase is added to the list", __func__); + uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase)); + + if (!uc_info) { + ALOGE("failed to allocate voip usecase mem"); + return -ENOMEM; + } + + uc_info->id = USECASE_COMPRESS_VOIP_CALL; + uc_info->type = VOIP_CALL; + if (voip_data.out_stream) + uc_info->stream.out = voip_data.out_stream; + else + uc_info->stream.out = adev->primary_output; + uc_info->in_snd_device = SND_DEVICE_NONE; + uc_info->out_snd_device = SND_DEVICE_NONE; + + list_add_tail(&adev->usecase_list, &uc_info->list); + + select_devices(adev, USECASE_COMPRESS_VOIP_CALL); + + pcm_dev_rx_id = platform_get_pcm_device_id(uc_info->id, PCM_PLAYBACK); + pcm_dev_tx_id = platform_get_pcm_device_id(uc_info->id, PCM_CAPTURE); + + if (pcm_dev_rx_id < 0 || pcm_dev_tx_id < 0) { + ALOGE("%s: Invalid PCM devices (rx: %d tx: %d) for the usecase(%d)", + __func__, pcm_dev_rx_id, pcm_dev_tx_id, uc_info->id); + ret = -EIO; + goto error_start_voip; + } + + ALOGD("%s: Opening PCM playback device card_id(%d) device_id(%d)", + __func__, adev->snd_card, pcm_dev_rx_id); + voip_data.pcm_rx = pcm_open(adev->snd_card, + pcm_dev_rx_id, + PCM_OUT, voip_config); + if (voip_data.pcm_rx && !pcm_is_ready(voip_data.pcm_rx)) { + ALOGE("%s: %s", __func__, pcm_get_error(voip_data.pcm_rx)); + pcm_close(voip_data.pcm_rx); + voip_data.pcm_rx = NULL; + ret = -EIO; + goto error_start_voip; + } + + ALOGD("%s: Opening PCM capture device card_id(%d) device_id(%d)", + __func__, adev->snd_card, pcm_dev_tx_id); + voip_data.pcm_tx = pcm_open(adev->snd_card, + pcm_dev_tx_id, + PCM_IN, voip_config); + if (voip_data.pcm_tx && !pcm_is_ready(voip_data.pcm_tx)) { + ALOGE("%s: %s", __func__, pcm_get_error(voip_data.pcm_tx)); + pcm_close(voip_data.pcm_rx); + voip_data.pcm_tx = NULL; + if (voip_data.pcm_rx) { + pcm_close(voip_data.pcm_rx); + voip_data.pcm_rx = NULL; + } + ret = -EIO; + goto error_start_voip; + } + pcm_start(voip_data.pcm_rx); + pcm_start(voip_data.pcm_tx); + + voice_extn_compress_voip_set_volume(adev, adev->voice.volume); + + if (ret < 0) { + ALOGE("%s: error %d\n", __func__, ret); + goto error_start_voip; + } + } else { + ALOGV("%s: voip usecase is already enabled", __func__); + if (voip_data.out_stream) + uc_info->stream.out = voip_data.out_stream; + else + uc_info->stream.out = adev->primary_output; + select_devices(adev, USECASE_COMPRESS_VOIP_CALL); + } + + return 0; + +error_start_voip: + voip_stop_call(adev); + + ALOGV("%s: exit: status(%d)", __func__, ret); + return ret; +} + +int voice_extn_compress_voip_set_parameters(struct audio_device *adev, + struct str_parms *parms) +{ + char *str; + char value[32]={0}; + int ret = 0, err, rate; + int min_rate, max_rate; + bool flag; + char *kv_pairs = str_parms_to_str(parms); + + ALOGV_IF(kv_pairs != NULL, "%s: enter: %s", __func__, kv_pairs); + + err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_VOIP_RATE, + value, sizeof(value)); + if (err >= 0) { + rate = atoi(value); + voip_set_rate(adev, rate); + voip_set_evrc_min_max_rate(adev, rate, rate); + } + + memset(value, 0, sizeof(value)); + err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_VOIP_EVRC_RATE_MIN, + value, sizeof(value)); + if (err >= 0) { + min_rate = atoi(value); + str_parms_del(parms, AUDIO_PARAMETER_KEY_VOIP_EVRC_RATE_MIN); + memset(value, 0, sizeof(value)); + err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_VOIP_EVRC_RATE_MAX, + value, sizeof(value)); + if (err >= 0) { + max_rate = atoi(value); + voip_set_evrc_min_max_rate(adev, min_rate, max_rate); + } else { + ALOGE("%s: AUDIO_PARAMETER_KEY_VOIP_EVRC_RATE_MAX not found", __func__); + ret = -EINVAL; + goto done; + } + } + + memset(value, 0, sizeof(value)); + err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_VOIP_DTX_MODE, + value, sizeof(value)); + if (err >= 0) { + flag = false; + if (strcmp(value, AUDIO_PARAMETER_VALUE_VOIP_TRUE) == 0) + flag = true; + voip_set_dtx(adev, flag); + } + +done: + ALOGV("%s: exit", __func__); + free(kv_pairs); + return ret; +} + +void voice_extn_compress_voip_get_parameters(struct str_parms *query, + struct str_parms *reply) +{ + int ret; + char value[32]={0}; + char *str = NULL; + + ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_VOIP_OUT_STREAM_COUNT, + value, sizeof(value)); + if (ret >= 0) { + str_parms_add_int(reply, AUDIO_PARAMETER_KEY_VOIP_OUT_STREAM_COUNT, + voip_data.out_stream_count); + } + + ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_VOIP_SAMPLE_RATE, + value, sizeof(value)); + if (ret >= 0) { + str_parms_add_int(reply, AUDIO_PARAMETER_KEY_VOIP_SAMPLE_RATE, + voip_data.sample_rate); + } +} + +void voice_extn_compress_voip_out_get_parameters(struct stream_out *out, + struct str_parms *query, + struct str_parms *reply) +{ + int ret, val; + char value[32]={0}; + + ALOGD("%s: enter", __func__); + + ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_VOIP_CHECK, value, sizeof(value)); + + if (ret >= 0) { + if (out->usecase == USECASE_COMPRESS_VOIP_CALL) + str_parms_add_int(reply, AUDIO_PARAMETER_KEY_VOIP_CHECK, true); + else + str_parms_add_int(reply, AUDIO_PARAMETER_KEY_VOIP_CHECK, false); + } + + ALOGV("%s: exit", __func__); +} + +void voice_extn_compress_voip_in_get_parameters(struct stream_in *in, + struct str_parms *query, + struct str_parms *reply) +{ + int ret, val; + char value[32]={0}; + char *kv_pairs = NULL; + + ALOGV("%s: enter", __func__); + + ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_VOIP_CHECK, value, sizeof(value)); + + if (ret >= 0) { + if (in->usecase == USECASE_COMPRESS_VOIP_CALL) + str_parms_add_int(reply, AUDIO_PARAMETER_KEY_VOIP_CHECK, true); + else + str_parms_add_int(reply, AUDIO_PARAMETER_KEY_VOIP_CHECK, false); + } + + kv_pairs = str_parms_to_str(reply); + ALOGD_IF(kv_pairs != NULL, "%s: exit: return - %s", __func__, kv_pairs); + free(kv_pairs); +} + +int voice_extn_compress_voip_out_get_buffer_size(struct stream_out *out) +{ + if (out->config.rate == 16000) + return COMPRESS_VOIP_IO_BUF_SIZE_WB; + else + return COMPRESS_VOIP_IO_BUF_SIZE_NB; +} + +int voice_extn_compress_voip_in_get_buffer_size(struct stream_in *in) +{ + if (in->config.rate == 16000) + return COMPRESS_VOIP_IO_BUF_SIZE_WB; + else + return COMPRESS_VOIP_IO_BUF_SIZE_NB; +} + +int voice_extn_compress_voip_start_output_stream(struct stream_out *out) +{ + int ret = 0; + struct audio_device *adev = out->dev; + struct audio_usecase *uc_info; + int snd_card_status = get_snd_card_state(adev); + + ALOGD("%s: enter", __func__); + + if (SND_CARD_STATE_OFFLINE == snd_card_status) { + ret = -ENETRESET; + ALOGE("%s: sound card is not active/SSR returning error %d ", __func__, ret); + goto error; + } + + if (!voip_data.out_stream_count) + ret = voice_extn_compress_voip_open_output_stream(out); + + ret = voip_start_call(adev, &out->config); + out->pcm = voip_data.pcm_rx; + uc_info = get_usecase_from_list(adev, USECASE_COMPRESS_VOIP_CALL); + if (uc_info) { + uc_info->stream.out = out; + uc_info->devices = out->devices; + } else { + ret = -EINVAL; + ALOGE("%s: exit(%d): failed to get use case info", __func__, ret); + goto error; + } + +error: + ALOGV("%s: exit: status(%d)", __func__, ret); + return ret; +} + +int voice_extn_compress_voip_start_input_stream(struct stream_in *in) +{ + int ret = 0; + struct audio_usecase *uc_info; + struct audio_device *adev = in->dev; + int snd_card_status = get_snd_card_state(adev); + + ALOGD("%s: enter", __func__); + + if (SND_CARD_STATE_OFFLINE == snd_card_status) { + ret = -ENETRESET; + ALOGE("%s: sound card is not active/SSR returning error %d ", __func__, ret); + goto error; + } + + if (!voip_data.in_stream_count) + ret = voice_extn_compress_voip_open_input_stream(in); + + adev->active_input = in; + ret = voip_start_call(adev, &in->config); + in->pcm = voip_data.pcm_tx; + +error: + ALOGV("%s: exit: status(%d)", __func__, ret); + return ret; +} + +int voice_extn_compress_voip_close_output_stream(struct audio_stream *stream) +{ + struct stream_out *out = (struct stream_out *)stream; + struct audio_device *adev = out->dev; + int ret = 0; + + ALOGD("%s: enter", __func__); + if (voip_data.out_stream_count > 0) { + voip_data.out_stream_count--; + ret = voip_stop_call(adev); + voip_data.out_stream = NULL; + out->pcm = NULL; + } + + ALOGV("%s: exit: status(%d)", __func__, ret); + return ret; +} + +int voice_extn_compress_voip_open_output_stream(struct stream_out *out) +{ + int mode, ret; + + ALOGD("%s: enter", __func__); + + out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_MONO; + out->channel_mask = AUDIO_CHANNEL_OUT_MONO; + out->usecase = USECASE_COMPRESS_VOIP_CALL; + if (out->sample_rate == 16000) + out->config = pcm_config_voip_wb; + else + out->config = pcm_config_voip_nb; + + voip_data.out_stream = out; + voip_data.out_stream_count++; + voip_data.sample_rate = out->sample_rate; + ret = voip_set_mode(out->dev, out->format); + + ALOGV("%s: exit", __func__); + return ret; +} + +int voice_extn_compress_voip_close_input_stream(struct audio_stream *stream) +{ + struct stream_in *in = (struct stream_in *)stream; + struct audio_device *adev = in->dev; + int status = 0; + + ALOGD("%s: enter", __func__); + + if(voip_data.in_stream_count > 0) { + adev->active_input = NULL; + voip_data.in_stream_count--; + status = voip_stop_call(adev); + in->pcm = NULL; + } + + ALOGV("%s: exit: status(%d)", __func__, status); + return status; +} + +int voice_extn_compress_voip_open_input_stream(struct stream_in *in) +{ + int sample_rate; + int buffer_size,frame_size; + int mode, ret; + + ALOGD("%s: enter", __func__); + + if ((voip_data.sample_rate != 0) && + (voip_data.sample_rate != in->config.rate)) { + ret = -ENOTSUP; + goto done; + } else { + voip_data.sample_rate = in->config.rate; + } + + in->usecase = USECASE_COMPRESS_VOIP_CALL; + if (in->config.rate == 16000) + in->config = pcm_config_voip_wb; + else + in->config = pcm_config_voip_nb; + + voip_data.in_stream_count++; + ret = voip_set_mode(in->dev, in->format); + +done: + ALOGV("%s: exit, ret=%d", __func__, ret); + return ret; +} + +int voice_extn_compress_voip_set_volume(struct audio_device *adev, float volume) +{ + int vol, err = 0; + + ALOGV("%s: enter", __func__); + + if (volume < 0.0) { + volume = 0.0; + } else if (volume > 1.0) { + volume = 1.0; + } + + vol = lrint(volume * 100.0); + + /* Voice volume levels from android are mapped to driver volume levels as follows. + * 0 -> 5, 20 -> 4, 40 ->3, 60 -> 2, 80 -> 1, 100 -> 0 + * So adjust the volume to get the correct volume index in driver + */ + vol = 100 - vol; + + err = voip_set_volume(adev, vol); + + ALOGV("%s: exit: status(%d)", __func__, err); + + return err; +} + +int voice_extn_compress_voip_set_mic_mute(struct audio_device *adev, bool state) +{ + int err = 0; + + ALOGV("%s: enter", __func__); + + err = voip_set_mic_mute(adev, state); + + ALOGV("%s: exit: status(%d)", __func__, err); + return err; +} + +bool voice_extn_compress_voip_pcm_prop_check() +{ + char prop_value[PROPERTY_VALUE_MAX] = {0}; + + property_get("use.voice.path.for.pcm.voip", prop_value, "0"); + if (!strncmp("true", prop_value, sizeof("true"))) + { + ALOGD("%s: VoIP PCM property is enabled", __func__); + return true; + } + else + return false; +} + +bool voice_extn_compress_voip_is_active(struct audio_device *adev) +{ + struct audio_usecase *voip_usecase = NULL; + voip_usecase = get_usecase_from_list(adev, USECASE_COMPRESS_VOIP_CALL); + + if (voip_usecase != NULL) + return true; + else + return false; +} + +bool voice_extn_compress_voip_is_format_supported(audio_format_t format) +{ + switch (format) { + case AUDIO_FORMAT_PCM_16_BIT: + if (voice_extn_compress_voip_pcm_prop_check()) + return true; + else + return false; + case AUDIO_FORMAT_AMR_NB: + case AUDIO_FORMAT_AMR_WB: + case AUDIO_FORMAT_EVRC: + case AUDIO_FORMAT_EVRCB: + case AUDIO_FORMAT_EVRCWB: + case AUDIO_FORMAT_EVRCNW: + return true; + default: + return false; + } +} + +bool voice_extn_compress_voip_is_config_supported(struct audio_config *config) +{ + bool ret = false; + + ret = voice_extn_compress_voip_is_format_supported(config->format); + if (ret) { + if ((popcount(config->channel_mask) == 1) && + (config->sample_rate == 8000 || config->sample_rate == 16000)) + ret = ((voip_data.sample_rate == 0) ? true: + (voip_data.sample_rate == config->sample_rate)); + else + ret = false; + } + return ret; +} diff --git a/audio/hal/voice_extn/voice_extn.c b/audio/hal/voice_extn/voice_extn.c new file mode 100644 index 0000000..e5b979f --- /dev/null +++ b/audio/hal/voice_extn/voice_extn.c @@ -0,0 +1,610 @@ +/* + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * Not a contribution. + * + * Copyright (C) 2013 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 "voice_extn" +/*#define LOG_NDEBUG 0*/ +#define LOG_NDDEBUG 0 + +#include +#include +#include +#include +#include +#include + +#include "audio_hw.h" +#include "voice.h" +#include "platform.h" +#include "platform_api.h" +#include "voice_extn.h" + +#define AUDIO_PARAMETER_KEY_VSID "vsid" +#define AUDIO_PARAMETER_KEY_CALL_STATE "call_state" +#define AUDIO_PARAMETER_KEY_AUDIO_MODE "audio_mode" +#define AUDIO_PARAMETER_KEY_ALL_CALL_STATES "all_call_states" +#define AUDIO_PARAMETER_KEY_DEVICE_MUTE "device_mute" +#define AUDIO_PARAMETER_KEY_DIRECTION "direction" +#define AUDIO_PARAMETER_KEY_IN_CALL "in_call" + +#define VOICE_EXTN_PARAMETER_VALUE_MAX_LEN 256 + +#define VOICE2_VSID 0x10DC1000 +#define VOLTE_VSID 0x10C02000 +#define QCHAT_VSID 0x10803000 +#define VOWLAN_VSID 0x10002000 +#define ALL_VSID 0xFFFFFFFF + +/* Voice Session Indices */ +#define VOICE2_SESS_IDX (VOICE_SESS_IDX + 1) +#define VOLTE_SESS_IDX (VOICE_SESS_IDX + 2) +#define QCHAT_SESS_IDX (VOICE_SESS_IDX + 3) +#define VOWLAN_SESS_IDX (VOICE_SESS_IDX + 4) + +/* Call States */ +#define CALL_HOLD (BASE_CALL_STATE + 2) +#define CALL_LOCAL_HOLD (BASE_CALL_STATE + 3) + +struct pcm_config pcm_config_incall_music = { + .channels = 1, + .rate = DEFAULT_OUTPUT_SAMPLING_RATE, + .period_size = LOW_LATENCY_OUTPUT_PERIOD_SIZE, + .period_count = LOW_LATENCY_OUTPUT_PERIOD_COUNT, + .format = PCM_FORMAT_S16_LE, + .start_threshold = LOW_LATENCY_OUTPUT_PERIOD_SIZE / 4, + .stop_threshold = INT_MAX, + .avail_min = LOW_LATENCY_OUTPUT_PERIOD_SIZE / 4, +}; + +int voice_extn_is_call_state_active(struct audio_device *adev, bool *is_call_active); + +static bool is_valid_call_state(int call_state) +{ + if (call_state < CALL_INACTIVE || call_state > CALL_LOCAL_HOLD) + return false; + else + return true; +} + +static bool is_valid_vsid(uint32_t vsid) +{ + if (vsid == VOICE_VSID || + vsid == VOICE2_VSID || + vsid == VOLTE_VSID || + vsid == QCHAT_VSID || + vsid == VOWLAN_VSID) + return true; + else + return false; +} + +static audio_usecase_t voice_extn_get_usecase_for_session_idx(const int index) +{ + audio_usecase_t usecase_id = -1; + + switch(index) { + case VOICE_SESS_IDX: + usecase_id = USECASE_VOICE_CALL; + break; + + case VOICE2_SESS_IDX: + usecase_id = USECASE_VOICE2_CALL; + break; + + case VOLTE_SESS_IDX: + usecase_id = USECASE_VOLTE_CALL; + break; + + case QCHAT_SESS_IDX: + usecase_id = USECASE_QCHAT_CALL; + break; + + case VOWLAN_SESS_IDX: + usecase_id = USECASE_VOWLAN_CALL; + break; + + default: + ALOGE("%s: Invalid voice session index\n", __func__); + } + + return usecase_id; +} + +static uint32_t get_session_id_with_state(struct audio_device *adev, + int call_state) +{ + struct voice_session *session = NULL; + int i = 0; + uint32_t session_id = 0; + + for (i = 0; i < MAX_VOICE_SESSIONS; i++) { + session = &adev->voice.session[i]; + if(session->state.current == call_state){ + session_id = session->vsid; + break; + } + } + + return session_id; +} + +static int update_calls(struct audio_device *adev) +{ + int i = 0; + audio_usecase_t usecase_id = 0; + enum voice_lch_mode lch_mode; + struct voice_session *session = NULL; + int fd = 0; + int ret = 0; + + ALOGD("%s: enter:", __func__); + + for (i = 0; i < MAX_VOICE_SESSIONS; i++) { + usecase_id = voice_extn_get_usecase_for_session_idx(i); + session = &adev->voice.session[i]; + ALOGD("%s: cur_state=%d new_state=%d vsid=%x", + __func__, session->state.current, session->state.new, session->vsid); + + switch(session->state.new) + { + case CALL_ACTIVE: + switch(session->state.current) + { + case CALL_INACTIVE: + ALOGD("%s: INACTIVE -> ACTIVE vsid:%x", __func__, session->vsid); + ret = voice_start_usecase(adev, usecase_id); + if(ret < 0) { + ALOGE("%s: voice_start_usecase() failed for usecase: %d\n", + __func__, usecase_id); + } else { + session->state.current = session->state.new; + } + break; + + case CALL_HOLD: + ALOGD("%s: HOLD -> ACTIVE vsid:%x", __func__, session->vsid); + session->state.current = session->state.new; + break; + + case CALL_LOCAL_HOLD: + ALOGD("%s: LOCAL_HOLD -> ACTIVE vsid:%x", __func__, session->vsid); + lch_mode = VOICE_LCH_STOP; + ret = platform_update_lch(adev->platform, session, lch_mode); + if (ret < 0) + ALOGE("%s: lch mode update failed, ret = %d", __func__, ret); + else + session->state.current = session->state.new; + break; + + default: + ALOGV("%s: CALL_ACTIVE cannot be handled in state=%d vsid:%x", + __func__, session->state.current, session->vsid); + break; + } + break; + + case CALL_INACTIVE: + switch(session->state.current) + { + case CALL_ACTIVE: + case CALL_HOLD: + case CALL_LOCAL_HOLD: + ALOGD("%s: ACTIVE/HOLD/LOCAL_HOLD -> INACTIVE vsid:%x", __func__, session->vsid); + ret = voice_stop_usecase(adev, usecase_id); + if(ret < 0) { + ALOGE("%s: voice_stop_usecase() failed for usecase: %d\n", + __func__, usecase_id); + } else { + session->state.current = session->state.new; + } + break; + + default: + ALOGV("%s: CALL_INACTIVE cannot be handled in state=%d vsid:%x", + __func__, session->state.current, session->vsid); + break; + } + break; + + case CALL_HOLD: + switch(session->state.current) + { + case CALL_ACTIVE: + ALOGD("%s: CALL_ACTIVE -> HOLD vsid:%x", __func__, session->vsid); + session->state.current = session->state.new; + break; + + case CALL_LOCAL_HOLD: + ALOGD("%s: CALL_LOCAL_HOLD -> HOLD vsid:%x", __func__, session->vsid); + lch_mode = VOICE_LCH_STOP; + ret = platform_update_lch(adev->platform, session, lch_mode); + if (ret < 0) + ALOGE("%s: lch mode update failed, ret = %d", __func__, ret); + else + session->state.current = session->state.new; + break; + + default: + ALOGV("%s: CALL_HOLD cannot be handled in state=%d vsid:%x", + __func__, session->state.current, session->vsid); + break; + } + break; + + case CALL_LOCAL_HOLD: + switch(session->state.current) + { + case CALL_ACTIVE: + case CALL_HOLD: + ALOGD("%s: ACTIVE/CALL_HOLD -> LOCAL_HOLD vsid:%x", __func__, + session->vsid); + lch_mode = VOICE_LCH_START; + ret = platform_update_lch(adev->platform, session, lch_mode); + if (ret < 0) + ALOGE("%s: lch mode update failed, ret = %d", __func__, ret); + else + session->state.current = session->state.new; + break; + + default: + ALOGV("%s: CALL_LOCAL_HOLD cannot be handled in state=%d vsid:%x", + __func__, session->state.current, session->vsid); + break; + } + break; + + default: + break; + } //end out switch loop + } //end for loop + + return ret; +} + +static int update_call_states(struct audio_device *adev, + const uint32_t vsid, const int call_state) +{ + struct voice_session *session = NULL; + int i = 0; + bool is_call_active; + + for (i = 0; i < MAX_VOICE_SESSIONS; i++) { + if (vsid == adev->voice.session[i].vsid) { + session = &adev->voice.session[i]; + break; + } + } + + if (session) { + session->state.new = call_state; + voice_extn_is_call_state_active(adev, &is_call_active); + ALOGD("%s is_call_active:%d in_call:%d, mode:%d\n", + __func__, is_call_active, adev->voice.in_call, adev->mode); + /* Dont start voice call before device routing for voice usescases has + * occured, otherwise voice calls will be started unintendedly on + * speaker. + */ + if (is_call_active || + (adev->voice.in_call && adev->mode == AUDIO_MODE_IN_CALL)) { + /* Device routing is not triggered for voice calls on the subsequent + * subs, Hence update the call states if voice call is already + * active on other sub. + */ + update_calls(adev); + } + } else { + return -EINVAL; + } + + return 0; + +} + +int voice_extn_get_active_session_id(struct audio_device *adev, + uint32_t *session_id) +{ + *session_id = get_session_id_with_state(adev, CALL_ACTIVE); + return 0; +} + +int voice_extn_is_call_state_active(struct audio_device *adev, bool *is_call_active) +{ + struct voice_session *session = NULL; + int i = 0; + *is_call_active = false; + + for (i = 0; i < MAX_VOICE_SESSIONS; i++) { + session = &adev->voice.session[i]; + if(session->state.current != CALL_INACTIVE){ + *is_call_active = true; + break; + } + } + + return 0; +} + +int voice_extn_is_in_call_rec_stream(struct stream_in *in, bool *in_call_rec) +{ + *in_call_rec = false; + + if(in->source == AUDIO_SOURCE_VOICE_DOWNLINK || + in->source == AUDIO_SOURCE_VOICE_UPLINK || + in->source == AUDIO_SOURCE_VOICE_CALL) { + *in_call_rec = true; + } + + return 0; +} + +void voice_extn_init(struct audio_device *adev) +{ + adev->voice.session[VOICE_SESS_IDX].vsid = VOICE_VSID; + adev->voice.session[VOICE2_SESS_IDX].vsid = VOICE2_VSID; + adev->voice.session[VOLTE_SESS_IDX].vsid = VOLTE_VSID; + adev->voice.session[QCHAT_SESS_IDX].vsid = QCHAT_VSID; + adev->voice.session[VOWLAN_SESS_IDX].vsid = VOWLAN_VSID; +} + +int voice_extn_get_session_from_use_case(struct audio_device *adev, + const audio_usecase_t usecase_id, + struct voice_session **session) +{ + + switch(usecase_id) + { + case USECASE_VOICE_CALL: + *session = &adev->voice.session[VOICE_SESS_IDX]; + break; + + case USECASE_VOICE2_CALL: + *session = &adev->voice.session[VOICE2_SESS_IDX]; + break; + + case USECASE_VOLTE_CALL: + *session = &adev->voice.session[VOLTE_SESS_IDX]; + break; + + case USECASE_QCHAT_CALL: + *session = &adev->voice.session[QCHAT_SESS_IDX]; + break; + + case USECASE_VOWLAN_CALL: + *session = &adev->voice.session[VOWLAN_SESS_IDX]; + break; + + default: + ALOGE("%s: Invalid usecase_id:%d\n", __func__, usecase_id); + *session = NULL; + return -EINVAL; + } + + return 0; +} + +int voice_extn_start_call(struct audio_device *adev) +{ + /* Start voice calls on sessions whose call state has been + * udpated. + */ + ALOGV("%s: enter:", __func__); + return update_calls(adev); +} + +int voice_extn_stop_call(struct audio_device *adev) +{ + int i; + int ret = 0; + + ALOGV("%s: enter:", __func__); + + /* If BT device is enabled and voice calls are ended, telephony will call + * set_mode(AUDIO_MODE_NORMAL) which will trigger audio policy manager to + * set routing with device BT A2DP profile. Hence end all voice calls when + * set_mode(AUDIO_MODE_NORMAL) before BT A2DP profile is selected. + */ + if (adev->mode == AUDIO_MODE_NORMAL) { + ALOGD("%s: end all calls", __func__); + for (i = 0; i < MAX_VOICE_SESSIONS; i++) { + adev->voice.session[i].state.new = CALL_INACTIVE; + } + + ret = update_calls(adev); + } + + return ret; +} + +int voice_extn_set_parameters(struct audio_device *adev, + struct str_parms *parms) +{ + char *str; + int value; + int ret = 0, err; + char *kv_pairs = str_parms_to_str(parms); + char str_value[256] = {0}; + + ALOGV_IF(kv_pairs != NULL, "%s: enter: %s", __func__, kv_pairs); + + err = str_parms_get_int(parms, AUDIO_PARAMETER_KEY_VSID, &value); + if (err >= 0) { + str_parms_del(parms, AUDIO_PARAMETER_KEY_VSID); + uint32_t vsid = value; + int call_state = -1; + err = str_parms_get_int(parms, AUDIO_PARAMETER_KEY_CALL_STATE, &value); + if (err >= 0) { + call_state = value; + str_parms_del(parms, AUDIO_PARAMETER_KEY_CALL_STATE); + } else { + ALOGE("%s: call_state key not found", __func__); + ret = -EINVAL; + goto done; + } + + if (is_valid_vsid(vsid) && is_valid_call_state(call_state)) { + ret = update_call_states(adev, vsid, call_state); + } else { + ALOGE("%s: invalid vsid:%x or call_state:%d", + __func__, vsid, call_state); + ret = -EINVAL; + goto done; + } + } + + err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_DEVICE_MUTE, str_value, + sizeof(str_value)); + if (err >= 0) { + str_parms_del(parms, AUDIO_PARAMETER_KEY_DEVICE_MUTE); + bool mute = false; + + if (!strncmp("true", str_value, sizeof("true"))) { + mute = true; + } + + err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_DIRECTION, str_value, + sizeof(str_value)); + if (err >= 0) { + str_parms_del(parms, AUDIO_PARAMETER_KEY_DIRECTION); + } else { + ALOGE("%s: direction key not found", __func__); + ret = -EINVAL; + goto done; + } + + ret = platform_set_device_mute(adev->platform, mute, str_value); + if (ret != 0) { + ALOGE("%s: Failed to set mute err:%d", __func__, ret); + ret = -EINVAL; + goto done; + } + } + + err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_IN_CALL, str_value, + sizeof(str_value)); + if (err >= 0) { + str_parms_del(parms, AUDIO_PARAMETER_KEY_IN_CALL); + if (!strncmp("true", str_value, sizeof("true"))) { + adev->voice.is_in_call = true; + } + } + +done: + ALOGV("%s: exit with code(%d)", __func__, ret); + free(kv_pairs); + return ret; +} + +static int get_all_call_states_str(const struct audio_device *adev, + char *value) +{ + int ret = 0; + char *cur_ptr = value; + int i, len=0; + + for (i = 0; i < MAX_VOICE_SESSIONS; i++) { + snprintf(cur_ptr, VOICE_EXTN_PARAMETER_VALUE_MAX_LEN - len, + "%d:%d,",adev->voice.session[i].vsid, + adev->voice.session[i].state.current); + len = strlen(cur_ptr); + cur_ptr = cur_ptr + len; + } + ALOGV("%s:value=%s", __func__, value); + return ret; +} + +void voice_extn_get_parameters(const struct audio_device *adev, + struct str_parms *query, + struct str_parms *reply) +{ + int ret; + char value[VOICE_EXTN_PARAMETER_VALUE_MAX_LEN] = {0}; + char *str = str_parms_to_str(query); + int val = 0; + + ALOGV_IF(str != NULL, "%s: enter %s", __func__, str); + free(str); + + ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_IN_CALL, value, + sizeof(value)); + if (ret >=0) { + if (adev->voice.is_in_call) + val = 1; + str_parms_add_int(reply, AUDIO_PARAMETER_KEY_IN_CALL, val); + } + + ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_AUDIO_MODE, value, + sizeof(value)); + if (ret >= 0) { + str_parms_add_int(reply, AUDIO_PARAMETER_KEY_AUDIO_MODE, adev->mode); + } + + ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_ALL_CALL_STATES, + value, sizeof(value)); + if (ret >= 0) { + ret = get_all_call_states_str(adev, value); + if (ret) { + ALOGE("%s: Error fetching call states, err:%d", __func__, ret); + return; + } + str_parms_add_str(reply, AUDIO_PARAMETER_KEY_ALL_CALL_STATES, value); + } + voice_extn_compress_voip_get_parameters(query, reply); + + str = str_parms_to_str(reply); + ALOGV_IF(str != NULL, "%s: exit: returns \"%s\"", __func__, str); + free(str); +} + +void voice_extn_out_get_parameters(struct stream_out *out, + struct str_parms *query, + struct str_parms *reply) +{ + voice_extn_compress_voip_out_get_parameters(out, query, reply); +} + +void voice_extn_in_get_parameters(struct stream_in *in, + struct str_parms *query, + struct str_parms *reply) +{ + voice_extn_compress_voip_in_get_parameters(in, query, reply); +} + +#ifdef INCALL_MUSIC_ENABLED +int voice_extn_check_and_set_incall_music_usecase(struct audio_device *adev, + struct stream_out *out) +{ + uint32_t session_id = 0; + + session_id = get_session_id_with_state(adev, CALL_LOCAL_HOLD); + if (session_id == VOICE_VSID) { + out->usecase = USECASE_INCALL_MUSIC_UPLINK; + } else if (session_id == VOICE2_VSID) { + out->usecase = USECASE_INCALL_MUSIC_UPLINK2; + } else { + ALOGE("%s: Invalid session id %x", __func__, session_id); + return -EINVAL; + } + + out->config = pcm_config_incall_music; + out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_MONO; + out->channel_mask = AUDIO_CHANNEL_OUT_MONO; + + return 0; +} +#endif + diff --git a/audio/hal/voice_extn/voice_extn.h b/audio/hal/voice_extn/voice_extn.h new file mode 100644 index 0000000..15e5248 --- /dev/null +++ b/audio/hal/voice_extn/voice_extn.h @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * Not a contribution. + * + * Copyright (C) 2013 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. + */ + +#ifndef VOICE_EXTN_H +#define VOICE_EXTN_H + +#ifdef MULTI_VOICE_SESSION_ENABLED +int voice_extn_start_call(struct audio_device *adev); +int voice_extn_stop_call(struct audio_device *adev); +int voice_extn_get_session_from_use_case(struct audio_device *adev, + const audio_usecase_t usecase_id, + struct voice_session **session); +void voice_extn_init(struct audio_device *adev); +int voice_extn_set_parameters(struct audio_device *adev, + struct str_parms *parms); +void voice_extn_get_parameters(const struct audio_device *adev, + struct str_parms *query, + struct str_parms *reply); +int voice_extn_is_in_call_rec_stream(struct stream_in *in, bool *in_call_rec); +int voice_extn_is_call_state_active(struct audio_device *adev, + bool *is_call_active); +int voice_extn_get_active_session_id(struct audio_device *adev, + uint32_t *session_id); +void voice_extn_in_get_parameters(struct stream_in *in, + struct str_parms *query, + struct str_parms *reply); +void voice_extn_out_get_parameters(struct stream_out *out, + struct str_parms *query, + struct str_parms *reply); +#else +static int voice_extn_start_call(struct audio_device *adev __unused) +{ + return -ENOSYS; +} + +static int voice_extn_stop_call(struct audio_device *adev __unused) +{ + return -ENOSYS; +} + +static int voice_extn_get_session_from_use_case(struct audio_device *adev __unused, + const audio_usecase_t usecase_id __unused, + struct voice_session **session __unused) +{ + return -ENOSYS; +} + +static void voice_extn_init(struct audio_device *adev __unused) +{ +} + +static int voice_extn_set_parameters(struct audio_device *adev __unused, + struct str_parms *parms __unused) +{ + return -ENOSYS; +} + +static void voice_extn_get_parameters(const struct audio_device *adev __unused, + struct str_parms *query __unused, + struct str_parms *reply __unused) +{ +} + +static int voice_extn_is_call_state_active(struct audio_device *adev __unused, + bool *is_call_active __unused) +{ + return -ENOSYS; +} + +static int voice_extn_is_in_call_rec_stream(struct stream_in *in __unused, bool *in_call_rec __unused) +{ + return -ENOSYS; +} + +static int voice_extn_get_active_session_id(struct audio_device *adev __unused, + uint32_t *session_id __unused) +{ + return -ENOSYS; +} + +static void voice_extn_in_get_parameters(struct stream_in *in __unused, + struct str_parms *query __unused, + struct str_parms *reply __unused) +{ +} + +static void voice_extn_out_get_parameters(struct stream_out *out __unused, + struct str_parms *query __unused, + struct str_parms *reply __unused) +{ +} +#endif + +#ifdef INCALL_MUSIC_ENABLED +int voice_extn_check_and_set_incall_music_usecase(struct audio_device *adev, + struct stream_out *out); +#else +static int voice_extn_check_and_set_incall_music_usecase(struct audio_device *adev __unused, + struct stream_out *out __unused) +{ + return -ENOSYS; +} +#endif + +#ifdef COMPRESS_VOIP_ENABLED +int voice_extn_compress_voip_close_output_stream(struct audio_stream *stream); +int voice_extn_compress_voip_open_output_stream(struct stream_out *out); + +int voice_extn_compress_voip_close_input_stream(struct audio_stream *stream); +int voice_extn_compress_voip_open_input_stream(struct stream_in *in); + +int voice_extn_compress_voip_out_get_buffer_size(struct stream_out *out); +int voice_extn_compress_voip_in_get_buffer_size(struct stream_in *in); + +int voice_extn_compress_voip_start_input_stream(struct stream_in *in); +int voice_extn_compress_voip_start_output_stream(struct stream_out *out); + +int voice_extn_compress_voip_set_mic_mute(struct audio_device *dev, bool state); +int voice_extn_compress_voip_set_volume(struct audio_device *adev, float volume); +int voice_extn_compress_voip_select_devices(struct audio_device *adev, + snd_device_t *out_snd_device, + snd_device_t *in_snd_device); +int voice_extn_compress_voip_set_parameters(struct audio_device *adev, + struct str_parms *parms); +void voice_extn_compress_voip_get_parameters(struct str_parms *query, + struct str_parms *reply); + +void voice_extn_compress_voip_out_get_parameters(struct stream_out *out, + struct str_parms *query, + struct str_parms *reply); +void voice_extn_compress_voip_in_get_parameters(struct stream_in *in, + struct str_parms *query, + struct str_parms *reply); +bool voice_extn_compress_voip_pcm_prop_check(); +bool voice_extn_compress_voip_is_active(struct audio_device *adev); +bool voice_extn_compress_voip_is_format_supported(audio_format_t format); +bool voice_extn_compress_voip_is_config_supported(struct audio_config *config); +#else +static int voice_extn_compress_voip_close_output_stream(struct audio_stream *stream __unused) +{ + ALOGE("%s: COMPRESS_VOIP_ENABLED is not defined", __func__); + return -ENOSYS; +} + +static int voice_extn_compress_voip_open_output_stream(struct stream_out *out __unused) +{ + ALOGE("%s: COMPRESS_VOIP_ENABLED is not defined", __func__); + return -ENOSYS; +} + +static int voice_extn_compress_voip_close_input_stream(struct audio_stream *stream __unused) +{ + ALOGE("%s: COMPRESS_VOIP_ENABLED is not defined", __func__); + return -ENOSYS; +} + +static int voice_extn_compress_voip_open_input_stream(struct stream_in *in __unused) +{ + ALOGE("%s: COMPRESS_VOIP_ENABLED is not defined", __func__); + return -ENOSYS; +} + +static int voice_extn_compress_voip_out_get_buffer_size(struct stream_out *stream __unused) +{ + ALOGE("%s: COMPRESS_VOIP_ENABLED is not defined", __func__); + return -ENOSYS; +} + +static int voice_extn_compress_voip_in_get_buffer_size(struct stream_in *in __unused) +{ + ALOGE("%s: COMPRESS_VOIP_ENABLED is not defined", __func__); + return -ENOSYS; +} + +static int voice_extn_compress_voip_start_input_stream(struct stream_in *in __unused) +{ + ALOGE("%s: COMPRESS_VOIP_ENABLED is not defined", __func__); + return -ENOSYS; +} + +static int voice_extn_compress_voip_start_output_stream(struct stream_out *out __unused) +{ + ALOGE("%s: COMPRESS_VOIP_ENABLED is not defined", __func__); + return -ENOSYS; +} + +static int voice_extn_compress_voip_set_mic_mute(struct audio_device *adev, bool state __unused) +{ + ALOGE("%s: COMPRESS_VOIP_ENABLED is not defined", __func__); + return 0; +} + +static int voice_extn_compress_voip_set_volume(struct audio_device *adev __unused, float volume __unused) +{ + ALOGE("%s: COMPRESS_VOIP_ENABLED is not defined", __func__); + return 0; +} + +static int voice_extn_compress_voip_select_devices(struct audio_device *adev __unused, + snd_device_t *out_snd_device __unused, + snd_device_t *in_snd_device __unused) +{ + ALOGE("%s: COMPRESS_VOIP_ENABLED is not defined", __func__); + return -ENOSYS; +} + +static int voice_extn_compress_voip_set_parameters(struct audio_device *adev __unused, + struct str_parms *parms __unused) +{ + ALOGE("%s: COMPRESS_VOIP_ENABLED is not defined", __func__); + return -ENOSYS; +} + +static void voice_extn_compress_voip_get_parameters(struct str_parms *query __unused, + struct str_parms *reply __unused) +{ + ALOGE("%s: COMPRESS_VOIP_ENABLED is not defined", __func__); +} + +static void voice_extn_compress_voip_out_get_parameters(struct stream_out *out __unused, + struct str_parms *query __unused, + struct str_parms *reply __unused) +{ + ALOGE("%s: COMPRESS_VOIP_ENABLED is not defined", __func__); +} + +static void voice_extn_compress_voip_in_get_parameters(struct stream_in *in __unused, + struct str_parms *query __unused, + struct str_parms *reply __unused) +{ + ALOGE("%s: COMPRESS_VOIP_ENABLED is not defined", __func__); +} + +static bool voice_extn_compress_voip_pcm_prop_check() +{ + ALOGE("%s: COMPRESS_VOIP_ENABLED is not defined", __func__); + return false; +} + +static bool voice_extn_compress_voip_is_active(struct audio_device *adev __unused) +{ + ALOGE("%s: COMPRESS_VOIP_ENABLED is not defined", __func__); + return false; +} + +static bool voice_extn_compress_voip_is_format_supported(audio_format_t format __unused) +{ + ALOGE("%s: COMPRESS_VOIP_ENABLED is not defined", __func__); + return true; +} + +static bool voice_extn_compress_voip_is_config_supported(struct audio_config *config __unused) +{ + ALOGE("%s: COMPRESS_VOIP_ENABLED is not defined", __func__); + return true; +} +#endif + +#endif //VOICE_EXTN_H diff --git a/audio/mm-audio/Android.mk b/audio/mm-audio/Android.mk new file mode 100644 index 0000000..3885afc --- /dev/null +++ b/audio/mm-audio/Android.mk @@ -0,0 +1,3 @@ +ifeq ($(strip $(TARGET_USES_QCOM_MM_AUDIO)),true) +include $(call all-subdir-makefiles) +endif diff --git a/audio/mm-audio/Makefile b/audio/mm-audio/Makefile new file mode 100644 index 0000000..2be93e9 --- /dev/null +++ b/audio/mm-audio/Makefile @@ -0,0 +1,10 @@ +all: + @echo "invoking omxaudio make" + $(MAKE) -C adec-mp3 + $(MAKE) -C adec-aac + $(MAKE) -C aenc-aac + +install: + $(MAKE) -C adec-mp3 install + $(MAKE) -C adec-aac install + $(MAKE) -C aenc-aac install diff --git a/audio/mm-audio/aenc-aac/Android.mk b/audio/mm-audio/aenc-aac/Android.mk new file mode 100644 index 0000000..986e7c9 --- /dev/null +++ b/audio/mm-audio/aenc-aac/Android.mk @@ -0,0 +1,8 @@ +ifneq ($(filter arm aarch64 arm64, $(TARGET_ARCH)),) + + +AENC_AAC_PATH:= $(call my-dir) + +include $(AENC_AAC_PATH)/qdsp6/Android.mk + +endif diff --git a/audio/mm-audio/aenc-aac/Makefile b/audio/mm-audio/aenc-aac/Makefile new file mode 100644 index 0000000..83d822b --- /dev/null +++ b/audio/mm-audio/aenc-aac/Makefile @@ -0,0 +1,6 @@ +all: + @echo "invoking omxaudio make" + $(MAKE) -C qdsp6 + +install: + $(MAKE) -C qdsp6 install diff --git a/audio/mm-audio/aenc-aac/qdsp6/Android.mk b/audio/mm-audio/aenc-aac/qdsp6/Android.mk new file mode 100644 index 0000000..1306cee --- /dev/null +++ b/audio/mm-audio/aenc-aac/qdsp6/Android.mk @@ -0,0 +1,68 @@ +ifneq ($(BUILD_TINY_ANDROID),true) + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +# --------------------------------------------------------------------------------- +# Common definitons +# --------------------------------------------------------------------------------- + +libOmxAacEnc-def := -g -O3 +libOmxAacEnc-def += -DQC_MODIFIED +libOmxAacEnc-def += -D_ANDROID_ +libOmxAacEnc-def += -D_ENABLE_QC_MSG_LOG_ +libOmxAacEnc-def += -DVERBOSE +libOmxAacEnc-def += -D_DEBUG +libOmxAacEnc-def += -Wconversion +libOmxAacEnc-def += -DAUDIOV2 + +# --------------------------------------------------------------------------------- +# Make the Shared library (libOmxAacEnc) +# --------------------------------------------------------------------------------- + +include $(CLEAR_VARS) + +libOmxAacEnc-inc := $(LOCAL_PATH)/inc +libOmxAacEnc-inc += $(TARGET_OUT_HEADERS)/mm-core/omxcore + +LOCAL_MODULE := libOmxAacEnc +LOCAL_MODULE_TAGS := optional +LOCAL_CFLAGS := $(libOmxAacEnc-def) +LOCAL_C_INCLUDES := $(libOmxAacEnc-inc) +LOCAL_PRELINK_MODULE := false +LOCAL_SHARED_LIBRARIES := libutils liblog + +LOCAL_SRC_FILES := src/aenc_svr.c +LOCAL_SRC_FILES += src/omx_aac_aenc.cpp + +include $(BUILD_SHARED_LIBRARY) + +# --------------------------------------------------------------------------------- +# Make the apps-test (mm-aenc-omxaac-test) +# --------------------------------------------------------------------------------- + +include $(CLEAR_VARS) + +mm-aac-enc-test-inc := $(LOCAL_PATH)/inc +mm-aac-enc-test-inc += $(LOCAL_PATH)/test +mm-aac-enc-test-inc += $(TARGET_OUT_HEADERS)/mm-audio/audio-alsa +mm-aac-enc-test-inc += $(TARGET_OUT_HEADERS)/mm-core/omxcore + +LOCAL_MODULE := mm-aenc-omxaac-test +LOCAL_MODULE_TAGS := optional +LOCAL_CFLAGS := $(libOmxAacEnc-def) +LOCAL_C_INCLUDES := $(mm-aac-enc-test-inc) +LOCAL_PRELINK_MODULE := false +LOCAL_SHARED_LIBRARIES := libmm-omxcore +LOCAL_SHARED_LIBRARIES += libOmxAacEnc +LOCAL_SHARED_LIBRARIES += libaudioalsa +LOCAL_SRC_FILES := test/omx_aac_enc_test.c + +include $(BUILD_EXECUTABLE) + +endif + +# --------------------------------------------------------------------------------- +# END +# --------------------------------------------------------------------------------- + diff --git a/audio/mm-audio/aenc-aac/qdsp6/Makefile b/audio/mm-audio/aenc-aac/qdsp6/Makefile new file mode 100644 index 0000000..5421d45 --- /dev/null +++ b/audio/mm-audio/aenc-aac/qdsp6/Makefile @@ -0,0 +1,81 @@ +# --------------------------------------------------------------------------------- +# MM-AUDIO-OSS-8K-AENC-AAC +# --------------------------------------------------------------------------------- + +# cross-compiler flags +CFLAGS += -Wall +CFLAGS += -Wundef +CFLAGS += -Wstrict-prototypes +CFLAGS += -Wno-trigraphs + +# cross-compile flags specific to shared objects +CFLAGS_SO += -fpic + +# required pre-processor flags +CPPFLAGS := -D__packed__= +CPPFLAGS += -DIMAGE_APPS_PROC +CPPFLAGS += -DFEATURE_Q_SINGLE_LINK +CPPFLAGS += -DFEATURE_Q_NO_SELF_QPTR +CPPFLAGS += -DFEATURE_LINUX +CPPFLAGS += -DFEATURE_NATIVELINUX +CPPFLAGS += -DFEATURE_DSM_DUP_ITEMS + +CPPFLAGS += -g +CPPFALGS += -D_DEBUG +CPPFLAGS += -Iinc + +# linker flags +LDFLAGS += -L$(SYSROOT)/usr/lib + +# linker flags for shared objects +LDFLAGS_SO := -shared + +# defintions +LIBMAJOR := $(basename $(basename $(LIBVER))) +LIBINSTALLDIR := $(DESTDIR)usr/lib +INCINSTALLDIR := $(DESTDIR)usr/include +BININSTALLDIR := $(DESTDIR)usr/bin + +# --------------------------------------------------------------------------------- +# BUILD +# --------------------------------------------------------------------------------- +all: libOmxAacEnc.so.$(LIBVER) mm-aenc-omxaac-test + +install: + echo "intalling aenc-aac in $(DESTDIR)" + if [ ! -d $(LIBINSTALLDIR) ]; then mkdir -p $(LIBINSTALLDIR); fi + if [ ! -d $(INCINSTALLDIR) ]; then mkdir -p $(INCINSTALLDIR); fi + if [ ! -d $(BININSTALLDIR) ]; then mkdir -p $(BININSTALLDIR); fi + install -m 555 libOmxAacEnc.so.$(LIBVER) $(LIBINSTALLDIR) + cd $(LIBINSTALLDIR) && ln -s libOmxAacEnc.so.$(LIBVER) libOmxAacEnc.so.$(LIBMAJOR) + cd $(LIBINSTALLDIR) && ln -s libOmxAacEnc.so.$(LIBMAJOR) libOmxAacEnc.so + install -m 555 mm-aenc-omxaac-test $(BININSTALLDIR) + +# --------------------------------------------------------------------------------- +# COMPILE LIBRARY +# --------------------------------------------------------------------------------- +LDLIBS := -lpthread +LDLIBS += -lstdc++ +LDLIBS += -lOmxCore + +SRCS := src/omx_aac_aenc.cpp +SRCS += src/aenc_svr.c + +libOmxAacEnc.so.$(LIBVER): $(SRCS) + $(CC) $(CPPFLAGS) $(CFLAGS_SO) $(LDFLAGS_SO) -Wl,-soname,libOmxAacEnc.so.$(LIBMAJOR) -o $@ $^ $(LDFLAGS) $(LDLIBS) + +# --------------------------------------------------------------------------------- +# COMPILE TEST APP +# --------------------------------------------------------------------------------- +TEST_LDLIBS := -lpthread +TEST_LDLIBS += -ldl +TEST_LDLIBS += -lOmxCore + +TEST_SRCS := test/omx_aac_enc_test.c + +mm-aenc-omxaac-test: libOmxAacEnc.so.$(LIBVER) $(TEST_SRCS) + $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $^ $(TEST_LDLIBS) + +# --------------------------------------------------------------------------------- +# END +# --------------------------------------------------------------------------------- diff --git a/audio/mm-audio/aenc-aac/qdsp6/inc/Map.h b/audio/mm-audio/aenc-aac/qdsp6/inc/Map.h new file mode 100644 index 0000000..aac96fd --- /dev/null +++ b/audio/mm-audio/aenc-aac/qdsp6/inc/Map.h @@ -0,0 +1,244 @@ +/*-------------------------------------------------------------------------- +Copyright (c) 2010, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of The Linux Foundation nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------*/ +#ifndef _MAP_H_ +#define _MAP_H_ + +#include +using namespace std; + +template +class Map +{ + struct node + { + T data; + T2 data2; + node* prev; + node* next; + node(T t, T2 t2,node* p, node* n) : + data(t), data2(t2), prev(p), next(n) {} + }; + node* head; + node* tail; + node* tmp; + unsigned size_of_list; + static Map *m_self; +public: + Map() : head( NULL ), tail ( NULL ),tmp(head),size_of_list(0) {} + bool empty() const { return ( !head || !tail ); } + operator bool() const { return !empty(); } + void insert(T,T2); + void show(); + int size(); + T2 find(T); // Return VALUE + T find_ele(T);// Check if the KEY is present or not + T2 begin(); //give the first ele + bool erase(T); + bool eraseall(); + bool isempty(); + ~Map() + { + while(head) + { + node* temp(head); + head=head->next; + size_of_list--; + delete temp; + } + } +}; + +template +T2 Map::find(T d1) +{ + tmp = head; + while(tmp) + { + if(tmp->data == d1) + { + return tmp->data2; + } + tmp = tmp->next; + } + return 0; +} + +template +T Map::find_ele(T d1) +{ + tmp = head; + while(tmp) + { + if(tmp->data == d1) + { + return tmp->data; + } + tmp = tmp->next; + } + return 0; +} + +template +T2 Map::begin() +{ + tmp = head; + if(tmp) + { + return (tmp->data2); + } + return 0; +} + +template +void Map::show() +{ + tmp = head; + while(tmp) + { + printf("%d-->%d\n",tmp->data,tmp->data2); + tmp = tmp->next; + } +} + +template +int Map::size() +{ + int count =0; + tmp = head; + while(tmp) + { + tmp = tmp->next; + count++; + } + return count; +} + +template +void Map::insert(T data, T2 data2) +{ + tail = new node(data, data2,tail, NULL); + if( tail->prev ) + tail->prev->next = tail; + + if( empty() ) + { + head = tail; + tmp=head; + } + tmp = head; + size_of_list++; +} + +template +bool Map::erase(T d) +{ + bool found = false; + tmp = head; + node* prevnode = tmp; + node *tempnode; + + while(tmp) + { + if((head == tail) && (head->data == d)) + { + found = true; + tempnode = head; + head = tail = NULL; + delete tempnode; + break; + } + if((tmp ==head) && (tmp->data ==d)) + { + found = true; + tempnode = tmp; + tmp = tmp->next; + tmp->prev = NULL; + head = tmp; + tempnode->next = NULL; + delete tempnode; + break; + } + if((tmp == tail) && (tmp->data ==d)) + { + found = true; + tempnode = tmp; + prevnode->next = NULL; + tmp->prev = NULL; + tail = prevnode; + delete tempnode; + break; + } + if(tmp->data == d) + { + found = true; + prevnode->next = tmp->next; + tmp->next->prev = prevnode->next; + tempnode = tmp; + //tmp = tmp->next; + delete tempnode; + break; + } + prevnode = tmp; + tmp = tmp->next; + } + if(found)size_of_list--; + return found; +} + +template +bool Map::eraseall() +{ + // Be careful while using this method + // it not only removes the node but FREES(not delete) the allocated + // memory. + node *tempnode; + tmp = head; + while(head) + { + tempnode = head; + head = head->next; + tempnode->next = NULL; + if(tempnode->data) + free(tempnode->data); + if(tempnode->data2) + free(tempnode->data2); + delete tempnode; + } + tail = head = NULL; + return true; +} + + +template +bool Map::isempty() +{ + if(!size_of_list) return true; + else return false; +} + +#endif // _MAP_H_ diff --git a/audio/mm-audio/aenc-aac/qdsp6/inc/aenc_svr.h b/audio/mm-audio/aenc-aac/qdsp6/inc/aenc_svr.h new file mode 100644 index 0000000..9bc7200 --- /dev/null +++ b/audio/mm-audio/aenc-aac/qdsp6/inc/aenc_svr.h @@ -0,0 +1,120 @@ +/*-------------------------------------------------------------------------- +Copyright (c) 2010, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of The Linux Foundation nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------*/ +#ifndef AENC_SVR_H +#define AENC_SVR_H + +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#include + +#ifdef _ANDROID_ +#define LOG_TAG "QC_AACENC" +#endif + +#ifndef LOGE +#define LOGE ALOGE +#endif + +#ifndef LOGW +#define LOGW ALOGW +#endif + +#ifndef LOGD +#define LOGD ALOGD +#endif + +#ifndef LOGV +#define LOGV ALOGV +#endif + +#ifndef LOGI +#define LOGI ALOGI +#endif + +#define DEBUG_PRINT_ERROR LOGE +#define DEBUG_PRINT LOGV +#define DEBUG_DETAIL LOGV + +typedef void (*message_func)(void* client_data, unsigned char id); + +/** + @brief audio encoder ipc info structure + + */ +struct aac_ipc_info +{ + pthread_t thr; + int pipe_in; + int pipe_out; + int dead; + message_func process_msg_cb; + void *client_data; + char thread_name[128]; +}; + +/** + @brief This function starts command server + + @param cb pointer to callback function from the client + @param client_data reference client wants to get back + through callback + @return handle to command server + */ +struct aac_ipc_info *omx_aac_thread_create(message_func cb, + void* client_data, + char *th_name); + +struct aac_ipc_info *omx_aac_event_thread_create(message_func cb, + void* client_data, + char *th_name); +/** + @brief This function stop command server + + @param svr handle to command server + @return none + */ +void omx_aac_thread_stop(struct aac_ipc_info *aac_ipc); + + +/** + @brief This function post message in the command server + + @param svr handle to command server + @return none + */ +void omx_aac_post_msg(struct aac_ipc_info *aac_ipc, + unsigned char id); + +#ifdef __cplusplus +} +#endif + +#endif /* AENC_SVR */ diff --git a/audio/mm-audio/aenc-aac/qdsp6/inc/omx_aac_aenc.h b/audio/mm-audio/aenc-aac/qdsp6/inc/omx_aac_aenc.h new file mode 100644 index 0000000..2e2abbf --- /dev/null +++ b/audio/mm-audio/aenc-aac/qdsp6/inc/omx_aac_aenc.h @@ -0,0 +1,629 @@ +/*-------------------------------------------------------------------------- +Copyright (c) 2010-2014, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of The Linux Foundation nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------*/ +#ifndef _AAC_ENC_H_ +#define _AAC_ENC_H_ +/*============================================================================ + Audio Encoder + +@file omx_aac_aenc.h +This module contains the class definition for openMAX encoder component. + + + +============================================================================*/ + +////////////////////////////////////////////////////////////////////////////// +// Include Files +////////////////////////////////////////////////////////////////////////////// + +/* Uncomment out below line #define LOG_NDEBUG 0 if we want to see + * all DEBUG_PRINT or LOGV messaging */ +#include +#include +#include +#include +#include +#include +#include "QOMX_AudioExtensions.h" +#include "QOMX_AudioIndexExtensions.h" +#include "OMX_Core.h" +#include "OMX_Audio.h" +#include "aenc_svr.h" +#include "qc_omx_component.h" +#include "Map.h" +#include +#include +#include +extern "C" { + void * get_omx_component_factory_fn(void); +} + + +////////////////////////////////////////////////////////////////////////////// +// Module specific globals +////////////////////////////////////////////////////////////////////////////// + + + +#define OMX_SPEC_VERSION 0x00000101 +#define min(x,y) (((x) < (y)) ? (x) : (y)) +#define MAX(x,y) (x >= y?x:y) + +////////////////////////////////////////////////////////////////////////////// +// Macros +////////////////////////////////////////////////////////////////////////////// +// + + +#define PrintFrameHdr(i,bufHdr) \ + DEBUG_PRINT("i=%d OMX bufHdr[%p]buf[%p]size[%d]TS[%lld]nFlags[0x%x]\n",\ + i,\ + bufHdr, \ + ((OMX_BUFFERHEADERTYPE *)bufHdr)->pBuffer, \ + (unsigned)((OMX_BUFFERHEADERTYPE *)bufHdr)->nFilledLen,\ + ((OMX_BUFFERHEADERTYPE *)bufHdr)->nTimeStamp, \ + (unsigned)((OMX_BUFFERHEADERTYPE *)bufHdr)->nFlags) + + +// BitMask Management logic +#define BITS_PER_BYTE 8 +#define BITMASK_SIZE(mIndex) \ + (((mIndex) + BITS_PER_BYTE - 1)/BITS_PER_BYTE) +#define BITMASK_OFFSET(mIndex)\ + ((mIndex)/BITS_PER_BYTE) +#define BITMASK_FLAG(mIndex) \ + (1 << ((mIndex) % BITS_PER_BYTE)) +#define BITMASK_CLEAR(mArray,mIndex)\ + (mArray)[BITMASK_OFFSET(mIndex)] &= ~(BITMASK_FLAG(mIndex)) +#define BITMASK_SET(mArray,mIndex)\ + (mArray)[BITMASK_OFFSET(mIndex)] |= BITMASK_FLAG(mIndex) +#define BITMASK_PRESENT(mArray,mIndex)\ + ((mArray)[BITMASK_OFFSET(mIndex)] & BITMASK_FLAG(mIndex)) +#define BITMASK_ABSENT(mArray,mIndex)\ + (((mArray)[BITMASK_OFFSET(mIndex)] & \ + BITMASK_FLAG(mIndex)) == 0x0) + +#define OMX_CORE_NUM_INPUT_BUFFERS 2 +#define OMX_CORE_NUM_OUTPUT_BUFFERS 16 + +#define OMX_CORE_INPUT_BUFFER_SIZE 8192 +#define OMX_CORE_CONTROL_CMDQ_SIZE 100 +#define OMX_AENC_VOLUME_STEP 0x147 +#define OMX_AENC_MIN 0 +#define OMX_AENC_MAX 100 +#define NON_TUNNEL 1 +#define TUNNEL 0 +#define IP_PORT_BITMASK 0x02 +#define OP_PORT_BITMASK 0x01 +#define IP_OP_PORT_BITMASK 0x03 + +#define DEFAULT_SF 44100 +#define DEFAULT_CH_CFG 2 +#define DEFAULT_BITRATE 64000 +#define OMX_AAC_DEFAULT_VOL 25 +// 14 bytes for input meta data +#define OMX_AENC_SIZEOF_META_BUF (OMX_CORE_INPUT_BUFFER_SIZE+14) + +#define TRUE 1 +#define FALSE 0 + + +#define NUMOFFRAMES 1 +#define MAXFRAMELENGTH 1536 +#define OMX_AAC_OUTPUT_BUFFER_SIZE ((NUMOFFRAMES * (sizeof(ENC_META_OUT)+ MAXFRAMELENGTH + 1)\ + + 1023) & (~1023)) +//Raw Header +#define AUDAAC_MAX_MP4FF_HEADER_LENGTH 2 + +#define AUDAAC_MP4FF_OBJ_TYPE 5 +#define AUDAAC_MP4FF_FREQ_IDX 4 +#define AUDAAC_MP4FF_CH_CONFIG 4 + +//ADIF Header +#define AUDAAC_MAX_ADIF_HEADER_LENGTH 17 + +#define AAC_COPYRIGHT_PRESENT_SIZE 1 +#define AAC_ORIGINAL_COPY_SIZE 1 +#define AAC_HOME_SIZE 1 +#define AAC_BITSTREAM_TYPE_SIZE 1 +#define AAC_BITRATE_SIZE 23 +#define AAC_NUM_PFE_SIZE 4 +#define AAC_BUFFER_FULLNESS_SIZE 20 +#define AAC_ELEMENT_INSTANCE_TAG_SIZE 4 +#define AAC_NUM_FRONT_CHANNEL_ELEMENTS_SIZE 4 +#define AAC_NUM_SIDE_CHANNEL_ELEMENTS_SIZE 4 +#define AAC_NUM_BACK_CHANNEL_ELEMENTS_SIZE 4 +#define AAC_NUM_LFE_CHANNEL_ELEMENTS_SIZE 2 +#define AAC_NUM_ASSOC_DATA_ELEMENTS_SIZE 3 +#define AAC_NUM_VALID_CC_ELEMENTS_SIZE 4 +#define AAC_MONO_MIXDOWN_PRESENT_SIZE 1 +#define AAC_MONO_MIXDOWN_ELEMENT_SIZE 4 +#define AAC_STEREO_MIXDOWN_PRESENT_SIZE 1 +#define AAC_STEREO_MIXDOWN_ELEMENT_SIZE 4 +#define AAC_MATRIX_MIXDOWN_PRESENT_SIZE 1 +#define AAC_MATRIX_MIXDOWN_SIZE 3 +#define AAC_FCE_SIZE 5 +#define AAC_SCE_SIZE 5 +#define AAC_BCE_SIZE 5 +#define AAC_LFE_SIZE 4 +#define AAC_ADE_SIZE 4 +#define AAC_VCE_SIZE 5 +#define AAC_COMMENT_FIELD_BYTES_SIZE 8 +#define AAC_COMMENT_FIELD_DATA_SIZE 8 +#define AAC_SAMPLING_FREQ_INDEX_SIZE 4 +#define AAC_PROFILE_SIZE 2 + + + +//Raw Header +#define AUDAAC_MAX_RAW_HEADER_LENGTH 8 + +#define AUDAAC_RAW_OBJ_TYPE 8 +#define AUDAAC_RAW_FREQ_IDX 8 +#define AUDAAC_RAW_CH_CONFIG 8 +#define AUDAAC_RAW_SBR_PRESENT 8 +#define AUDAAC_RAW_SBR_PS_PRESENT 8 +#define AUDAAC_RAW_EXT_OBJ_TYPE 8 +#define AUDAAC_RAW_EXT_FREQ_IDX 8 +#define AUDAAC_RAW_EXT_CH_CONFIG 8 + +struct sample_rate_idx { + OMX_U32 sample_rate; + OMX_U32 sample_rate_idx; +}; +static struct sample_rate_idx sample_idx_tbl[10] = { + {8000, 0x0b}, + {11025, 0x0a}, + {12000, 0x09}, + {16000, 0x08}, + {22050, 0x07}, + {24000, 0x06}, + {32000, 0x05}, + {44100, 0x04}, + {48000, 0x03}, + {64000, 0x02}, +}; +class omx_aac_aenc; + +// OMX AAC audio encoder class +class omx_aac_aenc: public qc_omx_component +{ +public: + omx_aac_aenc(); // constructor + virtual ~omx_aac_aenc(); // destructor + + OMX_ERRORTYPE allocate_buffer(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE **bufferHdr, + OMX_U32 port, + OMX_PTR appData, + OMX_U32 bytes); + + + OMX_ERRORTYPE component_deinit(OMX_HANDLETYPE hComp); + + OMX_ERRORTYPE component_init(OMX_STRING role); + + OMX_ERRORTYPE component_role_enum(OMX_HANDLETYPE hComp, + OMX_U8 *role, + OMX_U32 index); + + OMX_ERRORTYPE component_tunnel_request(OMX_HANDLETYPE hComp, + OMX_U32 port, + OMX_HANDLETYPE peerComponent, + OMX_U32 peerPort, + OMX_TUNNELSETUPTYPE *tunnelSetup); + + OMX_ERRORTYPE empty_this_buffer(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE *buffer); + + + OMX_ERRORTYPE empty_this_buffer_proxy(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE *buffer); + + + OMX_ERRORTYPE fill_this_buffer(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE *buffer); + + + OMX_ERRORTYPE free_buffer(OMX_HANDLETYPE hComp, + OMX_U32 port, + OMX_BUFFERHEADERTYPE *buffer); + + OMX_ERRORTYPE get_component_version(OMX_HANDLETYPE hComp, + OMX_STRING componentName, + OMX_VERSIONTYPE *componentVersion, + OMX_VERSIONTYPE * specVersion, + OMX_UUIDTYPE *componentUUID); + + OMX_ERRORTYPE get_config(OMX_HANDLETYPE hComp, + OMX_INDEXTYPE configIndex, + OMX_PTR configData); + + OMX_ERRORTYPE get_extension_index(OMX_HANDLETYPE hComp, + OMX_STRING paramName, + OMX_INDEXTYPE *indexType); + + OMX_ERRORTYPE get_parameter(OMX_HANDLETYPE hComp, + OMX_INDEXTYPE paramIndex, + OMX_PTR paramData); + + OMX_ERRORTYPE get_state(OMX_HANDLETYPE hComp, + OMX_STATETYPE *state); + + static void process_in_port_msg(void *client_data, + unsigned char id); + + static void process_out_port_msg(void *client_data, + unsigned char id); + + static void process_command_msg(void *client_data, + unsigned char id); + + static void process_event_cb(void *client_data, + unsigned char id); + + + OMX_ERRORTYPE set_callbacks(OMX_HANDLETYPE hComp, + OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData); + + OMX_ERRORTYPE set_config(OMX_HANDLETYPE hComp, + OMX_INDEXTYPE configIndex, + OMX_PTR configData); + + OMX_ERRORTYPE set_parameter(OMX_HANDLETYPE hComp, + OMX_INDEXTYPE paramIndex, + OMX_PTR paramData); + + OMX_ERRORTYPE use_buffer(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE **bufferHdr, + OMX_U32 port, + OMX_PTR appData, + OMX_U32 bytes, + OMX_U8 *buffer); + + OMX_ERRORTYPE use_EGL_image(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE **bufferHdr, + OMX_U32 port, + OMX_PTR appData, + void * eglImage); + + bool post_command(unsigned int p1, unsigned int p2, + unsigned char id); + + // Deferred callback identifiers + enum + { + //Event Callbacks from the component thread context + OMX_COMPONENT_GENERATE_EVENT = 0x1, + //Buffer Done callbacks from component thread context + OMX_COMPONENT_GENERATE_BUFFER_DONE = 0x2, + OMX_COMPONENT_GENERATE_ETB = 0x3, + //Command + OMX_COMPONENT_GENERATE_COMMAND = 0x4, + OMX_COMPONENT_GENERATE_FRAME_DONE = 0x05, + OMX_COMPONENT_GENERATE_FTB = 0x06, + OMX_COMPONENT_GENERATE_EOS = 0x07, + OMX_COMPONENT_PORTSETTINGS_CHANGED = 0x08, + OMX_COMPONENT_SUSPEND = 0x09, + OMX_COMPONENT_RESUME = 0x0a + }; +private: + + /////////////////////////////////////////////////////////// + // Type definitions + /////////////////////////////////////////////////////////// + // Bit Positions + enum flags_bit_positions + { + // Defer transition to IDLE + OMX_COMPONENT_IDLE_PENDING =0x1, + // Defer transition to LOADING + OMX_COMPONENT_LOADING_PENDING =0x2, + + OMX_COMPONENT_MUTED =0x3, + + // Defer transition to Enable + OMX_COMPONENT_INPUT_ENABLE_PENDING =0x4, + // Defer transition to Enable + OMX_COMPONENT_OUTPUT_ENABLE_PENDING =0x5, + // Defer transition to Disable + OMX_COMPONENT_INPUT_DISABLE_PENDING =0x6, + // Defer transition to Disable + OMX_COMPONENT_OUTPUT_DISABLE_PENDING =0x7 + }; + + #define MIN_BITRATE 24000 + #define MAX_BITRATE 192000 + #define MAX_BITRATE_MULFACTOR 12 + #define BITRATE_DIVFACTOR 2 + typedef Map + input_buffer_map; + + typedef Map + output_buffer_map; + + enum port_indexes + { + OMX_CORE_INPUT_PORT_INDEX =0, + OMX_CORE_OUTPUT_PORT_INDEX =1 + }; + + struct omx_event + { + unsigned long param1; + unsigned long param2; + unsigned char id; + }; + + struct omx_cmd_queue + { + omx_event m_q[OMX_CORE_CONTROL_CMDQ_SIZE]; + unsigned m_read; + unsigned m_write; + unsigned m_size; + + omx_cmd_queue(); + ~omx_cmd_queue(); + bool insert_entry(unsigned long p1, unsigned long p2, unsigned char id); + bool pop_entry(unsigned long *p1,unsigned long *p2, unsigned char *id); + bool get_msg_id(unsigned char *id); + bool get_msg_with_id(unsigned *p1,unsigned *p2, unsigned id); + }; + + typedef struct TIMESTAMP + { + unsigned int LowPart; + unsigned int HighPart; + }__attribute__((packed)) TIMESTAMP; + + typedef struct metadata_input + { + unsigned short offsetVal; + TIMESTAMP nTimeStamp; + unsigned int nFlags; + }__attribute__((packed)) META_IN; + + typedef struct enc_meta_out + { + unsigned int offset_to_frame; + unsigned int frame_size; + unsigned int encoded_pcm_samples; + unsigned int msw_ts; + unsigned int lsw_ts; + unsigned int nflags; + } __attribute__ ((packed))ENC_META_OUT; + + typedef struct + { + OMX_U32 tot_in_buf_len; + OMX_U32 tot_out_buf_len; + OMX_TICKS tot_pb_time; + OMX_U32 fbd_cnt; + OMX_U32 ftb_cnt; + OMX_U32 etb_cnt; + OMX_U32 ebd_cnt; + }AAC_PB_STATS; + + /////////////////////////////////////////////////////////// + // Member variables + /////////////////////////////////////////////////////////// + OMX_U8 *m_tmp_meta_buf; + OMX_U8 *m_tmp_out_meta_buf; + OMX_U8 m_flush_cnt ; + OMX_U8 m_comp_deinit; + + // the below var doesnt hold good if combo of use and alloc bufs are used + OMX_U8 m_eos_bm; + OMX_S32 m_volume;//Unit to be determined + OMX_U8 audaac_header_adif[AUDAAC_MAX_ADIF_HEADER_LENGTH]; + OMX_U8 audaac_header_mp4ff[AUDAAC_MAX_MP4FF_HEADER_LENGTH]; + OMX_U16 audaac_hdr_bit_index; + OMX_S32 sample_idx; + OMX_S32 adif_flag; + OMX_S32 mp4ff_flag; + OMX_PTR m_app_data;// Application data + int nNumInputBuf; + int nNumOutputBuf; + int m_drv_fd; // Kernel device node file handle + bool bFlushinprogress; + bool is_in_th_sleep; + bool is_out_th_sleep; + unsigned int m_flags; //encapsulate the waiting states. + OMX_U64 nTimestamp; + OMX_U64 ts; + uint32_t m_frame_count; + unsigned int frameduration; + unsigned int pcm_input; //tunnel or non-tunnel + unsigned int m_inp_act_buf_count; // Num of Input Buffers + unsigned int m_out_act_buf_count; // Numb of Output Buffers + unsigned int m_inp_current_buf_count; // Num of Input Buffers + unsigned int m_out_current_buf_count; // Numb of Output Buffers + unsigned int output_buffer_size; + unsigned int input_buffer_size; + unsigned short m_session_id; + // store I/P PORT state + OMX_BOOL m_inp_bEnabled; + // store O/P PORT state + OMX_BOOL m_out_bEnabled; + //Input port Populated + OMX_BOOL m_inp_bPopulated; + //Output port Populated + OMX_BOOL m_out_bPopulated; + sem_t sem_States; + sem_t sem_read_msg; + sem_t sem_write_msg; + + volatile int m_is_event_done; + volatile int m_is_in_th_sleep; + volatile int m_is_out_th_sleep; + input_buffer_map m_input_buf_hdrs; + output_buffer_map m_output_buf_hdrs; + omx_cmd_queue m_input_q; + omx_cmd_queue m_input_ctrl_cmd_q; + omx_cmd_queue m_input_ctrl_ebd_q; + omx_cmd_queue m_command_q; + omx_cmd_queue m_output_q; + omx_cmd_queue m_output_ctrl_cmd_q; + omx_cmd_queue m_output_ctrl_fbd_q; + pthread_mutexattr_t m_outputlock_attr; + pthread_mutexattr_t m_commandlock_attr; + pthread_mutexattr_t m_lock_attr; + pthread_mutexattr_t m_state_attr; + pthread_mutexattr_t m_flush_attr; + pthread_mutexattr_t m_in_th_attr_1; + pthread_mutexattr_t m_out_th_attr_1; + pthread_mutexattr_t m_event_attr; + pthread_mutexattr_t m_in_th_attr; + pthread_mutexattr_t m_out_th_attr; + pthread_mutexattr_t out_buf_count_lock_attr; + pthread_mutexattr_t in_buf_count_lock_attr; + pthread_cond_t cond; + pthread_cond_t in_cond; + pthread_cond_t out_cond; + pthread_mutex_t m_lock; + pthread_mutex_t m_commandlock; + pthread_mutex_t m_outputlock; + // Mutexes for state change + pthread_mutex_t m_state_lock; + // Mutexes for flush acks from input and output threads + pthread_mutex_t m_flush_lock; + pthread_mutex_t m_event_lock; + pthread_mutex_t m_in_th_lock; + pthread_mutex_t m_out_th_lock; + pthread_mutex_t m_in_th_lock_1; + pthread_mutex_t m_out_th_lock_1; + pthread_mutex_t out_buf_count_lock; + pthread_mutex_t in_buf_count_lock; + + OMX_STATETYPE m_state; // OMX State + OMX_STATETYPE nState; + OMX_CALLBACKTYPE m_cb; // Application callbacks + AAC_PB_STATS m_aac_pb_stats; + struct aac_ipc_info *m_ipc_to_in_th; // for input thread + struct aac_ipc_info *m_ipc_to_out_th; // for output thread + struct aac_ipc_info *m_ipc_to_cmd_th; // for command thread + OMX_PRIORITYMGMTTYPE m_priority_mgm ; + OMX_AUDIO_PARAM_AACPROFILETYPE m_aac_param; // Cache AAC encoder parameter + OMX_AUDIO_PARAM_PCMMODETYPE m_pcm_param; // Cache pcm parameter + OMX_PARAM_COMPONENTROLETYPE component_Role; + OMX_PARAM_BUFFERSUPPLIERTYPE m_buffer_supplier; + + /////////////////////////////////////////////////////////// + // Private methods + /////////////////////////////////////////////////////////// + OMX_ERRORTYPE allocate_output_buffer(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE **bufferHdr, + OMX_U32 port,OMX_PTR appData, + OMX_U32 bytes); + + OMX_ERRORTYPE allocate_input_buffer(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE **bufferHdr, + OMX_U32 port, + OMX_PTR appData, + OMX_U32 bytes); + + OMX_ERRORTYPE use_input_buffer(OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE **bufHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes, + OMX_IN OMX_U8* buffer); + + OMX_ERRORTYPE use_output_buffer(OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE **bufHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes, + OMX_IN OMX_U8* buffer); + + OMX_ERRORTYPE fill_this_buffer_proxy(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE *buffer); + + OMX_ERRORTYPE send_command_proxy(OMX_HANDLETYPE hComp, + OMX_COMMANDTYPE cmd, + OMX_U32 param1, + OMX_PTR cmdData); + + OMX_ERRORTYPE send_command(OMX_HANDLETYPE hComp, + OMX_COMMANDTYPE cmd, + OMX_U32 param1, + OMX_PTR cmdData); + + bool allocate_done(void); + + bool release_done(OMX_U32 param1); + + bool execute_omx_flush(OMX_IN OMX_U32 param1, bool cmd_cmpl=true); + + bool execute_input_omx_flush(void); + + bool execute_output_omx_flush(void); + + bool search_input_bufhdr(OMX_BUFFERHEADERTYPE *buffer); + + bool search_output_bufhdr(OMX_BUFFERHEADERTYPE *buffer); + + bool post_input(unsigned long p1, unsigned long p2, + unsigned char id); + + bool post_output(unsigned long p1, unsigned long p2, + unsigned char id); + + void process_events(omx_aac_aenc *client_data); + + void buffer_done_cb(OMX_BUFFERHEADERTYPE *bufHdr); + + void frame_done_cb(OMX_BUFFERHEADERTYPE *bufHdr); + + void wait_for_event(); + + void event_complete(); + + void in_th_goto_sleep(); + + void in_th_wakeup(); + + void out_th_goto_sleep(); + + void out_th_wakeup(); + + void flush_ack(); + void deinit_encoder(); + void audaac_rec_install_adif_header_variable (OMX_U16 byte_num, + OMX_U32 sample_index, OMX_U8 channel_config); + void audaac_rec_install_mp4ff_header_variable (OMX_U16 byte_num, + OMX_U32 sample_index,OMX_U8 channel_config); + void audaac_rec_install_bits(OMX_U8 *input, + OMX_U8 num_bits_reqd, + OMX_U32 value, + OMX_U16 *hdr_bit_index); + int get_updated_bit_rate(int bitrate); + +}; +#endif diff --git a/audio/mm-audio/aenc-aac/qdsp6/src/aenc_svr.c b/audio/mm-audio/aenc-aac/qdsp6/src/aenc_svr.c new file mode 100644 index 0000000..9a8448a --- /dev/null +++ b/audio/mm-audio/aenc-aac/qdsp6/src/aenc_svr.c @@ -0,0 +1,206 @@ +/*-------------------------------------------------------------------------- +Copyright (c) 2010, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of The Linux Foundation nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------*/ +#include +#include +#include + +#include +#include + +#include + +/** + @brief This function processes posted messages + + Once thread is being spawned, this function is run to + start processing commands posted by client + + @param info pointer to context + + */ +void *omx_aac_msg(void *info) +{ + struct aac_ipc_info *aac_info = (struct aac_ipc_info*)info; + unsigned char id; + ssize_t n; + + DEBUG_DETAIL("\n%s: message thread start\n", __FUNCTION__); + while (!aac_info->dead) + { + n = read(aac_info->pipe_in, &id, 1); + if (0 == n) break; + if (1 == n) + { + DEBUG_DETAIL("\n%s-->pipe_in=%d pipe_out=%d\n", + aac_info->thread_name, + aac_info->pipe_in, + aac_info->pipe_out); + + aac_info->process_msg_cb(aac_info->client_data, id); + } + if ((n < 0) && (errno != EINTR)) break; + } + DEBUG_DETAIL("%s: message thread stop\n", __FUNCTION__); + + return 0; +} + +void *omx_aac_events(void *info) +{ + struct aac_ipc_info *aac_info = (struct aac_ipc_info*)info; + unsigned char id = 0; + + DEBUG_DETAIL("%s: message thread start\n", aac_info->thread_name); + aac_info->process_msg_cb(aac_info->client_data, id); + DEBUG_DETAIL("%s: message thread stop\n", aac_info->thread_name); + return 0; +} + +/** + @brief This function starts command server + + @param cb pointer to callback function from the client + @param client_data reference client wants to get back + through callback + @return handle to msging thread + */ +struct aac_ipc_info *omx_aac_thread_create( + message_func cb, + void* client_data, + char* th_name) +{ + int r; + int fds[2]; + struct aac_ipc_info *aac_info; + + aac_info = calloc(1, sizeof(struct aac_ipc_info)); + if (!aac_info) + { + return 0; + } + + aac_info->client_data = client_data; + aac_info->process_msg_cb = cb; + strlcpy(aac_info->thread_name, th_name, sizeof(aac_info->thread_name)); + + if (pipe(fds)) + { + DEBUG_PRINT_ERROR("\n%s: pipe creation failed\n", __FUNCTION__); + goto fail_pipe; + } + + aac_info->pipe_in = fds[0]; + aac_info->pipe_out = fds[1]; + + r = pthread_create(&aac_info->thr, 0, omx_aac_msg, aac_info); + if (r < 0) goto fail_thread; + + DEBUG_DETAIL("Created thread for %s \n", aac_info->thread_name); + return aac_info; + + +fail_thread: + close(aac_info->pipe_in); + close(aac_info->pipe_out); + +fail_pipe: + free(aac_info); + + return 0; +} + +/** + * @brief This function starts command server + * + * @param cb pointer to callback function from the client + * @param client_data reference client wants to get back + * through callback + * @return handle to msging thread + * */ +struct aac_ipc_info *omx_aac_event_thread_create( + message_func cb, + void* client_data, + char* th_name) +{ + int r; + int fds[2]; + struct aac_ipc_info *aac_info; + + aac_info = calloc(1, sizeof(struct aac_ipc_info)); + if (!aac_info) + { + return 0; + } + + aac_info->client_data = client_data; + aac_info->process_msg_cb = cb; + strlcpy(aac_info->thread_name, th_name, sizeof(aac_info->thread_name)); + + if (pipe(fds)) + { + DEBUG_PRINT("\n%s: pipe creation failed\n", __FUNCTION__); + goto fail_pipe; + } + + aac_info->pipe_in = fds[0]; + aac_info->pipe_out = fds[1]; + + r = pthread_create(&aac_info->thr, 0, omx_aac_events, aac_info); + if (r < 0) goto fail_thread; + + DEBUG_DETAIL("Created thread for %s \n", aac_info->thread_name); + return aac_info; + + +fail_thread: + close(aac_info->pipe_in); + close(aac_info->pipe_out); + +fail_pipe: + free(aac_info); + + return 0; +} + +void omx_aac_thread_stop(struct aac_ipc_info *aac_info) { + DEBUG_DETAIL("%s stop server\n", __FUNCTION__); + close(aac_info->pipe_in); + close(aac_info->pipe_out); + pthread_join(aac_info->thr,NULL); + aac_info->pipe_out = -1; + aac_info->pipe_in = -1; + DEBUG_DETAIL("%s: message thread close fds%d %d\n", aac_info->thread_name, + aac_info->pipe_in,aac_info->pipe_out); + free(aac_info); +} + +void omx_aac_post_msg(struct aac_ipc_info *aac_info, unsigned char id) { + DEBUG_DETAIL("\n%s id=%d\n", __FUNCTION__,id); + + write(aac_info->pipe_out, &id, 1); +} diff --git a/audio/mm-audio/aenc-aac/qdsp6/src/omx_aac_aenc.cpp b/audio/mm-audio/aenc-aac/qdsp6/src/omx_aac_aenc.cpp new file mode 100644 index 0000000..6af9269 --- /dev/null +++ b/audio/mm-audio/aenc-aac/qdsp6/src/omx_aac_aenc.cpp @@ -0,0 +1,5062 @@ +/*-------------------------------------------------------------------------- +Copyright (c) 2010-2014, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of The Linux Foundation nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------*/ +/*============================================================================ +@file omx_aenc_aac.c + This module contains the implementation of the OpenMAX core & component. + +*//*========================================================================*/ +////////////////////////////////////////////////////////////////////////////// +// Include Files +////////////////////////////////////////////////////////////////////////////// + + +#include +#include +#include +#include "omx_aac_aenc.h" +#include + +using namespace std; + +#define SLEEP_MS 100 + +// omx_cmd_queue destructor +omx_aac_aenc::omx_cmd_queue::~omx_cmd_queue() +{ + // Nothing to do +} + +// omx cmd queue constructor +omx_aac_aenc::omx_cmd_queue::omx_cmd_queue(): m_read(0),m_write(0),m_size(0) +{ + memset(m_q, 0,sizeof(omx_event)*OMX_CORE_CONTROL_CMDQ_SIZE); +} + +// omx cmd queue insert +bool omx_aac_aenc::omx_cmd_queue::insert_entry(unsigned long p1, + unsigned long p2, + unsigned char id) +{ + bool ret = true; + if (m_size < OMX_CORE_CONTROL_CMDQ_SIZE) + { + m_q[m_write].id = id; + m_q[m_write].param1 = p1; + m_q[m_write].param2 = p2; + m_write++; + m_size ++; + if (m_write >= OMX_CORE_CONTROL_CMDQ_SIZE) + { + m_write = 0; + } + } else + { + ret = false; + DEBUG_PRINT_ERROR("ERROR!!! Command Queue Full"); + } + return ret; +} + +bool omx_aac_aenc::omx_cmd_queue::pop_entry(unsigned long *p1, + unsigned long *p2, unsigned char *id) +{ + bool ret = true; + if (m_size > 0) + { + *id = m_q[m_read].id; + *p1 = m_q[m_read].param1; + *p2 = m_q[m_read].param2; + // Move the read pointer ahead + ++m_read; + --m_size; + if (m_read >= OMX_CORE_CONTROL_CMDQ_SIZE) + { + m_read = 0; + + } + } else + { + ret = false; + DEBUG_PRINT_ERROR("ERROR Delete!!! Command Queue Empty"); + } + return ret; +} + +// factory function executed by the core to create instances +void *get_omx_component_factory_fn(void) +{ + return(new omx_aac_aenc); +} +bool omx_aac_aenc::omx_cmd_queue::get_msg_id(unsigned char *id) +{ + if(m_size > 0) + { + *id = m_q[m_read].id; + DEBUG_PRINT("get_msg_id=%d\n",*id); + } + else{ + return false; + } + return true; +} +/*============================================================================= +FUNCTION: + wait_for_event + +DESCRIPTION: + waits for a particular event + +INPUT/OUTPUT PARAMETERS: + None + +RETURN VALUE: + None + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +void omx_aac_aenc::wait_for_event() +{ + int rc; + struct timespec ts; + pthread_mutex_lock(&m_event_lock); + while (0 == m_is_event_done) + { + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += (SLEEP_MS/1000); + ts.tv_nsec += ((SLEEP_MS%1000) * 1000000); + rc = pthread_cond_timedwait(&cond, &m_event_lock, &ts); + if (rc == ETIMEDOUT && !m_is_event_done) { + DEBUG_PRINT("Timed out waiting for flush"); + if (ioctl( m_drv_fd, AUDIO_FLUSH, 0) == -1) + DEBUG_PRINT_ERROR("Flush:Input port, ioctl flush failed %d\n", + errno); + } + } + m_is_event_done = 0; + pthread_mutex_unlock(&m_event_lock); +} + +/*============================================================================= +FUNCTION: + event_complete + +DESCRIPTION: + informs about the occurance of an event + +INPUT/OUTPUT PARAMETERS: + None + +RETURN VALUE: + None + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +void omx_aac_aenc::event_complete() +{ + pthread_mutex_lock(&m_event_lock); + if (0 == m_is_event_done) + { + m_is_event_done = 1; + pthread_cond_signal(&cond); + } + pthread_mutex_unlock(&m_event_lock); +} + +// All this non-sense because of a single aac object +void omx_aac_aenc::in_th_goto_sleep() +{ + pthread_mutex_lock(&m_in_th_lock); + while (0 == m_is_in_th_sleep) + { + pthread_cond_wait(&in_cond, &m_in_th_lock); + } + m_is_in_th_sleep = 0; + pthread_mutex_unlock(&m_in_th_lock); +} + +void omx_aac_aenc::in_th_wakeup() +{ + pthread_mutex_lock(&m_in_th_lock); + if (0 == m_is_in_th_sleep) + { + m_is_in_th_sleep = 1; + pthread_cond_signal(&in_cond); + } + pthread_mutex_unlock(&m_in_th_lock); +} + +void omx_aac_aenc::out_th_goto_sleep() +{ + + pthread_mutex_lock(&m_out_th_lock); + while (0 == m_is_out_th_sleep) + { + pthread_cond_wait(&out_cond, &m_out_th_lock); + } + m_is_out_th_sleep = 0; + pthread_mutex_unlock(&m_out_th_lock); +} + +void omx_aac_aenc::out_th_wakeup() +{ + pthread_mutex_lock(&m_out_th_lock); + if (0 == m_is_out_th_sleep) + { + m_is_out_th_sleep = 1; + pthread_cond_signal(&out_cond); + } + pthread_mutex_unlock(&m_out_th_lock); +} +/* ====================================================================== +FUNCTION + omx_aac_aenc::omx_aac_aenc + +DESCRIPTION + Constructor + +PARAMETERS + None + +RETURN VALUE + None. +========================================================================== */ +omx_aac_aenc::omx_aac_aenc(): m_tmp_meta_buf(NULL), + m_tmp_out_meta_buf(NULL), + m_flush_cnt(255), + m_comp_deinit(0), + adif_flag(0), + mp4ff_flag(0), + m_app_data(NULL), + nNumOutputBuf(0), + m_drv_fd(-1), + bFlushinprogress(0), + is_in_th_sleep(false), + is_out_th_sleep(false), + m_flags(0), + nTimestamp(0), + ts(0), + frameduration(0), + m_inp_act_buf_count (OMX_CORE_NUM_INPUT_BUFFERS), + m_out_act_buf_count (OMX_CORE_NUM_OUTPUT_BUFFERS), + m_inp_current_buf_count(0), + m_out_current_buf_count(0), + output_buffer_size((OMX_U32)OMX_AAC_OUTPUT_BUFFER_SIZE), + input_buffer_size(OMX_CORE_INPUT_BUFFER_SIZE), + m_session_id(0), + m_inp_bEnabled(OMX_TRUE), + m_out_bEnabled(OMX_TRUE), + m_inp_bPopulated(OMX_FALSE), + m_out_bPopulated(OMX_FALSE), + m_is_event_done(0), + m_state(OMX_StateInvalid), + m_ipc_to_in_th(NULL), + m_ipc_to_out_th(NULL), + m_ipc_to_cmd_th(NULL) +{ + int cond_ret = 0; + component_Role.nSize = 0; + memset(&m_cmp, 0, sizeof(m_cmp)); + memset(&m_cb, 0, sizeof(m_cb)); + memset(&m_aac_pb_stats, 0, sizeof(m_aac_pb_stats)); + memset(&m_pcm_param, 0, sizeof(m_pcm_param)); + memset(&m_aac_param, 0, sizeof(m_aac_param)); + memset(&m_buffer_supplier, 0, sizeof(m_buffer_supplier)); + memset(&m_priority_mgm, 0, sizeof(m_priority_mgm)); + + pthread_mutexattr_init(&m_lock_attr); + pthread_mutex_init(&m_lock, &m_lock_attr); + pthread_mutexattr_init(&m_commandlock_attr); + pthread_mutex_init(&m_commandlock, &m_commandlock_attr); + + pthread_mutexattr_init(&m_outputlock_attr); + pthread_mutex_init(&m_outputlock, &m_outputlock_attr); + + pthread_mutexattr_init(&m_state_attr); + pthread_mutex_init(&m_state_lock, &m_state_attr); + + pthread_mutexattr_init(&m_event_attr); + pthread_mutex_init(&m_event_lock, &m_event_attr); + + pthread_mutexattr_init(&m_flush_attr); + pthread_mutex_init(&m_flush_lock, &m_flush_attr); + + pthread_mutexattr_init(&m_event_attr); + pthread_mutex_init(&m_event_lock, &m_event_attr); + + pthread_mutexattr_init(&m_in_th_attr); + pthread_mutex_init(&m_in_th_lock, &m_in_th_attr); + + pthread_mutexattr_init(&m_out_th_attr); + pthread_mutex_init(&m_out_th_lock, &m_out_th_attr); + + pthread_mutexattr_init(&m_in_th_attr_1); + pthread_mutex_init(&m_in_th_lock_1, &m_in_th_attr_1); + + pthread_mutexattr_init(&m_out_th_attr_1); + pthread_mutex_init(&m_out_th_lock_1, &m_out_th_attr_1); + + pthread_mutexattr_init(&out_buf_count_lock_attr); + pthread_mutex_init(&out_buf_count_lock, &out_buf_count_lock_attr); + + pthread_mutexattr_init(&in_buf_count_lock_attr); + pthread_mutex_init(&in_buf_count_lock, &in_buf_count_lock_attr); + if ((cond_ret = pthread_cond_init (&cond, NULL)) != 0) + { + DEBUG_PRINT_ERROR("pthread_cond_init returns non zero for cond\n"); + if (cond_ret == EAGAIN) + DEBUG_PRINT_ERROR("The system lacked necessary \ + resources(other than mem)\n"); + else if (cond_ret == ENOMEM) + DEBUG_PRINT_ERROR("Insufficient memory to \ + initialise condition variable\n"); + } + if ((cond_ret = pthread_cond_init (&in_cond, NULL)) != 0) + { + DEBUG_PRINT_ERROR("pthread_cond_init returns non zero for in_cond\n"); + if (cond_ret == EAGAIN) + DEBUG_PRINT_ERROR("The system lacked necessary \ + resources(other than mem)\n"); + else if (cond_ret == ENOMEM) + DEBUG_PRINT_ERROR("Insufficient memory to \ + initialise condition variable\n"); + } + if ((cond_ret = pthread_cond_init (&out_cond, NULL)) != 0) + { + DEBUG_PRINT_ERROR("pthread_cond_init returns non zero for out_cond\n"); + if (cond_ret == EAGAIN) + DEBUG_PRINT_ERROR("The system lacked necessary \ + resources(other than mem)\n"); + else if (cond_ret == ENOMEM) + DEBUG_PRINT_ERROR("Insufficient memory to \ + initialise condition variable\n"); + } + + sem_init(&sem_read_msg,0, 0); + sem_init(&sem_write_msg,0, 0); + sem_init(&sem_States,0, 0); + return; +} + + +/* ====================================================================== +FUNCTION + omx_aac_aenc::~omx_aac_aenc + +DESCRIPTION + Destructor + +PARAMETERS + None + +RETURN VALUE + None. +========================================================================== */ +omx_aac_aenc::~omx_aac_aenc() +{ + DEBUG_PRINT_ERROR("AAC Object getting destroyed comp-deinit=%d\n", + m_comp_deinit); + if ( !m_comp_deinit ) + { + deinit_encoder(); + } + pthread_mutexattr_destroy(&m_lock_attr); + pthread_mutex_destroy(&m_lock); + + pthread_mutexattr_destroy(&m_commandlock_attr); + pthread_mutex_destroy(&m_commandlock); + + pthread_mutexattr_destroy(&m_outputlock_attr); + pthread_mutex_destroy(&m_outputlock); + + pthread_mutexattr_destroy(&m_state_attr); + pthread_mutex_destroy(&m_state_lock); + + pthread_mutexattr_destroy(&m_event_attr); + pthread_mutex_destroy(&m_event_lock); + + pthread_mutexattr_destroy(&m_flush_attr); + pthread_mutex_destroy(&m_flush_lock); + + pthread_mutexattr_destroy(&m_in_th_attr); + pthread_mutex_destroy(&m_in_th_lock); + + pthread_mutexattr_destroy(&m_out_th_attr); + pthread_mutex_destroy(&m_out_th_lock); + + pthread_mutexattr_destroy(&out_buf_count_lock_attr); + pthread_mutex_destroy(&out_buf_count_lock); + + pthread_mutexattr_destroy(&in_buf_count_lock_attr); + pthread_mutex_destroy(&in_buf_count_lock); + + pthread_mutexattr_destroy(&m_in_th_attr_1); + pthread_mutex_destroy(&m_in_th_lock_1); + + pthread_mutexattr_destroy(&m_out_th_attr_1); + pthread_mutex_destroy(&m_out_th_lock_1); + pthread_mutex_destroy(&out_buf_count_lock); + pthread_mutex_destroy(&in_buf_count_lock); + pthread_cond_destroy(&cond); + pthread_cond_destroy(&in_cond); + pthread_cond_destroy(&out_cond); + sem_destroy (&sem_read_msg); + sem_destroy (&sem_write_msg); + sem_destroy (&sem_States); + DEBUG_PRINT_ERROR("OMX AAC component destroyed\n"); + return; +} + +/** + @brief memory function for sending EmptyBufferDone event + back to IL client + + @param bufHdr OMX buffer header to be passed back to IL client + @return none + */ +void omx_aac_aenc::buffer_done_cb(OMX_BUFFERHEADERTYPE *bufHdr) +{ + if (m_cb.EmptyBufferDone) + { + PrintFrameHdr(OMX_COMPONENT_GENERATE_BUFFER_DONE,bufHdr); + bufHdr->nFilledLen = 0; + + m_cb.EmptyBufferDone(&m_cmp, m_app_data, bufHdr); + pthread_mutex_lock(&in_buf_count_lock); + m_aac_pb_stats.ebd_cnt++; + nNumInputBuf--; + DEBUG_DETAIL("EBD CB:: in_buf_len=%d nNumInputBuf=%d ebd_cnd %d\n",\ + m_aac_pb_stats.tot_in_buf_len, + nNumInputBuf, m_aac_pb_stats.ebd_cnt); + pthread_mutex_unlock(&in_buf_count_lock); + } + + return; +} + +/*============================================================================= +FUNCTION: + flush_ack + +DESCRIPTION: + + +INPUT/OUTPUT PARAMETERS: + None + +RETURN VALUE: + None + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +void omx_aac_aenc::flush_ack() +{ + // Decrement the FLUSH ACK count and notify the waiting recepients + pthread_mutex_lock(&m_flush_lock); + --m_flush_cnt; + if (0 == m_flush_cnt) + { + event_complete(); + } + DEBUG_PRINT("Rxed FLUSH ACK cnt=%d\n",m_flush_cnt); + pthread_mutex_unlock(&m_flush_lock); +} +void omx_aac_aenc::frame_done_cb(OMX_BUFFERHEADERTYPE *bufHdr) +{ + if (m_cb.FillBufferDone) + { + PrintFrameHdr(OMX_COMPONENT_GENERATE_FRAME_DONE,bufHdr); + m_aac_pb_stats.fbd_cnt++; + pthread_mutex_lock(&out_buf_count_lock); + nNumOutputBuf--; + DEBUG_PRINT("FBD CB:: nNumOutputBuf=%d out_buf_len=%u fbd_cnt=%u\n",\ + nNumOutputBuf, + m_aac_pb_stats.tot_out_buf_len, + m_aac_pb_stats.fbd_cnt); + m_aac_pb_stats.tot_out_buf_len += bufHdr->nFilledLen; + m_aac_pb_stats.tot_pb_time = bufHdr->nTimeStamp; + DEBUG_PRINT("FBD:in_buf_len=%u out_buf_len=%u\n", + m_aac_pb_stats.tot_in_buf_len, + m_aac_pb_stats.tot_out_buf_len); + pthread_mutex_unlock(&out_buf_count_lock); + m_cb.FillBufferDone(&m_cmp, m_app_data, bufHdr); + } + return; +} + +/*============================================================================= +FUNCTION: + process_out_port_msg + +DESCRIPTION: + Function for handling all commands from IL client +IL client commands are processed and callbacks are generated through +this routine Audio Command Server provides the thread context for this routine + +INPUT/OUTPUT PARAMETERS: + [INOUT] client_data + [IN] id + +RETURN VALUE: + None + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +void omx_aac_aenc::process_out_port_msg(void *client_data, unsigned char id) +{ + unsigned long p1 = 0; // Parameter - 1 + unsigned long p2 = 0; // Parameter - 2 + unsigned char ident = 0; + unsigned qsize = 0; // qsize + unsigned tot_qsize = 0; + omx_aac_aenc *pThis = (omx_aac_aenc *) client_data; + OMX_STATETYPE state; + +loopback_out: + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + if ( state == OMX_StateLoaded ) + { + DEBUG_PRINT(" OUT: IN LOADED STATE RETURN\n"); + return; + } + pthread_mutex_lock(&pThis->m_outputlock); + + qsize = pThis->m_output_ctrl_cmd_q.m_size; + tot_qsize = pThis->m_output_ctrl_cmd_q.m_size; + tot_qsize += pThis->m_output_ctrl_fbd_q.m_size; + tot_qsize += pThis->m_output_q.m_size; + + if ( 0 == tot_qsize ) + { + pthread_mutex_unlock(&pThis->m_outputlock); + DEBUG_DETAIL("OUT-->BREAK FROM LOOP...%d\n",tot_qsize); + return; + } + if ( (state != OMX_StateExecuting) && !qsize ) + { + pthread_mutex_unlock(&pThis->m_outputlock); + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + if ( state == OMX_StateLoaded ) + return; + + DEBUG_DETAIL("OUT:1.SLEEPING OUT THREAD\n"); + pthread_mutex_lock(&pThis->m_out_th_lock_1); + pThis->is_out_th_sleep = true; + pthread_mutex_unlock(&pThis->m_out_th_lock_1); + pThis->out_th_goto_sleep(); + + /* Get the updated state */ + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + } + + if ( ((!pThis->m_output_ctrl_cmd_q.m_size) && !pThis->m_out_bEnabled) ) + { + // case where no port reconfig and nothing in the flush q + DEBUG_DETAIL("No flush/port reconfig qsize=%d tot_qsize=%d",\ + qsize,tot_qsize); + pthread_mutex_unlock(&pThis->m_outputlock); + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + if ( state == OMX_StateLoaded ) + return; + + if(pThis->m_output_ctrl_cmd_q.m_size || !(pThis->bFlushinprogress)) + { + DEBUG_PRINT("OUT:2. SLEEPING OUT THREAD \n"); + pthread_mutex_lock(&pThis->m_out_th_lock_1); + pThis->is_out_th_sleep = true; + pthread_mutex_unlock(&pThis->m_out_th_lock_1); + pThis->out_th_goto_sleep(); + } + /* Get the updated state */ + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + } + + qsize = pThis->m_output_ctrl_cmd_q.m_size; + tot_qsize = pThis->m_output_ctrl_cmd_q.m_size; + tot_qsize += pThis->m_output_ctrl_fbd_q.m_size; + tot_qsize += pThis->m_output_q.m_size; + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + DEBUG_DETAIL("OUT-->QSIZE-flush=%d,fbd=%d QSIZE=%d state=%d\n",\ + pThis->m_output_ctrl_cmd_q.m_size, + pThis->m_output_ctrl_fbd_q.m_size, + pThis->m_output_q.m_size,state); + + + if (qsize) + { + // process FLUSH message + pThis->m_output_ctrl_cmd_q.pop_entry(&p1,&p2,&ident); + } else if ( (qsize = pThis->m_output_ctrl_fbd_q.m_size) && + (pThis->m_out_bEnabled) && (state == OMX_StateExecuting) ) + { + // then process EBD's + pThis->m_output_ctrl_fbd_q.pop_entry(&p1,&p2,&ident); + } else if ( (qsize = pThis->m_output_q.m_size) && + (pThis->m_out_bEnabled) && (state == OMX_StateExecuting) ) + { + // if no FLUSH and FBD's then process FTB's + pThis->m_output_q.pop_entry(&p1,&p2,&ident); + } else if ( state == OMX_StateLoaded ) + { + pthread_mutex_unlock(&pThis->m_outputlock); + DEBUG_PRINT("IN: ***in OMX_StateLoaded so exiting\n"); + return ; + } else + { + qsize = 0; + DEBUG_PRINT("OUT--> Empty Queue state=%d %d %d %d\n",state, + pThis->m_output_ctrl_cmd_q.m_size, + pThis->m_output_ctrl_fbd_q.m_size, + pThis->m_output_q.m_size); + + if(state == OMX_StatePause) + { + DEBUG_DETAIL("OUT: SLEEPING AGAIN OUT THREAD\n"); + pthread_mutex_lock(&pThis->m_out_th_lock_1); + pThis->is_out_th_sleep = true; + pthread_mutex_unlock(&pThis->m_out_th_lock_1); + pthread_mutex_unlock(&pThis->m_outputlock); + pThis->out_th_goto_sleep(); + goto loopback_out; + } + } + pthread_mutex_unlock(&pThis->m_outputlock); + + if ( qsize > 0 ) + { + id = ident; + ident = 0; + DEBUG_DETAIL("OUT->state[%d]ident[%d]flushq[%d]fbd[%d]dataq[%d]\n",\ + pThis->m_state, + ident, + pThis->m_output_ctrl_cmd_q.m_size, + pThis->m_output_ctrl_fbd_q.m_size, + pThis->m_output_q.m_size); + + if ( OMX_COMPONENT_GENERATE_FRAME_DONE == id ) + { + pThis->frame_done_cb((OMX_BUFFERHEADERTYPE *)p2); + } else if ( OMX_COMPONENT_GENERATE_FTB == id ) + { + pThis->fill_this_buffer_proxy((OMX_HANDLETYPE)p1, + (OMX_BUFFERHEADERTYPE *)p2); + } else if ( OMX_COMPONENT_GENERATE_EOS == id ) + { + pThis->m_cb.EventHandler(&pThis->m_cmp, + pThis->m_app_data, + OMX_EventBufferFlag, + 1, 1, NULL ); + + } + else if(id == OMX_COMPONENT_RESUME) + { + DEBUG_PRINT("RESUMED...\n"); + } + else if(id == OMX_COMPONENT_GENERATE_COMMAND) + { + // Execute FLUSH command + if ( OMX_CommandFlush == p1 ) + { + DEBUG_DETAIL("Executing FLUSH command on Output port\n"); + pThis->execute_output_omx_flush(); + } else + { + DEBUG_DETAIL("Invalid command[%lu]\n",p1); + } + } else + { + DEBUG_PRINT_ERROR("ERROR:OUT-->Invalid Id[%d]\n",id); + } + } else + { + DEBUG_DETAIL("ERROR: OUT--> Empty OUTPUTQ\n"); + } + + return; +} + +/*============================================================================= +FUNCTION: + process_command_msg + +DESCRIPTION: + + +INPUT/OUTPUT PARAMETERS: + [INOUT] client_data + [IN] id + +RETURN VALUE: + None + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +void omx_aac_aenc::process_command_msg(void *client_data, unsigned char id) +{ + unsigned long p1 = 0; // Parameter - 1 + unsigned long p2 = 0; // Parameter - 2 + unsigned char ident = 0; + unsigned qsize = 0; + omx_aac_aenc *pThis = (omx_aac_aenc*)client_data; + pthread_mutex_lock(&pThis->m_commandlock); + + qsize = pThis->m_command_q.m_size; + DEBUG_DETAIL("CMD-->QSIZE=%d state=%d\n",pThis->m_command_q.m_size, + pThis->m_state); + + if (!qsize) + { + DEBUG_DETAIL("CMD-->BREAKING FROM LOOP\n"); + pthread_mutex_unlock(&pThis->m_commandlock); + return; + } else + { + pThis->m_command_q.pop_entry(&p1,&p2,&ident); + } + pthread_mutex_unlock(&pThis->m_commandlock); + + id = ident; + DEBUG_DETAIL("CMD->state[%d]id[%d]cmdq[%d]n",\ + pThis->m_state,ident, \ + pThis->m_command_q.m_size); + + if (OMX_COMPONENT_GENERATE_EVENT == id) + { + if (pThis->m_cb.EventHandler) + { + if (OMX_CommandStateSet == p1) + { + pthread_mutex_lock(&pThis->m_state_lock); + pThis->m_state = (OMX_STATETYPE) p2; + pthread_mutex_unlock(&pThis->m_state_lock); + DEBUG_PRINT("CMD:Process->state set to %d \n", \ + pThis->m_state); + + if (pThis->m_state == OMX_StateExecuting || + pThis->m_state == OMX_StateLoaded) + { + + pthread_mutex_lock(&pThis->m_in_th_lock_1); + if (pThis->is_in_th_sleep) + { + pThis->is_in_th_sleep = false; + DEBUG_DETAIL("CMD:WAKING UP IN THREADS\n"); + pThis->in_th_wakeup(); + } + pthread_mutex_unlock(&pThis->m_in_th_lock_1); + + pthread_mutex_lock(&pThis->m_out_th_lock_1); + if (pThis->is_out_th_sleep) + { + DEBUG_DETAIL("CMD:WAKING UP OUT THREADS\n"); + pThis->is_out_th_sleep = false; + pThis->out_th_wakeup(); + } + pthread_mutex_unlock(&pThis->m_out_th_lock_1); + } + } + if (OMX_StateInvalid == pThis->m_state) + { + pThis->m_cb.EventHandler(&pThis->m_cmp, + pThis->m_app_data, + OMX_EventError, + OMX_ErrorInvalidState, + 0, NULL ); + } else if ((signed)p2 == OMX_ErrorPortUnpopulated) + { + pThis->m_cb.EventHandler(&pThis->m_cmp, + pThis->m_app_data, + OMX_EventError, + (OMX_U32)p2, + 0, + 0); + } else + { + pThis->m_cb.EventHandler(&pThis->m_cmp, + pThis->m_app_data, + OMX_EventCmdComplete, + (OMX_U32)p1, (OMX_U32)p2, NULL ); + } + } else + { + DEBUG_PRINT_ERROR("ERROR:CMD-->EventHandler NULL \n"); + } + } else if (OMX_COMPONENT_GENERATE_COMMAND == id) + { + pThis->send_command_proxy(&pThis->m_cmp, + (OMX_COMMANDTYPE)p1, + (OMX_U32)p2,(OMX_PTR)NULL); + } else if (OMX_COMPONENT_PORTSETTINGS_CHANGED == id) + { + DEBUG_DETAIL("CMD-->RXED PORTSETTINGS_CHANGED"); + pThis->m_cb.EventHandler(&pThis->m_cmp, + pThis->m_app_data, + OMX_EventPortSettingsChanged, + 1, 1, NULL ); + } + else + { + DEBUG_PRINT_ERROR("CMD->state[%d]id[%d]\n",pThis->m_state,ident); + } + return; +} + +/*============================================================================= +FUNCTION: + process_in_port_msg + +DESCRIPTION: + + +INPUT/OUTPUT PARAMETERS: + [INOUT] client_data + [IN] id + +RETURN VALUE: + None + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +void omx_aac_aenc::process_in_port_msg(void *client_data, unsigned char id) +{ + unsigned long p1 = 0; // Parameter - 1 + unsigned long p2 = 0; // Parameter - 2 + unsigned char ident = 0; + unsigned qsize = 0; + unsigned tot_qsize = 0; + omx_aac_aenc *pThis = (omx_aac_aenc *) client_data; + OMX_STATETYPE state; + + if (!pThis) + { + DEBUG_PRINT_ERROR("ERROR:IN--> Invalid Obj \n"); + return; + } +loopback_in: + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + if ( state == OMX_StateLoaded ) + { + DEBUG_PRINT(" IN: IN LOADED STATE RETURN\n"); + return; + } + // Protect the shared queue data structure + pthread_mutex_lock(&pThis->m_lock); + + qsize = pThis->m_input_ctrl_cmd_q.m_size; + tot_qsize = qsize; + tot_qsize += pThis->m_input_ctrl_ebd_q.m_size; + tot_qsize += pThis->m_input_q.m_size; + + if ( 0 == tot_qsize ) + { + DEBUG_DETAIL("IN-->BREAKING FROM IN LOOP"); + pthread_mutex_unlock(&pThis->m_lock); + return; + } + + if ( (state != OMX_StateExecuting) && ! (pThis->m_input_ctrl_cmd_q.m_size)) + { + pthread_mutex_unlock(&pThis->m_lock); + DEBUG_DETAIL("SLEEPING IN THREAD\n"); + pthread_mutex_lock(&pThis->m_in_th_lock_1); + pThis->is_in_th_sleep = true; + pthread_mutex_unlock(&pThis->m_in_th_lock_1); + pThis->in_th_goto_sleep(); + + /* Get the updated state */ + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + } + else if ((state == OMX_StatePause)) + { + if(!(pThis->m_input_ctrl_cmd_q.m_size)) + { + pthread_mutex_unlock(&pThis->m_lock); + + DEBUG_DETAIL("IN: SLEEPING IN THREAD\n"); + pthread_mutex_lock(&pThis->m_in_th_lock_1); + pThis->is_in_th_sleep = true; + pthread_mutex_unlock(&pThis->m_in_th_lock_1); + pThis->in_th_goto_sleep(); + + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + } + } + + qsize = pThis->m_input_ctrl_cmd_q.m_size; + tot_qsize = qsize; + tot_qsize += pThis->m_input_ctrl_ebd_q.m_size; + tot_qsize += pThis->m_input_q.m_size; + + DEBUG_DETAIL("Input-->QSIZE-flush=%d,ebd=%d QSIZE=%d state=%d\n",\ + pThis->m_input_ctrl_cmd_q.m_size, + pThis->m_input_ctrl_ebd_q.m_size, + pThis->m_input_q.m_size, state); + + + if ( qsize ) + { + // process FLUSH message + pThis->m_input_ctrl_cmd_q.pop_entry(&p1,&p2,&ident); + } else if ( (qsize = pThis->m_input_ctrl_ebd_q.m_size) && + (state == OMX_StateExecuting) ) + { + // then process EBD's + pThis->m_input_ctrl_ebd_q.pop_entry(&p1,&p2,&ident); + } else if ((qsize = pThis->m_input_q.m_size) && + (state == OMX_StateExecuting)) + { + // if no FLUSH and EBD's then process ETB's + pThis->m_input_q.pop_entry(&p1, &p2, &ident); + } else if ( state == OMX_StateLoaded ) + { + pthread_mutex_unlock(&pThis->m_lock); + DEBUG_PRINT("IN: ***in OMX_StateLoaded so exiting\n"); + return ; + } else + { + qsize = 0; + DEBUG_PRINT("IN-->state[%d]cmdq[%d]ebdq[%d]in[%d]\n",\ + state,pThis->m_input_ctrl_cmd_q.m_size, + pThis->m_input_ctrl_ebd_q.m_size, + pThis->m_input_q.m_size); + + if(state == OMX_StatePause) + { + DEBUG_DETAIL("IN: SLEEPING AGAIN IN THREAD\n"); + pthread_mutex_lock(&pThis->m_in_th_lock_1); + pThis->is_in_th_sleep = true; + pthread_mutex_unlock(&pThis->m_in_th_lock_1); + pthread_mutex_unlock(&pThis->m_lock); + pThis->in_th_goto_sleep(); + goto loopback_in; + } + } + pthread_mutex_unlock(&pThis->m_lock); + + if ( qsize > 0 ) + { + id = ident; + DEBUG_DETAIL("Input->state[%d]id[%d]flushq[%d]ebdq[%d]dataq[%d]\n",\ + pThis->m_state, + ident, + pThis->m_input_ctrl_cmd_q.m_size, + pThis->m_input_ctrl_ebd_q.m_size, + pThis->m_input_q.m_size); + if ( OMX_COMPONENT_GENERATE_BUFFER_DONE == id ) + { + pThis->buffer_done_cb((OMX_BUFFERHEADERTYPE *)p2); + } + else if(id == OMX_COMPONENT_GENERATE_EOS) + { + pThis->m_cb.EventHandler(&pThis->m_cmp, pThis->m_app_data, + OMX_EventBufferFlag, 0, 1, NULL ); + } else if ( OMX_COMPONENT_GENERATE_ETB == id ) + { + pThis->empty_this_buffer_proxy((OMX_HANDLETYPE)p1, + (OMX_BUFFERHEADERTYPE *)p2); + } else if ( OMX_COMPONENT_GENERATE_COMMAND == id ) + { + // Execute FLUSH command + if ( OMX_CommandFlush == p1 ) + { + DEBUG_DETAIL(" Executing FLUSH command on Input port\n"); + pThis->execute_input_omx_flush(); + } else + { + DEBUG_DETAIL("Invalid command[%lu]\n",p1); + } + } + else + { + DEBUG_PRINT_ERROR("ERROR:IN-->Invalid Id[%d]\n",id); + } + } else + { + DEBUG_DETAIL("ERROR:IN-->Empty INPUT Q\n"); + } + return; +} + +/** + @brief member function for performing component initialization + + @param role C string mandating role of this component + @return Error status + */ +OMX_ERRORTYPE omx_aac_aenc::component_init(OMX_STRING role) +{ + + OMX_ERRORTYPE eRet = OMX_ErrorNone; + m_state = OMX_StateLoaded; + + /* DSP does not give information about the bitstream + randomly assign the value right now. Query will result in + incorrect param */ + memset(&m_aac_param, 0, sizeof(m_aac_param)); + m_aac_param.nSize = (OMX_U32)sizeof(m_aac_param); + m_aac_param.nChannels = DEFAULT_CH_CFG; + m_aac_param.nSampleRate = DEFAULT_SF; + m_aac_param.nBitRate = DEFAULT_BITRATE; + m_volume = OMX_AAC_DEFAULT_VOL; /* Close to unity gain */ + memset(&m_aac_pb_stats,0,sizeof(AAC_PB_STATS)); + memset(&m_pcm_param, 0, sizeof(m_pcm_param)); + m_pcm_param.nSize = (OMX_U32)sizeof(m_pcm_param); + m_pcm_param.nChannels = DEFAULT_CH_CFG; + m_pcm_param.nSamplingRate = DEFAULT_SF; + + nTimestamp = 0; + ts = 0; + m_frame_count = 0; + frameduration = 0; + nNumInputBuf = 0; + nNumOutputBuf = 0; + m_ipc_to_in_th = NULL; // Command server instance + m_ipc_to_out_th = NULL; // Client server instance + m_ipc_to_cmd_th = NULL; // command instance + m_is_out_th_sleep = 0; + m_is_in_th_sleep = 0; + is_out_th_sleep= false; + + is_in_th_sleep=false; + adif_flag = 0; + mp4ff_flag = 0; + memset(&m_priority_mgm, 0, sizeof(m_priority_mgm)); + m_priority_mgm.nGroupID =0; + m_priority_mgm.nGroupPriority=0; + + memset(&m_buffer_supplier, 0, sizeof(m_buffer_supplier)); + m_buffer_supplier.nPortIndex=OMX_BufferSupplyUnspecified; + + DEBUG_PRINT_ERROR(" component init: role = %s\n",role); + + DEBUG_PRINT(" component init: role = %s\n",role); + component_Role.nVersion.nVersion = OMX_SPEC_VERSION; + if (!strcmp(role,"OMX.qcom.audio.encoder.aac")) + { + pcm_input = 1; + component_Role.nSize = (OMX_U32)sizeof(role); + strlcpy((char *)component_Role.cRole, (const char*)role, + sizeof(component_Role.cRole)); + DEBUG_PRINT("\ncomponent_init: Component %s LOADED \n", role); + } else if (!strcmp(role,"OMX.qcom.audio.encoder.tunneled.aac")) + { + pcm_input = 0; + component_Role.nSize = (OMX_U32)sizeof(role); + strlcpy((char *)component_Role.cRole, (const char*)role, + sizeof(component_Role.cRole)); + DEBUG_PRINT("\ncomponent_init: Component %s LOADED \n", role); + } else + { + component_Role.nSize = (OMX_U32)sizeof("\0"); + strlcpy((char *)component_Role.cRole, (const char*)"\0", + sizeof(component_Role.cRole)); + DEBUG_PRINT_ERROR("\ncomponent_init: Component %s LOADED is invalid\n", + role); + return OMX_ErrorInsufficientResources; + } + if(pcm_input) + { + m_tmp_meta_buf = (OMX_U8*) malloc(sizeof(OMX_U8) * + (OMX_CORE_INPUT_BUFFER_SIZE + sizeof(META_IN))); + + if (m_tmp_meta_buf == NULL) { + DEBUG_PRINT_ERROR("Mem alloc failed for in meta buf\n"); + return OMX_ErrorInsufficientResources; + } + } + m_tmp_out_meta_buf = + (OMX_U8*)malloc(sizeof(OMX_U8)*OMX_AAC_OUTPUT_BUFFER_SIZE); + if ( m_tmp_out_meta_buf == NULL ) { + DEBUG_PRINT_ERROR("Mem alloc failed for out meta buf\n"); + return OMX_ErrorInsufficientResources; + } + + if(0 == pcm_input) + { + m_drv_fd = open("/dev/msm_aac_in",O_RDONLY); + } + else + { + m_drv_fd = open("/dev/msm_aac_in",O_RDWR); + } + if (m_drv_fd < 0) + { + DEBUG_PRINT_ERROR("Component_init Open Failed[%d] errno[%d]",\ + m_drv_fd,errno); + + return OMX_ErrorInsufficientResources; + } + if(ioctl(m_drv_fd, AUDIO_GET_SESSION_ID,&m_session_id) == -1) + { + DEBUG_PRINT_ERROR("AUDIO_GET_SESSION_ID FAILED\n"); + } + if(pcm_input) + { + if (!m_ipc_to_in_th) + { + m_ipc_to_in_th = omx_aac_thread_create(process_in_port_msg, + this, (char *)"INPUT_THREAD"); + if (!m_ipc_to_in_th) + { + DEBUG_PRINT_ERROR("ERROR!!! Failed to start \ + Input port thread\n"); + return OMX_ErrorInsufficientResources; + } + } + } + + if (!m_ipc_to_cmd_th) + { + m_ipc_to_cmd_th = omx_aac_thread_create(process_command_msg, + this, (char *)"CMD_THREAD"); + if (!m_ipc_to_cmd_th) + { + DEBUG_PRINT_ERROR("ERROR!!!Failed to start " + "command message thread\n"); + return OMX_ErrorInsufficientResources; + } + } + + if (!m_ipc_to_out_th) + { + m_ipc_to_out_th = omx_aac_thread_create(process_out_port_msg, + this, (char *)"OUTPUT_THREAD"); + if (!m_ipc_to_out_th) + { + DEBUG_PRINT_ERROR("ERROR!!! Failed to start output " + "port thread\n"); + return OMX_ErrorInsufficientResources; + } + } + return eRet; +} + +/** + + @brief member function to retrieve version of component + + + + @param hComp handle to this component instance + @param componentName name of component + @param componentVersion pointer to memory space which stores the + version number + @param specVersion pointer to memory sapce which stores version of + openMax specification + @param componentUUID + @return Error status + */ +OMX_ERRORTYPE omx_aac_aenc::get_component_version +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_OUT OMX_STRING componentName, + OMX_OUT OMX_VERSIONTYPE* componentVersion, + OMX_OUT OMX_VERSIONTYPE* specVersion, + OMX_OUT OMX_UUIDTYPE* componentUUID) +{ + if((hComp == NULL) || (componentName == NULL) || + (specVersion == NULL) || (componentUUID == NULL)) + { + componentVersion = NULL; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_state == OMX_StateInvalid) + { + DEBUG_PRINT_ERROR("Get Comp Version in Invalid State\n"); + return OMX_ErrorInvalidState; + } + componentVersion->nVersion = OMX_SPEC_VERSION; + specVersion->nVersion = OMX_SPEC_VERSION; + return OMX_ErrorNone; +} +/** + @brief member function handles command from IL client + + This function simply queue up commands from IL client. + Commands will be processed in command server thread context later + + @param hComp handle to component instance + @param cmd type of command + @param param1 parameters associated with the command type + @param cmdData + @return Error status +*/ +OMX_ERRORTYPE omx_aac_aenc::send_command(OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_COMMANDTYPE cmd, + OMX_IN OMX_U32 param1, + OMX_IN OMX_PTR cmdData) +{ + int portIndex = (int)param1; + + if(hComp == NULL) + { + cmdData = cmdData; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (OMX_StateInvalid == m_state) + { + return OMX_ErrorInvalidState; + } + if ( (cmd == OMX_CommandFlush) && (portIndex > 1) ) + { + return OMX_ErrorBadPortIndex; + } + post_command((unsigned)cmd,(unsigned)param1,OMX_COMPONENT_GENERATE_COMMAND); + DEBUG_PRINT("Send Command : returns with OMX_ErrorNone \n"); + DEBUG_PRINT("send_command : recieved state before semwait= %u\n",param1); + sem_wait (&sem_States); + DEBUG_PRINT("send_command : recieved state after semwait\n"); + return OMX_ErrorNone; +} + +/** + @brief member function performs actual processing of commands excluding + empty buffer call + + @param hComp handle to component + @param cmd command type + @param param1 parameter associated with the command + @param cmdData + + @return error status +*/ +OMX_ERRORTYPE omx_aac_aenc::send_command_proxy(OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_COMMANDTYPE cmd, + OMX_IN OMX_U32 param1, + OMX_IN OMX_PTR cmdData) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + // Handle only IDLE and executing + OMX_STATETYPE eState = (OMX_STATETYPE) param1; + int bFlag = 1; + nState = eState; + + if(hComp == NULL) + { + cmdData = cmdData; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (OMX_CommandStateSet == cmd) + { + /***************************/ + /* Current State is Loaded */ + /***************************/ + if (OMX_StateLoaded == m_state) + { + if (OMX_StateIdle == eState) + { + + if (allocate_done() || + (m_inp_bEnabled == OMX_FALSE + && m_out_bEnabled == OMX_FALSE)) + { + DEBUG_PRINT("SCP-->Allocate Done Complete\n"); + } + else + { + DEBUG_PRINT("SCP-->Loaded to Idle-Pending\n"); + BITMASK_SET(&m_flags, OMX_COMPONENT_IDLE_PENDING); + bFlag = 0; + } + + } else if (eState == OMX_StateLoaded) + { + DEBUG_PRINT("OMXCORE-SM: Loaded-->Loaded\n"); + m_cb.EventHandler(&this->m_cmp, + this->m_app_data, + OMX_EventError, + OMX_ErrorSameState, + 0, NULL ); + eRet = OMX_ErrorSameState; + } + + else if (eState == OMX_StateWaitForResources) + { + DEBUG_PRINT("OMXCORE-SM: Loaded-->WaitForResources\n"); + eRet = OMX_ErrorNone; + } + + else if (eState == OMX_StateExecuting) + { + DEBUG_PRINT("OMXCORE-SM: Loaded-->Executing\n"); + m_cb.EventHandler(&this->m_cmp, + this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } + + else if (eState == OMX_StatePause) + { + DEBUG_PRINT("OMXCORE-SM: Loaded-->Pause\n"); + m_cb.EventHandler(&this->m_cmp, + this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } + + else if (eState == OMX_StateInvalid) + { + DEBUG_PRINT("OMXCORE-SM: Loaded-->Invalid\n"); + m_cb.EventHandler(&this->m_cmp, + this->m_app_data, + OMX_EventError, + OMX_ErrorInvalidState, + 0, NULL ); + m_state = OMX_StateInvalid; + eRet = OMX_ErrorInvalidState; + } else + { + DEBUG_PRINT_ERROR("SCP-->Loaded to Invalid(%d))\n",eState); + eRet = OMX_ErrorBadParameter; + } + } + + /***************************/ + /* Current State is IDLE */ + /***************************/ + else if (OMX_StateIdle == m_state) + { + if (OMX_StateLoaded == eState) + { + if (release_done(-1)) + { + if (ioctl(m_drv_fd, AUDIO_STOP, 0) == -1) + { + DEBUG_PRINT_ERROR("SCP:Idle->Loaded,ioctl \ + stop failed %d\n", errno); + } + nTimestamp=0; + ts = 0; + frameduration = 0; + DEBUG_PRINT("SCP-->Idle to Loaded\n"); + } else + { + DEBUG_PRINT("SCP--> Idle to Loaded-Pending\n"); + BITMASK_SET(&m_flags, OMX_COMPONENT_LOADING_PENDING); + // Skip the event notification + bFlag = 0; + } + } + else if (OMX_StateExecuting == eState) + { + + struct msm_audio_aac_enc_config drv_aac_enc_config; + struct msm_audio_aac_config drv_aac_config; + struct msm_audio_stream_config drv_stream_config; + struct msm_audio_buf_cfg buf_cfg; + struct msm_audio_config pcm_cfg; + if(ioctl(m_drv_fd, AUDIO_GET_STREAM_CONFIG, &drv_stream_config) + == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_GET_STREAM_CONFIG failed, \ + errno[%d]\n", errno); + } + if(ioctl(m_drv_fd, AUDIO_SET_STREAM_CONFIG, &drv_stream_config) + == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_SET_STREAM_CONFIG failed, \ + errno[%d]\n", errno); + } + + if(ioctl(m_drv_fd, AUDIO_GET_AAC_ENC_CONFIG, + &drv_aac_enc_config) == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_GET_AAC_ENC_CONFIG failed, \ + errno[%d]\n", errno); + } + drv_aac_enc_config.channels = m_aac_param.nChannels; + drv_aac_enc_config.sample_rate = m_aac_param.nSampleRate; + drv_aac_enc_config.bit_rate = + get_updated_bit_rate(m_aac_param.nBitRate); + DEBUG_PRINT("aac config %u,%u,%u %d updated bitrate %d\n", + m_aac_param.nChannels,m_aac_param.nSampleRate, + m_aac_param.nBitRate,m_aac_param.eAACStreamFormat, + drv_aac_enc_config.bit_rate); + switch(m_aac_param.eAACStreamFormat) + { + + case 0: + case 1: + { + drv_aac_enc_config.stream_format = 65535; + DEBUG_PRINT("Setting AUDIO_AAC_FORMAT_ADTS\n"); + break; + } + case 4: + case 5: + case 6: + { + drv_aac_enc_config.stream_format = AUDIO_AAC_FORMAT_RAW; + DEBUG_PRINT("Setting AUDIO_AAC_FORMAT_RAW\n"); + break; + } + default: + break; + } + DEBUG_PRINT("Stream format = %d\n", + drv_aac_enc_config.stream_format); + if(ioctl(m_drv_fd, AUDIO_SET_AAC_ENC_CONFIG, + &drv_aac_enc_config) == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_SET_AAC_ENC_CONFIG failed, \ + errno[%d]\n", errno); + } + if (ioctl(m_drv_fd, AUDIO_GET_AAC_CONFIG, &drv_aac_config) + == -1) { + DEBUG_PRINT_ERROR("ioctl AUDIO_GET_AAC_CONFIG failed, \ + errno[%d]\n", errno); + } + + drv_aac_config.sbr_on_flag = 0; + drv_aac_config.sbr_ps_on_flag = 0; + /* Other members of drv_aac_config are not used, + so not setting them */ + switch(m_aac_param.eAACProfile) + { + case OMX_AUDIO_AACObjectLC: + { + DEBUG_PRINT("AAC_Profile: OMX_AUDIO_AACObjectLC\n"); + drv_aac_config.sbr_on_flag = 0; + drv_aac_config.sbr_ps_on_flag = 0; + break; + } + case OMX_AUDIO_AACObjectHE: + { + DEBUG_PRINT("AAC_Profile: OMX_AUDIO_AACObjectHE\n"); + drv_aac_config.sbr_on_flag = 1; + drv_aac_config.sbr_ps_on_flag = 0; + break; + } + case OMX_AUDIO_AACObjectHE_PS: + { + DEBUG_PRINT("AAC_Profile: OMX_AUDIO_AACObjectHE_PS\n"); + drv_aac_config.sbr_on_flag = 1; + drv_aac_config.sbr_ps_on_flag = 1; + break; + } + default: + { + DEBUG_PRINT_ERROR("Unsupported AAC Profile Type = %d\n", + m_aac_param.eAACProfile); + break; + } + } + DEBUG_PRINT("sbr_flag = %d, sbr_ps_flag = %d\n", + drv_aac_config.sbr_on_flag, + drv_aac_config.sbr_ps_on_flag); + + if (ioctl(m_drv_fd, AUDIO_SET_AAC_CONFIG, &drv_aac_config) + == -1) { + DEBUG_PRINT_ERROR("ioctl AUDIO_SET_AAC_CONFIG failed, \ + errno[%d]\n", errno); + } + + if (ioctl(m_drv_fd, AUDIO_GET_BUF_CFG, &buf_cfg) == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_GET_BUF_CFG, errno[%d]\n", + errno); + } + buf_cfg.meta_info_enable = 1; + buf_cfg.frames_per_buf = NUMOFFRAMES; + if (ioctl(m_drv_fd, AUDIO_SET_BUF_CFG, &buf_cfg) == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_SET_BUF_CFG, errno[%d]\n", + errno); + } + if(pcm_input) + { + if (ioctl(m_drv_fd, AUDIO_GET_CONFIG, &pcm_cfg) == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_GET_CONFIG, errno[%d]\n", + errno); + } + pcm_cfg.channel_count = m_pcm_param.nChannels; + pcm_cfg.sample_rate = m_pcm_param.nSamplingRate; + pcm_cfg.buffer_size = input_buffer_size; + pcm_cfg.buffer_count = m_inp_current_buf_count; + DEBUG_PRINT("pcm config %u %u\n",m_pcm_param.nChannels, + m_pcm_param.nSamplingRate); + + if (ioctl(m_drv_fd, AUDIO_SET_CONFIG, &pcm_cfg) == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_SET_CONFIG, errno[%d]\n", + errno); + } + } + if(ioctl(m_drv_fd, AUDIO_START, 0) == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_START failed, errno[%d]\n", + errno); + m_state = OMX_StateInvalid; + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, OMX_ErrorInvalidState, + 0, NULL ); + eRet = OMX_ErrorInvalidState; + } + DEBUG_PRINT("SCP-->Idle to Executing\n"); + nState = eState; + frameduration = (1024*1000000)/m_aac_param.nSampleRate; + } else if (eState == OMX_StateIdle) + { + DEBUG_PRINT("OMXCORE-SM: Idle-->Idle\n"); + m_cb.EventHandler(&this->m_cmp, + this->m_app_data, + OMX_EventError, + OMX_ErrorSameState, + 0, NULL ); + eRet = OMX_ErrorSameState; + } else if (eState == OMX_StateWaitForResources) + { + DEBUG_PRINT("OMXCORE-SM: Idle-->WaitForResources\n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } + + else if (eState == OMX_StatePause) + { + DEBUG_PRINT("OMXCORE-SM: Idle-->Pause\n"); + } + + else if (eState == OMX_StateInvalid) + { + DEBUG_PRINT("OMXCORE-SM: Idle-->Invalid\n"); + m_state = OMX_StateInvalid; + this->m_cb.EventHandler(&this->m_cmp, + this->m_app_data, + OMX_EventError, + OMX_ErrorInvalidState, + 0, NULL ); + eRet = OMX_ErrorInvalidState; + } else + { + DEBUG_PRINT_ERROR("SCP--> Idle to %d Not Handled\n",eState); + eRet = OMX_ErrorBadParameter; + } + } + + /******************************/ + /* Current State is Executing */ + /******************************/ + else if (OMX_StateExecuting == m_state) + { + if (OMX_StateIdle == eState) + { + DEBUG_PRINT("SCP-->Executing to Idle \n"); + if(pcm_input) + execute_omx_flush(-1,false); + else + execute_omx_flush(1,false); + + + } else if (OMX_StatePause == eState) + { + DEBUG_DETAIL("*************************\n"); + DEBUG_PRINT("SCP-->RXED PAUSE STATE\n"); + DEBUG_DETAIL("*************************\n"); + //ioctl(m_drv_fd, AUDIO_PAUSE, 0); + } else if (eState == OMX_StateLoaded) + { + DEBUG_PRINT("\n OMXCORE-SM: Executing --> Loaded \n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } else if (eState == OMX_StateWaitForResources) + { + DEBUG_PRINT("\n OMXCORE-SM: Executing --> WaitForResources \n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } else if (eState == OMX_StateExecuting) + { + DEBUG_PRINT("\n OMXCORE-SM: Executing --> Executing \n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, OMX_ErrorSameState, + 0, NULL ); + eRet = OMX_ErrorSameState; + } else if (eState == OMX_StateInvalid) + { + DEBUG_PRINT("\n OMXCORE-SM: Executing --> Invalid \n"); + m_state = OMX_StateInvalid; + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, OMX_ErrorInvalidState, + 0, NULL ); + eRet = OMX_ErrorInvalidState; + } else + { + DEBUG_PRINT_ERROR("SCP--> Executing to %d Not Handled\n", + eState); + eRet = OMX_ErrorBadParameter; + } + } + /***************************/ + /* Current State is Pause */ + /***************************/ + else if (OMX_StatePause == m_state) + { + if( (eState == OMX_StateExecuting || eState == OMX_StateIdle) ) + { + pthread_mutex_lock(&m_out_th_lock_1); + if(is_out_th_sleep) + { + DEBUG_DETAIL("PE: WAKING UP OUT THREAD\n"); + is_out_th_sleep = false; + out_th_wakeup(); + } + pthread_mutex_unlock(&m_out_th_lock_1); + } + if ( OMX_StateExecuting == eState ) + { + nState = eState; + } else if ( OMX_StateIdle == eState ) + { + DEBUG_PRINT("SCP-->Paused to Idle \n"); + DEBUG_PRINT ("\n Internal flush issued"); + pthread_mutex_lock(&m_flush_lock); + m_flush_cnt = 2; + pthread_mutex_unlock(&m_flush_lock); + if(pcm_input) + execute_omx_flush(-1,false); + else + execute_omx_flush(1,false); + + } else if ( eState == OMX_StateLoaded ) + { + DEBUG_PRINT("\n Pause --> loaded \n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } else if (eState == OMX_StateWaitForResources) + { + DEBUG_PRINT("\n Pause --> WaitForResources \n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } else if (eState == OMX_StatePause) + { + DEBUG_PRINT("\n Pause --> Pause \n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, OMX_ErrorSameState, + 0, NULL ); + eRet = OMX_ErrorSameState; + } else if (eState == OMX_StateInvalid) + { + DEBUG_PRINT("\n Pause --> Invalid \n"); + m_state = OMX_StateInvalid; + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, OMX_ErrorInvalidState, + 0, NULL ); + eRet = OMX_ErrorInvalidState; + } else + { + DEBUG_PRINT("SCP-->Paused to %d Not Handled\n",eState); + eRet = OMX_ErrorBadParameter; + } + } + /**************************************/ + /* Current State is WaitForResources */ + /**************************************/ + else if (m_state == OMX_StateWaitForResources) + { + if (eState == OMX_StateLoaded) + { + DEBUG_PRINT("OMXCORE-SM: WaitForResources-->Loaded\n"); + } else if (eState == OMX_StateWaitForResources) + { + DEBUG_PRINT("OMXCORE-SM: \ + WaitForResources-->WaitForResources\n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, OMX_ErrorSameState, + 0, NULL ); + eRet = OMX_ErrorSameState; + } else if (eState == OMX_StateExecuting) + { + DEBUG_PRINT("OMXCORE-SM: WaitForResources-->Executing\n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } else if (eState == OMX_StatePause) + { + DEBUG_PRINT("OMXCORE-SM: WaitForResources-->Pause\n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } else if (eState == OMX_StateInvalid) + { + DEBUG_PRINT("OMXCORE-SM: WaitForResources-->Invalid\n"); + m_state = OMX_StateInvalid; + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorInvalidState, + 0, NULL ); + eRet = OMX_ErrorInvalidState; + } else + { + DEBUG_PRINT_ERROR("SCP--> %d to %d(Not Handled)\n", + m_state,eState); + eRet = OMX_ErrorBadParameter; + } + } + /****************************/ + /* Current State is Invalid */ + /****************************/ + else if (m_state == OMX_StateInvalid) + { + if (OMX_StateLoaded == eState || OMX_StateWaitForResources == eState + || OMX_StateIdle == eState || OMX_StateExecuting == eState + || OMX_StatePause == eState || OMX_StateInvalid == eState) + { + DEBUG_PRINT("OMXCORE-SM: Invalid-->Loaded/Idle/Executing" + "/Pause/Invalid/WaitForResources\n"); + m_state = OMX_StateInvalid; + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, OMX_ErrorInvalidState, + 0, NULL ); + eRet = OMX_ErrorInvalidState; + } + } else + { + DEBUG_PRINT_ERROR("OMXCORE-SM: %d --> %d(Not Handled)\n",\ + m_state,eState); + eRet = OMX_ErrorBadParameter; + } + } else if (OMX_CommandFlush == cmd) + { + DEBUG_DETAIL("*************************\n"); + DEBUG_PRINT("SCP-->RXED FLUSH COMMAND port=%u\n",param1); + DEBUG_DETAIL("*************************\n"); + bFlag = 0; + if ( param1 == OMX_CORE_INPUT_PORT_INDEX || + param1 == OMX_CORE_OUTPUT_PORT_INDEX || + (signed)param1 == -1 ) + { + execute_omx_flush(param1); + } else + { + eRet = OMX_ErrorBadPortIndex; + m_cb.EventHandler(&m_cmp, m_app_data, OMX_EventError, + OMX_CommandFlush, OMX_ErrorBadPortIndex, NULL ); + } + } else if ( cmd == OMX_CommandPortDisable ) + { + bFlag = 0; + if ( param1 == OMX_CORE_INPUT_PORT_INDEX || param1 == OMX_ALL ) + { + DEBUG_PRINT("SCP: Disabling Input port Indx\n"); + m_inp_bEnabled = OMX_FALSE; + if ( (m_state == OMX_StateLoaded || m_state == OMX_StateIdle) + && release_done(0) ) + { + DEBUG_PRINT("send_command_proxy:OMX_CommandPortDisable:\ + OMX_CORE_INPUT_PORT_INDEX:release_done \n"); + DEBUG_PRINT("************* OMX_CommandPortDisable:\ + m_inp_bEnabled=%d********\n",m_inp_bEnabled); + + post_command(OMX_CommandPortDisable, + OMX_CORE_INPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + } + + else + { + if (m_state == OMX_StatePause ||m_state == OMX_StateExecuting) + { + DEBUG_PRINT("SCP: execute_omx_flush in Disable in "\ + " param1=%u m_state=%d \n",param1, m_state); + execute_omx_flush(param1); + } + DEBUG_PRINT("send_command_proxy:OMX_CommandPortDisable:\ + OMX_CORE_INPUT_PORT_INDEX \n"); + BITMASK_SET(&m_flags, OMX_COMPONENT_INPUT_DISABLE_PENDING); + // Skip the event notification + + } + + } + if (param1 == OMX_CORE_OUTPUT_PORT_INDEX || param1 == OMX_ALL) + { + + DEBUG_PRINT("SCP: Disabling Output port Indx\n"); + m_out_bEnabled = OMX_FALSE; + if ((m_state == OMX_StateLoaded || m_state == OMX_StateIdle) + && release_done(1)) + { + DEBUG_PRINT("send_command_proxy:OMX_CommandPortDisable:\ + OMX_CORE_OUTPUT_PORT_INDEX:release_done \n"); + DEBUG_PRINT("************* OMX_CommandPortDisable:\ + m_out_bEnabled=%d********\n",m_inp_bEnabled); + + post_command(OMX_CommandPortDisable, + OMX_CORE_OUTPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + } else + { + if (m_state == OMX_StatePause ||m_state == OMX_StateExecuting) + { + DEBUG_PRINT("SCP: execute_omx_flush in Disable out "\ + "param1=%u m_state=%d \n",param1, m_state); + execute_omx_flush(param1); + } + BITMASK_SET(&m_flags, OMX_COMPONENT_OUTPUT_DISABLE_PENDING); + // Skip the event notification + + } + } else + { + DEBUG_PRINT_ERROR("OMX_CommandPortDisable: disable wrong port ID"); + } + + } else if (cmd == OMX_CommandPortEnable) + { + bFlag = 0; + if (param1 == OMX_CORE_INPUT_PORT_INDEX || param1 == OMX_ALL) + { + m_inp_bEnabled = OMX_TRUE; + DEBUG_PRINT("SCP: Enabling Input port Indx\n"); + if ((m_state == OMX_StateLoaded + && !BITMASK_PRESENT(&m_flags,OMX_COMPONENT_IDLE_PENDING)) + || (m_state == OMX_StateWaitForResources) + || (m_inp_bPopulated == OMX_TRUE)) + { + post_command(OMX_CommandPortEnable, + OMX_CORE_INPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + + + } else + { + BITMASK_SET(&m_flags, OMX_COMPONENT_INPUT_ENABLE_PENDING); + // Skip the event notification + + } + } + + if (param1 == OMX_CORE_OUTPUT_PORT_INDEX || param1 == OMX_ALL) + { + DEBUG_PRINT("SCP: Enabling Output port Indx\n"); + m_out_bEnabled = OMX_TRUE; + if ((m_state == OMX_StateLoaded + && !BITMASK_PRESENT(&m_flags,OMX_COMPONENT_IDLE_PENDING)) + || (m_state == OMX_StateWaitForResources) + || (m_out_bPopulated == OMX_TRUE)) + { + post_command(OMX_CommandPortEnable, + OMX_CORE_OUTPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + } else + { + DEBUG_PRINT("send_command_proxy:OMX_CommandPortEnable:\ + OMX_CORE_OUTPUT_PORT_INDEX:release_done \n"); + BITMASK_SET(&m_flags, OMX_COMPONENT_OUTPUT_ENABLE_PENDING); + // Skip the event notification + + } + pthread_mutex_lock(&m_in_th_lock_1); + if(is_in_th_sleep) + { + is_in_th_sleep = false; + DEBUG_DETAIL("SCP:WAKING UP IN THREADS\n"); + in_th_wakeup(); + } + pthread_mutex_unlock(&m_in_th_lock_1); + pthread_mutex_lock(&m_out_th_lock_1); + if (is_out_th_sleep) + { + is_out_th_sleep = false; + DEBUG_PRINT("SCP:WAKING OUT THR, OMX_CommandPortEnable\n"); + out_th_wakeup(); + } + pthread_mutex_unlock(&m_out_th_lock_1); + } else + { + DEBUG_PRINT_ERROR("OMX_CommandPortEnable: disable wrong port ID"); + } + + } else + { + DEBUG_PRINT_ERROR("SCP-->ERROR: Invali Command [%d]\n",cmd); + eRet = OMX_ErrorNotImplemented; + } + DEBUG_PRINT("posting sem_States\n"); + sem_post (&sem_States); + if (eRet == OMX_ErrorNone && bFlag) + { + post_command(cmd,eState,OMX_COMPONENT_GENERATE_EVENT); + } + return eRet; +} + +/*============================================================================= +FUNCTION: + execute_omx_flush + +DESCRIPTION: + Function that flushes buffers that are pending to be written to driver + +INPUT/OUTPUT PARAMETERS: + [IN] param1 + [IN] cmd_cmpl + +RETURN VALUE: + true + false + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +bool omx_aac_aenc::execute_omx_flush(OMX_IN OMX_U32 param1, bool cmd_cmpl) +{ + bool bRet = true; + + DEBUG_PRINT("Execute_omx_flush Port[%u]", param1); + struct timespec abs_timeout; + abs_timeout.tv_sec = 1; + abs_timeout.tv_nsec = 0; + + if ((signed)param1 == -1) + { + bFlushinprogress = true; + DEBUG_PRINT("Execute flush for both I/p O/p port\n"); + pthread_mutex_lock(&m_flush_lock); + m_flush_cnt = 2; + pthread_mutex_unlock(&m_flush_lock); + + // Send Flush commands to input and output threads + post_input(OMX_CommandFlush, + OMX_CORE_INPUT_PORT_INDEX,OMX_COMPONENT_GENERATE_COMMAND); + post_output(OMX_CommandFlush, + OMX_CORE_OUTPUT_PORT_INDEX,OMX_COMPONENT_GENERATE_COMMAND); + // Send Flush to the kernel so that the in and out buffers are released + if (ioctl( m_drv_fd, AUDIO_FLUSH, 0) == -1) + DEBUG_PRINT_ERROR("FLush:ioctl flush failed errno=%d\n",errno); + DEBUG_DETAIL("****************************************"); + DEBUG_DETAIL("is_in_th_sleep=%d is_out_th_sleep=%d\n",\ + is_in_th_sleep,is_out_th_sleep); + DEBUG_DETAIL("****************************************"); + + pthread_mutex_lock(&m_in_th_lock_1); + if (is_in_th_sleep) + { + is_in_th_sleep = false; + DEBUG_DETAIL("For FLUSH-->WAKING UP IN THREADS\n"); + in_th_wakeup(); + } + pthread_mutex_unlock(&m_in_th_lock_1); + + pthread_mutex_lock(&m_out_th_lock_1); + if (is_out_th_sleep) + { + is_out_th_sleep = false; + DEBUG_DETAIL("For FLUSH-->WAKING UP OUT THREADS\n"); + out_th_wakeup(); + } + pthread_mutex_unlock(&m_out_th_lock_1); + + // sleep till the FLUSH ACK are done by both the input and + // output threads + DEBUG_DETAIL("WAITING FOR FLUSH ACK's param1=%d",param1); + wait_for_event(); + + DEBUG_PRINT("RECIEVED BOTH FLUSH ACK's param1=%u cmd_cmpl=%d",\ + param1,cmd_cmpl); + + // If not going to idle state, Send FLUSH complete message + // to the Client, now that FLUSH ACK's have been recieved. + if (cmd_cmpl) + { + m_cb.EventHandler(&m_cmp, m_app_data, OMX_EventCmdComplete, + OMX_CommandFlush, OMX_CORE_INPUT_PORT_INDEX, + NULL ); + m_cb.EventHandler(&m_cmp, m_app_data, OMX_EventCmdComplete, + OMX_CommandFlush, OMX_CORE_OUTPUT_PORT_INDEX, + NULL ); + DEBUG_PRINT("Inside FLUSH.. sending FLUSH CMPL\n"); + } + bFlushinprogress = false; + } + else if (param1 == OMX_CORE_INPUT_PORT_INDEX) + { + DEBUG_PRINT("Execute FLUSH for I/p port\n"); + pthread_mutex_lock(&m_flush_lock); + m_flush_cnt = 1; + pthread_mutex_unlock(&m_flush_lock); + post_input(OMX_CommandFlush, + OMX_CORE_INPUT_PORT_INDEX,OMX_COMPONENT_GENERATE_COMMAND); + if (ioctl( m_drv_fd, AUDIO_FLUSH, 0) == -1) + DEBUG_PRINT_ERROR("Flush:Input port, ioctl flush failed %d\n", + errno); + DEBUG_DETAIL("****************************************"); + DEBUG_DETAIL("is_in_th_sleep=%d is_out_th_sleep=%d\n",\ + is_in_th_sleep,is_out_th_sleep); + DEBUG_DETAIL("****************************************"); + + if (is_in_th_sleep) + { + pthread_mutex_lock(&m_in_th_lock_1); + is_in_th_sleep = false; + pthread_mutex_unlock(&m_in_th_lock_1); + DEBUG_DETAIL("For FLUSH-->WAKING UP IN THREADS\n"); + in_th_wakeup(); + } + + if (is_out_th_sleep) + { + pthread_mutex_lock(&m_out_th_lock_1); + is_out_th_sleep = false; + pthread_mutex_unlock(&m_out_th_lock_1); + DEBUG_DETAIL("For FLUSH-->WAKING UP OUT THREADS\n"); + out_th_wakeup(); + } + + //sleep till the FLUSH ACK are done by both the input and output threads + DEBUG_DETAIL("Executing FLUSH for I/p port\n"); + DEBUG_DETAIL("WAITING FOR FLUSH ACK's param1=%d",param1); + wait_for_event(); + DEBUG_DETAIL(" RECIEVED FLUSH ACK FOR I/P PORT param1=%d",param1); + + // Send FLUSH complete message to the Client, + // now that FLUSH ACK's have been recieved. + if (cmd_cmpl) + { + m_cb.EventHandler(&m_cmp, m_app_data, OMX_EventCmdComplete, + OMX_CommandFlush, OMX_CORE_INPUT_PORT_INDEX, + NULL ); + } + } else if (OMX_CORE_OUTPUT_PORT_INDEX == param1) + { + DEBUG_PRINT("Executing FLUSH for O/p port\n"); + pthread_mutex_lock(&m_flush_lock); + m_flush_cnt = 1; + pthread_mutex_unlock(&m_flush_lock); + DEBUG_DETAIL("Executing FLUSH for O/p port\n"); + DEBUG_DETAIL("WAITING FOR FLUSH ACK's param1=%d",param1); + post_output(OMX_CommandFlush, + OMX_CORE_OUTPUT_PORT_INDEX,OMX_COMPONENT_GENERATE_COMMAND); + if (ioctl( m_drv_fd, AUDIO_FLUSH, 0) ==-1) + DEBUG_PRINT_ERROR("Flush:Output port, ioctl flush failed %d\n", + errno); + DEBUG_DETAIL("****************************************"); + DEBUG_DETAIL("is_in_th_sleep=%d is_out_th_sleep=%d\n",\ + is_in_th_sleep,is_out_th_sleep); + DEBUG_DETAIL("****************************************"); + if (is_in_th_sleep) + { + pthread_mutex_lock(&m_in_th_lock_1); + is_in_th_sleep = false; + pthread_mutex_unlock(&m_in_th_lock_1); + DEBUG_DETAIL("For FLUSH-->WAKING UP IN THREADS\n"); + in_th_wakeup(); + } + + if (is_out_th_sleep) + { + pthread_mutex_lock(&m_out_th_lock_1); + is_out_th_sleep = false; + pthread_mutex_unlock(&m_out_th_lock_1); + DEBUG_DETAIL("For FLUSH-->WAKING UP OUT THREADS\n"); + out_th_wakeup(); + } + + // sleep till the FLUSH ACK are done by both the input + // and output threads + wait_for_event(); + // Send FLUSH complete message to the Client, + // now that FLUSH ACK's have been recieved. + if (cmd_cmpl) + { + m_cb.EventHandler(&m_cmp, m_app_data, OMX_EventCmdComplete, + OMX_CommandFlush, OMX_CORE_OUTPUT_PORT_INDEX, + NULL ); + } + DEBUG_DETAIL("RECIEVED FLUSH ACK FOR O/P PORT param1=%d",param1); + } else + { + DEBUG_PRINT("Invalid Port ID[%u]",param1); + } + return bRet; +} + +/*============================================================================= +FUNCTION: + execute_input_omx_flush + +DESCRIPTION: + Function that flushes buffers that are pending to be written to driver + +INPUT/OUTPUT PARAMETERS: + None + +RETURN VALUE: + true + false + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +bool omx_aac_aenc::execute_input_omx_flush() +{ + OMX_BUFFERHEADERTYPE *omx_buf; + unsigned long p1 = 0; // Parameter - 1 + unsigned long p2 = 0; // Parameter - 2 + unsigned char ident = 0; + unsigned qsize=0; // qsize + unsigned tot_qsize=0; // qsize + + DEBUG_PRINT("Execute_omx_flush on input port"); + + pthread_mutex_lock(&m_lock); + do + { + qsize = m_input_q.m_size; + tot_qsize = qsize; + tot_qsize += m_input_ctrl_ebd_q.m_size; + + DEBUG_DETAIL("Input FLUSH-->flushq[%d] ebd[%d]dataq[%d]",\ + m_input_ctrl_cmd_q.m_size, + m_input_ctrl_ebd_q.m_size,qsize); + if (!tot_qsize) + { + DEBUG_DETAIL("Input-->BREAKING FROM execute_input_flush LOOP"); + pthread_mutex_unlock(&m_lock); + break; + } + if (qsize) + { + m_input_q.pop_entry(&p1, &p2, &ident); + if ((ident == OMX_COMPONENT_GENERATE_ETB) || + (ident == OMX_COMPONENT_GENERATE_BUFFER_DONE)) + { + omx_buf = (OMX_BUFFERHEADERTYPE *) p2; + DEBUG_DETAIL("Flush:Input dataq=%p \n", omx_buf); + omx_buf->nFilledLen = 0; + buffer_done_cb((OMX_BUFFERHEADERTYPE *)omx_buf); + } + } else if (m_input_ctrl_ebd_q.m_size) + { + m_input_ctrl_ebd_q.pop_entry(&p1, &p2, &ident); + if (ident == OMX_COMPONENT_GENERATE_BUFFER_DONE) + { + omx_buf = (OMX_BUFFERHEADERTYPE *) p2; + omx_buf->nFilledLen = 0; + DEBUG_DETAIL("Flush:ctrl dataq=%p \n", omx_buf); + buffer_done_cb((OMX_BUFFERHEADERTYPE *)omx_buf); + } + } else + { + } + }while (tot_qsize>0); + DEBUG_DETAIL("*************************\n"); + DEBUG_DETAIL("IN-->FLUSHING DONE\n"); + DEBUG_DETAIL("*************************\n"); + flush_ack(); + pthread_mutex_unlock(&m_lock); + return true; +} + +/*============================================================================= +FUNCTION: + execute_output_omx_flush + +DESCRIPTION: + Function that flushes buffers that are pending to be written to driver + +INPUT/OUTPUT PARAMETERS: + None + +RETURN VALUE: + true + false + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +bool omx_aac_aenc::execute_output_omx_flush() +{ + OMX_BUFFERHEADERTYPE *omx_buf; + unsigned long p1 = 0; // Parameter - 1 + unsigned long p2 = 0; // Parameter - 2 + unsigned char ident = 0; + unsigned qsize=0; // qsize + unsigned tot_qsize=0; // qsize + + DEBUG_PRINT("Execute_omx_flush on output port"); + + pthread_mutex_lock(&m_outputlock); + do + { + qsize = m_output_q.m_size; + DEBUG_DETAIL("OUT FLUSH-->flushq[%d] fbd[%d]dataq[%d]",\ + m_output_ctrl_cmd_q.m_size, + m_output_ctrl_fbd_q.m_size,qsize); + tot_qsize = qsize; + tot_qsize += m_output_ctrl_fbd_q.m_size; + if (!tot_qsize) + { + DEBUG_DETAIL("OUT-->BREAKING FROM execute_input_flush LOOP"); + pthread_mutex_unlock(&m_outputlock); + break; + } + if (qsize) + { + m_output_q.pop_entry(&p1,&p2,&ident); + if ( (OMX_COMPONENT_GENERATE_FTB == ident) || + (OMX_COMPONENT_GENERATE_FRAME_DONE == ident)) + { + omx_buf = (OMX_BUFFERHEADERTYPE *) p2; + DEBUG_DETAIL("Ouput Buf_Addr=%p TS[0x%x] \n",\ + omx_buf,nTimestamp); + omx_buf->nTimeStamp = nTimestamp; + omx_buf->nFilledLen = 0; + frame_done_cb((OMX_BUFFERHEADERTYPE *)omx_buf); + DEBUG_DETAIL("CALLING FBD FROM FLUSH"); + } + } else if ((qsize = m_output_ctrl_fbd_q.m_size)) + { + m_output_ctrl_fbd_q.pop_entry(&p1, &p2, &ident); + if (OMX_COMPONENT_GENERATE_FRAME_DONE == ident) + { + omx_buf = (OMX_BUFFERHEADERTYPE *) p2; + DEBUG_DETAIL("Ouput Buf_Addr=%p TS[0x%x] \n", \ + omx_buf,nTimestamp); + omx_buf->nTimeStamp = nTimestamp; + omx_buf->nFilledLen = 0; + frame_done_cb((OMX_BUFFERHEADERTYPE *)omx_buf); + DEBUG_DETAIL("CALLING FROM CTRL-FBDQ FROM FLUSH"); + } + } + }while (qsize>0); + DEBUG_DETAIL("*************************\n"); + DEBUG_DETAIL("OUT-->FLUSHING DONE\n"); + DEBUG_DETAIL("*************************\n"); + flush_ack(); + pthread_mutex_unlock(&m_outputlock); + return true; +} + +/*============================================================================= +FUNCTION: + post_input + +DESCRIPTION: + Function that posts command in the command queue + +INPUT/OUTPUT PARAMETERS: + [IN] p1 + [IN] p2 + [IN] id - command ID + [IN] lock - self-locking mode + +RETURN VALUE: + true + false + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +bool omx_aac_aenc::post_input(unsigned long p1, + unsigned long p2, + unsigned char id) +{ + bool bRet = false; + pthread_mutex_lock(&m_lock); + + if((OMX_COMPONENT_GENERATE_COMMAND == id) || (id == OMX_COMPONENT_SUSPEND)) + { + // insert flush message and ebd + m_input_ctrl_cmd_q.insert_entry(p1,p2,id); + } else if ((OMX_COMPONENT_GENERATE_BUFFER_DONE == id)) + { + // insert ebd + m_input_ctrl_ebd_q.insert_entry(p1,p2,id); + } else + { + // ETBS in this queue + m_input_q.insert_entry(p1,p2,id); + } + + if (m_ipc_to_in_th) + { + bRet = true; + omx_aac_post_msg(m_ipc_to_in_th, id); + } + + DEBUG_DETAIL("PostInput-->state[%d]id[%d]flushq[%d]ebdq[%d]dataq[%d] \n",\ + m_state, + id, + m_input_ctrl_cmd_q.m_size, + m_input_ctrl_ebd_q.m_size, + m_input_q.m_size); + + pthread_mutex_unlock(&m_lock); + return bRet; +} + +/*============================================================================= +FUNCTION: + post_command + +DESCRIPTION: + Function that posts command in the command queue + +INPUT/OUTPUT PARAMETERS: + [IN] p1 + [IN] p2 + [IN] id - command ID + [IN] lock - self-locking mode + +RETURN VALUE: + true + false + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +bool omx_aac_aenc::post_command(unsigned int p1, + unsigned int p2, + unsigned char id) +{ + bool bRet = false; + + pthread_mutex_lock(&m_commandlock); + + m_command_q.insert_entry(p1,p2,id); + + if (m_ipc_to_cmd_th) + { + bRet = true; + omx_aac_post_msg(m_ipc_to_cmd_th, id); + } + + DEBUG_DETAIL("PostCmd-->state[%d]id[%d]cmdq[%d]flags[%x]\n",\ + m_state, + id, + m_command_q.m_size, + m_flags >> 3); + + pthread_mutex_unlock(&m_commandlock); + return bRet; +} + +/*============================================================================= +FUNCTION: + post_output + +DESCRIPTION: + Function that posts command in the command queue + +INPUT/OUTPUT PARAMETERS: + [IN] p1 + [IN] p2 + [IN] id - command ID + [IN] lock - self-locking mode + +RETURN VALUE: + true + false + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +bool omx_aac_aenc::post_output(unsigned long p1, + unsigned long p2, + unsigned char id) +{ + bool bRet = false; + + pthread_mutex_lock(&m_outputlock); + if((OMX_COMPONENT_GENERATE_COMMAND == id) || (id == OMX_COMPONENT_SUSPEND) + || (id == OMX_COMPONENT_RESUME)) + { + // insert flush message and fbd + m_output_ctrl_cmd_q.insert_entry(p1,p2,id); + } else if ( (OMX_COMPONENT_GENERATE_FRAME_DONE == id) ) + { + // insert flush message and fbd + m_output_ctrl_fbd_q.insert_entry(p1,p2,id); + } else + { + m_output_q.insert_entry(p1,p2,id); + } + if ( m_ipc_to_out_th ) + { + bRet = true; + omx_aac_post_msg(m_ipc_to_out_th, id); + } + DEBUG_DETAIL("PostOutput-->state[%d]id[%d]flushq[%d]ebdq[%d]dataq[%d]\n",\ + m_state, + id, + m_output_ctrl_cmd_q.m_size, + m_output_ctrl_fbd_q.m_size, + m_output_q.m_size); + + pthread_mutex_unlock(&m_outputlock); + return bRet; +} +/** + @brief member function that return parameters to IL client + + @param hComp handle to component instance + @param paramIndex Parameter type + @param paramData pointer to memory space which would hold the + paramter + @return error status +*/ +OMX_ERRORTYPE omx_aac_aenc::get_parameter(OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_INDEXTYPE paramIndex, + OMX_INOUT OMX_PTR paramData) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_state == OMX_StateInvalid) + { + DEBUG_PRINT_ERROR("Get Param in Invalid State\n"); + return OMX_ErrorInvalidState; + } + if (paramData == NULL) + { + DEBUG_PRINT("get_parameter: paramData is NULL\n"); + return OMX_ErrorBadParameter; + } + + switch ((int)paramIndex) + { + case OMX_IndexParamPortDefinition: + { + OMX_PARAM_PORTDEFINITIONTYPE *portDefn; + portDefn = (OMX_PARAM_PORTDEFINITIONTYPE *) paramData; + + DEBUG_PRINT("OMX_IndexParamPortDefinition " \ + "portDefn->nPortIndex = %u\n", + portDefn->nPortIndex); + + portDefn->nVersion.nVersion = OMX_SPEC_VERSION; + portDefn->nSize = (OMX_U32)sizeof(portDefn); + portDefn->eDomain = OMX_PortDomainAudio; + + if (0 == portDefn->nPortIndex) + { + portDefn->eDir = OMX_DirInput; + portDefn->bEnabled = m_inp_bEnabled; + portDefn->bPopulated = m_inp_bPopulated; + portDefn->nBufferCountActual = m_inp_act_buf_count; + portDefn->nBufferCountMin = OMX_CORE_NUM_INPUT_BUFFERS; + portDefn->nBufferSize = input_buffer_size; + portDefn->format.audio.bFlagErrorConcealment = OMX_TRUE; + portDefn->format.audio.eEncoding = OMX_AUDIO_CodingPCM; + portDefn->format.audio.pNativeRender = 0; + } else if (1 == portDefn->nPortIndex) + { + portDefn->eDir = OMX_DirOutput; + portDefn->bEnabled = m_out_bEnabled; + portDefn->bPopulated = m_out_bPopulated; + portDefn->nBufferCountActual = m_out_act_buf_count; + portDefn->nBufferCountMin = OMX_CORE_NUM_OUTPUT_BUFFERS; + portDefn->nBufferSize = output_buffer_size; + portDefn->format.audio.bFlagErrorConcealment = OMX_TRUE; + portDefn->format.audio.eEncoding = OMX_AUDIO_CodingAAC; + portDefn->format.audio.pNativeRender = 0; + } else + { + portDefn->eDir = OMX_DirMax; + DEBUG_PRINT_ERROR("Bad Port idx %d\n",\ + (int)portDefn->nPortIndex); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + + case OMX_IndexParamAudioInit: + { + OMX_PORT_PARAM_TYPE *portParamType = + (OMX_PORT_PARAM_TYPE *) paramData; + DEBUG_PRINT("OMX_IndexParamAudioInit\n"); + + portParamType->nVersion.nVersion = OMX_SPEC_VERSION; + portParamType->nSize = (OMX_U32)sizeof(portParamType); + portParamType->nPorts = 2; + portParamType->nStartPortNumber = 0; + break; + } + + case OMX_IndexParamAudioPortFormat: + { + OMX_AUDIO_PARAM_PORTFORMATTYPE *portFormatType = + (OMX_AUDIO_PARAM_PORTFORMATTYPE *) paramData; + DEBUG_PRINT("OMX_IndexParamAudioPortFormat\n"); + portFormatType->nVersion.nVersion = OMX_SPEC_VERSION; + portFormatType->nSize = (OMX_U32)sizeof(portFormatType); + + if (OMX_CORE_INPUT_PORT_INDEX == portFormatType->nPortIndex) + { + + portFormatType->eEncoding = OMX_AUDIO_CodingPCM; + } else if (OMX_CORE_OUTPUT_PORT_INDEX == + portFormatType->nPortIndex) + { + DEBUG_PRINT("get_parameter: OMX_IndexParamAudioFormat: "\ + "%u\n", portFormatType->nIndex); + + portFormatType->eEncoding = OMX_AUDIO_CodingAAC; + } else + { + DEBUG_PRINT_ERROR("get_parameter: Bad port index %d\n", + (int)portFormatType->nPortIndex); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + + case OMX_IndexParamAudioAac: + { + OMX_AUDIO_PARAM_AACPROFILETYPE *aacParam = + (OMX_AUDIO_PARAM_AACPROFILETYPE *) paramData; + DEBUG_PRINT("OMX_IndexParamAudioAac\n"); + if (OMX_CORE_OUTPUT_PORT_INDEX== aacParam->nPortIndex) + { + memcpy(aacParam,&m_aac_param, + sizeof(OMX_AUDIO_PARAM_AACPROFILETYPE)); + + } else + { + DEBUG_PRINT_ERROR("get_parameter:OMX_IndexParamAudioAac "\ + "OMX_ErrorBadPortIndex %d\n", \ + (int)aacParam->nPortIndex); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + case QOMX_IndexParamAudioSessionId: + { + QOMX_AUDIO_STREAM_INFO_DATA *streaminfoparam = + (QOMX_AUDIO_STREAM_INFO_DATA *) paramData; + streaminfoparam->sessionId = (OMX_U8)m_session_id; + break; + } + + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmparam = + (OMX_AUDIO_PARAM_PCMMODETYPE *) paramData; + + if (OMX_CORE_INPUT_PORT_INDEX== pcmparam->nPortIndex) + { + memcpy(pcmparam,&m_pcm_param,\ + sizeof(OMX_AUDIO_PARAM_PCMMODETYPE)); + + DEBUG_PRINT("get_parameter: Sampling rate %u",\ + pcmparam->nSamplingRate); + DEBUG_PRINT("get_parameter: Number of channels %u",\ + pcmparam->nChannels); + } else + { + DEBUG_PRINT_ERROR("get_parameter:OMX_IndexParamAudioPcm "\ + "OMX_ErrorBadPortIndex %d\n", \ + (int)pcmparam->nPortIndex); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + case OMX_IndexParamComponentSuspended: + { + OMX_PARAM_SUSPENSIONTYPE *suspend = + (OMX_PARAM_SUSPENSIONTYPE *) paramData; + DEBUG_PRINT("get_parameter: OMX_IndexParamComponentSuspended %p\n", + suspend); + break; + } + case OMX_IndexParamVideoInit: + { + OMX_PORT_PARAM_TYPE *portParamType = + (OMX_PORT_PARAM_TYPE *) paramData; + DEBUG_PRINT("get_parameter: OMX_IndexParamVideoInit\n"); + portParamType->nVersion.nVersion = OMX_SPEC_VERSION; + portParamType->nSize = (OMX_U32)sizeof(portParamType); + portParamType->nPorts = 0; + portParamType->nStartPortNumber = 0; + break; + } + case OMX_IndexParamPriorityMgmt: + { + OMX_PRIORITYMGMTTYPE *priorityMgmtType = + (OMX_PRIORITYMGMTTYPE*)paramData; + DEBUG_PRINT("get_parameter: OMX_IndexParamPriorityMgmt\n"); + priorityMgmtType->nSize = (OMX_U32)sizeof(priorityMgmtType); + priorityMgmtType->nVersion.nVersion = OMX_SPEC_VERSION; + priorityMgmtType->nGroupID = m_priority_mgm.nGroupID; + priorityMgmtType->nGroupPriority = + m_priority_mgm.nGroupPriority; + break; + } + case OMX_IndexParamImageInit: + { + OMX_PORT_PARAM_TYPE *portParamType = + (OMX_PORT_PARAM_TYPE *) paramData; + DEBUG_PRINT("get_parameter: OMX_IndexParamImageInit\n"); + portParamType->nVersion.nVersion = OMX_SPEC_VERSION; + portParamType->nSize = (OMX_U32)sizeof(portParamType); + portParamType->nPorts = 0; + portParamType->nStartPortNumber = 0; + break; + } + + case OMX_IndexParamCompBufferSupplier: + { + DEBUG_PRINT("get_parameter: \ + OMX_IndexParamCompBufferSupplier\n"); + OMX_PARAM_BUFFERSUPPLIERTYPE *bufferSupplierType + = (OMX_PARAM_BUFFERSUPPLIERTYPE*) paramData; + DEBUG_PRINT("get_parameter: \ + OMX_IndexParamCompBufferSupplier\n"); + + bufferSupplierType->nSize = (OMX_U32)sizeof(bufferSupplierType); + bufferSupplierType->nVersion.nVersion = OMX_SPEC_VERSION; + if (OMX_CORE_INPUT_PORT_INDEX == + bufferSupplierType->nPortIndex) + { + bufferSupplierType->nPortIndex = + OMX_BufferSupplyUnspecified; + } else if (OMX_CORE_OUTPUT_PORT_INDEX == + bufferSupplierType->nPortIndex) + { + bufferSupplierType->nPortIndex = + OMX_BufferSupplyUnspecified; + } else + { + DEBUG_PRINT_ERROR("get_parameter:"\ + "OMX_IndexParamCompBufferSupplier eRet"\ + "%08x\n", eRet); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + + /*Component should support this port definition*/ + case OMX_IndexParamOtherInit: + { + OMX_PORT_PARAM_TYPE *portParamType = + (OMX_PORT_PARAM_TYPE *) paramData; + DEBUG_PRINT("get_parameter: OMX_IndexParamOtherInit\n"); + portParamType->nVersion.nVersion = OMX_SPEC_VERSION; + portParamType->nSize = (OMX_U32)sizeof(portParamType); + portParamType->nPorts = 0; + portParamType->nStartPortNumber = 0; + break; + } + case OMX_IndexParamStandardComponentRole: + { + OMX_PARAM_COMPONENTROLETYPE *componentRole; + componentRole = (OMX_PARAM_COMPONENTROLETYPE*)paramData; + componentRole->nSize = component_Role.nSize; + componentRole->nVersion = component_Role.nVersion; + strlcpy((char *)componentRole->cRole, + (const char*)component_Role.cRole, + sizeof(componentRole->cRole)); + DEBUG_PRINT_ERROR("nSize = %d , nVersion = %d, cRole = %s\n", + component_Role.nSize, + component_Role.nVersion, + component_Role.cRole); + break; + + } + default: + { + DEBUG_PRINT_ERROR("unknown param %08x\n", paramIndex); + eRet = OMX_ErrorUnsupportedIndex; + } + } + return eRet; + +} + +/** + @brief member function that set paramter from IL client + + @param hComp handle to component instance + @param paramIndex parameter type + @param paramData pointer to memory space which holds the paramter + @return error status + */ +OMX_ERRORTYPE omx_aac_aenc::set_parameter(OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_INDEXTYPE paramIndex, + OMX_IN OMX_PTR paramData) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + unsigned int loop=0; + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_state != OMX_StateLoaded) + { + DEBUG_PRINT_ERROR("set_parameter is not in proper state\n"); + return OMX_ErrorIncorrectStateOperation; + } + if (paramData == NULL) + { + DEBUG_PRINT("param data is NULL"); + return OMX_ErrorBadParameter; + } + + switch (paramIndex) + { + case OMX_IndexParamAudioAac: + { + DEBUG_PRINT("OMX_IndexParamAudioAac"); + OMX_AUDIO_PARAM_AACPROFILETYPE *aacparam + = (OMX_AUDIO_PARAM_AACPROFILETYPE *) paramData; + memcpy(&m_aac_param,aacparam, + sizeof(OMX_AUDIO_PARAM_AACPROFILETYPE)); + + for (loop=0; loop< sizeof(sample_idx_tbl) / \ + sizeof(struct sample_rate_idx); \ + loop++) + { + if(sample_idx_tbl[loop].sample_rate == m_aac_param.nSampleRate) + { + sample_idx = sample_idx_tbl[loop].sample_rate_idx; + } + } + break; + } + case OMX_IndexParamPortDefinition: + { + OMX_PARAM_PORTDEFINITIONTYPE *portDefn; + portDefn = (OMX_PARAM_PORTDEFINITIONTYPE *) paramData; + + if (((m_state == OMX_StateLoaded)&& + !BITMASK_PRESENT(&m_flags,OMX_COMPONENT_IDLE_PENDING)) + || (m_state == OMX_StateWaitForResources && + ((OMX_DirInput == portDefn->eDir && + m_inp_bEnabled == true)|| + (OMX_DirInput == portDefn->eDir && + m_out_bEnabled == true))) + ||(((OMX_DirInput == portDefn->eDir && + m_inp_bEnabled == false)|| + (OMX_DirInput == portDefn->eDir && + m_out_bEnabled == false)) && + (m_state != OMX_StateWaitForResources))) + { + DEBUG_PRINT("Set Parameter called in valid state\n"); + } else + { + DEBUG_PRINT_ERROR("Set Parameter called in \ + Invalid State\n"); + return OMX_ErrorIncorrectStateOperation; + } + DEBUG_PRINT("OMX_IndexParamPortDefinition portDefn->nPortIndex " + "= %u\n",portDefn->nPortIndex); + if (OMX_CORE_INPUT_PORT_INDEX == portDefn->nPortIndex) + { + if ( portDefn->nBufferCountActual > + OMX_CORE_NUM_INPUT_BUFFERS ) + { + m_inp_act_buf_count = portDefn->nBufferCountActual; + } else + { + m_inp_act_buf_count =OMX_CORE_NUM_INPUT_BUFFERS; + } + input_buffer_size = portDefn->nBufferSize; + + } else if (OMX_CORE_OUTPUT_PORT_INDEX == portDefn->nPortIndex) + { + if ( portDefn->nBufferCountActual > + OMX_CORE_NUM_OUTPUT_BUFFERS ) + { + m_out_act_buf_count = portDefn->nBufferCountActual; + } else + { + m_out_act_buf_count =OMX_CORE_NUM_OUTPUT_BUFFERS; + } + output_buffer_size = portDefn->nBufferSize; + } else + { + DEBUG_PRINT(" set_parameter: Bad Port idx %d",\ + (int)portDefn->nPortIndex); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + case OMX_IndexParamPriorityMgmt: + { + DEBUG_PRINT("set_parameter: OMX_IndexParamPriorityMgmt\n"); + + if (m_state != OMX_StateLoaded) + { + DEBUG_PRINT_ERROR("Set Parameter called in \ + Invalid State\n"); + return OMX_ErrorIncorrectStateOperation; + } + OMX_PRIORITYMGMTTYPE *priorityMgmtype + = (OMX_PRIORITYMGMTTYPE*) paramData; + DEBUG_PRINT("set_parameter: OMX_IndexParamPriorityMgmt %u\n", + priorityMgmtype->nGroupID); + + DEBUG_PRINT("set_parameter: priorityMgmtype %u\n", + priorityMgmtype->nGroupPriority); + + m_priority_mgm.nGroupID = priorityMgmtype->nGroupID; + m_priority_mgm.nGroupPriority = priorityMgmtype->nGroupPriority; + + break; + } + case OMX_IndexParamAudioPortFormat: + { + + OMX_AUDIO_PARAM_PORTFORMATTYPE *portFormatType = + (OMX_AUDIO_PARAM_PORTFORMATTYPE *) paramData; + DEBUG_PRINT("set_parameter: OMX_IndexParamAudioPortFormat\n"); + + if (OMX_CORE_INPUT_PORT_INDEX== portFormatType->nPortIndex) + { + portFormatType->eEncoding = OMX_AUDIO_CodingPCM; + } else if (OMX_CORE_OUTPUT_PORT_INDEX == + portFormatType->nPortIndex) + { + DEBUG_PRINT("set_parameter: OMX_IndexParamAudioFormat:"\ + " %u\n", portFormatType->nIndex); + portFormatType->eEncoding = OMX_AUDIO_CodingAAC; + } else + { + DEBUG_PRINT_ERROR("set_parameter: Bad port index %d\n", \ + (int)portFormatType->nPortIndex); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + + + case OMX_IndexParamCompBufferSupplier: + { + DEBUG_PRINT("set_parameter: \ + OMX_IndexParamCompBufferSupplier\n"); + OMX_PARAM_BUFFERSUPPLIERTYPE *bufferSupplierType + = (OMX_PARAM_BUFFERSUPPLIERTYPE*) paramData; + DEBUG_PRINT("set_param: OMX_IndexParamCompBufferSupplier %d",\ + bufferSupplierType->eBufferSupplier); + + if (bufferSupplierType->nPortIndex == OMX_CORE_INPUT_PORT_INDEX + || bufferSupplierType->nPortIndex == OMX_CORE_OUTPUT_PORT_INDEX) + { + DEBUG_PRINT("set_parameter:\ + OMX_IndexParamCompBufferSupplier\n"); + m_buffer_supplier.eBufferSupplier = + bufferSupplierType->eBufferSupplier; + } else + { + DEBUG_PRINT_ERROR("set_param:IndexParamCompBufferSup\ + %08x\n", eRet); + eRet = OMX_ErrorBadPortIndex; + } + + break; } + + case OMX_IndexParamAudioPcm: + { + DEBUG_PRINT("set_parameter: OMX_IndexParamAudioPcm\n"); + OMX_AUDIO_PARAM_PCMMODETYPE *pcmparam + = (OMX_AUDIO_PARAM_PCMMODETYPE *) paramData; + + if (OMX_CORE_INPUT_PORT_INDEX== pcmparam->nPortIndex) + { + + memcpy(&m_pcm_param,pcmparam,\ + sizeof(OMX_AUDIO_PARAM_PCMMODETYPE)); + + DEBUG_PRINT("set_pcm_parameter: %u %u",\ + m_pcm_param.nChannels, + m_pcm_param.nSamplingRate); + } else + { + DEBUG_PRINT_ERROR("Set_parameter:OMX_IndexParamAudioPcm " + "OMX_ErrorBadPortIndex %d\n", + (int)pcmparam->nPortIndex); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + case OMX_IndexParamSuspensionPolicy: + { + eRet = OMX_ErrorNotImplemented; + break; + } + case OMX_IndexParamStandardComponentRole: + { + OMX_PARAM_COMPONENTROLETYPE *componentRole; + componentRole = (OMX_PARAM_COMPONENTROLETYPE*)paramData; + component_Role.nSize = componentRole->nSize; + component_Role.nVersion = componentRole->nVersion; + strlcpy((char *)component_Role.cRole, + (const char*)componentRole->cRole, + sizeof(component_Role.cRole)); + break; + } + + default: + { + DEBUG_PRINT_ERROR("unknown param %d\n", paramIndex); + eRet = OMX_ErrorUnsupportedIndex; + } + } + return eRet; +} + +/* ====================================================================== +FUNCTION + omx_aac_aenc::GetConfig + +DESCRIPTION + OMX Get Config Method implementation. + +PARAMETERS + . + +RETURN VALUE + OMX Error None if successful. + +========================================================================== */ +OMX_ERRORTYPE omx_aac_aenc::get_config(OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_INDEXTYPE configIndex, + OMX_INOUT OMX_PTR configData) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_state == OMX_StateInvalid) + { + DEBUG_PRINT_ERROR("Get Config in Invalid State\n"); + return OMX_ErrorInvalidState; + } + + switch (configIndex) + { + case OMX_IndexConfigAudioVolume: + { + OMX_AUDIO_CONFIG_VOLUMETYPE *volume = + (OMX_AUDIO_CONFIG_VOLUMETYPE*) configData; + + if (OMX_CORE_INPUT_PORT_INDEX == volume->nPortIndex) + { + volume->nSize = (OMX_U32)sizeof(volume); + volume->nVersion.nVersion = OMX_SPEC_VERSION; + volume->bLinear = OMX_TRUE; + volume->sVolume.nValue = m_volume; + volume->sVolume.nMax = OMX_AENC_MAX; + volume->sVolume.nMin = OMX_AENC_MIN; + } else + { + eRet = OMX_ErrorBadPortIndex; + } + } + break; + + case OMX_IndexConfigAudioMute: + { + OMX_AUDIO_CONFIG_MUTETYPE *mute = + (OMX_AUDIO_CONFIG_MUTETYPE*) configData; + + if (OMX_CORE_INPUT_PORT_INDEX == mute->nPortIndex) + { + mute->nSize = (OMX_U32)sizeof(mute); + mute->nVersion.nVersion = OMX_SPEC_VERSION; + mute->bMute = (BITMASK_PRESENT(&m_flags, + OMX_COMPONENT_MUTED)?OMX_TRUE:OMX_FALSE); + } else + { + eRet = OMX_ErrorBadPortIndex; + } + } + break; + + default: + eRet = OMX_ErrorUnsupportedIndex; + break; + } + return eRet; +} + +/* ====================================================================== +FUNCTION + omx_aac_aenc::SetConfig + +DESCRIPTION + OMX Set Config method implementation + +PARAMETERS + . + +RETURN VALUE + OMX Error None if successful. +========================================================================== */ +OMX_ERRORTYPE omx_aac_aenc::set_config(OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_INDEXTYPE configIndex, + OMX_IN OMX_PTR configData) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_state == OMX_StateInvalid) + { + DEBUG_PRINT_ERROR("Set Config in Invalid State\n"); + return OMX_ErrorInvalidState; + } + if ( m_state == OMX_StateExecuting) + { + DEBUG_PRINT_ERROR("set_config:Ignore in Exe state\n"); + return OMX_ErrorInvalidState; + } + + switch (configIndex) + { + case OMX_IndexConfigAudioVolume: + { + OMX_AUDIO_CONFIG_VOLUMETYPE *vol = + (OMX_AUDIO_CONFIG_VOLUMETYPE*)configData; + if (vol->nPortIndex == OMX_CORE_INPUT_PORT_INDEX) + { + if ((vol->sVolume.nValue <= OMX_AENC_MAX) && + (vol->sVolume.nValue >= OMX_AENC_MIN)) + { + m_volume = vol->sVolume.nValue; + if (BITMASK_ABSENT(&m_flags, OMX_COMPONENT_MUTED)) + { + /* ioctl(m_drv_fd, AUDIO_VOLUME, + m_volume * OMX_AENC_VOLUME_STEP); */ + } + + } else + { + eRet = OMX_ErrorBadParameter; + } + } else + { + eRet = OMX_ErrorBadPortIndex; + } + } + break; + + case OMX_IndexConfigAudioMute: + { + OMX_AUDIO_CONFIG_MUTETYPE *mute = (OMX_AUDIO_CONFIG_MUTETYPE*) + configData; + if (mute->nPortIndex == OMX_CORE_INPUT_PORT_INDEX) + { + if (mute->bMute == OMX_TRUE) + { + BITMASK_SET(&m_flags, OMX_COMPONENT_MUTED); + /* ioctl(m_drv_fd, AUDIO_VOLUME, 0); */ + } else + { + BITMASK_CLEAR(&m_flags, OMX_COMPONENT_MUTED); + /* ioctl(m_drv_fd, AUDIO_VOLUME, + m_volume * OMX_AENC_VOLUME_STEP); */ + } + } else + { + eRet = OMX_ErrorBadPortIndex; + } + } + break; + + default: + eRet = OMX_ErrorUnsupportedIndex; + break; + } + return eRet; +} + +/* ====================================================================== +FUNCTION + omx_aac_aenc::GetExtensionIndex + +DESCRIPTION + OMX GetExtensionIndex method implementaion. + +PARAMETERS + . + +RETURN VALUE + OMX Error None if everything successful. + +========================================================================== */ +OMX_ERRORTYPE omx_aac_aenc::get_extension_index( + OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_STRING paramName, + OMX_OUT OMX_INDEXTYPE* indexType) +{ + if((hComp == NULL) || (paramName == NULL) || (indexType == NULL)) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_state == OMX_StateInvalid) + { + DEBUG_PRINT_ERROR("Get Extension Index in Invalid State\n"); + return OMX_ErrorInvalidState; + } + if(strncmp(paramName,"OMX.Qualcomm.index.audio.sessionId", + strlen("OMX.Qualcomm.index.audio.sessionId")) == 0) + { + *indexType =(OMX_INDEXTYPE)QOMX_IndexParamAudioSessionId; + DEBUG_PRINT("Extension index type - %d\n", *indexType); + + } + else + { + return OMX_ErrorBadParameter; + + } + return OMX_ErrorNone; +} + +/* ====================================================================== +FUNCTION + omx_aac_aenc::GetState + +DESCRIPTION + Returns the state information back to the caller. + +PARAMETERS + . + +RETURN VALUE + Error None if everything is successful. +========================================================================== */ +OMX_ERRORTYPE omx_aac_aenc::get_state(OMX_IN OMX_HANDLETYPE hComp, + OMX_OUT OMX_STATETYPE* state) +{ + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + *state = m_state; + DEBUG_PRINT("Returning the state %d\n",*state); + return OMX_ErrorNone; +} + +/* ====================================================================== +FUNCTION + omx_aac_aenc::ComponentTunnelRequest + +DESCRIPTION + OMX Component Tunnel Request method implementation. + +PARAMETERS + None. + +RETURN VALUE + OMX Error None if everything successful. + +========================================================================== */ +OMX_ERRORTYPE omx_aac_aenc::component_tunnel_request +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_U32 port, + OMX_IN OMX_HANDLETYPE peerComponent, + OMX_IN OMX_U32 peerPort, + OMX_INOUT OMX_TUNNELSETUPTYPE* tunnelSetup) +{ + DEBUG_PRINT_ERROR("Error: component_tunnel_request Not Implemented\n"); + + if((hComp == NULL) || (peerComponent == NULL) || (tunnelSetup == NULL)) + { + port = port; + peerPort = peerPort; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + return OMX_ErrorNotImplemented; +} + +/* ====================================================================== +FUNCTION + omx_aac_aenc::AllocateInputBuffer + +DESCRIPTION + Helper function for allocate buffer in the input pin + +PARAMETERS + None. + +RETURN VALUE + true/false + +========================================================================== */ +OMX_ERRORTYPE omx_aac_aenc::allocate_input_buffer +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE** bufferHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + OMX_BUFFERHEADERTYPE *bufHdr; + unsigned nBufSize = MAX(bytes, input_buffer_size); + char *buf_ptr; + if(m_inp_current_buf_count < m_inp_act_buf_count) + { + buf_ptr = (char *) calloc((nBufSize + \ + sizeof(OMX_BUFFERHEADERTYPE)+sizeof(META_IN)) , 1); + + if(hComp == NULL) + { + port = port; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + free(buf_ptr); + return OMX_ErrorBadParameter; + } + if (buf_ptr != NULL) + { + bufHdr = (OMX_BUFFERHEADERTYPE *) buf_ptr; + *bufferHdr = bufHdr; + memset(bufHdr,0,sizeof(OMX_BUFFERHEADERTYPE)); + + bufHdr->pBuffer = (OMX_U8 *)((buf_ptr) + sizeof(META_IN)+ + sizeof(OMX_BUFFERHEADERTYPE)); + bufHdr->nSize = (OMX_U32)sizeof(OMX_BUFFERHEADERTYPE); + bufHdr->nVersion.nVersion = OMX_SPEC_VERSION; + bufHdr->nAllocLen = nBufSize; + bufHdr->pAppPrivate = appData; + bufHdr->nInputPortIndex = OMX_CORE_INPUT_PORT_INDEX; + m_input_buf_hdrs.insert(bufHdr, NULL); + + m_inp_current_buf_count++; + DEBUG_PRINT("AIB:bufHdr %p bufHdr->pBuffer %p m_inp_buf_cnt=%d \ + bytes=%u",bufHdr, bufHdr->pBuffer,m_inp_current_buf_count, + bytes); + + } else + { + DEBUG_PRINT("Input buffer memory allocation failed 1 \n"); + eRet = OMX_ErrorInsufficientResources; + } + } + else + { + DEBUG_PRINT("Input buffer memory allocation failed 2\n"); + eRet = OMX_ErrorInsufficientResources; + } + return eRet; +} + +OMX_ERRORTYPE omx_aac_aenc::allocate_output_buffer +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE** bufferHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + OMX_BUFFERHEADERTYPE *bufHdr; + unsigned nBufSize = MAX(bytes,output_buffer_size); + char *buf_ptr; + + if(hComp == NULL) + { + port = port; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_out_current_buf_count < m_out_act_buf_count) + { + buf_ptr = (char *) calloc( (nBufSize + sizeof(OMX_BUFFERHEADERTYPE)),1); + + if (buf_ptr != NULL) + { + bufHdr = (OMX_BUFFERHEADERTYPE *) buf_ptr; + *bufferHdr = bufHdr; + memset(bufHdr,0,sizeof(OMX_BUFFERHEADERTYPE)); + + bufHdr->pBuffer = (OMX_U8 *)((buf_ptr)+ + sizeof(OMX_BUFFERHEADERTYPE)); + + bufHdr->nSize = (OMX_U32)sizeof(OMX_BUFFERHEADERTYPE); + bufHdr->nVersion.nVersion = OMX_SPEC_VERSION; + bufHdr->nAllocLen = nBufSize; + bufHdr->pAppPrivate = appData; + bufHdr->nOutputPortIndex = OMX_CORE_OUTPUT_PORT_INDEX; + m_output_buf_hdrs.insert(bufHdr, NULL); + m_out_current_buf_count++; + DEBUG_PRINT("AOB::bufHdr %p bufHdr->pBuffer %p m_out_buf_cnt=%d "\ + "bytes=%u",bufHdr, bufHdr->pBuffer,\ + m_out_current_buf_count, bytes); + } else + { + DEBUG_PRINT("Output buffer memory allocation failed 1 \n"); + eRet = OMX_ErrorInsufficientResources; + } + } else + { + DEBUG_PRINT("Output buffer memory allocation failed\n"); + eRet = OMX_ErrorInsufficientResources; + } + return eRet; +} + + +// AllocateBuffer -- API Call +/* ====================================================================== +FUNCTION + omx_aac_aenc::AllocateBuffer + +DESCRIPTION + Returns zero if all the buffers released.. + +PARAMETERS + None. + +RETURN VALUE + true/false + +========================================================================== */ +OMX_ERRORTYPE omx_aac_aenc::allocate_buffer +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE** bufferHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes) +{ + + OMX_ERRORTYPE eRet = OMX_ErrorNone; // OMX return type + + if (m_state == OMX_StateInvalid) + { + DEBUG_PRINT_ERROR("Allocate Buf in Invalid State\n"); + return OMX_ErrorInvalidState; + } + // What if the client calls again. + if (OMX_CORE_INPUT_PORT_INDEX == port) + { + eRet = allocate_input_buffer(hComp,bufferHdr,port,appData,bytes); + } else if (OMX_CORE_OUTPUT_PORT_INDEX == port) + { + eRet = allocate_output_buffer(hComp,bufferHdr,port,appData,bytes); + } else + { + DEBUG_PRINT_ERROR("Error: Invalid Port Index received %d\n", + (int)port); + eRet = OMX_ErrorBadPortIndex; + } + + if (eRet == OMX_ErrorNone) + { + DEBUG_PRINT("allocate_buffer: before allocate_done \n"); + if (allocate_done()) + { + DEBUG_PRINT("allocate_buffer: after allocate_done \n"); + if (BITMASK_PRESENT(&m_flags,OMX_COMPONENT_IDLE_PENDING)) + { + BITMASK_CLEAR(&m_flags, OMX_COMPONENT_IDLE_PENDING); + post_command(OMX_CommandStateSet,OMX_StateIdle, + OMX_COMPONENT_GENERATE_EVENT); + DEBUG_PRINT("allocate_buffer: post idle transition event \n"); + } + DEBUG_PRINT("allocate_buffer: complete \n"); + } + if (port == OMX_CORE_INPUT_PORT_INDEX && m_inp_bPopulated) + { + if (BITMASK_PRESENT(&m_flags,OMX_COMPONENT_INPUT_ENABLE_PENDING)) + { + BITMASK_CLEAR((&m_flags),OMX_COMPONENT_INPUT_ENABLE_PENDING); + post_command(OMX_CommandPortEnable, OMX_CORE_INPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + } + } + if (port == OMX_CORE_OUTPUT_PORT_INDEX && m_out_bPopulated) + { + if (BITMASK_PRESENT(&m_flags,OMX_COMPONENT_OUTPUT_ENABLE_PENDING)) + { + BITMASK_CLEAR((&m_flags),OMX_COMPONENT_OUTPUT_ENABLE_PENDING); + m_out_bEnabled = OMX_TRUE; + + DEBUG_PRINT("AllocBuf-->is_out_th_sleep=%d\n",is_out_th_sleep); + pthread_mutex_lock(&m_out_th_lock_1); + if (is_out_th_sleep) + { + is_out_th_sleep = false; + DEBUG_DETAIL("AllocBuf:WAKING UP OUT THREADS\n"); + out_th_wakeup(); + } + pthread_mutex_unlock(&m_out_th_lock_1); + pthread_mutex_lock(&m_in_th_lock_1); + if(is_in_th_sleep) + { + is_in_th_sleep = false; + DEBUG_DETAIL("AB:WAKING UP IN THREADS\n"); + in_th_wakeup(); + } + pthread_mutex_unlock(&m_in_th_lock_1); + post_command(OMX_CommandPortEnable, OMX_CORE_OUTPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + } + } + } + DEBUG_PRINT("Allocate Buffer exit with ret Code %d\n", eRet); + return eRet; +} + +/*============================================================================= +FUNCTION: + use_buffer + +DESCRIPTION: + OMX Use Buffer method implementation. + +INPUT/OUTPUT PARAMETERS: + [INOUT] bufferHdr + [IN] hComp + [IN] port + [IN] appData + [IN] bytes + [IN] buffer + +RETURN VALUE: + OMX_ERRORTYPE + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +OMX_ERRORTYPE omx_aac_aenc::use_buffer +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE** bufferHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes, + OMX_IN OMX_U8* buffer) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + + if (OMX_CORE_INPUT_PORT_INDEX == port) + { + eRet = use_input_buffer(hComp,bufferHdr,port,appData,bytes,buffer); + + } else if (OMX_CORE_OUTPUT_PORT_INDEX == port) + { + eRet = use_output_buffer(hComp,bufferHdr,port,appData,bytes,buffer); + } else + { + DEBUG_PRINT_ERROR("Error: Invalid Port Index received %d\n",(int)port); + eRet = OMX_ErrorBadPortIndex; + } + + if (eRet == OMX_ErrorNone) + { + DEBUG_PRINT("Checking for Output Allocate buffer Done"); + if (allocate_done()) + { + if (BITMASK_PRESENT(&m_flags,OMX_COMPONENT_IDLE_PENDING)) + { + BITMASK_CLEAR(&m_flags, OMX_COMPONENT_IDLE_PENDING); + post_command(OMX_CommandStateSet,OMX_StateIdle, + OMX_COMPONENT_GENERATE_EVENT); + } + } + if (port == OMX_CORE_INPUT_PORT_INDEX && m_inp_bPopulated) + { + if (BITMASK_PRESENT(&m_flags,OMX_COMPONENT_INPUT_ENABLE_PENDING)) + { + BITMASK_CLEAR((&m_flags),OMX_COMPONENT_INPUT_ENABLE_PENDING); + post_command(OMX_CommandPortEnable, OMX_CORE_INPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + + } + } + if (port == OMX_CORE_OUTPUT_PORT_INDEX && m_out_bPopulated) + { + if (BITMASK_PRESENT(&m_flags,OMX_COMPONENT_OUTPUT_ENABLE_PENDING)) + { + BITMASK_CLEAR((&m_flags),OMX_COMPONENT_OUTPUT_ENABLE_PENDING); + post_command(OMX_CommandPortEnable, OMX_CORE_OUTPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + pthread_mutex_lock(&m_out_th_lock_1); + if (is_out_th_sleep) + { + is_out_th_sleep = false; + DEBUG_DETAIL("UseBuf:WAKING UP OUT THREADS\n"); + out_th_wakeup(); + } + pthread_mutex_unlock(&m_out_th_lock_1); + pthread_mutex_lock(&m_in_th_lock_1); + if(is_in_th_sleep) + { + is_in_th_sleep = false; + DEBUG_DETAIL("UB:WAKING UP IN THREADS\n"); + in_th_wakeup(); + } + pthread_mutex_unlock(&m_in_th_lock_1); + } + } + } + DEBUG_PRINT("Use Buffer for port[%u] eRet[%d]\n", port,eRet); + return eRet; +} +/*============================================================================= +FUNCTION: + use_input_buffer + +DESCRIPTION: + Helper function for Use buffer in the input pin + +INPUT/OUTPUT PARAMETERS: + [INOUT] bufferHdr + [IN] hComp + [IN] port + [IN] appData + [IN] bytes + [IN] buffer + +RETURN VALUE: + OMX_ERRORTYPE + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +OMX_ERRORTYPE omx_aac_aenc::use_input_buffer +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE** bufferHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes, + OMX_IN OMX_U8* buffer) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + OMX_BUFFERHEADERTYPE *bufHdr; + unsigned nBufSize = MAX(bytes, input_buffer_size); + char *buf_ptr; + + if(hComp == NULL) + { + port = port; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if(bytes < input_buffer_size) + { + /* return if i\p buffer size provided by client + is less than min i\p buffer size supported by omx component*/ + return OMX_ErrorInsufficientResources; + } + if (m_inp_current_buf_count < m_inp_act_buf_count) + { + buf_ptr = (char *) calloc(sizeof(OMX_BUFFERHEADERTYPE), 1); + + if (buf_ptr != NULL) + { + bufHdr = (OMX_BUFFERHEADERTYPE *) buf_ptr; + *bufferHdr = bufHdr; + memset(bufHdr,0,sizeof(OMX_BUFFERHEADERTYPE)); + + bufHdr->pBuffer = (OMX_U8 *)(buffer); + DEBUG_PRINT("use_input_buffer:bufHdr %p bufHdr->pBuffer %p \ + bytes=%u", bufHdr, bufHdr->pBuffer,bytes); + bufHdr->nSize = (OMX_U32)sizeof(OMX_BUFFERHEADERTYPE); + bufHdr->nVersion.nVersion = OMX_SPEC_VERSION; + bufHdr->nAllocLen = nBufSize; + input_buffer_size = nBufSize; + bufHdr->pAppPrivate = appData; + bufHdr->nInputPortIndex = OMX_CORE_INPUT_PORT_INDEX; + bufHdr->nOffset = 0; + m_input_buf_hdrs.insert(bufHdr, NULL); + m_inp_current_buf_count++; + } else + { + DEBUG_PRINT("Input buffer memory allocation failed 1 \n"); + eRet = OMX_ErrorInsufficientResources; + } + } else + { + DEBUG_PRINT("Input buffer memory allocation failed\n"); + eRet = OMX_ErrorInsufficientResources; + } + return eRet; +} + +/*============================================================================= +FUNCTION: + use_output_buffer + +DESCRIPTION: + Helper function for Use buffer in the output pin + +INPUT/OUTPUT PARAMETERS: + [INOUT] bufferHdr + [IN] hComp + [IN] port + [IN] appData + [IN] bytes + [IN] buffer + +RETURN VALUE: + OMX_ERRORTYPE + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +OMX_ERRORTYPE omx_aac_aenc::use_output_buffer +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE** bufferHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes, + OMX_IN OMX_U8* buffer) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + OMX_BUFFERHEADERTYPE *bufHdr; + unsigned nBufSize = MAX(bytes,output_buffer_size); + char *buf_ptr; + + if(hComp == NULL) + { + port = port; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (bytes < output_buffer_size) + { + /* return if o\p buffer size provided by client + is less than min o\p buffer size supported by omx component*/ + return OMX_ErrorInsufficientResources; + } + + DEBUG_PRINT("Inside omx_aac_aenc::use_output_buffer"); + if (m_out_current_buf_count < m_out_act_buf_count) + { + + buf_ptr = (char *) calloc(sizeof(OMX_BUFFERHEADERTYPE), 1); + + if (buf_ptr != NULL) + { + bufHdr = (OMX_BUFFERHEADERTYPE *) buf_ptr; + DEBUG_PRINT("BufHdr=%p buffer=%p\n",bufHdr,buffer); + *bufferHdr = bufHdr; + memset(bufHdr,0,sizeof(OMX_BUFFERHEADERTYPE)); + + bufHdr->pBuffer = (OMX_U8 *)(buffer); + DEBUG_PRINT("use_output_buffer:bufHdr %p bufHdr->pBuffer %p \ + len=%u\n", bufHdr, bufHdr->pBuffer,bytes); + bufHdr->nSize = (OMX_U32)sizeof(OMX_BUFFERHEADERTYPE); + bufHdr->nVersion.nVersion = OMX_SPEC_VERSION; + bufHdr->nAllocLen = nBufSize; + output_buffer_size = nBufSize; + bufHdr->pAppPrivate = appData; + bufHdr->nOutputPortIndex = OMX_CORE_OUTPUT_PORT_INDEX; + bufHdr->nOffset = 0; + m_output_buf_hdrs.insert(bufHdr, NULL); + m_out_current_buf_count++; + + } else + { + DEBUG_PRINT("Output buffer memory allocation failed\n"); + eRet = OMX_ErrorInsufficientResources; + } + } else + { + DEBUG_PRINT("Output buffer memory allocation failed 2\n"); + eRet = OMX_ErrorInsufficientResources; + } + return eRet; +} +/** + @brief member function that searches for caller buffer + + @param buffer pointer to buffer header + @return bool value indicating whether buffer is found + */ +bool omx_aac_aenc::search_input_bufhdr(OMX_BUFFERHEADERTYPE *buffer) +{ + + bool eRet = false; + OMX_BUFFERHEADERTYPE *temp = NULL; + + //access only in IL client context + temp = m_input_buf_hdrs.find_ele(buffer); + if (buffer && temp) + { + DEBUG_DETAIL("search_input_bufhdr %p \n", buffer); + eRet = true; + } + return eRet; +} + +/** + @brief member function that searches for caller buffer + + @param buffer pointer to buffer header + @return bool value indicating whether buffer is found + */ +bool omx_aac_aenc::search_output_bufhdr(OMX_BUFFERHEADERTYPE *buffer) +{ + + bool eRet = false; + OMX_BUFFERHEADERTYPE *temp = NULL; + + //access only in IL client context + temp = m_output_buf_hdrs.find_ele(buffer); + if (buffer && temp) + { + DEBUG_DETAIL("search_output_bufhdr %p \n", buffer); + eRet = true; + } + return eRet; +} + +// Free Buffer - API call +/** + @brief member function that handles free buffer command from IL client + + This function is a block-call function that handles IL client request to + freeing the buffer + + @param hComp handle to component instance + @param port id of port which holds the buffer + @param buffer buffer header + @return Error status +*/ +OMX_ERRORTYPE omx_aac_aenc::free_buffer(OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_U32 port, + OMX_IN OMX_BUFFERHEADERTYPE* buffer) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + + DEBUG_PRINT("Free_Buffer buf %p\n", buffer); + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_state == OMX_StateIdle && + (BITMASK_PRESENT(&m_flags ,OMX_COMPONENT_LOADING_PENDING))) + { + DEBUG_PRINT(" free buffer while Component in Loading pending\n"); + } else if ((m_inp_bEnabled == OMX_FALSE && + port == OMX_CORE_INPUT_PORT_INDEX)|| + (m_out_bEnabled == OMX_FALSE && + port == OMX_CORE_OUTPUT_PORT_INDEX)) + { + DEBUG_PRINT("Free Buffer while port %u disabled\n", port); + } else if (m_state == OMX_StateExecuting || m_state == OMX_StatePause) + { + DEBUG_PRINT("Invalid state to free buffer,ports need to be disabled:\ + OMX_ErrorPortUnpopulated\n"); + post_command(OMX_EventError, + OMX_ErrorPortUnpopulated, + OMX_COMPONENT_GENERATE_EVENT); + + return eRet; + } else + { + DEBUG_PRINT("free_buffer: Invalid state to free buffer,ports need to be\ + disabled:OMX_ErrorPortUnpopulated\n"); + post_command(OMX_EventError, + OMX_ErrorPortUnpopulated, + OMX_COMPONENT_GENERATE_EVENT); + } + if (OMX_CORE_INPUT_PORT_INDEX == port) + { + if (m_inp_current_buf_count != 0) + { + m_inp_bPopulated = OMX_FALSE; + if (true == search_input_bufhdr(buffer)) + { + /* Buffer exist */ + //access only in IL client context + DEBUG_PRINT("Free_Buf:in_buffer[%p]\n",buffer); + m_input_buf_hdrs.erase(buffer); + free(buffer); + m_inp_current_buf_count--; + } else + { + DEBUG_PRINT_ERROR("Free_Buf:Error-->free_buffer, \ + Invalid Input buffer header\n"); + eRet = OMX_ErrorBadParameter; + } + } else + { + DEBUG_PRINT_ERROR("Error: free_buffer,Port Index calculation \ + came out Invalid\n"); + eRet = OMX_ErrorBadPortIndex; + } + if (BITMASK_PRESENT((&m_flags),OMX_COMPONENT_INPUT_DISABLE_PENDING) + && release_done(0)) + { + DEBUG_PRINT("INPUT PORT MOVING TO DISABLED STATE \n"); + BITMASK_CLEAR((&m_flags),OMX_COMPONENT_INPUT_DISABLE_PENDING); + post_command(OMX_CommandPortDisable, + OMX_CORE_INPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + } + } else if (OMX_CORE_OUTPUT_PORT_INDEX == port) + { + if (m_out_current_buf_count != 0) + { + m_out_bPopulated = OMX_FALSE; + if (true == search_output_bufhdr(buffer)) + { + /* Buffer exist */ + //access only in IL client context + DEBUG_PRINT("Free_Buf:out_buffer[%p]\n",buffer); + m_output_buf_hdrs.erase(buffer); + free(buffer); + m_out_current_buf_count--; + } else + { + DEBUG_PRINT("Free_Buf:Error-->free_buffer , \ + Invalid Output buffer header\n"); + eRet = OMX_ErrorBadParameter; + } + } else + { + eRet = OMX_ErrorBadPortIndex; + } + + if (BITMASK_PRESENT((&m_flags),OMX_COMPONENT_OUTPUT_DISABLE_PENDING) + && release_done(1)) + { + DEBUG_PRINT("OUTPUT PORT MOVING TO DISABLED STATE \n"); + BITMASK_CLEAR((&m_flags),OMX_COMPONENT_OUTPUT_DISABLE_PENDING); + post_command(OMX_CommandPortDisable, + OMX_CORE_OUTPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + + } + } else + { + eRet = OMX_ErrorBadPortIndex; + } + if ((OMX_ErrorNone == eRet) && + (BITMASK_PRESENT(&m_flags ,OMX_COMPONENT_LOADING_PENDING))) + { + if (release_done(-1)) + { + if(ioctl(m_drv_fd, AUDIO_STOP, 0) < 0) + DEBUG_PRINT_ERROR("AUDIO STOP in free buffer failed\n"); + else + DEBUG_PRINT("AUDIO STOP in free buffer passed\n"); + + DEBUG_PRINT("Free_Buf: Free buffer\n"); + + // Send the callback now + BITMASK_CLEAR((&m_flags),OMX_COMPONENT_LOADING_PENDING); + DEBUG_PRINT("Before OMX_StateLoaded \ + OMX_COMPONENT_GENERATE_EVENT\n"); + post_command(OMX_CommandStateSet, + OMX_StateLoaded,OMX_COMPONENT_GENERATE_EVENT); + DEBUG_PRINT("After OMX_StateLoaded OMX_COMPONENT_GENERATE_EVENT\n"); + + } + } + return eRet; +} + + +/** + @brief member function that that handles empty this buffer command + + This function meremly queue up the command and data would be consumed + in command server thread context + + @param hComp handle to component instance + @param buffer pointer to buffer header + @return error status + */ +OMX_ERRORTYPE omx_aac_aenc::empty_this_buffer( + OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_BUFFERHEADERTYPE* buffer) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + + DEBUG_PRINT("ETB:Buf:%p Len %u TS %lld numInBuf=%d\n", \ + buffer, buffer->nFilledLen, buffer->nTimeStamp, (nNumInputBuf)); + if (m_state == OMX_StateInvalid) + { + DEBUG_PRINT("Empty this buffer in Invalid State\n"); + return OMX_ErrorInvalidState; + } + if (!m_inp_bEnabled) + { + DEBUG_PRINT("empty_this_buffer OMX_ErrorIncorrectStateOperation "\ + "Port Status %d \n", m_inp_bEnabled); + return OMX_ErrorIncorrectStateOperation; + } + if (buffer->nSize != sizeof(OMX_BUFFERHEADERTYPE)) + { + DEBUG_PRINT("omx_aac_aenc::etb--> Buffer Size Invalid\n"); + return OMX_ErrorBadParameter; + } + if (buffer->nVersion.nVersion != OMX_SPEC_VERSION) + { + DEBUG_PRINT("omx_aac_aenc::etb--> OMX Version Invalid\n"); + return OMX_ErrorVersionMismatch; + } + + if (buffer->nInputPortIndex != OMX_CORE_INPUT_PORT_INDEX) + { + return OMX_ErrorBadPortIndex; + } + if ((m_state != OMX_StateExecuting) && + (m_state != OMX_StatePause)) + { + DEBUG_PRINT_ERROR("Invalid state\n"); + eRet = OMX_ErrorInvalidState; + } + if (OMX_ErrorNone == eRet) + { + if (search_input_bufhdr(buffer) == true) + { + post_input((unsigned long)hComp, + (unsigned long) buffer,OMX_COMPONENT_GENERATE_ETB); + } else + { + DEBUG_PRINT_ERROR("Bad header %p \n", buffer); + eRet = OMX_ErrorBadParameter; + } + } + pthread_mutex_lock(&in_buf_count_lock); + nNumInputBuf++; + m_aac_pb_stats.etb_cnt++; + pthread_mutex_unlock(&in_buf_count_lock); + return eRet; +} +/** + @brief member function that writes data to kernel driver + + @param hComp handle to component instance + @param buffer pointer to buffer header + @return error status + */ +OMX_ERRORTYPE omx_aac_aenc::empty_this_buffer_proxy +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE* buffer) +{ + OMX_STATETYPE state; + META_IN meta_in; + //Pointer to the starting location of the data to be transcoded + OMX_U8 *srcStart; + //The total length of the data to be transcoded + srcStart = buffer->pBuffer; + OMX_U8 *data = NULL; + PrintFrameHdr(OMX_COMPONENT_GENERATE_ETB,buffer); + memset(&meta_in,0,sizeof(meta_in)); + if ( search_input_bufhdr(buffer) == false ) + { + DEBUG_PRINT("ETBP: INVALID BUF HDR\n"); + buffer_done_cb((OMX_BUFFERHEADERTYPE *)buffer); + return OMX_ErrorBadParameter; + } + if (m_tmp_meta_buf) + { + data = m_tmp_meta_buf; + + // copy the metadata info from the BufHdr and insert to payload + meta_in.offsetVal = (OMX_U16)sizeof(META_IN); + meta_in.nTimeStamp.LowPart = + (unsigned int)((((OMX_BUFFERHEADERTYPE*)buffer)->nTimeStamp)& 0xFFFFFFFF); + meta_in.nTimeStamp.HighPart = + (unsigned int) (((((OMX_BUFFERHEADERTYPE*)buffer)->nTimeStamp) >> 32) & 0xFFFFFFFF); + meta_in.nFlags &= ~OMX_BUFFERFLAG_EOS; + if(buffer->nFlags & OMX_BUFFERFLAG_EOS) + { + DEBUG_PRINT("EOS OCCURED \n"); + meta_in.nFlags |= OMX_BUFFERFLAG_EOS; + } + memcpy(data,&meta_in, meta_in.offsetVal); + DEBUG_PRINT("meta_in.nFlags = %d\n",meta_in.nFlags); + } + + if (ts == 0) { + DEBUG_PRINT("Anchor time %lld", buffer->nTimeStamp); + ts = buffer->nTimeStamp; + } + + memcpy(&data[sizeof(META_IN)],buffer->pBuffer,buffer->nFilledLen); + write(m_drv_fd, data, buffer->nFilledLen+sizeof(META_IN)); + pthread_mutex_lock(&m_state_lock); + get_state(&m_cmp, &state); + pthread_mutex_unlock(&m_state_lock); + + if (OMX_StateExecuting == state) + { + DEBUG_DETAIL("In Exe state, EBD CB"); + buffer_done_cb((OMX_BUFFERHEADERTYPE *)buffer); + } else + { + /* Assume empty this buffer function has already checked + validity of buffer */ + DEBUG_PRINT("Empty buffer %p to kernel driver\n", buffer); + post_input((unsigned long) & hComp,(unsigned long) buffer, + OMX_COMPONENT_GENERATE_BUFFER_DONE); + } + return OMX_ErrorNone; +} + +OMX_ERRORTYPE omx_aac_aenc::fill_this_buffer_proxy +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE* buffer) +{ + OMX_STATETYPE state; + ENC_META_OUT *meta_out = NULL; + ssize_t nReadbytes = 0; + int szadifhr = 0; + int numframes = 0; + int metainfo = 0; + OMX_U8 *src = buffer->pBuffer; + + pthread_mutex_lock(&m_state_lock); + get_state(&m_cmp, &state); + pthread_mutex_unlock(&m_state_lock); + + if (true == search_output_bufhdr(buffer)) + { + if((m_aac_param.eAACStreamFormat == OMX_AUDIO_AACStreamFormatADIF) + && (adif_flag == 0)) + { + + DEBUG_PRINT("\nBefore Read..m_drv_fd = %d,\n",m_drv_fd); + nReadbytes = read(m_drv_fd,m_tmp_out_meta_buf,output_buffer_size ); + DEBUG_DETAIL("FTBP->Al_len[%lu]buf[%p]size[%d]numOutBuf[%d]\n",\ + buffer->nAllocLen,m_tmp_out_meta_buf, + nReadbytes,nNumOutputBuf); + if(*m_tmp_out_meta_buf <= 0) + return OMX_ErrorBadParameter; + szadifhr = AUDAAC_MAX_ADIF_HEADER_LENGTH; + numframes = *m_tmp_out_meta_buf; + metainfo = (int)((sizeof(ENC_META_OUT) * numframes)+ + sizeof(unsigned char)); + audaac_rec_install_adif_header_variable(0,sample_idx, + (OMX_U8)m_aac_param.nChannels); + memcpy(buffer->pBuffer,m_tmp_out_meta_buf,metainfo); + memcpy(buffer->pBuffer + metainfo,&audaac_header_adif[0],szadifhr); + memcpy(buffer->pBuffer + metainfo + szadifhr, + m_tmp_out_meta_buf + metainfo,(nReadbytes - metainfo)); + src += sizeof(unsigned char); + meta_out = (ENC_META_OUT *)src; + meta_out->frame_size += szadifhr; + numframes--; + while(numframes > 0) + { + src += sizeof(ENC_META_OUT); + meta_out = (ENC_META_OUT *)src; + meta_out->offset_to_frame += szadifhr; + numframes--; + } + buffer->nFlags = OMX_BUFFERFLAG_CODECCONFIG; + adif_flag++; + } + else if((m_aac_param.eAACStreamFormat == OMX_AUDIO_AACStreamFormatMP4FF) + &&(mp4ff_flag == 0)) + { + DEBUG_PRINT("OMX_AUDIO_AACStreamFormatMP4FF\n"); + audaac_rec_install_mp4ff_header_variable(0,sample_idx, + (OMX_U8)m_aac_param.nChannels); + memcpy(buffer->pBuffer,&audaac_header_mp4ff[0], + AUDAAC_MAX_MP4FF_HEADER_LENGTH); + buffer->nFilledLen = AUDAAC_MAX_MP4FF_HEADER_LENGTH; + buffer->nTimeStamp = 0; + buffer->nFlags = OMX_BUFFERFLAG_CODECCONFIG; + frame_done_cb((OMX_BUFFERHEADERTYPE *)buffer); + mp4ff_flag++; + return OMX_ErrorNone; + + } + else + { + + DEBUG_PRINT("\nBefore Read..m_drv_fd = %d,\n",m_drv_fd); + nReadbytes = read(m_drv_fd,buffer->pBuffer,output_buffer_size ); + DEBUG_DETAIL("FTBP->Al_len[%d]buf[%p]size[%d]numOutBuf[%d]\n",\ + buffer->nAllocLen,buffer->pBuffer, + nReadbytes,nNumOutputBuf); + if(nReadbytes <= 0) + { + buffer->nFilledLen = 0; + buffer->nOffset = 0; + buffer->nTimeStamp = nTimestamp; + frame_done_cb((OMX_BUFFERHEADERTYPE *)buffer); + return OMX_ErrorNone; + } + } + + meta_out = (ENC_META_OUT *)(buffer->pBuffer + sizeof(unsigned char)); + buffer->nTimeStamp = ts + (frameduration * m_frame_count); + ++m_frame_count; + nTimestamp = buffer->nTimeStamp; + buffer->nFlags |= meta_out->nflags; + buffer->nOffset = meta_out->offset_to_frame + 1; + buffer->nFilledLen = (OMX_U32)(nReadbytes - buffer->nOffset + szadifhr); + DEBUG_PRINT("nflags %d frame_size %d offset_to_frame %d \ + timestamp %lld\n", meta_out->nflags, + meta_out->frame_size, + meta_out->offset_to_frame, + buffer->nTimeStamp); + + if ((buffer->nFlags & OMX_BUFFERFLAG_EOS) == OMX_BUFFERFLAG_EOS ) + { + buffer->nFilledLen = 0; + buffer->nOffset = 0; + buffer->nTimeStamp = nTimestamp; + frame_done_cb((OMX_BUFFERHEADERTYPE *)buffer); + if ((buffer->nFlags & OMX_BUFFERFLAG_EOS) == OMX_BUFFERFLAG_EOS ) + { + DEBUG_PRINT("FTBP: Now, Send EOS flag to Client \n"); + m_cb.EventHandler(&m_cmp, + m_app_data, + OMX_EventBufferFlag, + 1, 1, NULL ); + DEBUG_PRINT("FTBP: END OF STREAM m_eos_bm=%d\n",m_eos_bm); + } + + return OMX_ErrorNone; + } + DEBUG_PRINT("nState %d \n",nState ); + + pthread_mutex_lock(&m_state_lock); + get_state(&m_cmp, &state); + pthread_mutex_unlock(&m_state_lock); + + if (state == OMX_StatePause) + { + DEBUG_PRINT("FTBP:Post the FBD to event thread currstate=%d\n",\ + state); + post_output((unsigned long) & hComp,(unsigned long) buffer, + OMX_COMPONENT_GENERATE_FRAME_DONE); + } + else + { + frame_done_cb((OMX_BUFFERHEADERTYPE *)buffer); + DEBUG_PRINT("FTBP*******************************************\n"); + + } + + + } + else + DEBUG_PRINT("\n FTBP-->Invalid buffer in FTB \n"); + + + return OMX_ErrorNone; +} + +/* ====================================================================== +FUNCTION + omx_aac_aenc::FillThisBuffer + +DESCRIPTION + IL client uses this method to release the frame buffer + after displaying them. + + + +PARAMETERS + + None. + +RETURN VALUE + true/false + +========================================================================== */ +OMX_ERRORTYPE omx_aac_aenc::fill_this_buffer +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_BUFFERHEADERTYPE* buffer) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + if (buffer->nSize != sizeof(OMX_BUFFERHEADERTYPE)) + { + DEBUG_PRINT("omx_aac_aenc::ftb--> Buffer Size Invalid\n"); + return OMX_ErrorBadParameter; + } + if (m_out_bEnabled == OMX_FALSE) + { + return OMX_ErrorIncorrectStateOperation; + } + + if (buffer->nVersion.nVersion != OMX_SPEC_VERSION) + { + DEBUG_PRINT("omx_aac_aenc::ftb--> OMX Version Invalid\n"); + return OMX_ErrorVersionMismatch; + } + if (buffer->nOutputPortIndex != OMX_CORE_OUTPUT_PORT_INDEX) + { + return OMX_ErrorBadPortIndex; + } + pthread_mutex_lock(&out_buf_count_lock); + nNumOutputBuf++; + m_aac_pb_stats.ftb_cnt++; + DEBUG_DETAIL("FTB:nNumOutputBuf is %d", nNumOutputBuf); + pthread_mutex_unlock(&out_buf_count_lock); + post_output((unsigned long)hComp, + (unsigned long) buffer,OMX_COMPONENT_GENERATE_FTB); + return eRet; +} + +/* ====================================================================== +FUNCTION + omx_aac_aenc::SetCallbacks + +DESCRIPTION + Set the callbacks. + +PARAMETERS + None. + +RETURN VALUE + OMX Error None if everything successful. + +========================================================================== */ +OMX_ERRORTYPE omx_aac_aenc::set_callbacks(OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_CALLBACKTYPE* callbacks, + OMX_IN OMX_PTR appData) +{ + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + m_cb = *callbacks; + m_app_data = appData; + + return OMX_ErrorNone; +} + +/* ====================================================================== +FUNCTION + omx_aac_aenc::ComponentDeInit + +DESCRIPTION + Destroys the component and release memory allocated to the heap. + +PARAMETERS + . + +RETURN VALUE + OMX Error None if everything successful. + +========================================================================== */ +OMX_ERRORTYPE omx_aac_aenc::component_deinit(OMX_IN OMX_HANDLETYPE hComp) +{ + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (OMX_StateLoaded != m_state && OMX_StateInvalid != m_state) + { + DEBUG_PRINT_ERROR("Warning: Rxed DeInit when not in LOADED state %d\n", + m_state); + } + deinit_encoder(); + + DEBUG_PRINT_ERROR("%s:COMPONENT DEINIT...\n", __FUNCTION__); + return OMX_ErrorNone; +} + +/* ====================================================================== +FUNCTION + omx_aac_aenc::deinit_encoder + +DESCRIPTION + Closes all the threads and release memory allocated to the heap. + +PARAMETERS + None. + +RETURN VALUE + None. + +========================================================================== */ +void omx_aac_aenc::deinit_encoder() +{ + DEBUG_PRINT("Component-deinit being processed\n"); + DEBUG_PRINT("********************************\n"); + DEBUG_PRINT("STATS: in-buf-len[%u]out-buf-len[%u] tot-pb-time[%lld]",\ + m_aac_pb_stats.tot_in_buf_len, + m_aac_pb_stats.tot_out_buf_len, + m_aac_pb_stats.tot_pb_time); + DEBUG_PRINT("STATS: fbd-cnt[%u]ftb-cnt[%u]etb-cnt[%u]ebd-cnt[%u]",\ + m_aac_pb_stats.fbd_cnt,m_aac_pb_stats.ftb_cnt, + m_aac_pb_stats.etb_cnt, + m_aac_pb_stats.ebd_cnt); + memset(&m_aac_pb_stats,0,sizeof(AAC_PB_STATS)); + + if((OMX_StateLoaded != m_state) && (OMX_StateInvalid != m_state)) + { + DEBUG_PRINT_ERROR("%s,Deinit called in state[%d]\n",__FUNCTION__,\ + m_state); + // Get back any buffers from driver + if(pcm_input) + execute_omx_flush(-1,false); + else + execute_omx_flush(1,false); + // force state change to loaded so that all threads can be exited + pthread_mutex_lock(&m_state_lock); + m_state = OMX_StateLoaded; + pthread_mutex_unlock(&m_state_lock); + DEBUG_PRINT_ERROR("Freeing Buf:inp_current_buf_count[%d][%d]\n",\ + m_inp_current_buf_count, + m_input_buf_hdrs.size()); + m_input_buf_hdrs.eraseall(); + DEBUG_PRINT_ERROR("Freeing Buf:out_current_buf_count[%d][%d]\n",\ + m_out_current_buf_count, + m_output_buf_hdrs.size()); + m_output_buf_hdrs.eraseall(); + + } + if(pcm_input) + { + pthread_mutex_lock(&m_in_th_lock_1); + if (is_in_th_sleep) + { + is_in_th_sleep = false; + DEBUG_DETAIL("Deinit:WAKING UP IN THREADS\n"); + in_th_wakeup(); + } + pthread_mutex_unlock(&m_in_th_lock_1); + } + pthread_mutex_lock(&m_out_th_lock_1); + if (is_out_th_sleep) + { + is_out_th_sleep = false; + DEBUG_DETAIL("SCP:WAKING UP OUT THREADS\n"); + out_th_wakeup(); + } + pthread_mutex_unlock(&m_out_th_lock_1); + if(pcm_input) + { + if (m_ipc_to_in_th != NULL) + { + omx_aac_thread_stop(m_ipc_to_in_th); + m_ipc_to_in_th = NULL; + } + } + + if (m_ipc_to_cmd_th != NULL) + { + omx_aac_thread_stop(m_ipc_to_cmd_th); + m_ipc_to_cmd_th = NULL; + } + if (m_ipc_to_out_th != NULL) + { + DEBUG_DETAIL("Inside omx_aac_thread_stop\n"); + omx_aac_thread_stop(m_ipc_to_out_th); + m_ipc_to_out_th = NULL; + } + + + if(ioctl(m_drv_fd, AUDIO_STOP, 0) <0) + DEBUG_PRINT_ERROR("De-init: AUDIO_STOP FAILED\n"); + + if(pcm_input && m_tmp_meta_buf ) + { + free(m_tmp_meta_buf); + } + + if(m_tmp_out_meta_buf) + { + free(m_tmp_out_meta_buf); + } + nNumInputBuf = 0; + nNumOutputBuf = 0; + m_inp_current_buf_count=0; + m_out_current_buf_count=0; + m_out_act_buf_count = 0; + m_inp_act_buf_count = 0; + m_inp_bEnabled = OMX_FALSE; + m_out_bEnabled = OMX_FALSE; + m_inp_bPopulated = OMX_FALSE; + m_out_bPopulated = OMX_FALSE; + adif_flag = 0; + mp4ff_flag = 0; + ts = 0; + nTimestamp = 0; + frameduration = 0; + if ( m_drv_fd >= 0 ) + { + if(close(m_drv_fd) < 0) + DEBUG_PRINT("De-init: Driver Close Failed \n"); + m_drv_fd = -1; + } + else + { + DEBUG_PRINT_ERROR(" AAC device already closed\n"); + } + m_comp_deinit=1; + m_is_out_th_sleep = 1; + m_is_in_th_sleep = 1; + DEBUG_PRINT("************************************\n"); + DEBUG_PRINT(" DEINIT COMPLETED"); + DEBUG_PRINT("************************************\n"); + +} + +/* ====================================================================== +FUNCTION + omx_aac_aenc::UseEGLImage + +DESCRIPTION + OMX Use EGL Image method implementation . + +PARAMETERS + . + +RETURN VALUE + Not Implemented error. + +========================================================================== */ +OMX_ERRORTYPE omx_aac_aenc::use_EGL_image +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE** bufferHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN void* eglImage) +{ + DEBUG_PRINT_ERROR("Error : use_EGL_image: Not Implemented \n"); + + if((hComp == NULL) || (appData == NULL) || (eglImage == NULL)) + { + bufferHdr = bufferHdr; + port = port; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + return OMX_ErrorNotImplemented; +} + +/* ====================================================================== +FUNCTION + omx_aac_aenc::ComponentRoleEnum + +DESCRIPTION + OMX Component Role Enum method implementation. + +PARAMETERS + . + +RETURN VALUE + OMX Error None if everything is successful. +========================================================================== */ +OMX_ERRORTYPE omx_aac_aenc::component_role_enum(OMX_IN OMX_HANDLETYPE hComp, + OMX_OUT OMX_U8* role, + OMX_IN OMX_U32 index) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + const char *cmp_role = "audio_encoder.aac"; + + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (index == 0 && role) + { + memcpy(role, cmp_role, strlen(cmp_role)); + *(((char *) role) + strlen(cmp_role) + 1) = '\0'; + } else + { + eRet = OMX_ErrorNoMore; + } + return eRet; +} + + + + +/* ====================================================================== +FUNCTION + omx_aac_aenc::AllocateDone + +DESCRIPTION + Checks if entire buffer pool is allocated by IL Client or not. + Need this to move to IDLE state. + +PARAMETERS + None. + +RETURN VALUE + true/false. + +========================================================================== */ +bool omx_aac_aenc::allocate_done(void) +{ + OMX_BOOL bRet = OMX_FALSE; + if (pcm_input==1) + { + if ((m_inp_act_buf_count == m_inp_current_buf_count) + &&(m_out_act_buf_count == m_out_current_buf_count)) + { + bRet=OMX_TRUE; + + } + if ((m_inp_act_buf_count == m_inp_current_buf_count) && m_inp_bEnabled ) + { + m_inp_bPopulated = OMX_TRUE; + } + + if ((m_out_act_buf_count == m_out_current_buf_count) && m_out_bEnabled ) + { + m_out_bPopulated = OMX_TRUE; + } + } else if (pcm_input==0) + { + if (m_out_act_buf_count == m_out_current_buf_count) + { + bRet=OMX_TRUE; + + } + if ((m_out_act_buf_count == m_out_current_buf_count) && m_out_bEnabled ) + { + m_out_bPopulated = OMX_TRUE; + } + + } + return bRet; +} + + +/* ====================================================================== +FUNCTION + omx_aac_aenc::ReleaseDone + +DESCRIPTION + Checks if IL client has released all the buffers. + +PARAMETERS + None. + +RETURN VALUE + true/false + +========================================================================== */ +bool omx_aac_aenc::release_done(OMX_U32 param1) +{ + DEBUG_PRINT("Inside omx_aac_aenc::release_done"); + OMX_BOOL bRet = OMX_FALSE; + + if (param1 == OMX_ALL) + { + + if ((0 == m_inp_current_buf_count)&&(0 == m_out_current_buf_count)) + { + bRet=OMX_TRUE; + } + } else if (param1 == OMX_CORE_INPUT_PORT_INDEX ) + { + if ((0 == m_inp_current_buf_count)) + { + bRet=OMX_TRUE; + } + } else if (param1 == OMX_CORE_OUTPUT_PORT_INDEX) + { + if ((0 == m_out_current_buf_count)) + { + bRet=OMX_TRUE; + } + } + return bRet; +} + +void omx_aac_aenc::audaac_rec_install_adif_header_variable (OMX_U16 byte_num, + OMX_U32 sample_index, + OMX_U8 channel_config) +{ + OMX_U8 buf8; + OMX_U32 value; + OMX_U32 dummy = 0; + OMX_U8 num_pfe, num_fce, num_sce, num_bce; + OMX_U8 num_lfe, num_ade, num_vce, num_com; + OMX_U8 pfe_index; + OMX_U8 i; + OMX_BOOL variable_bit_rate = OMX_FALSE; + + (void)byte_num; + (void)channel_config; + num_pfe = num_sce = num_bce = + num_lfe = num_ade = num_vce = num_com = 0; + audaac_hdr_bit_index = 32; + num_fce = 1; + /* Store Header Id "ADIF" first */ + memcpy(&audaac_header_adif[0], "ADIF", sizeof(unsigned int)); + + /* copyright_id_present field, 1 bit */ + value = 0; + audaac_rec_install_bits(audaac_header_adif, + AAC_COPYRIGHT_PRESENT_SIZE, + value, + &(audaac_hdr_bit_index)); + + if (value) { + /* Copyright present, 72 bits; skip it for now, + * just install dummy value */ + audaac_rec_install_bits(audaac_header_adif, + 72, + dummy, + &(audaac_hdr_bit_index)); + } + + /* original_copy field, 1 bit */ + value = 0; + audaac_rec_install_bits(audaac_header_adif, + AAC_ORIGINAL_COPY_SIZE, + 0, + &(audaac_hdr_bit_index)); + + /* home field, 1 bit */ + value = 0; + audaac_rec_install_bits(audaac_header_adif, + AAC_HOME_SIZE, + 0, + &(audaac_hdr_bit_index)); + + /* bitstream_type = 1, varibable bit rate, 1 bit */ + value = 0; + audaac_rec_install_bits(audaac_header_adif, + AAC_BITSTREAM_TYPE_SIZE, + value, + &(audaac_hdr_bit_index)); + + /* bit_rate field, 23 bits */ + audaac_rec_install_bits(audaac_header_adif, + AAC_BITRATE_SIZE, + (OMX_U32)m_aac_param.nBitRate, + &(audaac_hdr_bit_index)); + + /* num_program_config_elements, 4 bits */ + num_pfe = 0; + audaac_rec_install_bits(audaac_header_adif, + AAC_NUM_PFE_SIZE, + (OMX_U32)num_pfe, + &(audaac_hdr_bit_index)); + + /* below is to install program_config_elements field, + * for now only one element is supported */ + for (pfe_index=0; pfe_index < num_pfe+1; pfe_index++) { + + + if (variable_bit_rate == OMX_FALSE) { + /* impossible, put dummy value for now */ + audaac_rec_install_bits(audaac_header_adif, + AAC_BUFFER_FULLNESS_SIZE, + 0, + &(audaac_hdr_bit_index)); + + } + + dummy = 0; + + /* element_instance_tag field, 4 bits */ + audaac_rec_install_bits(audaac_header_adif, + AAC_ELEMENT_INSTANCE_TAG_SIZE, + dummy, + &(audaac_hdr_bit_index)); + + /* object_type, 2 bits, AAC LC is supported */ + value = 1; + audaac_rec_install_bits(audaac_header_adif, + AAC_PROFILE_SIZE, /* object type */ + value, + &(audaac_hdr_bit_index)); + + /* sampling_frequency_index, 4 bits */ + audaac_rec_install_bits(audaac_header_adif, + AAC_SAMPLING_FREQ_INDEX_SIZE, + (OMX_U32)sample_index, + &(audaac_hdr_bit_index)); + + /* num_front_channel_elements, 4 bits */ + audaac_rec_install_bits(audaac_header_adif, + AAC_NUM_FRONT_CHANNEL_ELEMENTS_SIZE, + num_fce, + &(audaac_hdr_bit_index)); + + /* num_side_channel_elements, 4 bits */ + audaac_rec_install_bits(audaac_header_adif, + AAC_NUM_SIDE_CHANNEL_ELEMENTS_SIZE, + dummy, + &(audaac_hdr_bit_index)); + + /* num_back_channel_elements, 4 bits */ + audaac_rec_install_bits(audaac_header_adif, + AAC_NUM_BACK_CHANNEL_ELEMENTS_SIZE, + dummy, + &(audaac_hdr_bit_index)); + + /* num_lfe_channel_elements, 2 bits */ + audaac_rec_install_bits(audaac_header_adif, + AAC_NUM_LFE_CHANNEL_ELEMENTS_SIZE, + dummy, + &(audaac_hdr_bit_index)); + + /* num_assoc_data_elements, 3 bits */ + audaac_rec_install_bits(audaac_header_adif, + AAC_NUM_ASSOC_DATA_ELEMENTS_SIZE, + num_ade, + &(audaac_hdr_bit_index)); + + /* num_valid_cc_elements, 4 bits */ + audaac_rec_install_bits(audaac_header_adif, + AAC_NUM_VALID_CC_ELEMENTS_SIZE, + num_vce, + &(audaac_hdr_bit_index)); + + /* mono_mixdown_present, 1 bits */ + audaac_rec_install_bits(audaac_header_adif, + AAC_MONO_MIXDOWN_PRESENT_SIZE, + dummy, + &(audaac_hdr_bit_index)); + + if (dummy) { + audaac_rec_install_bits(audaac_header_adif, + AAC_MONO_MIXDOWN_ELEMENT_SIZE, + dummy, + &(audaac_hdr_bit_index)); + } + + /* stereo_mixdown_present */ + audaac_rec_install_bits(audaac_header_adif, + AAC_STEREO_MIXDOWN_PRESENT_SIZE, + dummy, + &(audaac_hdr_bit_index)); + + if (dummy) { + audaac_rec_install_bits(audaac_header_adif, + AAC_STEREO_MIXDOWN_ELEMENT_SIZE, + dummy, + &(audaac_hdr_bit_index)); + } + + /* matrix_mixdown_idx_present, 1 bit */ + audaac_rec_install_bits(audaac_header_adif, + AAC_MATRIX_MIXDOWN_PRESENT_SIZE, + dummy, + &(audaac_hdr_bit_index)); + + if (dummy) { + audaac_rec_install_bits(audaac_header_adif, + AAC_MATRIX_MIXDOWN_SIZE, + dummy, + &(audaac_hdr_bit_index)); + } + if(m_aac_param.nChannels == 2) + value = 16; + else + value = 0; + for (i=0; i> 3; + bit_index = (*hdr_bit_index) & 0x07; + + bits_avail_in_byte = (OMX_U8)(8 - bit_index); + + num_to_copy = min(bits_avail_in_byte, num_remaining); + + byte_to_copy = (OMX_U8)((OMX_U8)((value >> (num_remaining - num_to_copy)) & 0xFF) << + (bits_avail_in_byte - num_to_copy)); + + input[byte_index] &= ((OMX_U8)(bit_mask << bits_avail_in_byte)); + input[byte_index] |= byte_to_copy; + + *hdr_bit_index = (OMX_U16)(*hdr_bit_index + num_to_copy); + + num_remaining = (OMX_U8)(num_remaining - num_to_copy); + } +} +void omx_aac_aenc::audaac_rec_install_mp4ff_header_variable (OMX_U16 byte_num, + OMX_U32 sample_index, + OMX_U8 channel_config) +{ + OMX_U16 audaac_hdr_bit_index; + (void)byte_num; + audaac_header_mp4ff[0] = 0; + audaac_header_mp4ff[1] = 0; + audaac_hdr_bit_index = 0; + + /* Audio object type, 5 bit */ + audaac_rec_install_bits(audaac_header_mp4ff, + AUDAAC_MP4FF_OBJ_TYPE, + 2, + &(audaac_hdr_bit_index)); + + /* Frequency index, 4 bit */ + audaac_rec_install_bits(audaac_header_mp4ff, + AUDAAC_MP4FF_FREQ_IDX, + (OMX_U32)sample_index, + &(audaac_hdr_bit_index)); + + /* Channel config filed, 4 bit */ + audaac_rec_install_bits(audaac_header_mp4ff, + AUDAAC_MP4FF_CH_CONFIG, + channel_config, + &(audaac_hdr_bit_index)); + +} + +int omx_aac_aenc::get_updated_bit_rate(int bitrate) +{ + int updated_rate, min_bitrate, max_bitrate; + + max_bitrate = m_aac_param.nSampleRate * + MAX_BITRATE_MULFACTOR; + switch(m_aac_param.eAACProfile) + { + case OMX_AUDIO_AACObjectLC: + min_bitrate = m_aac_param.nSampleRate; + if (m_aac_param.nChannels == 1) { + min_bitrate = min_bitrate/BITRATE_DIVFACTOR; + max_bitrate = max_bitrate/BITRATE_DIVFACTOR; + } + break; + case OMX_AUDIO_AACObjectHE: + min_bitrate = MIN_BITRATE; + if (m_aac_param.nChannels == 1) + max_bitrate = max_bitrate/BITRATE_DIVFACTOR; + break; + case OMX_AUDIO_AACObjectHE_PS: + min_bitrate = MIN_BITRATE; + break; + default: + return bitrate; + break; + } + /* Update MIN and MAX values*/ + if (min_bitrate > MIN_BITRATE) + min_bitrate = MIN_BITRATE; + if (max_bitrate > MAX_BITRATE) + max_bitrate = MAX_BITRATE; + /* Update the bitrate in the range */ + if (bitrate < min_bitrate) + updated_rate = min_bitrate; + else if(bitrate > max_bitrate) + updated_rate = max_bitrate; + else + updated_rate = bitrate; + return updated_rate; +} diff --git a/audio/mm-audio/aenc-aac/qdsp6/test/omx_aac_enc_test.c b/audio/mm-audio/aenc-aac/qdsp6/test/omx_aac_enc_test.c new file mode 100644 index 0000000..174cde6 --- /dev/null +++ b/audio/mm-audio/aenc-aac/qdsp6/test/omx_aac_enc_test.c @@ -0,0 +1,1293 @@ +/*-------------------------------------------------------------------------- +Copyright (c) 2010-2014, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of The Linux Foundation nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------*/ + + +/* + An Open max test application .... +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "OMX_Core.h" +#include "OMX_Component.h" +#include "pthread.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "QOMX_AudioExtensions.h" +#include "QOMX_AudioIndexExtensions.h" +#ifdef AUDIOV2 +#include "control.h" +#endif +#include + +typedef unsigned char uint8; +typedef unsigned char byte; +typedef unsigned int uint32; +typedef unsigned int uint16; +#define AUDAAC_MAX_ADIF_HEADER_LENGTH 64 +/* ADTS variable frame header, frame length field */ +#define AUDAAC_ADTS_FRAME_LENGTH_SIZE 13 +QOMX_AUDIO_STREAM_INFO_DATA streaminfoparam; +void audaac_rec_install_bits +( + uint8 *input, + byte num_bits_reqd, + uint32 value, + uint16 *hdr_bit_index +); + +/* maximum ADTS frame header length */ +#define AUDAAC_MAX_ADTS_HEADER_LENGTH 7 +void audaac_rec_install_adts_header_variable (uint16 byte_num); +void Release_Encoder(); + +#ifdef AUDIOV2 +unsigned short session_id; +int device_id; +int control = 0; +const char *device="handset_tx"; +#define DIR_TX 2 +#endif + +#define AACHDR_LAYER_SIZE 2 +#define AACHDR_CRC_SIZE 1 +#define AAC_PROFILE_SIZE 2 +#define AAC_SAMPLING_FREQ_INDEX_SIZE 4 +#define AAC_ORIGINAL_COPY_SIZE 1 +#define AAC_HOME_SIZE 1 + +#define MIN(A,B) (((A) < (B))?(A):(B)) + +uint8 audaac_header[AUDAAC_MAX_ADTS_HEADER_LENGTH]; +unsigned int audaac_hdr_bit_index; + + +FILE *F1 = NULL; + +uint32_t samplerate = 44100; +uint32_t channels = 2; +uint32_t bitrate = 128000; +uint32_t pcmplayback = 0; +uint32_t tunnel = 0; +uint32_t rectime = 0; +uint32_t format = 1; +uint32_t profile = OMX_AUDIO_AACObjectLC; +#define DEBUG_PRINT printf +unsigned to_idle_transition = 0; + +typedef enum adts_sample_index__ { + +ADTS_SAMPLE_INDEX_96000=0x0, +ADTS_SAMPLE_INDEX_88200, +ADTS_SAMPLE_INDEX_64000, +ADTS_SAMPLE_INDEX_48000, +ADTS_SAMPLE_INDEX_44100, +ADTS_SAMPLE_INDEX_32000, +ADTS_SAMPLE_INDEX_24000, +ADTS_SAMPLE_INDEX_22050, +ADTS_SAMPLE_INDEX_16000, +ADTS_SAMPLE_INDEX_12000, +ADTS_SAMPLE_INDEX_11025, +ADTS_SAMPLE_INDEX_8000, +ADTS_SAMPLE_INDEX_7350, +ADTS_SAMPLE_INDEX_MAX + +}adts_sample_index; +/************************************************************************/ +/* #DEFINES */ +/************************************************************************/ +#define false 0 +#define true 1 + +#define CONFIG_VERSION_SIZE(param) \ + param.nVersion.nVersion = CURRENT_OMX_SPEC_VERSION;\ + param.nSize = sizeof(param); + +#define FAILED(result) (result != OMX_ErrorNone) + +#define SUCCEEDED(result) (result == OMX_ErrorNone) + +/************************************************************************/ +/* GLOBAL DECLARATIONS */ +/************************************************************************/ + +pthread_mutex_t lock; +pthread_cond_t cond; +pthread_mutex_t elock; +pthread_cond_t econd; +pthread_cond_t fcond; +pthread_mutex_t etb_lock; +pthread_mutex_t etb_lock1; +pthread_cond_t etb_cond; +FILE * inputBufferFile; +FILE * outputBufferFile; +OMX_PARAM_PORTDEFINITIONTYPE inputportFmt; +OMX_PARAM_PORTDEFINITIONTYPE outputportFmt; +OMX_AUDIO_PARAM_AACPROFILETYPE aacparam; +OMX_AUDIO_PARAM_PCMMODETYPE pcmparam; +OMX_PORT_PARAM_TYPE portParam; +OMX_ERRORTYPE error; + + + + +#define ID_RIFF 0x46464952 +#define ID_WAVE 0x45564157 +#define ID_FMT 0x20746d66 +#define ID_DATA 0x61746164 + +#define FORMAT_PCM 1 + +struct wav_header { + uint32_t riff_id; + uint32_t riff_sz; + uint32_t riff_fmt; + uint32_t fmt_id; + uint32_t fmt_sz; + uint16_t audio_format; + uint16_t num_channels; + uint32_t sample_rate; + uint32_t byte_rate; /* sample_rate * num_channels * bps / 8 */ + uint16_t block_align; /* num_channels * bps / 8 */ + uint16_t bits_per_sample; + uint32_t data_id; + uint32_t data_sz; +}; +struct enc_meta_out{ + unsigned int offset_to_frame; + unsigned int frame_size; + unsigned int encoded_pcm_samples; + unsigned int msw_ts; + unsigned int lsw_ts; + unsigned int nflags; +} __attribute__ ((packed)); + +static int totaldatalen = 0; +/************************************************************************/ +/* GLOBAL INIT */ +/************************************************************************/ + +unsigned int input_buf_cnt = 0; +unsigned int output_buf_cnt = 0; +int used_ip_buf_cnt = 0; +volatile int event_is_done = 0; +volatile int ebd_event_is_done = 0; +volatile int fbd_event_is_done = 0; +volatile int etb_event_is_done = 0; +int ebd_cnt; +int bInputEosReached = 0; +int bOutputEosReached = 0; +int bInputEosReached_tunnel = 0; +static int etb_done = 0; +int bFlushing = false; +int bPause = false; +const char *in_filename; +const char *out_filename; + +int timeStampLfile = 0; +int timestampInterval = 100; + +//* OMX Spec Version supported by the wrappers. Version = 1.1 */ +const OMX_U32 CURRENT_OMX_SPEC_VERSION = 0x00000101; +OMX_COMPONENTTYPE* aac_enc_handle = 0; + +OMX_BUFFERHEADERTYPE **pInputBufHdrs = NULL; +OMX_BUFFERHEADERTYPE **pOutputBufHdrs = NULL; + +/************************************************************************/ +/* GLOBAL FUNC DECL */ +/************************************************************************/ +int Init_Encoder(char*); +int Play_Encoder(); +OMX_STRING aud_comp; +/**************************************************************************/ +/* STATIC DECLARATIONS */ +/**************************************************************************/ + +static int open_audio_file (); +static int Read_Buffer(OMX_BUFFERHEADERTYPE *pBufHdr ); +static OMX_ERRORTYPE Allocate_Buffer ( OMX_COMPONENTTYPE *aac_enc_handle, + OMX_BUFFERHEADERTYPE ***pBufHdrs, + OMX_U32 nPortIndex, + unsigned int bufCntMin, unsigned int bufSize); + + +static OMX_ERRORTYPE EventHandler(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_EVENTTYPE eEvent, + OMX_IN OMX_U32 nData1, OMX_IN OMX_U32 nData2, + OMX_IN OMX_PTR pEventData); +static OMX_ERRORTYPE EmptyBufferDone(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer); + +static OMX_ERRORTYPE FillBufferDone(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer); +static OMX_ERRORTYPE parse_pcm_header(); +void wait_for_event(void) +{ + pthread_mutex_lock(&lock); + DEBUG_PRINT("%s: event_is_done=%d", __FUNCTION__, event_is_done); + while (event_is_done == 0) { + pthread_cond_wait(&cond, &lock); + } + event_is_done = 0; + pthread_mutex_unlock(&lock); +} + +void event_complete(void ) +{ + pthread_mutex_lock(&lock); + if (event_is_done == 0) { + event_is_done = 1; + pthread_cond_broadcast(&cond); + } + pthread_mutex_unlock(&lock); +} + +void etb_wait_for_event(void) +{ + pthread_mutex_lock(&etb_lock1); + DEBUG_PRINT("%s: etb_event_is_done=%d", __FUNCTION__, etb_event_is_done); + while (etb_event_is_done == 0) { + pthread_cond_wait(&etb_cond, &etb_lock1); + } + etb_event_is_done = 0; + pthread_mutex_unlock(&etb_lock1); +} + +void etb_event_complete(void ) +{ + pthread_mutex_lock(&etb_lock1); + if (etb_event_is_done == 0) { + etb_event_is_done = 1; + pthread_cond_broadcast(&etb_cond); + } + pthread_mutex_unlock(&etb_lock1); +} + + +OMX_ERRORTYPE EventHandler(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_EVENTTYPE eEvent, + OMX_IN OMX_U32 nData1, OMX_IN OMX_U32 nData2, + OMX_IN OMX_PTR pEventData) +{ + DEBUG_PRINT("Function %s \n", __FUNCTION__); + /* To remove warning for unused variable to keep prototype same */ + (void)hComponent; + (void)pAppData; + (void)pEventData; + + switch(eEvent) { + case OMX_EventCmdComplete: + DEBUG_PRINT("\n OMX_EventCmdComplete event=%d data1=%u data2=%u\n",(OMX_EVENTTYPE)eEvent, + nData1,nData2); + event_complete(); + break; + case OMX_EventError: + DEBUG_PRINT("\n OMX_EventError \n"); + break; + case OMX_EventBufferFlag: + DEBUG_PRINT("\n OMX_EventBufferFlag \n"); + bOutputEosReached = true; + event_complete(); + break; + case OMX_EventPortSettingsChanged: + DEBUG_PRINT("\n OMX_EventPortSettingsChanged \n"); + break; + default: + DEBUG_PRINT("\n Unknown Event \n"); + break; + } + return OMX_ErrorNone; +} + +OMX_ERRORTYPE FillBufferDone(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) +{ + size_t bytes_writen = 0; + size_t total_bytes_writen = 0; + size_t len = 0; + struct enc_meta_out *meta = NULL; + OMX_U8 *src = pBuffer->pBuffer; + unsigned int num_of_frames = 1; + + /* To remove warning for unused variable to keep prototype same */ + (void)pAppData; + + if(((pBuffer->nFlags & OMX_BUFFERFLAG_EOS) == OMX_BUFFERFLAG_EOS)) { + DEBUG_PRINT("FBD::EOS on output port\n "); + bOutputEosReached = true; + return OMX_ErrorNone; + } + if(bInputEosReached_tunnel || bOutputEosReached) + { + DEBUG_PRINT("EOS REACHED NO MORE PROCESSING OF BUFFERS\n"); + return OMX_ErrorNone; + } + if(num_of_frames != src[0]){ + + printf("Data corrupt\n"); + return OMX_ErrorNone; + } + /* Skip the first bytes */ + + + + src += sizeof(unsigned char); + meta = (struct enc_meta_out *)src; + while (num_of_frames > 0) { + meta = (struct enc_meta_out *)src; + /*printf("offset=%d framesize=%d encoded_pcm[%d] msw_ts[%d]lsw_ts[%d] nflags[%d]\n", + meta->offset_to_frame, + meta->frame_size, + meta->encoded_pcm_samples, meta->msw_ts, meta->lsw_ts, meta->nflags);*/ + len = meta->frame_size; + + if(format == 6) + { + audaac_rec_install_adts_header_variable((uint16_t)(len + AUDAAC_MAX_ADTS_HEADER_LENGTH)); + bytes_writen = fwrite(audaac_header,1,AUDAAC_MAX_ADTS_HEADER_LENGTH,outputBufferFile); + if(bytes_writen < AUDAAC_MAX_ADTS_HEADER_LENGTH) + { + DEBUG_PRINT("error: invalid adts header length\n"); + return OMX_ErrorNone; + } + } + bytes_writen = fwrite(pBuffer->pBuffer + sizeof(unsigned char) + meta->offset_to_frame,1,len,outputBufferFile); + if(bytes_writen < len) + { + DEBUG_PRINT("error: invalid AAC encoded data \n"); + return OMX_ErrorNone; + } + src += sizeof(struct enc_meta_out); + num_of_frames--; + total_bytes_writen += len; + } + DEBUG_PRINT(" FillBufferDone size writen to file %zu\n",total_bytes_writen); + totaldatalen = totaldatalen + (int)total_bytes_writen; + + DEBUG_PRINT(" FBD calling FTB\n"); + OMX_FillThisBuffer(hComponent,pBuffer); + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE EmptyBufferDone(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) +{ + int readBytes =0; + + /* To remove warning for unused variable to keep prototype same */ + (void)pAppData; + + ebd_cnt++; + used_ip_buf_cnt--; + pthread_mutex_lock(&etb_lock); + if(!etb_done) + { + DEBUG_PRINT("\n*********************************************\n"); + DEBUG_PRINT("Wait till first set of buffers are given to component\n"); + DEBUG_PRINT("\n*********************************************\n"); + etb_done++; + pthread_mutex_unlock(&etb_lock); + etb_wait_for_event(); + } + else + { + pthread_mutex_unlock(&etb_lock); + } + + + if(bInputEosReached) + { + DEBUG_PRINT("\n*********************************************\n"); + DEBUG_PRINT(" EBD::EOS on input port\n "); + DEBUG_PRINT("*********************************************\n"); + return OMX_ErrorNone; + }else if (bFlushing == true) { + DEBUG_PRINT("omx_aac_adec_test: bFlushing is set to TRUE used_ip_buf_cnt=%d\n",used_ip_buf_cnt); + if (used_ip_buf_cnt == 0) { + bFlushing = false; + } else { + DEBUG_PRINT("omx_aac_adec_test: more buffer to come back used_ip_buf_cnt=%d\n",used_ip_buf_cnt); + return OMX_ErrorNone; + } + } + + if((readBytes = Read_Buffer(pBuffer)) > 0) { + pBuffer->nFilledLen = (OMX_U32)readBytes; + used_ip_buf_cnt++; + OMX_EmptyThisBuffer(hComponent,pBuffer); + } + else{ + pBuffer->nFlags |= OMX_BUFFERFLAG_EOS; + used_ip_buf_cnt++; + bInputEosReached = true; + pBuffer->nFilledLen = 0; + OMX_EmptyThisBuffer(hComponent,pBuffer); + DEBUG_PRINT("EBD..Either EOS or Some Error while reading file\n"); + } + return OMX_ErrorNone; +} + +void signal_handler(int sig_id) { + + /* Flush */ + if (sig_id == SIGUSR1) { + DEBUG_PRINT("%s Initiate flushing\n", __FUNCTION__); + bFlushing = true; + OMX_SendCommand(aac_enc_handle, OMX_CommandFlush, OMX_ALL, NULL); + } else if (sig_id == SIGUSR2) { + if (bPause == true) { + DEBUG_PRINT("%s resume playback\n", __FUNCTION__); + bPause = false; + OMX_SendCommand(aac_enc_handle, OMX_CommandStateSet, OMX_StateExecuting, NULL); + } else { + DEBUG_PRINT("%s pause playback\n", __FUNCTION__); + bPause = true; + OMX_SendCommand(aac_enc_handle, OMX_CommandStateSet, OMX_StatePause, NULL); + } + } +} + +int main(int argc, char **argv) +{ + unsigned int bufCnt=0; + OMX_ERRORTYPE result; + + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = &signal_handler; + sigaction(SIGABRT, &sa, NULL); + sigaction(SIGUSR1, &sa, NULL); + sigaction(SIGUSR2, &sa, NULL); + + (void) signal(SIGINT, Release_Encoder); + + pthread_cond_init(&cond, 0); + pthread_mutex_init(&lock, 0); + pthread_cond_init(&etb_cond, 0); + pthread_mutex_init(&etb_lock, 0); + pthread_mutex_init(&etb_lock1, 0); + + if (argc >= 9) { + in_filename = argv[1]; + out_filename = argv[2]; + samplerate = (uint32_t)atoi(argv[3]); + channels = (uint32_t)atoi(argv[4]); + tunnel = (uint32_t)atoi(argv[5]); + rectime = (uint32_t)atoi(argv[6]); + bitrate = (uint32_t)atoi(argv[7]); + format = (uint32_t)atoi(argv[8]); + profile = (uint32_t)atoi(argv[9]); + + DEBUG_PRINT("Input parameters: samplerate = %d, channels = %d, tunnel = %d," + " rectime = %d, bitrate = %d, format = %d, profile = %d\n", + samplerate, channels, tunnel, rectime, bitrate, format, profile); + + if (!((profile == 2) || (profile == 5) || (profile == 29))) { + DEBUG_PRINT("profile = %d, not supported. Supported " + "profile values are AAC_LC(2), AAC+(5), EAAC+(29)\n", profile); + return 0; + } + if (!((format == 1) || (format == 6))) { + DEBUG_PRINT("format = %d, not supported. Supported " + "formats are ADTS(1), RAW(6)\n", format); + return 0; + } + if ((channels > 2) || (channels <= 0)) { + DEBUG_PRINT("channels = %d, not supported. Supported " + "number of channels are 1 and 2\n", channels); + return 0; + } + if ((samplerate < 8000) && (samplerate > 48000)) { + DEBUG_PRINT("samplerate = %d, not supported, Supported " + "samplerates are 8000, 11025, 12000, 16000, 22050, " + "24000, 32000, 44100, 48000\n", samplerate); + return 0; + } else { + if ((profile == 5) || (profile == 29)) { + if (samplerate < 24000) { + DEBUG_PRINT("samplerate = %d, not supported for AAC+/EAAC+." + " Supported samplerates are 24000, 32000," + " 44100, 48000\n", samplerate); + return 0; + } + } + } + } else { + DEBUG_PRINT(" invalid format: \n"); + DEBUG_PRINT("ex: ./mm-aenc-omxaac INPUTFILE AAC_OUTPUTFILE SAMPFREQ CHANNEL TUNNEL RECORDTIME BITRATE FORMAT PROFILE\n"); + DEBUG_PRINT("FOR TUNNEL MOD PASS INPUT FILE AS ZERO\n"); + DEBUG_PRINT("RECORDTIME in seconds for AST Automation ...TUNNEL MODE ONLY\n"); + DEBUG_PRINT("FORMAT::ADTS(1), RAW(6)\n"); + DEBUG_PRINT("BITRATE in bits/sec \n"); + DEBUG_PRINT("PROFILE::AAC_LC(2), AAC+(5), EAAC+(29)\n"); + return 0; + } + if(tunnel == 0) + aud_comp = "OMX.qcom.audio.encoder.aac"; + else + aud_comp = "OMX.qcom.audio.encoder.tunneled.aac"; + if(Init_Encoder(aud_comp)!= 0x00) + { + DEBUG_PRINT("Decoder Init failed\n"); + return -1; + } + + fcntl(0, F_SETFL, O_NONBLOCK); + + if(Play_Encoder() != 0x00) + { + DEBUG_PRINT("Play_Decoder failed\n"); + return -1; + } + + // Wait till EOS is reached... + if(rectime && tunnel) + { + sleep(rectime); + rectime = 0; + bInputEosReached_tunnel = 1; + DEBUG_PRINT("\EOS ON INPUT PORT\n"); + } + else + { + wait_for_event(); + } + + if((bInputEosReached_tunnel) || ((bOutputEosReached) && !tunnel)) + { + + DEBUG_PRINT("\nMoving the decoder to idle state \n"); + OMX_SendCommand(aac_enc_handle, OMX_CommandStateSet, OMX_StateIdle,0); + wait_for_event(); + DEBUG_PRINT("\nMoving the encoder to loaded state \n"); + OMX_SendCommand(aac_enc_handle, OMX_CommandStateSet, OMX_StateLoaded,0); + sleep(1); + if (!tunnel) + { + DEBUG_PRINT("\nFillBufferDone: Deallocating i/p buffers \n"); + for(bufCnt=0; bufCnt < input_buf_cnt; ++bufCnt) { + OMX_FreeBuffer(aac_enc_handle, 0, pInputBufHdrs[bufCnt]); + } + } + + DEBUG_PRINT ("\nFillBufferDone: Deallocating o/p buffers \n"); + for(bufCnt=0; bufCnt < output_buf_cnt; ++bufCnt) { + OMX_FreeBuffer(aac_enc_handle, 1, pOutputBufHdrs[bufCnt]); + } + wait_for_event(); + + result = OMX_FreeHandle(aac_enc_handle); + if (result != OMX_ErrorNone) { + DEBUG_PRINT ("\nOMX_FreeHandle error. Error code: %d\n", result); + } + /* Deinit OpenMAX */ + if(tunnel) + { + #ifdef AUDIOV2 + if (msm_route_stream(DIR_TX,session_id,device_id, 0)) + { + DEBUG_PRINT("\ncould not set stream routing\n"); + return -1; + } + if (msm_en_device(device_id, 0)) + { + DEBUG_PRINT("\ncould not enable device\n"); + return -1; + } + msm_mixer_close(); + #endif + } + OMX_Deinit(); + ebd_cnt=0; + bOutputEosReached = false; + bInputEosReached_tunnel = false; + bInputEosReached = 0; + aac_enc_handle = NULL; + pthread_cond_destroy(&cond); + pthread_mutex_destroy(&lock); + fclose(outputBufferFile); + DEBUG_PRINT("*****************************************\n"); + DEBUG_PRINT("******...AAC ENC TEST COMPLETED...***************\n"); + DEBUG_PRINT("*****************************************\n"); + } + return 0; +} + +void Release_Encoder() +{ + static int cnt=0; + OMX_ERRORTYPE result; + + DEBUG_PRINT("END OF AAC ENCODING: EXITING PLEASE WAIT\n"); + bInputEosReached_tunnel = 1; + event_complete(); + cnt++; + if(cnt > 1) + { + /* FORCE RESET */ + aac_enc_handle = NULL; + ebd_cnt=0; + bInputEosReached_tunnel = false; + + result = OMX_FreeHandle(aac_enc_handle); + if (result != OMX_ErrorNone) { + DEBUG_PRINT ("\nOMX_FreeHandle error. Error code: %d\n", result); + } + + /* Deinit OpenMAX */ + + OMX_Deinit(); + + pthread_cond_destroy(&cond); + pthread_mutex_destroy(&lock); + DEBUG_PRINT("*****************************************\n"); + DEBUG_PRINT("******...AAC ENC TEST COMPLETED...***************\n"); + DEBUG_PRINT("*****************************************\n"); + exit(0); + } +} + +int Init_Encoder(OMX_STRING audio_component) +{ + DEBUG_PRINT("Inside %s \n", __FUNCTION__); + OMX_ERRORTYPE omxresult; + OMX_U32 total = 0; + typedef OMX_U8* OMX_U8_PTR; + char *role ="audio_encoder"; + + static OMX_CALLBACKTYPE call_back = { + &EventHandler,&EmptyBufferDone,&FillBufferDone + }; + + /* Init. the OpenMAX Core */ + DEBUG_PRINT("\nInitializing OpenMAX Core....\n"); + omxresult = OMX_Init(); + + if(OMX_ErrorNone != omxresult) { + DEBUG_PRINT("\n Failed to Init OpenMAX core"); + return -1; + } + else { + DEBUG_PRINT("\nOpenMAX Core Init Done\n"); + } + + /* Query for audio decoders*/ + DEBUG_PRINT("Aac_test: Before entering OMX_GetComponentOfRole"); + OMX_GetComponentsOfRole(role, &total, 0); + DEBUG_PRINT ("\nTotal components of role=%s :%u", role, total); + + + omxresult = OMX_GetHandle((OMX_HANDLETYPE*)(&aac_enc_handle), + (OMX_STRING)audio_component, NULL, &call_back); + if (FAILED(omxresult)) { + DEBUG_PRINT("\nFailed to Load the component:%s\n", audio_component); + return -1; + } + else + { + DEBUG_PRINT("\nComponent %s is in LOADED state\n", audio_component); + } + + /* Get the port information */ + CONFIG_VERSION_SIZE(portParam); + omxresult = OMX_GetParameter(aac_enc_handle, OMX_IndexParamAudioInit, + (OMX_PTR)&portParam); + + if(FAILED(omxresult)) { + DEBUG_PRINT("\nFailed to get Port Param\n"); + return -1; + } + else + { + DEBUG_PRINT("\nportParam.nPorts:%u\n", portParam.nPorts); + DEBUG_PRINT("\nportParam.nStartPortNumber:%u\n", + portParam.nStartPortNumber); + } + return 0; +} + +int Play_Encoder() +{ + unsigned int i; + int Size=0; + DEBUG_PRINT("Inside %s \n", __FUNCTION__); + OMX_ERRORTYPE ret; + OMX_INDEXTYPE index; +#ifdef __LP64__ + DEBUG_PRINT("sizeof[%ld]\n", sizeof(OMX_BUFFERHEADERTYPE)); +#else + DEBUG_PRINT("sizeof[%d]\n", sizeof(OMX_BUFFERHEADERTYPE)); +#endif + + /* open the i/p and o/p files based on the video file format passed */ + if(open_audio_file()) { + DEBUG_PRINT("\n Returning -1"); + return -1; + } + + /* Query the encoder input min buf requirements */ + CONFIG_VERSION_SIZE(inputportFmt); + + /* Port for which the Client needs to obtain info */ + inputportFmt.nPortIndex = portParam.nStartPortNumber; + + OMX_GetParameter(aac_enc_handle,OMX_IndexParamPortDefinition,&inputportFmt); + DEBUG_PRINT ("\nEnc Input Buffer Count %u\n", inputportFmt.nBufferCountMin); + DEBUG_PRINT ("\nEnc: Input Buffer Size %u\n", inputportFmt.nBufferSize); + + if(OMX_DirInput != inputportFmt.eDir) { + DEBUG_PRINT ("\nEnc: Expect Input Port\n"); + return -1; + } + + pcmparam.nPortIndex = 0; + pcmparam.nChannels = channels; + pcmparam.nSamplingRate = samplerate; + OMX_SetParameter(aac_enc_handle,OMX_IndexParamAudioPcm,&pcmparam); + + + /* Query the encoder outport's min buf requirements */ + CONFIG_VERSION_SIZE(outputportFmt); + /* Port for which the Client needs to obtain info */ + outputportFmt.nPortIndex = portParam.nStartPortNumber + 1; + + OMX_GetParameter(aac_enc_handle,OMX_IndexParamPortDefinition,&outputportFmt); + DEBUG_PRINT ("\nEnc: Output Buffer Count %u\n", outputportFmt.nBufferCountMin); + DEBUG_PRINT ("\nEnc: Output Buffer Size %u\n", outputportFmt.nBufferSize); + + if(OMX_DirOutput != outputportFmt.eDir) { + DEBUG_PRINT ("\nEnc: Expect Output Port\n"); + return -1; + } + + + CONFIG_VERSION_SIZE(aacparam); + + + aacparam.nPortIndex = 1; + aacparam.nChannels = channels; //2 ; /* 1-> mono 2-> stereo*/ + aacparam.nBitRate = bitrate; + aacparam.nSampleRate = samplerate; + aacparam.eChannelMode = OMX_AUDIO_ChannelModeStereo; + aacparam.eAACStreamFormat = (OMX_AUDIO_AACSTREAMFORMATTYPE)format; + aacparam.eAACProfile = (OMX_AUDIO_AACPROFILETYPE)profile; + OMX_SetParameter(aac_enc_handle,OMX_IndexParamAudioAac,&aacparam); + OMX_GetExtensionIndex(aac_enc_handle,"OMX.Qualcomm.index.audio.sessionId",&index); + OMX_GetParameter(aac_enc_handle,index,&streaminfoparam); + if(tunnel) + { + #ifdef AUDIOV2 + session_id = streaminfoparam.sessionId; + control = msm_mixer_open("/dev/snd/controlC0", 0); + if(control < 0) + printf("ERROR opening the device\n"); + device_id = msm_get_device(device); + DEBUG_PRINT ("\ndevice_id = %d\n",device_id); + DEBUG_PRINT("\nsession_id = %d\n",session_id); + if (msm_en_device(device_id, 1)) + { + perror("could not enable device\n"); + return -1; + } + + if (msm_route_stream(DIR_TX,session_id,device_id, 1)) + { + perror("could not set stream routing\n"); + return -1; + } + #endif + } + DEBUG_PRINT ("\nOMX_SendCommand Encoder -> IDLE\n"); + OMX_SendCommand(aac_enc_handle, OMX_CommandStateSet, OMX_StateIdle,0); + /* wait_for_event(); should not wait here event complete status will + not come until enough buffer are allocated */ + if (tunnel == 0) + { + input_buf_cnt = inputportFmt.nBufferCountActual; // inputportFmt.nBufferCountMin + 5; + DEBUG_PRINT("Transition to Idle State succesful...\n"); + /* Allocate buffer on decoder's i/p port */ + error = Allocate_Buffer(aac_enc_handle, &pInputBufHdrs, inputportFmt.nPortIndex, + input_buf_cnt, inputportFmt.nBufferSize); + if (error != OMX_ErrorNone || pInputBufHdrs == NULL) { + DEBUG_PRINT ("\nOMX_AllocateBuffer Input buffer error\n"); + return -1; + } + else { + DEBUG_PRINT ("\nOMX_AllocateBuffer Input buffer success\n"); + } + } + output_buf_cnt = outputportFmt.nBufferCountMin ; + + /* Allocate buffer on encoder's O/Pp port */ + error = Allocate_Buffer(aac_enc_handle, &pOutputBufHdrs, outputportFmt.nPortIndex, + output_buf_cnt, outputportFmt.nBufferSize); + if (error != OMX_ErrorNone || pOutputBufHdrs == NULL) { + DEBUG_PRINT ("\nOMX_AllocateBuffer Output buffer error\n"); + return -1; + } + else { + DEBUG_PRINT ("\nOMX_AllocateBuffer Output buffer success\n"); + } + + wait_for_event(); + + + if (tunnel == 1) + { + DEBUG_PRINT ("\nOMX_SendCommand to enable TUNNEL MODE during IDLE\n"); + OMX_SendCommand(aac_enc_handle, OMX_CommandPortDisable,0,0); // disable input port + wait_for_event(); + } + + DEBUG_PRINT ("\nOMX_SendCommand encoder -> Executing\n"); + OMX_SendCommand(aac_enc_handle, OMX_CommandStateSet, OMX_StateExecuting,0); + wait_for_event(); + + DEBUG_PRINT(" Start sending OMX_FILLthisbuffer\n"); + + for(i=0; i < output_buf_cnt; i++) { + DEBUG_PRINT ("\nOMX_FillThisBuffer on output buf no.%d\n",i); + pOutputBufHdrs[i]->nOutputPortIndex = 1; + pOutputBufHdrs[i]->nFlags = pOutputBufHdrs[i]->nFlags & (unsigned)~OMX_BUFFERFLAG_EOS; + ret = OMX_FillThisBuffer(aac_enc_handle, pOutputBufHdrs[i]); + if (OMX_ErrorNone != ret) { + DEBUG_PRINT("OMX_FillThisBuffer failed with result %d\n", ret); + } + else { + DEBUG_PRINT("OMX_FillThisBuffer success!\n"); + } + } + +if(tunnel == 0) +{ + DEBUG_PRINT(" Start sending OMX_emptythisbuffer\n"); + for (i = 0;i < input_buf_cnt;i++) { + DEBUG_PRINT ("\nOMX_EmptyThisBuffer on Input buf no.%d\n",i); + pInputBufHdrs[i]->nInputPortIndex = 0; + Size = Read_Buffer(pInputBufHdrs[i]); + if(Size <=0 ){ + DEBUG_PRINT("NO DATA READ\n"); + bInputEosReached = true; + pInputBufHdrs[i]->nFlags= OMX_BUFFERFLAG_EOS; + } + pInputBufHdrs[i]->nFilledLen = (OMX_U32)Size; + pInputBufHdrs[i]->nInputPortIndex = 0; + used_ip_buf_cnt++; + ret = OMX_EmptyThisBuffer(aac_enc_handle, pInputBufHdrs[i]); + if (OMX_ErrorNone != ret) { + DEBUG_PRINT("OMX_EmptyThisBuffer failed with result %d\n", ret); + } + else { + DEBUG_PRINT("OMX_EmptyThisBuffer success!\n"); + } + if(Size <=0 ){ + break;//eos reached + } + } + pthread_mutex_lock(&etb_lock); + if(etb_done) +{ + DEBUG_PRINT("Component is waiting for EBD to be released.\n"); + etb_event_complete(); + } + else + { + DEBUG_PRINT("\n****************************\n"); + DEBUG_PRINT("EBD not yet happened ...\n"); + DEBUG_PRINT("\n****************************\n"); + etb_done++; + } + pthread_mutex_unlock(&etb_lock); +} + + return 0; +} + + + +static OMX_ERRORTYPE Allocate_Buffer ( OMX_COMPONENTTYPE *avc_enc_handle, + OMX_BUFFERHEADERTYPE ***pBufHdrs, + OMX_U32 nPortIndex, + unsigned int bufCntMin, unsigned int bufSize) +{ + DEBUG_PRINT("Inside %s \n", __FUNCTION__); + OMX_ERRORTYPE error=OMX_ErrorNone; + unsigned int bufCnt=0; + /* To remove warning for unused variable to keep prototype same */ + (void)avc_enc_handle; + + *pBufHdrs= (OMX_BUFFERHEADERTYPE **) + malloc(sizeof(OMX_BUFFERHEADERTYPE*)*bufCntMin); + + for(bufCnt=0; bufCnt < bufCntMin; ++bufCnt) { + DEBUG_PRINT("\n OMX_AllocateBuffer No %d \n", bufCnt); + error = OMX_AllocateBuffer(aac_enc_handle, &((*pBufHdrs)[bufCnt]), + nPortIndex, NULL, bufSize); + } + + return error; +} + + + + +static int Read_Buffer (OMX_BUFFERHEADERTYPE *pBufHdr ) +{ + + size_t bytes_read=0; + + + pBufHdr->nFilledLen = 0; + pBufHdr->nFlags |= OMX_BUFFERFLAG_EOS; + + bytes_read = fread(pBufHdr->pBuffer, 1, pBufHdr->nAllocLen , inputBufferFile); + + pBufHdr->nFilledLen = (OMX_U32)bytes_read; + if(bytes_read == 0) + { + + pBufHdr->nFlags |= OMX_BUFFERFLAG_EOS; + DEBUG_PRINT ("\nBytes read zero\n"); + } + else + { + pBufHdr->nFlags = pBufHdr->nFlags & (unsigned)~OMX_BUFFERFLAG_EOS; + } + + return (int)bytes_read; +} + + + +//In Encoder this Should Open a PCM or WAV file for input. + +static int open_audio_file () +{ + int error_code = 0; + + if (!tunnel) + { + DEBUG_PRINT("Inside %s filename=%s\n", __FUNCTION__, in_filename); + inputBufferFile = fopen (in_filename, "rb"); + if (inputBufferFile == NULL) { + DEBUG_PRINT("\ni/p file %s could NOT be opened\n", + in_filename); + error_code = -1; + } + if(parse_pcm_header() != 0x00) + { + DEBUG_PRINT("PCM parser failed \n"); + return -1; + } + } + + DEBUG_PRINT("Inside %s filename=%s\n", __FUNCTION__, out_filename); + outputBufferFile = fopen (out_filename, "wb"); + if (outputBufferFile == NULL) { + DEBUG_PRINT("\ni/p file %s could NOT be opened\n", + out_filename); + error_code = -1; + } + return error_code; +} + + +void audaac_rec_install_bits +( + uint8 *input, + byte num_bits_reqd, + uint32 value, + uint16 *hdr_bit_index +) +{ + uint32 byte_index; + byte bit_index; + byte bits_avail_in_byte; + byte num_to_copy; + byte byte_to_copy; + + byte num_remaining = num_bits_reqd; + uint8 bit_mask; + + bit_mask = 0xFF; + + while (num_remaining) { + + byte_index = (*hdr_bit_index) >> 3; + bit_index = (*hdr_bit_index) & 0x07; + + bits_avail_in_byte = (uint8)(8 - bit_index); + + num_to_copy = MIN(bits_avail_in_byte, num_remaining); + + byte_to_copy = (uint8)(((value >> (num_remaining - num_to_copy)) & 0xFF) << + (bits_avail_in_byte - num_to_copy)); + + input[byte_index] &= ((uint8)(bit_mask << bits_avail_in_byte)); + input[byte_index] |= byte_to_copy; + + *hdr_bit_index += num_to_copy; + + num_remaining = (uint8)(num_remaining - num_to_copy); + } /* while (num_remaining) */ +} /* audaac_rec_install_bits */ + +adts_sample_index map_adts_sample_index(uint32 srate) +{ + adts_sample_index ret; + + switch(srate){ + + case 96000: + ret= ADTS_SAMPLE_INDEX_96000; + break; + case 88200: + ret= ADTS_SAMPLE_INDEX_88200; + break; + case 64000: + ret= ADTS_SAMPLE_INDEX_64000; + break; + case 48000: + ret=ADTS_SAMPLE_INDEX_48000; + break; + case 44100: + ret=ADTS_SAMPLE_INDEX_44100; + break; + case 32000: + ret=ADTS_SAMPLE_INDEX_32000; + break; + case 24000: + ret=ADTS_SAMPLE_INDEX_24000; + break; + case 22050: + ret=ADTS_SAMPLE_INDEX_22050; + break; + case 16000: + ret=ADTS_SAMPLE_INDEX_16000; + break; + case 12000: + ret=ADTS_SAMPLE_INDEX_12000; + break; + case 11025: + ret=ADTS_SAMPLE_INDEX_11025; + break; + case 8000: + ret=ADTS_SAMPLE_INDEX_8000; + break; + case 7350: + ret=ADTS_SAMPLE_INDEX_7350; + break; + default: + ret=ADTS_SAMPLE_INDEX_44100; + break; + } + return ret; +} + +void audaac_rec_install_adts_header_variable (uint16 byte_num) +{ + //uint16 bit_index=0; + + adts_sample_index srate_enum; + uint32 value; + + uint32 sample_index = samplerate; + uint8 channel_config = (uint8)channels; + + /* Store Sync word first */ + audaac_header[0] = 0xFF; + audaac_header[1] = 0xF0; + + audaac_hdr_bit_index = 12; + + if ((format == OMX_AUDIO_AACStreamFormatRAW) && + ((profile == OMX_AUDIO_AACObjectHE) || + (profile == OMX_AUDIO_AACObjectHE_PS))){ + if (samplerate >= 24000) + sample_index = samplerate/2; + } + + /* ID field, 1 bit */ + value = 1; + audaac_rec_install_bits(audaac_header, + 1, + value, + &(audaac_hdr_bit_index)); + + /* Layer field, 2 bits */ + value = 0; + audaac_rec_install_bits(audaac_header, + AACHDR_LAYER_SIZE, + value, + &(audaac_hdr_bit_index)); + + /* Protection_absent field, 1 bit */ + value = 1; + audaac_rec_install_bits(audaac_header, + AACHDR_CRC_SIZE, + value, + &(audaac_hdr_bit_index)); + + /* profile_ObjectType field, 2 bit */ + value = 1; + audaac_rec_install_bits(audaac_header, + AAC_PROFILE_SIZE, + value, + &(audaac_hdr_bit_index)); + + /* sampling_frequency_index field, 4 bits */ + srate_enum = map_adts_sample_index(sample_index); + audaac_rec_install_bits(audaac_header, + AAC_SAMPLING_FREQ_INDEX_SIZE, + (uint32)srate_enum, + &(audaac_hdr_bit_index)); + + DEBUG_PRINT("%s: sample_index=%d; srate_enum = %d \n", + __FUNCTION__, sample_index, srate_enum); + + /* pravate_bit field, 1 bits */ + audaac_rec_install_bits(audaac_header, + 1, + 0, + &(audaac_hdr_bit_index)); + + /* channel_configuration field, 3 bits */ + audaac_rec_install_bits(audaac_header, + 3, + channel_config, + &(audaac_hdr_bit_index)); + + + /* original/copy field, 1 bits */ + audaac_rec_install_bits(audaac_header, + AAC_ORIGINAL_COPY_SIZE, + 0, + &(audaac_hdr_bit_index)); + + + /* home field, 1 bits */ + audaac_rec_install_bits(audaac_header, + AAC_HOME_SIZE, + 0, + &(audaac_hdr_bit_index)); + + // bit_index = audaac_hdr_bit_index; + // bit_index += 2; + + /* copyr. id. bit, 1 bits */ + audaac_rec_install_bits(audaac_header, + 1, + 0, + &(audaac_hdr_bit_index)); + + /* copyr. id. start, 1 bits */ + audaac_rec_install_bits(audaac_header, + 1, + 0, + &(audaac_hdr_bit_index)); + + /* aac_frame_length field, 13 bits */ + audaac_rec_install_bits(audaac_header, + AUDAAC_ADTS_FRAME_LENGTH_SIZE, + byte_num, + &audaac_hdr_bit_index); + + /* adts_buffer_fullness field, 11 bits */ + audaac_rec_install_bits(audaac_header, + 11, + 0x660,/*0x660 = CBR,0x7FF = VBR*/ + &audaac_hdr_bit_index); + + /* number_of_raw_data_blocks_in_frame, 2 bits */ + audaac_rec_install_bits(audaac_header, + 2, + 0, + &audaac_hdr_bit_index); + +} /* audaac_rec_install_adts_header_variable */ + +static OMX_ERRORTYPE parse_pcm_header() +{ + struct wav_header hdr; + + DEBUG_PRINT("\n***************************************************************\n"); + if(fread(&hdr, 1, sizeof(hdr),inputBufferFile)!=sizeof(hdr)) + { + DEBUG_PRINT("Wav file cannot read header\n"); + return -1; + } + + if ((hdr.riff_id != ID_RIFF) || + (hdr.riff_fmt != ID_WAVE)|| + (hdr.fmt_id != ID_FMT)) + { + DEBUG_PRINT("Wav file is not a riff/wave file\n"); + return -1; + } + + if (hdr.audio_format != FORMAT_PCM) + { + DEBUG_PRINT("Wav file is not adpcm format %d and fmt size is %d\n", + hdr.audio_format, hdr.fmt_sz); + return -1; + } + + DEBUG_PRINT("Samplerate is %d\n", hdr.sample_rate); + DEBUG_PRINT("Channel Count is %d\n", hdr.num_channels); + DEBUG_PRINT("\n***************************************************************\n"); + + samplerate = hdr.sample_rate; + channels = hdr.num_channels; + + return OMX_ErrorNone; +} diff --git a/audio/mm-audio/aenc-amrnb/Android.mk b/audio/mm-audio/aenc-amrnb/Android.mk new file mode 100644 index 0000000..4834ad5 --- /dev/null +++ b/audio/mm-audio/aenc-amrnb/Android.mk @@ -0,0 +1,8 @@ +ifneq ($(filter arm aarch64 arm64, $(TARGET_ARCH)),) + + +AENC_AMR_PATH:= $(call my-dir) + +include $(AENC_AMR_PATH)/qdsp6/Android.mk + +endif diff --git a/audio/mm-audio/aenc-amrnb/Makefile b/audio/mm-audio/aenc-amrnb/Makefile new file mode 100644 index 0000000..83d822b --- /dev/null +++ b/audio/mm-audio/aenc-amrnb/Makefile @@ -0,0 +1,6 @@ +all: + @echo "invoking omxaudio make" + $(MAKE) -C qdsp6 + +install: + $(MAKE) -C qdsp6 install diff --git a/audio/mm-audio/aenc-amrnb/qdsp6/Android.mk b/audio/mm-audio/aenc-amrnb/qdsp6/Android.mk new file mode 100644 index 0000000..2bd71b9 --- /dev/null +++ b/audio/mm-audio/aenc-amrnb/qdsp6/Android.mk @@ -0,0 +1,68 @@ +ifneq ($(BUILD_TINY_ANDROID),true) + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +# --------------------------------------------------------------------------------- +# Common definitons +# --------------------------------------------------------------------------------- + +libOmxAmrEnc-def := -g -O3 +libOmxAmrEnc-def += -DQC_MODIFIED +libOmxAmrEnc-def += -D_ANDROID_ +libOmxAmrEnc-def += -D_ENABLE_QC_MSG_LOG_ +libOmxAmrEnc-def += -DVERBOSE +libOmxAmrEnc-def += -D_DEBUG +libOmxAmrEnc-def += -Wconversion +libOmxAmrEnc-def += -DAUDIOV2 + +# --------------------------------------------------------------------------------- +# Make the Shared library (libOmxAmrEnc) +# --------------------------------------------------------------------------------- + +include $(CLEAR_VARS) + +libOmxAmrEnc-inc := $(LOCAL_PATH)/inc +libOmxAmrEnc-inc += $(TARGET_OUT_HEADERS)/mm-core/omxcore + +LOCAL_MODULE := libOmxAmrEnc +LOCAL_MODULE_TAGS := optional +LOCAL_CFLAGS := $(libOmxAmrEnc-def) +LOCAL_C_INCLUDES := $(libOmxAmrEnc-inc) +LOCAL_PRELINK_MODULE := false +LOCAL_SHARED_LIBRARIES := libutils liblog + +LOCAL_SRC_FILES := src/aenc_svr.c +LOCAL_SRC_FILES += src/omx_amr_aenc.cpp + +include $(BUILD_SHARED_LIBRARY) + +# --------------------------------------------------------------------------------- +# Make the apps-test (mm-aenc-omxamr-test) +# --------------------------------------------------------------------------------- + +include $(CLEAR_VARS) + +mm-amr-enc-test-inc := $(LOCAL_PATH)/inc +mm-amr-enc-test-inc += $(LOCAL_PATH)/test + +mm-amr-enc-test-inc += $(TARGET_OUT_HEADERS)/mm-core/omxcore +mm-amr-enc-test-inc += $(TARGET_OUT_HEADERS)/mm-audio/audio-alsa +LOCAL_MODULE := mm-aenc-omxamr-test +LOCAL_MODULE_TAGS := optional +LOCAL_CFLAGS := $(libOmxAmrEnc-def) +LOCAL_C_INCLUDES := $(mm-amr-enc-test-inc) +LOCAL_PRELINK_MODULE := false +LOCAL_SHARED_LIBRARIES := libmm-omxcore +LOCAL_SHARED_LIBRARIES += libOmxAmrEnc +LOCAL_SHARED_LIBRARIES += libaudioalsa +LOCAL_SRC_FILES := test/omx_amr_enc_test.c + +include $(BUILD_EXECUTABLE) + +endif + +# --------------------------------------------------------------------------------- +# END +# --------------------------------------------------------------------------------- + diff --git a/audio/mm-audio/aenc-amrnb/qdsp6/Makefile b/audio/mm-audio/aenc-amrnb/qdsp6/Makefile new file mode 100644 index 0000000..0abd31c --- /dev/null +++ b/audio/mm-audio/aenc-amrnb/qdsp6/Makefile @@ -0,0 +1,81 @@ +# --------------------------------------------------------------------------------- +# MM-AUDIO-OSS-8K-AENC-AMR +# --------------------------------------------------------------------------------- + +# cross-compiler flags +CFLAGS += -Wall +CFLAGS += -Wundef +CFLAGS += -Wstrict-prototypes +CFLAGS += -Wno-trigraphs + +# cross-compile flags specific to shared objects +CFLAGS_SO += -fpic + +# required pre-processor flags +CPPFLAGS := -D__packed__= +CPPFLAGS += -DIMAGE_APPS_PROC +CPPFLAGS += -DFEATURE_Q_SINGLE_LINK +CPPFLAGS += -DFEATURE_Q_NO_SELF_QPTR +CPPFLAGS += -DFEATURE_LINUX +CPPFLAGS += -DFEATURE_NATIVELINUX +CPPFLAGS += -DFEATURE_DSM_DUP_ITEMS + +CPPFLAGS += -g +CPPFALGS += -D_DEBUG +CPPFLAGS += -Iinc + +# linker flags +LDFLAGS += -L$(SYSROOT)/usr/lib + +# linker flags for shared objects +LDFLAGS_SO := -shared + +# defintions +LIBMAJOR := $(basename $(basename $(LIBVER))) +LIBINSTALLDIR := $(DESTDIR)usr/lib +INCINSTALLDIR := $(DESTDIR)usr/include +BININSTALLDIR := $(DESTDIR)usr/bin + +# --------------------------------------------------------------------------------- +# BUILD +# --------------------------------------------------------------------------------- +all: libOmxAmrEnc.so.$(LIBVER) mm-aenc-omxamr-test + +install: + echo "intalling aenc-amr in $(DESTDIR)" + if [ ! -d $(LIBINSTALLDIR) ]; then mkdir -p $(LIBINSTALLDIR); fi + if [ ! -d $(INCINSTALLDIR) ]; then mkdir -p $(INCINSTALLDIR); fi + if [ ! -d $(BININSTALLDIR) ]; then mkdir -p $(BININSTALLDIR); fi + install -m 555 libOmxAmrEnc.so.$(LIBVER) $(LIBINSTALLDIR) + cd $(LIBINSTALLDIR) && ln -s libOmxAmrEnc.so.$(LIBVER) libOmxAmrEnc.so.$(LIBMAJOR) + cd $(LIBINSTALLDIR) && ln -s libOmxAmrEnc.so.$(LIBMAJOR) libOmxAmrEnc.so + install -m 555 mm-aenc-omxamr-test $(BININSTALLDIR) + +# --------------------------------------------------------------------------------- +# COMPILE LIBRARY +# --------------------------------------------------------------------------------- +LDLIBS := -lpthread +LDLIBS += -lstdc++ +LDLIBS += -lOmxCore + +SRCS := src/omx_amr_aenc.cpp +SRCS += src/aenc_svr.c + +libOmxAmrEnc.so.$(LIBVER): $(SRCS) + $(CC) $(CPPFLAGS) $(CFLAGS_SO) $(LDFLAGS_SO) -Wl,-soname,libOmxAmrEnc.so.$(LIBMAJOR) -o $@ $^ $(LDFLAGS) $(LDLIBS) + +# --------------------------------------------------------------------------------- +# COMPILE TEST APP +# --------------------------------------------------------------------------------- +TEST_LDLIBS := -lpthread +TEST_LDLIBS += -ldl +TEST_LDLIBS += -lOmxCore + +TEST_SRCS := test/omx_amr_enc_test.c + +mm-aenc-omxamr-test: libOmxAmrEnc.so.$(LIBVER) $(TEST_SRCS) + $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $^ $(TEST_LDLIBS) + +# --------------------------------------------------------------------------------- +# END +# --------------------------------------------------------------------------------- diff --git a/audio/mm-audio/aenc-amrnb/qdsp6/inc/Map.h b/audio/mm-audio/aenc-amrnb/qdsp6/inc/Map.h new file mode 100644 index 0000000..aac96fd --- /dev/null +++ b/audio/mm-audio/aenc-amrnb/qdsp6/inc/Map.h @@ -0,0 +1,244 @@ +/*-------------------------------------------------------------------------- +Copyright (c) 2010, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of The Linux Foundation nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------*/ +#ifndef _MAP_H_ +#define _MAP_H_ + +#include +using namespace std; + +template +class Map +{ + struct node + { + T data; + T2 data2; + node* prev; + node* next; + node(T t, T2 t2,node* p, node* n) : + data(t), data2(t2), prev(p), next(n) {} + }; + node* head; + node* tail; + node* tmp; + unsigned size_of_list; + static Map *m_self; +public: + Map() : head( NULL ), tail ( NULL ),tmp(head),size_of_list(0) {} + bool empty() const { return ( !head || !tail ); } + operator bool() const { return !empty(); } + void insert(T,T2); + void show(); + int size(); + T2 find(T); // Return VALUE + T find_ele(T);// Check if the KEY is present or not + T2 begin(); //give the first ele + bool erase(T); + bool eraseall(); + bool isempty(); + ~Map() + { + while(head) + { + node* temp(head); + head=head->next; + size_of_list--; + delete temp; + } + } +}; + +template +T2 Map::find(T d1) +{ + tmp = head; + while(tmp) + { + if(tmp->data == d1) + { + return tmp->data2; + } + tmp = tmp->next; + } + return 0; +} + +template +T Map::find_ele(T d1) +{ + tmp = head; + while(tmp) + { + if(tmp->data == d1) + { + return tmp->data; + } + tmp = tmp->next; + } + return 0; +} + +template +T2 Map::begin() +{ + tmp = head; + if(tmp) + { + return (tmp->data2); + } + return 0; +} + +template +void Map::show() +{ + tmp = head; + while(tmp) + { + printf("%d-->%d\n",tmp->data,tmp->data2); + tmp = tmp->next; + } +} + +template +int Map::size() +{ + int count =0; + tmp = head; + while(tmp) + { + tmp = tmp->next; + count++; + } + return count; +} + +template +void Map::insert(T data, T2 data2) +{ + tail = new node(data, data2,tail, NULL); + if( tail->prev ) + tail->prev->next = tail; + + if( empty() ) + { + head = tail; + tmp=head; + } + tmp = head; + size_of_list++; +} + +template +bool Map::erase(T d) +{ + bool found = false; + tmp = head; + node* prevnode = tmp; + node *tempnode; + + while(tmp) + { + if((head == tail) && (head->data == d)) + { + found = true; + tempnode = head; + head = tail = NULL; + delete tempnode; + break; + } + if((tmp ==head) && (tmp->data ==d)) + { + found = true; + tempnode = tmp; + tmp = tmp->next; + tmp->prev = NULL; + head = tmp; + tempnode->next = NULL; + delete tempnode; + break; + } + if((tmp == tail) && (tmp->data ==d)) + { + found = true; + tempnode = tmp; + prevnode->next = NULL; + tmp->prev = NULL; + tail = prevnode; + delete tempnode; + break; + } + if(tmp->data == d) + { + found = true; + prevnode->next = tmp->next; + tmp->next->prev = prevnode->next; + tempnode = tmp; + //tmp = tmp->next; + delete tempnode; + break; + } + prevnode = tmp; + tmp = tmp->next; + } + if(found)size_of_list--; + return found; +} + +template +bool Map::eraseall() +{ + // Be careful while using this method + // it not only removes the node but FREES(not delete) the allocated + // memory. + node *tempnode; + tmp = head; + while(head) + { + tempnode = head; + head = head->next; + tempnode->next = NULL; + if(tempnode->data) + free(tempnode->data); + if(tempnode->data2) + free(tempnode->data2); + delete tempnode; + } + tail = head = NULL; + return true; +} + + +template +bool Map::isempty() +{ + if(!size_of_list) return true; + else return false; +} + +#endif // _MAP_H_ diff --git a/audio/mm-audio/aenc-amrnb/qdsp6/inc/aenc_svr.h b/audio/mm-audio/aenc-amrnb/qdsp6/inc/aenc_svr.h new file mode 100644 index 0000000..782641b --- /dev/null +++ b/audio/mm-audio/aenc-amrnb/qdsp6/inc/aenc_svr.h @@ -0,0 +1,120 @@ +/*-------------------------------------------------------------------------- +Copyright (c) 2010, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of The Linux Foundation nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------*/ +#ifndef AENC_SVR_H +#define AENC_SVR_H + +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#include + +#ifdef _ANDROID_ +#define LOG_TAG "QC_AMRENC" +#endif + +#ifndef LOGE +#define LOGE ALOGE +#endif + +#ifndef LOGW +#define LOGW ALOGW +#endif + +#ifndef LOGD +#define LOGD ALOGD +#endif + +#ifndef LOGV +#define LOGV ALOGV +#endif + +#ifndef LOGI +#define LOGI ALOGI +#endif + +#define DEBUG_PRINT_ERROR LOGE +#define DEBUG_PRINT LOGI +#define DEBUG_DETAIL LOGV + +typedef void (*message_func)(void* client_data, unsigned char id); + +/** + @brief audio encoder ipc info structure + + */ +struct amr_ipc_info +{ + pthread_t thr; + int pipe_in; + int pipe_out; + int dead; + message_func process_msg_cb; + void *client_data; + char thread_name[128]; +}; + +/** + @brief This function starts command server + + @param cb pointer to callback function from the client + @param client_data reference client wants to get back + through callback + @return handle to command server + */ +struct amr_ipc_info *omx_amr_thread_create(message_func cb, + void* client_data, + char *th_name); + +struct amr_ipc_info *omx_amr_event_thread_create(message_func cb, + void* client_data, + char *th_name); +/** + @brief This function stop command server + + @param svr handle to command server + @return none + */ +void omx_amr_thread_stop(struct amr_ipc_info *amr_ipc); + + +/** + @brief This function post message in the command server + + @param svr handle to command server + @return none + */ +void omx_amr_post_msg(struct amr_ipc_info *amr_ipc, + unsigned char id); + +#ifdef __cplusplus +} +#endif + +#endif /* AENC_SVR */ diff --git a/audio/mm-audio/aenc-amrnb/qdsp6/inc/omx_amr_aenc.h b/audio/mm-audio/aenc-amrnb/qdsp6/inc/omx_amr_aenc.h new file mode 100644 index 0000000..ed7f758 --- /dev/null +++ b/audio/mm-audio/aenc-amrnb/qdsp6/inc/omx_amr_aenc.h @@ -0,0 +1,538 @@ +/*-------------------------------------------------------------------------- + +Copyright (c) 2010,2014 The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of The Linux Foundation nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------*/ +#ifndef _AMR_ENC_H_ +#define _AMR_ENC_H_ +/*============================================================================ + Audio Encoder + +@file omx_amr_aenc.h +This module contains the class definition for openMAX encoder component. + + + +============================================================================*/ + +////////////////////////////////////////////////////////////////////////////// +// Include Files +////////////////////////////////////////////////////////////////////////////// + +/* Uncomment out below line #define LOG_NDEBUG 0 if we want to see + * all DEBUG_PRINT or LOGV messaging */ +#include +#include +#include +#include +#include +#include +#include "QOMX_AudioExtensions.h" +#include "QOMX_AudioIndexExtensions.h" +#include "OMX_Core.h" +#include "OMX_Audio.h" +#include "aenc_svr.h" +#include "qc_omx_component.h" +#include "Map.h" +#include +#include +#include +extern "C" { + void * get_omx_component_factory_fn(void); +} + + +////////////////////////////////////////////////////////////////////////////// +// Module specific globals +////////////////////////////////////////////////////////////////////////////// + + + +#define OMX_SPEC_VERSION 0x00000101 +#define MIN(x,y) (((x) < (y)) ? (x) : (y)) +#define MAX(x,y) (x >= y?x:y) + +////////////////////////////////////////////////////////////////////////////// +// Macros +////////////////////////////////////////////////////////////////////////////// +// + + +#define PrintFrameHdr(i,bufHdr) \ + DEBUG_PRINT("i=%d OMX bufHdr[%p]buf[%p]size[%d]TS[%lld]nFlags[0x%x]\n",\ + i,\ + bufHdr, \ + ((OMX_BUFFERHEADERTYPE *)bufHdr)->pBuffer, \ + (unsigned)((OMX_BUFFERHEADERTYPE *)bufHdr)->nFilledLen,\ + ((OMX_BUFFERHEADERTYPE *)bufHdr)->nTimeStamp, \ + (unsigned)((OMX_BUFFERHEADERTYPE *)bufHdr)->nFlags) + + +// BitMask Management logic +#define BITS_PER_BYTE 8 +#define BITMASK_SIZE(mIndex) \ + (((mIndex) + BITS_PER_BYTE - 1)/BITS_PER_BYTE) +#define BITMASK_OFFSET(mIndex)\ + ((mIndex)/BITS_PER_BYTE) +#define BITMASK_FLAG(mIndex) \ + (1 << ((mIndex) % BITS_PER_BYTE)) +#define BITMASK_CLEAR(mArray,mIndex)\ + (mArray)[BITMASK_OFFSET(mIndex)] &= ~(BITMASK_FLAG(mIndex)) +#define BITMASK_SET(mArray,mIndex)\ + (mArray)[BITMASK_OFFSET(mIndex)] |= BITMASK_FLAG(mIndex) +#define BITMASK_PRESENT(mArray,mIndex)\ + ((mArray)[BITMASK_OFFSET(mIndex)] & BITMASK_FLAG(mIndex)) +#define BITMASK_ABSENT(mArray,mIndex)\ + (((mArray)[BITMASK_OFFSET(mIndex)] & \ + BITMASK_FLAG(mIndex)) == 0x0) + +#define OMX_CORE_NUM_INPUT_BUFFERS 2 +#define OMX_CORE_NUM_OUTPUT_BUFFERS 16 + +#define OMX_CORE_INPUT_BUFFER_SIZE 8160 // Multiple of 160 +#define OMX_CORE_CONTROL_CMDQ_SIZE 100 +#define OMX_AENC_VOLUME_STEP 0x147 +#define OMX_AENC_MIN 0 +#define OMX_AENC_MAX 100 +#define NON_TUNNEL 1 +#define TUNNEL 0 +#define IP_PORT_BITMASK 0x02 +#define OP_PORT_BITMASK 0x01 +#define IP_OP_PORT_BITMASK 0x03 + +#define OMX_AMR_DEFAULT_SF 8000 +#define OMX_AMR_DEFAULT_CH_CFG 1 +#define OMX_AMR_DEFAULT_VOL 25 +// 14 bytes for input meta data +#define OMX_AENC_SIZEOF_META_BUF (OMX_CORE_INPUT_BUFFER_SIZE+14) + +#define TRUE 1 +#define FALSE 0 + +#define NUMOFFRAMES 1 +#define MAXFRAMELENGTH 32 +#define OMX_AMR_OUTPUT_BUFFER_SIZE ((NUMOFFRAMES * (sizeof(ENC_META_OUT) + MAXFRAMELENGTH) \ + + 1)) +#define FRAMEDURATION 20000 + +class omx_amr_aenc; + +// OMX AMR audio encoder class +class omx_amr_aenc: public qc_omx_component +{ +public: + omx_amr_aenc(); // constructor + virtual ~omx_amr_aenc(); // destructor + + OMX_ERRORTYPE allocate_buffer(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE **bufferHdr, + OMX_U32 port, + OMX_PTR appData, + OMX_U32 bytes); + + + OMX_ERRORTYPE component_deinit(OMX_HANDLETYPE hComp); + + OMX_ERRORTYPE component_init(OMX_STRING role); + + OMX_ERRORTYPE component_role_enum(OMX_HANDLETYPE hComp, + OMX_U8 *role, + OMX_U32 index); + + OMX_ERRORTYPE component_tunnel_request(OMX_HANDLETYPE hComp, + OMX_U32 port, + OMX_HANDLETYPE peerComponent, + OMX_U32 peerPort, + OMX_TUNNELSETUPTYPE *tunnelSetup); + + OMX_ERRORTYPE empty_this_buffer(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE *buffer); + + + OMX_ERRORTYPE empty_this_buffer_proxy(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE *buffer); + + + OMX_ERRORTYPE fill_this_buffer(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE *buffer); + + + OMX_ERRORTYPE free_buffer(OMX_HANDLETYPE hComp, + OMX_U32 port, + OMX_BUFFERHEADERTYPE *buffer); + + OMX_ERRORTYPE get_component_version(OMX_HANDLETYPE hComp, + OMX_STRING componentName, + OMX_VERSIONTYPE *componentVersion, + OMX_VERSIONTYPE * specVersion, + OMX_UUIDTYPE *componentUUID); + + OMX_ERRORTYPE get_config(OMX_HANDLETYPE hComp, + OMX_INDEXTYPE configIndex, + OMX_PTR configData); + + OMX_ERRORTYPE get_extension_index(OMX_HANDLETYPE hComp, + OMX_STRING paramName, + OMX_INDEXTYPE *indexType); + + OMX_ERRORTYPE get_parameter(OMX_HANDLETYPE hComp, + OMX_INDEXTYPE paramIndex, + OMX_PTR paramData); + + OMX_ERRORTYPE get_state(OMX_HANDLETYPE hComp, + OMX_STATETYPE *state); + + static void process_in_port_msg(void *client_data, + unsigned char id); + + static void process_out_port_msg(void *client_data, + unsigned char id); + + static void process_command_msg(void *client_data, + unsigned char id); + + static void process_event_cb(void *client_data, + unsigned char id); + + + OMX_ERRORTYPE set_callbacks(OMX_HANDLETYPE hComp, + OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData); + + OMX_ERRORTYPE set_config(OMX_HANDLETYPE hComp, + OMX_INDEXTYPE configIndex, + OMX_PTR configData); + + OMX_ERRORTYPE set_parameter(OMX_HANDLETYPE hComp, + OMX_INDEXTYPE paramIndex, + OMX_PTR paramData); + + OMX_ERRORTYPE use_buffer(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE **bufferHdr, + OMX_U32 port, + OMX_PTR appData, + OMX_U32 bytes, + OMX_U8 *buffer); + + OMX_ERRORTYPE use_EGL_image(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE **bufferHdr, + OMX_U32 port, + OMX_PTR appData, + void * eglImage); + + bool post_command(unsigned int p1, unsigned int p2, + unsigned char id); + + // Deferred callback identifiers + enum + { + //Event Callbacks from the component thread context + OMX_COMPONENT_GENERATE_EVENT = 0x1, + //Buffer Done callbacks from component thread context + OMX_COMPONENT_GENERATE_BUFFER_DONE = 0x2, + OMX_COMPONENT_GENERATE_ETB = 0x3, + //Command + OMX_COMPONENT_GENERATE_COMMAND = 0x4, + OMX_COMPONENT_GENERATE_FRAME_DONE = 0x05, + OMX_COMPONENT_GENERATE_FTB = 0x06, + OMX_COMPONENT_GENERATE_EOS = 0x07, + OMX_COMPONENT_PORTSETTINGS_CHANGED = 0x08, + OMX_COMPONENT_SUSPEND = 0x09, + OMX_COMPONENT_RESUME = 0x0a + }; +private: + + /////////////////////////////////////////////////////////// + // Type definitions + /////////////////////////////////////////////////////////// + // Bit Positions + enum flags_bit_positions + { + // Defer transition to IDLE + OMX_COMPONENT_IDLE_PENDING =0x1, + // Defer transition to LOADING + OMX_COMPONENT_LOADING_PENDING =0x2, + + OMX_COMPONENT_MUTED =0x3, + + // Defer transition to Enable + OMX_COMPONENT_INPUT_ENABLE_PENDING =0x4, + // Defer transition to Enable + OMX_COMPONENT_OUTPUT_ENABLE_PENDING =0x5, + // Defer transition to Disable + OMX_COMPONENT_INPUT_DISABLE_PENDING =0x6, + // Defer transition to Disable + OMX_COMPONENT_OUTPUT_DISABLE_PENDING =0x7 + }; + + + typedef Map + input_buffer_map; + + typedef Map + output_buffer_map; + + enum port_indexes + { + OMX_CORE_INPUT_PORT_INDEX =0, + OMX_CORE_OUTPUT_PORT_INDEX =1 + }; + + struct omx_event + { + unsigned long param1; + unsigned long param2; + unsigned char id; + }; + + struct omx_cmd_queue + { + omx_event m_q[OMX_CORE_CONTROL_CMDQ_SIZE]; + unsigned m_read; + unsigned m_write; + unsigned m_size; + + omx_cmd_queue(); + ~omx_cmd_queue(); + bool insert_entry(unsigned long p1, unsigned long p2, unsigned char id); + bool pop_entry(unsigned long *p1,unsigned long *p2, unsigned char *id); + bool get_msg_id(unsigned char *id); + bool get_msg_with_id(unsigned *p1,unsigned *p2, unsigned id); + }; + + typedef struct TIMESTAMP + { + unsigned int LowPart; + unsigned int HighPart; + }__attribute__((packed)) TIMESTAMP; + + typedef struct metadata_input + { + unsigned short offsetVal; + TIMESTAMP nTimeStamp; + unsigned int nFlags; + }__attribute__((packed)) META_IN; + + typedef struct enc_meta_out + { + unsigned int offset_to_frame; + unsigned int frame_size; + unsigned int encoded_pcm_samples; + unsigned int msw_ts; + unsigned int lsw_ts; + unsigned int nflags; + } __attribute__ ((packed))ENC_META_OUT; + + typedef struct + { + OMX_U32 tot_in_buf_len; + OMX_U32 tot_out_buf_len; + OMX_TICKS tot_pb_time; + OMX_U32 fbd_cnt; + OMX_U32 ftb_cnt; + OMX_U32 etb_cnt; + OMX_U32 ebd_cnt; + }AMR_PB_STATS; + + /////////////////////////////////////////////////////////// + // Member variables + /////////////////////////////////////////////////////////// + OMX_U8 *m_tmp_meta_buf; + OMX_U8 *m_tmp_out_meta_buf; + OMX_U8 m_flush_cnt ; + OMX_U8 m_comp_deinit; + + // the below var doesnt hold good if combo of use and alloc bufs are used + OMX_S32 m_volume;//Unit to be determined + OMX_PTR m_app_data;// Application data + int nNumInputBuf; + int nNumOutputBuf; + int m_drv_fd; // Kernel device node file handle + bool bFlushinprogress; + bool is_in_th_sleep; + bool is_out_th_sleep; + unsigned int m_flags; //encapsulate the waiting states. + OMX_U64 nTimestamp; + OMX_U64 ts; + unsigned int pcm_input; //tunnel or non-tunnel + unsigned int m_inp_act_buf_count; // Num of Input Buffers + unsigned int m_out_act_buf_count; // Numb of Output Buffers + unsigned int m_inp_current_buf_count; // Num of Input Buffers + unsigned int m_out_current_buf_count; // Numb of Output Buffers + unsigned int output_buffer_size; + unsigned int input_buffer_size; + unsigned short m_session_id; + // store I/P PORT state + OMX_BOOL m_inp_bEnabled; + // store O/P PORT state + OMX_BOOL m_out_bEnabled; + //Input port Populated + OMX_BOOL m_inp_bPopulated; + //Output port Populated + OMX_BOOL m_out_bPopulated; + sem_t sem_States; + sem_t sem_read_msg; + sem_t sem_write_msg; + + volatile int m_is_event_done; + volatile int m_is_in_th_sleep; + volatile int m_is_out_th_sleep; + input_buffer_map m_input_buf_hdrs; + output_buffer_map m_output_buf_hdrs; + omx_cmd_queue m_input_q; + omx_cmd_queue m_input_ctrl_cmd_q; + omx_cmd_queue m_input_ctrl_ebd_q; + omx_cmd_queue m_command_q; + omx_cmd_queue m_output_q; + omx_cmd_queue m_output_ctrl_cmd_q; + omx_cmd_queue m_output_ctrl_fbd_q; + pthread_mutexattr_t m_outputlock_attr; + pthread_mutexattr_t m_commandlock_attr; + pthread_mutexattr_t m_lock_attr; + pthread_mutexattr_t m_state_attr; + pthread_mutexattr_t m_flush_attr; + pthread_mutexattr_t m_in_th_attr_1; + pthread_mutexattr_t m_out_th_attr_1; + pthread_mutexattr_t m_event_attr; + pthread_mutexattr_t m_in_th_attr; + pthread_mutexattr_t m_out_th_attr; + pthread_mutexattr_t out_buf_count_lock_attr; + pthread_mutexattr_t in_buf_count_lock_attr; + pthread_cond_t cond; + pthread_cond_t in_cond; + pthread_cond_t out_cond; + pthread_mutex_t m_lock; + pthread_mutex_t m_commandlock; + pthread_mutex_t m_outputlock; + // Mutexes for state change + pthread_mutex_t m_state_lock; + // Mutexes for flush acks from input and output threads + pthread_mutex_t m_flush_lock; + pthread_mutex_t m_event_lock; + pthread_mutex_t m_in_th_lock; + pthread_mutex_t m_out_th_lock; + pthread_mutex_t m_in_th_lock_1; + pthread_mutex_t m_out_th_lock_1; + pthread_mutex_t out_buf_count_lock; + pthread_mutex_t in_buf_count_lock; + + OMX_STATETYPE m_state; // OMX State + OMX_STATETYPE nState; + OMX_CALLBACKTYPE m_cb; // Application callbacks + AMR_PB_STATS m_amr_pb_stats; + struct amr_ipc_info *m_ipc_to_in_th; // for input thread + struct amr_ipc_info *m_ipc_to_out_th; // for output thread + struct amr_ipc_info *m_ipc_to_cmd_th; // for command thread + OMX_PRIORITYMGMTTYPE m_priority_mgm ; + OMX_AUDIO_PARAM_AMRTYPE m_amr_param; // Cache AMR encoder parameter + OMX_AUDIO_PARAM_PCMMODETYPE m_pcm_param; // Cache pcm parameter + OMX_PARAM_COMPONENTROLETYPE component_Role; + OMX_PARAM_BUFFERSUPPLIERTYPE m_buffer_supplier; + + /////////////////////////////////////////////////////////// + // Private methods + /////////////////////////////////////////////////////////// + OMX_ERRORTYPE allocate_output_buffer(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE **bufferHdr, + OMX_U32 port,OMX_PTR appData, + OMX_U32 bytes); + + OMX_ERRORTYPE allocate_input_buffer(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE **bufferHdr, + OMX_U32 port, + OMX_PTR appData, + OMX_U32 bytes); + + OMX_ERRORTYPE use_input_buffer(OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE **bufHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes, + OMX_IN OMX_U8* buffer); + + OMX_ERRORTYPE use_output_buffer(OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE **bufHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes, + OMX_IN OMX_U8* buffer); + + OMX_ERRORTYPE fill_this_buffer_proxy(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE *buffer); + + OMX_ERRORTYPE send_command_proxy(OMX_HANDLETYPE hComp, + OMX_COMMANDTYPE cmd, + OMX_U32 param1, + OMX_PTR cmdData); + + OMX_ERRORTYPE send_command(OMX_HANDLETYPE hComp, + OMX_COMMANDTYPE cmd, + OMX_U32 param1, + OMX_PTR cmdData); + + bool allocate_done(void); + + bool release_done(OMX_U32 param1); + + bool execute_omx_flush(OMX_IN OMX_U32 param1, bool cmd_cmpl=true); + + bool execute_input_omx_flush(void); + + bool execute_output_omx_flush(void); + + bool search_input_bufhdr(OMX_BUFFERHEADERTYPE *buffer); + + bool search_output_bufhdr(OMX_BUFFERHEADERTYPE *buffer); + + bool post_input(unsigned long p1, unsigned long p2, + unsigned char id); + + bool post_output(unsigned long p1, unsigned long p2, + unsigned char id); + + void process_events(omx_amr_aenc *client_data); + + void buffer_done_cb(OMX_BUFFERHEADERTYPE *bufHdr); + + void frame_done_cb(OMX_BUFFERHEADERTYPE *bufHdr); + + void wait_for_event(); + + void event_complete(); + + void in_th_goto_sleep(); + + void in_th_wakeup(); + + void out_th_goto_sleep(); + + void out_th_wakeup(); + + void flush_ack(); + void deinit_encoder(); + +}; +#endif diff --git a/audio/mm-audio/aenc-amrnb/qdsp6/src/aenc_svr.c b/audio/mm-audio/aenc-amrnb/qdsp6/src/aenc_svr.c new file mode 100644 index 0000000..dea7c7f --- /dev/null +++ b/audio/mm-audio/aenc-amrnb/qdsp6/src/aenc_svr.c @@ -0,0 +1,205 @@ +/*-------------------------------------------------------------------------- +Copyright (c) 2010, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of The Linux Foundation nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------*/ +#include +#include +#include + +#include +#include + +#include + +/** + @brief This function processes posted messages + + Once thread is being spawned, this function is run to + start processing commands posted by client + + @param info pointer to context + + */ +void *omx_amr_msg(void *info) +{ + struct amr_ipc_info *amr_info = (struct amr_ipc_info*)info; + unsigned char id; + ssize_t n; + + DEBUG_DETAIL("\n%s: message thread start\n", __FUNCTION__); + while (!amr_info->dead) + { + n = read(amr_info->pipe_in, &id, 1); + if (0 == n) break; + if (1 == n) + { + DEBUG_DETAIL("\n%s-->pipe_in=%d pipe_out=%d\n", + amr_info->thread_name, + amr_info->pipe_in, + amr_info->pipe_out); + + amr_info->process_msg_cb(amr_info->client_data, id); + } + if ((n < 0) && (errno != EINTR)) break; + } + DEBUG_DETAIL("%s: message thread stop\n", __FUNCTION__); + + return 0; +} + +void *omx_amr_events(void *info) +{ + struct amr_ipc_info *amr_info = (struct amr_ipc_info*)info; + unsigned char id = 0; + + DEBUG_DETAIL("%s: message thread start\n", amr_info->thread_name); + amr_info->process_msg_cb(amr_info->client_data, id); + DEBUG_DETAIL("%s: message thread stop\n", amr_info->thread_name); + return 0; +} + +/** + @brief This function starts command server + + @param cb pointer to callback function from the client + @param client_data reference client wants to get back + through callback + @return handle to msging thread + */ +struct amr_ipc_info *omx_amr_thread_create( + message_func cb, + void* client_data, + char* th_name) +{ + int r; + int fds[2]; + struct amr_ipc_info *amr_info; + + amr_info = calloc(1, sizeof(struct amr_ipc_info)); + if (!amr_info) + { + return 0; + } + + amr_info->client_data = client_data; + amr_info->process_msg_cb = cb; + strlcpy(amr_info->thread_name, th_name, sizeof(amr_info->thread_name)); + + if (pipe(fds)) + { + DEBUG_PRINT_ERROR("\n%s: pipe creation failed\n", __FUNCTION__); + goto fail_pipe; + } + + amr_info->pipe_in = fds[0]; + amr_info->pipe_out = fds[1]; + + r = pthread_create(&amr_info->thr, 0, omx_amr_msg, amr_info); + if (r < 0) goto fail_thread; + + DEBUG_DETAIL("Created thread for %s \n", amr_info->thread_name); + return amr_info; + + +fail_thread: + close(amr_info->pipe_in); + close(amr_info->pipe_out); + +fail_pipe: + free(amr_info); + + return 0; +} + +/** + * @brief This function starts command server + * + * @param cb pointer to callback function from the client + * @param client_data reference client wants to get back + * through callback + * @return handle to msging thread + * */ +struct amr_ipc_info *omx_amr_event_thread_create( + message_func cb, + void* client_data, + char* th_name) +{ + int r; + int fds[2]; + struct amr_ipc_info *amr_info; + + amr_info = calloc(1, sizeof(struct amr_ipc_info)); + if (!amr_info) + { + return 0; + } + + amr_info->client_data = client_data; + amr_info->process_msg_cb = cb; + strlcpy(amr_info->thread_name, th_name, sizeof(amr_info->thread_name)); + + if (pipe(fds)) + { + DEBUG_PRINT("\n%s: pipe creation failed\n", __FUNCTION__); + goto fail_pipe; + } + + amr_info->pipe_in = fds[0]; + amr_info->pipe_out = fds[1]; + + r = pthread_create(&amr_info->thr, 0, omx_amr_events, amr_info); + if (r < 0) goto fail_thread; + + DEBUG_DETAIL("Created thread for %s \n", amr_info->thread_name); + return amr_info; + + +fail_thread: + close(amr_info->pipe_in); + close(amr_info->pipe_out); + +fail_pipe: + free(amr_info); + + return 0; +} + +void omx_amr_thread_stop(struct amr_ipc_info *amr_info) { + DEBUG_DETAIL("%s stop server\n", __FUNCTION__); + close(amr_info->pipe_in); + close(amr_info->pipe_out); + pthread_join(amr_info->thr,NULL); + amr_info->pipe_out = -1; + amr_info->pipe_in = -1; + DEBUG_DETAIL("%s: message thread close fds%d %d\n", amr_info->thread_name, + amr_info->pipe_in,amr_info->pipe_out); + free(amr_info); +} + +void omx_amr_post_msg(struct amr_ipc_info *amr_info, unsigned char id) { + DEBUG_DETAIL("\n%s id=%d\n", __FUNCTION__,id); + write(amr_info->pipe_out, &id, 1); +} diff --git a/audio/mm-audio/aenc-amrnb/qdsp6/src/omx_amr_aenc.cpp b/audio/mm-audio/aenc-amrnb/qdsp6/src/omx_amr_aenc.cpp new file mode 100644 index 0000000..5e9ee4e --- /dev/null +++ b/audio/mm-audio/aenc-amrnb/qdsp6/src/omx_amr_aenc.cpp @@ -0,0 +1,4532 @@ +/*-------------------------------------------------------------------------- +Copyright (c) 2010, 2014 The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of The Linux Foundation nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------*/ +/*============================================================================ +@file omx_aenc_amr.c + This module contains the implementation of the OpenMAX core & component. + +*//*========================================================================*/ +////////////////////////////////////////////////////////////////////////////// +// Include Files +////////////////////////////////////////////////////////////////////////////// + + +#include +#include +#include +#include "omx_amr_aenc.h" +#include + +using namespace std; +#define SLEEP_MS 100 + +// omx_cmd_queue destructor +omx_amr_aenc::omx_cmd_queue::~omx_cmd_queue() +{ + // Nothing to do +} + +// omx cmd queue constructor +omx_amr_aenc::omx_cmd_queue::omx_cmd_queue(): m_read(0),m_write(0),m_size(0) +{ + memset(m_q, 0,sizeof(omx_event)*OMX_CORE_CONTROL_CMDQ_SIZE); +} + +// omx cmd queue insert +bool omx_amr_aenc::omx_cmd_queue::insert_entry(unsigned long p1, + unsigned long p2, + unsigned char id) +{ + bool ret = true; + if (m_size < OMX_CORE_CONTROL_CMDQ_SIZE) + { + m_q[m_write].id = id; + m_q[m_write].param1 = p1; + m_q[m_write].param2 = p2; + m_write++; + m_size ++; + if (m_write >= OMX_CORE_CONTROL_CMDQ_SIZE) + { + m_write = 0; + } + } else + { + ret = false; + DEBUG_PRINT_ERROR("ERROR!!! Command Queue Full"); + } + return ret; +} + +bool omx_amr_aenc::omx_cmd_queue::pop_entry(unsigned long *p1, + unsigned long *p2, unsigned char *id) +{ + bool ret = true; + if (m_size > 0) + { + *id = m_q[m_read].id; + *p1 = m_q[m_read].param1; + *p2 = m_q[m_read].param2; + // Move the read pointer ahead + ++m_read; + --m_size; + if (m_read >= OMX_CORE_CONTROL_CMDQ_SIZE) + { + m_read = 0; + + } + } else + { + ret = false; + DEBUG_PRINT_ERROR("ERROR Delete!!! Command Queue Empty"); + } + return ret; +} + +// factory function executed by the core to create instances +void *get_omx_component_factory_fn(void) +{ + return(new omx_amr_aenc); +} +bool omx_amr_aenc::omx_cmd_queue::get_msg_id(unsigned char *id) +{ + if(m_size > 0) + { + *id = m_q[m_read].id; + DEBUG_PRINT("get_msg_id=%d\n",*id); + } + else{ + return false; + } + return true; +} +/*============================================================================= +FUNCTION: + wait_for_event + +DESCRIPTION: + waits for a particular event + +INPUT/OUTPUT PARAMETERS: + None + +RETURN VALUE: + None + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +void omx_amr_aenc::wait_for_event() +{ + int rc; + struct timespec ts; + pthread_mutex_lock(&m_event_lock); + while (0 == m_is_event_done) + { + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += (SLEEP_MS/1000); + ts.tv_nsec += ((SLEEP_MS%1000) * 1000000); + rc = pthread_cond_timedwait(&cond, &m_event_lock, &ts); + if (rc == ETIMEDOUT && !m_is_event_done) { + DEBUG_PRINT("Timed out waiting for flush"); + if (ioctl( m_drv_fd, AUDIO_FLUSH, 0) == -1) + DEBUG_PRINT_ERROR("Flush:Input port, ioctl flush failed %d\n", + errno); + } + } + m_is_event_done = 0; + pthread_mutex_unlock(&m_event_lock); +} + +/*============================================================================= +FUNCTION: + event_complete + +DESCRIPTION: + informs about the occurance of an event + +INPUT/OUTPUT PARAMETERS: + None + +RETURN VALUE: + None + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +void omx_amr_aenc::event_complete() +{ + pthread_mutex_lock(&m_event_lock); + if (0 == m_is_event_done) + { + m_is_event_done = 1; + pthread_cond_signal(&cond); + } + pthread_mutex_unlock(&m_event_lock); +} + +// All this non-sense because of a single amr object +void omx_amr_aenc::in_th_goto_sleep() +{ + pthread_mutex_lock(&m_in_th_lock); + while (0 == m_is_in_th_sleep) + { + pthread_cond_wait(&in_cond, &m_in_th_lock); + } + m_is_in_th_sleep = 0; + pthread_mutex_unlock(&m_in_th_lock); +} + +void omx_amr_aenc::in_th_wakeup() +{ + pthread_mutex_lock(&m_in_th_lock); + if (0 == m_is_in_th_sleep) + { + m_is_in_th_sleep = 1; + pthread_cond_signal(&in_cond); + } + pthread_mutex_unlock(&m_in_th_lock); +} + +void omx_amr_aenc::out_th_goto_sleep() +{ + + pthread_mutex_lock(&m_out_th_lock); + while (0 == m_is_out_th_sleep) + { + pthread_cond_wait(&out_cond, &m_out_th_lock); + } + m_is_out_th_sleep = 0; + pthread_mutex_unlock(&m_out_th_lock); +} + +void omx_amr_aenc::out_th_wakeup() +{ + pthread_mutex_lock(&m_out_th_lock); + if (0 == m_is_out_th_sleep) + { + m_is_out_th_sleep = 1; + pthread_cond_signal(&out_cond); + } + pthread_mutex_unlock(&m_out_th_lock); +} +/* ====================================================================== +FUNCTION + omx_amr_aenc::omx_amr_aenc + +DESCRIPTION + Constructor + +PARAMETERS + None + +RETURN VALUE + None. +========================================================================== */ +omx_amr_aenc::omx_amr_aenc(): m_tmp_meta_buf(NULL), + m_tmp_out_meta_buf(NULL), + m_flush_cnt(255), + m_comp_deinit(0), + m_volume(25), + m_app_data(NULL), + nNumInputBuf(0), + nNumOutputBuf(0), + m_drv_fd(-1), + bFlushinprogress(0), + is_in_th_sleep(false), + is_out_th_sleep(false), + m_flags(0), + nTimestamp(0), + ts(0), + pcm_input(0), + m_inp_act_buf_count (OMX_CORE_NUM_INPUT_BUFFERS), + m_out_act_buf_count (OMX_CORE_NUM_OUTPUT_BUFFERS), + m_inp_current_buf_count(0), + m_out_current_buf_count(0), + output_buffer_size((OMX_U32)OMX_AMR_OUTPUT_BUFFER_SIZE), + input_buffer_size(OMX_CORE_INPUT_BUFFER_SIZE), + m_session_id(0), + m_inp_bEnabled(OMX_TRUE), + m_out_bEnabled(OMX_TRUE), + m_inp_bPopulated(OMX_FALSE), + m_out_bPopulated(OMX_FALSE), + m_is_event_done(0), + m_state(OMX_StateInvalid), + m_ipc_to_in_th(NULL), + m_ipc_to_out_th(NULL), + m_ipc_to_cmd_th(NULL) +{ + int cond_ret = 0; + component_Role.nSize = 0; + memset(&m_cmp, 0, sizeof(m_cmp)); + memset(&m_cb, 0, sizeof(m_cb)); + memset(&m_pcm_param, 0, sizeof(m_pcm_param)); + memset(&m_amr_param, 0, sizeof(m_amr_param)); + memset(&m_amr_pb_stats, 0, sizeof(m_amr_pb_stats)); + memset(&m_buffer_supplier, 0, sizeof(m_buffer_supplier)); + memset(&m_priority_mgm, 0, sizeof(m_priority_mgm)); + + pthread_mutexattr_init(&m_lock_attr); + pthread_mutex_init(&m_lock, &m_lock_attr); + pthread_mutexattr_init(&m_commandlock_attr); + pthread_mutex_init(&m_commandlock, &m_commandlock_attr); + + pthread_mutexattr_init(&m_outputlock_attr); + pthread_mutex_init(&m_outputlock, &m_outputlock_attr); + + pthread_mutexattr_init(&m_state_attr); + pthread_mutex_init(&m_state_lock, &m_state_attr); + + pthread_mutexattr_init(&m_event_attr); + pthread_mutex_init(&m_event_lock, &m_event_attr); + + pthread_mutexattr_init(&m_flush_attr); + pthread_mutex_init(&m_flush_lock, &m_flush_attr); + + pthread_mutexattr_init(&m_event_attr); + pthread_mutex_init(&m_event_lock, &m_event_attr); + + pthread_mutexattr_init(&m_in_th_attr); + pthread_mutex_init(&m_in_th_lock, &m_in_th_attr); + + pthread_mutexattr_init(&m_out_th_attr); + pthread_mutex_init(&m_out_th_lock, &m_out_th_attr); + + pthread_mutexattr_init(&m_in_th_attr_1); + pthread_mutex_init(&m_in_th_lock_1, &m_in_th_attr_1); + + pthread_mutexattr_init(&m_out_th_attr_1); + pthread_mutex_init(&m_out_th_lock_1, &m_out_th_attr_1); + + pthread_mutexattr_init(&out_buf_count_lock_attr); + pthread_mutex_init(&out_buf_count_lock, &out_buf_count_lock_attr); + + pthread_mutexattr_init(&in_buf_count_lock_attr); + pthread_mutex_init(&in_buf_count_lock, &in_buf_count_lock_attr); + if ((cond_ret = pthread_cond_init (&cond, NULL)) != 0) + { + DEBUG_PRINT_ERROR("pthread_cond_init returns non zero for cond\n"); + if (cond_ret == EAGAIN) + DEBUG_PRINT_ERROR("The system lacked necessary \ + resources(other than mem)\n"); + else if (cond_ret == ENOMEM) + DEBUG_PRINT_ERROR("Insufficient memory to initialise \ + condition variable\n"); + } + if ((cond_ret = pthread_cond_init (&in_cond, NULL)) != 0) + { + DEBUG_PRINT_ERROR("pthread_cond_init returns non zero for in_cond\n"); + if (cond_ret == EAGAIN) + DEBUG_PRINT_ERROR("The system lacked necessary \ + resources(other than mem)\n"); + else if (cond_ret == ENOMEM) + DEBUG_PRINT_ERROR("Insufficient memory to initialise \ + condition variable\n"); + } + if ((cond_ret = pthread_cond_init (&out_cond, NULL)) != 0) + { + DEBUG_PRINT_ERROR("pthread_cond_init returns non zero for out_cond\n"); + if (cond_ret == EAGAIN) + DEBUG_PRINT_ERROR("The system lacked necessary \ + resources(other than mem)\n"); + else if (cond_ret == ENOMEM) + DEBUG_PRINT_ERROR("Insufficient memory to initialise \ + condition variable\n"); + } + + sem_init(&sem_read_msg,0, 0); + sem_init(&sem_write_msg,0, 0); + sem_init(&sem_States,0, 0); + return; +} + + +/* ====================================================================== +FUNCTION + omx_amr_aenc::~omx_amr_aenc + +DESCRIPTION + Destructor + +PARAMETERS + None + +RETURN VALUE + None. +========================================================================== */ +omx_amr_aenc::~omx_amr_aenc() +{ + DEBUG_PRINT_ERROR("AMR Object getting destroyed comp-deinit=%d\n", + m_comp_deinit); + if ( !m_comp_deinit ) + { + deinit_encoder(); + } + pthread_mutexattr_destroy(&m_lock_attr); + pthread_mutex_destroy(&m_lock); + + pthread_mutexattr_destroy(&m_commandlock_attr); + pthread_mutex_destroy(&m_commandlock); + + pthread_mutexattr_destroy(&m_outputlock_attr); + pthread_mutex_destroy(&m_outputlock); + + pthread_mutexattr_destroy(&m_state_attr); + pthread_mutex_destroy(&m_state_lock); + + pthread_mutexattr_destroy(&m_event_attr); + pthread_mutex_destroy(&m_event_lock); + + pthread_mutexattr_destroy(&m_flush_attr); + pthread_mutex_destroy(&m_flush_lock); + + pthread_mutexattr_destroy(&m_in_th_attr); + pthread_mutex_destroy(&m_in_th_lock); + + pthread_mutexattr_destroy(&m_out_th_attr); + pthread_mutex_destroy(&m_out_th_lock); + + pthread_mutexattr_destroy(&out_buf_count_lock_attr); + pthread_mutex_destroy(&out_buf_count_lock); + + pthread_mutexattr_destroy(&in_buf_count_lock_attr); + pthread_mutex_destroy(&in_buf_count_lock); + + pthread_mutexattr_destroy(&m_in_th_attr_1); + pthread_mutex_destroy(&m_in_th_lock_1); + + pthread_mutexattr_destroy(&m_out_th_attr_1); + pthread_mutex_destroy(&m_out_th_lock_1); + pthread_mutex_destroy(&out_buf_count_lock); + pthread_mutex_destroy(&in_buf_count_lock); + pthread_cond_destroy(&cond); + pthread_cond_destroy(&in_cond); + pthread_cond_destroy(&out_cond); + sem_destroy (&sem_read_msg); + sem_destroy (&sem_write_msg); + sem_destroy (&sem_States); + DEBUG_PRINT_ERROR("OMX AMR component destroyed\n"); + return; +} + +/** + @brief memory function for sending EmptyBufferDone event + back to IL client + + @param bufHdr OMX buffer header to be passed back to IL client + @return none + */ +void omx_amr_aenc::buffer_done_cb(OMX_BUFFERHEADERTYPE *bufHdr) +{ + if (m_cb.EmptyBufferDone) + { + PrintFrameHdr(OMX_COMPONENT_GENERATE_BUFFER_DONE,bufHdr); + bufHdr->nFilledLen = 0; + + m_cb.EmptyBufferDone(&m_cmp, m_app_data, bufHdr); + pthread_mutex_lock(&in_buf_count_lock); + m_amr_pb_stats.ebd_cnt++; + nNumInputBuf--; + DEBUG_DETAIL("EBD CB:: in_buf_len=%d nNumInputBuf=%d ebd_cnt=%d\n",\ + m_amr_pb_stats.tot_in_buf_len, + nNumInputBuf, m_amr_pb_stats.ebd_cnt); + pthread_mutex_unlock(&in_buf_count_lock); + } + + return; +} + +/*============================================================================= +FUNCTION: + flush_ack + +DESCRIPTION: + + +INPUT/OUTPUT PARAMETERS: + None + +RETURN VALUE: + None + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +void omx_amr_aenc::flush_ack() +{ + // Decrement the FLUSH ACK count and notify the waiting recepients + pthread_mutex_lock(&m_flush_lock); + --m_flush_cnt; + if (0 == m_flush_cnt) + { + event_complete(); + } + DEBUG_PRINT("Rxed FLUSH ACK cnt=%d\n",m_flush_cnt); + pthread_mutex_unlock(&m_flush_lock); +} +void omx_amr_aenc::frame_done_cb(OMX_BUFFERHEADERTYPE *bufHdr) +{ + if (m_cb.FillBufferDone) + { + PrintFrameHdr(OMX_COMPONENT_GENERATE_FRAME_DONE,bufHdr); + m_amr_pb_stats.fbd_cnt++; + pthread_mutex_lock(&out_buf_count_lock); + nNumOutputBuf--; + DEBUG_PRINT("FBD CB:: nNumOutputBuf=%d out_buf_len=%u fbd_cnt=%u\n",\ + nNumOutputBuf, + m_amr_pb_stats.tot_out_buf_len, + m_amr_pb_stats.fbd_cnt); + m_amr_pb_stats.tot_out_buf_len += bufHdr->nFilledLen; + m_amr_pb_stats.tot_pb_time = bufHdr->nTimeStamp; + DEBUG_PRINT("FBD:in_buf_len=%u out_buf_len=%u\n", + m_amr_pb_stats.tot_in_buf_len, + m_amr_pb_stats.tot_out_buf_len); + + pthread_mutex_unlock(&out_buf_count_lock); + m_cb.FillBufferDone(&m_cmp, m_app_data, bufHdr); + } + return; +} + +/*============================================================================= +FUNCTION: + process_out_port_msg + +DESCRIPTION: + Function for handling all commands from IL client +IL client commands are processed and callbacks are generated through +this routine Audio Command Server provides the thread context for this routine + +INPUT/OUTPUT PARAMETERS: + [INOUT] client_data + [IN] id + +RETURN VALUE: + None + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +void omx_amr_aenc::process_out_port_msg(void *client_data, unsigned char id) +{ + unsigned long p1 = 0; // Parameter - 1 + unsigned long p2 = 0; // Parameter - 2 + unsigned char ident = 0; + unsigned qsize = 0; // qsize + unsigned tot_qsize = 0; + omx_amr_aenc *pThis = (omx_amr_aenc *) client_data; + OMX_STATETYPE state; + +loopback_out: + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + if ( state == OMX_StateLoaded ) + { + DEBUG_PRINT(" OUT: IN LOADED STATE RETURN\n"); + return; + } + pthread_mutex_lock(&pThis->m_outputlock); + + qsize = pThis->m_output_ctrl_cmd_q.m_size; + tot_qsize = pThis->m_output_ctrl_cmd_q.m_size; + tot_qsize += pThis->m_output_ctrl_fbd_q.m_size; + tot_qsize += pThis->m_output_q.m_size; + + if ( 0 == tot_qsize ) + { + pthread_mutex_unlock(&pThis->m_outputlock); + DEBUG_DETAIL("OUT-->BREAK FROM LOOP...%d\n",tot_qsize); + return; + } + if ( (state != OMX_StateExecuting) && !qsize ) + { + pthread_mutex_unlock(&pThis->m_outputlock); + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + if ( state == OMX_StateLoaded ) + return; + + DEBUG_DETAIL("OUT:1.SLEEPING OUT THREAD\n"); + pthread_mutex_lock(&pThis->m_out_th_lock_1); + pThis->is_out_th_sleep = true; + pthread_mutex_unlock(&pThis->m_out_th_lock_1); + pThis->out_th_goto_sleep(); + + /* Get the updated state */ + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + } + + if ( ((!pThis->m_output_ctrl_cmd_q.m_size) && !pThis->m_out_bEnabled) ) + { + // case where no port reconfig and nothing in the flush q + DEBUG_DETAIL("No flush/port reconfig qsize=%d tot_qsize=%d",\ + qsize,tot_qsize); + pthread_mutex_unlock(&pThis->m_outputlock); + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + if ( state == OMX_StateLoaded ) + return; + + if(pThis->m_output_ctrl_cmd_q.m_size || !(pThis->bFlushinprogress)) + { + DEBUG_PRINT("OUT:2. SLEEPING OUT THREAD \n"); + pthread_mutex_lock(&pThis->m_out_th_lock_1); + pThis->is_out_th_sleep = true; + pthread_mutex_unlock(&pThis->m_out_th_lock_1); + pThis->out_th_goto_sleep(); + } + /* Get the updated state */ + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + } + qsize = pThis->m_output_ctrl_cmd_q.m_size; + tot_qsize = pThis->m_output_ctrl_cmd_q.m_size; + tot_qsize += pThis->m_output_ctrl_fbd_q.m_size; + tot_qsize += pThis->m_output_q.m_size; + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + DEBUG_DETAIL("OUT-->QSIZE-flush=%d,fbd=%d QSIZE=%d state=%d\n",\ + pThis->m_output_ctrl_cmd_q.m_size, + pThis->m_output_ctrl_fbd_q.m_size, + pThis->m_output_q.m_size,state); + + + if (qsize) + { + // process FLUSH message + pThis->m_output_ctrl_cmd_q.pop_entry(&p1,&p2,&ident); + } else if ( (qsize = pThis->m_output_ctrl_fbd_q.m_size) && + (pThis->m_out_bEnabled) && (state == OMX_StateExecuting) ) + { + // then process EBD's + pThis->m_output_ctrl_fbd_q.pop_entry(&p1,&p2,&ident); + } else if ( (qsize = pThis->m_output_q.m_size) && + (pThis->m_out_bEnabled) && (state == OMX_StateExecuting) ) + { + // if no FLUSH and FBD's then process FTB's + pThis->m_output_q.pop_entry(&p1,&p2,&ident); + } else if ( state == OMX_StateLoaded ) + { + pthread_mutex_unlock(&pThis->m_outputlock); + DEBUG_PRINT("IN: ***in OMX_StateLoaded so exiting\n"); + return ; + } else + { + qsize = 0; + DEBUG_PRINT("OUT--> Empty Queue state=%d %d %d %d\n",state, + pThis->m_output_ctrl_cmd_q.m_size, + pThis->m_output_ctrl_fbd_q.m_size, + pThis->m_output_q.m_size); + + if(state == OMX_StatePause) + { + DEBUG_DETAIL("OUT: SLEEPING AGAIN OUT THREAD\n"); + pthread_mutex_lock(&pThis->m_out_th_lock_1); + pThis->is_out_th_sleep = true; + pthread_mutex_unlock(&pThis->m_out_th_lock_1); + pthread_mutex_unlock(&pThis->m_outputlock); + pThis->out_th_goto_sleep(); + goto loopback_out; + } + } + pthread_mutex_unlock(&pThis->m_outputlock); + + if ( qsize > 0 ) + { + id = ident; + ident = 0; + DEBUG_DETAIL("OUT->state[%d]ident[%d]flushq[%d]fbd[%d]dataq[%d]\n",\ + pThis->m_state, + ident, + pThis->m_output_ctrl_cmd_q.m_size, + pThis->m_output_ctrl_fbd_q.m_size, + pThis->m_output_q.m_size); + + if ( OMX_COMPONENT_GENERATE_FRAME_DONE == id ) + { + pThis->frame_done_cb((OMX_BUFFERHEADERTYPE *)p2); + } else if ( OMX_COMPONENT_GENERATE_FTB == id ) + { + pThis->fill_this_buffer_proxy((OMX_HANDLETYPE)p1, + (OMX_BUFFERHEADERTYPE *)p2); + } else if ( OMX_COMPONENT_GENERATE_EOS == id ) + { + pThis->m_cb.EventHandler(&pThis->m_cmp, + pThis->m_app_data, + OMX_EventBufferFlag, + 1, 1, NULL ); + + } + else if(id == OMX_COMPONENT_RESUME) + { + DEBUG_PRINT("RESUMED...\n"); + } + else if(id == OMX_COMPONENT_GENERATE_COMMAND) + { + // Execute FLUSH command + if ( OMX_CommandFlush == p1 ) + { + DEBUG_DETAIL("Executing FLUSH command on Output port\n"); + pThis->execute_output_omx_flush(); + } else + { + DEBUG_DETAIL("Invalid command[%lu]\n",p1); + } + } else + { + DEBUG_PRINT_ERROR("ERROR:OUT-->Invalid Id[%d]\n",id); + } + } else + { + DEBUG_DETAIL("ERROR: OUT--> Empty OUTPUTQ\n"); + } + + return; +} + +/*============================================================================= +FUNCTION: + process_command_msg + +DESCRIPTION: + + +INPUT/OUTPUT PARAMETERS: + [INOUT] client_data + [IN] id + +RETURN VALUE: + None + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +void omx_amr_aenc::process_command_msg(void *client_data, unsigned char id) +{ + unsigned long p1 = 0; // Parameter - 1 + unsigned long p2 = 0; // Parameter - 2 + unsigned char ident = 0; + unsigned qsize = 0; + omx_amr_aenc *pThis = (omx_amr_aenc*)client_data; + pthread_mutex_lock(&pThis->m_commandlock); + + qsize = pThis->m_command_q.m_size; + DEBUG_DETAIL("CMD-->QSIZE=%d state=%d\n",pThis->m_command_q.m_size, + pThis->m_state); + + if (!qsize) + { + DEBUG_DETAIL("CMD-->BREAKING FROM LOOP\n"); + pthread_mutex_unlock(&pThis->m_commandlock); + return; + } else + { + pThis->m_command_q.pop_entry(&p1,&p2,&ident); + } + pthread_mutex_unlock(&pThis->m_commandlock); + + id = ident; + DEBUG_DETAIL("CMD->state[%d]id[%d]cmdq[%d]n",\ + pThis->m_state,ident, \ + pThis->m_command_q.m_size); + + if (OMX_COMPONENT_GENERATE_EVENT == id) + { + if (pThis->m_cb.EventHandler) + { + if (OMX_CommandStateSet == p1) + { + pthread_mutex_lock(&pThis->m_state_lock); + pThis->m_state = (OMX_STATETYPE) p2; + pthread_mutex_unlock(&pThis->m_state_lock); + DEBUG_PRINT("CMD:Process->state set to %d \n", \ + pThis->m_state); + + if (pThis->m_state == OMX_StateExecuting || + pThis->m_state == OMX_StateLoaded) + { + + pthread_mutex_lock(&pThis->m_in_th_lock_1); + if (pThis->is_in_th_sleep) + { + pThis->is_in_th_sleep = false; + DEBUG_DETAIL("CMD:WAKING UP IN THREADS\n"); + pThis->in_th_wakeup(); + } + pthread_mutex_unlock(&pThis->m_in_th_lock_1); + + pthread_mutex_lock(&pThis->m_out_th_lock_1); + if (pThis->is_out_th_sleep) + { + DEBUG_DETAIL("CMD:WAKING UP OUT THREADS\n"); + pThis->is_out_th_sleep = false; + pThis->out_th_wakeup(); + } + pthread_mutex_unlock(&pThis->m_out_th_lock_1); + } + } + if (OMX_StateInvalid == pThis->m_state) + { + pThis->m_cb.EventHandler(&pThis->m_cmp, + pThis->m_app_data, + OMX_EventError, + OMX_ErrorInvalidState, + 0, NULL ); + } else if ((signed)p2 == OMX_ErrorPortUnpopulated) + { + pThis->m_cb.EventHandler(&pThis->m_cmp, + pThis->m_app_data, + OMX_EventError, + (OMX_U32)p2, + 0, + 0 ); + } else + { + pThis->m_cb.EventHandler(&pThis->m_cmp, + pThis->m_app_data, + OMX_EventCmdComplete, + (OMX_U32)p1, (OMX_U32)p2, NULL ); + } + } else + { + DEBUG_PRINT_ERROR("ERROR:CMD-->EventHandler NULL \n"); + } + } else if (OMX_COMPONENT_GENERATE_COMMAND == id) + { + pThis->send_command_proxy(&pThis->m_cmp, + (OMX_COMMANDTYPE)p1, + (OMX_U32)p2,(OMX_PTR)NULL); + } else if (OMX_COMPONENT_PORTSETTINGS_CHANGED == id) + { + DEBUG_DETAIL("CMD-->RXED PORTSETTINGS_CHANGED"); + pThis->m_cb.EventHandler(&pThis->m_cmp, + pThis->m_app_data, + OMX_EventPortSettingsChanged, + 1, 1, NULL ); + } + else + { + DEBUG_PRINT_ERROR("CMD->state[%d]id[%d]\n",pThis->m_state,ident); + } + return; +} + +/*============================================================================= +FUNCTION: + process_in_port_msg + +DESCRIPTION: + + +INPUT/OUTPUT PARAMETERS: + [INOUT] client_data + [IN] id + +RETURN VALUE: + None + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +void omx_amr_aenc::process_in_port_msg(void *client_data, unsigned char id) +{ + unsigned long p1 = 0; // Parameter - 1 + unsigned long p2 = 0; // Parameter - 2 + unsigned char ident = 0; + unsigned qsize = 0; + unsigned tot_qsize = 0; + omx_amr_aenc *pThis = (omx_amr_aenc *) client_data; + OMX_STATETYPE state; + + if (!pThis) + { + DEBUG_PRINT_ERROR("ERROR:IN--> Invalid Obj \n"); + return; + } +loopback_in: + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + if ( state == OMX_StateLoaded ) + { + DEBUG_PRINT(" IN: IN LOADED STATE RETURN\n"); + return; + } + // Protect the shared queue data structure + pthread_mutex_lock(&pThis->m_lock); + + qsize = pThis->m_input_ctrl_cmd_q.m_size; + tot_qsize = qsize; + tot_qsize += pThis->m_input_ctrl_ebd_q.m_size; + tot_qsize += pThis->m_input_q.m_size; + + if ( 0 == tot_qsize ) + { + DEBUG_DETAIL("IN-->BREAKING FROM IN LOOP"); + pthread_mutex_unlock(&pThis->m_lock); + return; + } + + if ( (state != OMX_StateExecuting) && ! (pThis->m_input_ctrl_cmd_q.m_size)) + { + pthread_mutex_unlock(&pThis->m_lock); + DEBUG_DETAIL("SLEEPING IN THREAD\n"); + pthread_mutex_lock(&pThis->m_in_th_lock_1); + pThis->is_in_th_sleep = true; + pthread_mutex_unlock(&pThis->m_in_th_lock_1); + pThis->in_th_goto_sleep(); + + /* Get the updated state */ + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + } + else if ((state == OMX_StatePause)) + { + if(!(pThis->m_input_ctrl_cmd_q.m_size)) + { + pthread_mutex_unlock(&pThis->m_lock); + + DEBUG_DETAIL("IN: SLEEPING IN THREAD\n"); + pthread_mutex_lock(&pThis->m_in_th_lock_1); + pThis->is_in_th_sleep = true; + pthread_mutex_unlock(&pThis->m_in_th_lock_1); + pThis->in_th_goto_sleep(); + + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + } + } + + qsize = pThis->m_input_ctrl_cmd_q.m_size; + tot_qsize = qsize; + tot_qsize += pThis->m_input_ctrl_ebd_q.m_size; + tot_qsize += pThis->m_input_q.m_size; + + DEBUG_DETAIL("Input-->QSIZE-flush=%d,ebd=%d QSIZE=%d state=%d\n",\ + pThis->m_input_ctrl_cmd_q.m_size, + pThis->m_input_ctrl_ebd_q.m_size, + pThis->m_input_q.m_size, state); + + + if ( qsize ) + { + // process FLUSH message + pThis->m_input_ctrl_cmd_q.pop_entry(&p1,&p2,&ident); + } else if ( (qsize = pThis->m_input_ctrl_ebd_q.m_size) && + (state == OMX_StateExecuting) ) + { + // then process EBD's + pThis->m_input_ctrl_ebd_q.pop_entry(&p1,&p2,&ident); + } else if ((qsize = pThis->m_input_q.m_size) && + (state == OMX_StateExecuting)) + { + // if no FLUSH and EBD's then process ETB's + pThis->m_input_q.pop_entry(&p1, &p2, &ident); + } else if ( state == OMX_StateLoaded ) + { + pthread_mutex_unlock(&pThis->m_lock); + DEBUG_PRINT("IN: ***in OMX_StateLoaded so exiting\n"); + return ; + } else + { + qsize = 0; + DEBUG_PRINT("IN-->state[%d]cmdq[%d]ebdq[%d]in[%d]\n",\ + state,pThis->m_input_ctrl_cmd_q.m_size, + pThis->m_input_ctrl_ebd_q.m_size, + pThis->m_input_q.m_size); + + if(state == OMX_StatePause) + { + DEBUG_DETAIL("IN: SLEEPING AGAIN IN THREAD\n"); + pthread_mutex_lock(&pThis->m_in_th_lock_1); + pThis->is_in_th_sleep = true; + pthread_mutex_unlock(&pThis->m_in_th_lock_1); + pthread_mutex_unlock(&pThis->m_lock); + pThis->in_th_goto_sleep(); + goto loopback_in; + } + } + pthread_mutex_unlock(&pThis->m_lock); + + if ( qsize > 0 ) + { + id = ident; + DEBUG_DETAIL("Input->state[%d]id[%d]flushq[%d]ebdq[%d]dataq[%d]\n",\ + pThis->m_state, + ident, + pThis->m_input_ctrl_cmd_q.m_size, + pThis->m_input_ctrl_ebd_q.m_size, + pThis->m_input_q.m_size); + if ( OMX_COMPONENT_GENERATE_BUFFER_DONE == id ) + { + pThis->buffer_done_cb((OMX_BUFFERHEADERTYPE *)p2); + } + else if(id == OMX_COMPONENT_GENERATE_EOS) + { + pThis->m_cb.EventHandler(&pThis->m_cmp, pThis->m_app_data, + OMX_EventBufferFlag, 0, 1, NULL ); + } else if ( OMX_COMPONENT_GENERATE_ETB == id ) + { + pThis->empty_this_buffer_proxy((OMX_HANDLETYPE)p1, + (OMX_BUFFERHEADERTYPE *)p2); + } else if ( OMX_COMPONENT_GENERATE_COMMAND == id ) + { + // Execute FLUSH command + if ( OMX_CommandFlush == p1 ) + { + DEBUG_DETAIL(" Executing FLUSH command on Input port\n"); + pThis->execute_input_omx_flush(); + } else + { + DEBUG_DETAIL("Invalid command[%lu]\n",p1); + } + } + else + { + DEBUG_PRINT_ERROR("ERROR:IN-->Invalid Id[%d]\n",id); + } + } else + { + DEBUG_DETAIL("ERROR:IN-->Empty INPUT Q\n"); + } + return; +} + +/** + @brief member function for performing component initialization + + @param role C string mandating role of this component + @return Error status + */ +OMX_ERRORTYPE omx_amr_aenc::component_init(OMX_STRING role) +{ + + OMX_ERRORTYPE eRet = OMX_ErrorNone; + m_state = OMX_StateLoaded; + + /* DSP does not give information about the bitstream + randomly assign the value right now. Query will result in + incorrect param */ + memset(&m_amr_param, 0, sizeof(m_amr_param)); + m_amr_param.nSize = (OMX_U32)sizeof(m_amr_param); + m_amr_param.nChannels = OMX_AMR_DEFAULT_CH_CFG; + m_volume = OMX_AMR_DEFAULT_VOL; /* Close to unity gain */ + memset(&m_amr_pb_stats,0,sizeof(AMR_PB_STATS)); + memset(&m_pcm_param, 0, sizeof(m_pcm_param)); + m_pcm_param.nSize = (OMX_U32)sizeof(m_pcm_param); + m_pcm_param.nChannels = OMX_AMR_DEFAULT_CH_CFG; + m_pcm_param.nSamplingRate = OMX_AMR_DEFAULT_SF; + nTimestamp = 0; + ts = 0; + + nNumInputBuf = 0; + nNumOutputBuf = 0; + m_ipc_to_in_th = NULL; // Command server instance + m_ipc_to_out_th = NULL; // Client server instance + m_ipc_to_cmd_th = NULL; // command instance + m_is_out_th_sleep = 0; + m_is_in_th_sleep = 0; + is_out_th_sleep= false; + + is_in_th_sleep=false; + + memset(&m_priority_mgm, 0, sizeof(m_priority_mgm)); + m_priority_mgm.nGroupID =0; + m_priority_mgm.nGroupPriority=0; + + memset(&m_buffer_supplier, 0, sizeof(m_buffer_supplier)); + m_buffer_supplier.nPortIndex=OMX_BufferSupplyUnspecified; + + DEBUG_PRINT_ERROR(" component init: role = %s\n",role); + + DEBUG_PRINT(" component init: role = %s\n",role); + component_Role.nVersion.nVersion = OMX_SPEC_VERSION; + if (!strcmp(role,"OMX.qcom.audio.encoder.amrnb")) + { + pcm_input = 1; + component_Role.nSize = (OMX_U32)sizeof(role); + strlcpy((char *)component_Role.cRole, (const char*)role, + sizeof(component_Role.cRole)); + DEBUG_PRINT("\ncomponent_init: Component %s LOADED \n", role); + } else if (!strcmp(role,"OMX.qcom.audio.encoder.tunneled.amrnb")) + { + pcm_input = 0; + component_Role.nSize = (OMX_U32)sizeof(role); + strlcpy((char *)component_Role.cRole, (const char*)role, + sizeof(component_Role.cRole)); + DEBUG_PRINT("\ncomponent_init: Component %s LOADED \n", role); + } else + { + component_Role.nSize = (OMX_U32)sizeof("\0"); + strlcpy((char *)component_Role.cRole, (const char*)"\0", + sizeof(component_Role.cRole)); + DEBUG_PRINT("\ncomponent_init: Component %s LOADED is invalid\n", role); + } + if(pcm_input) + { + m_tmp_meta_buf = (OMX_U8*) malloc(sizeof(OMX_U8) * + (OMX_CORE_INPUT_BUFFER_SIZE + sizeof(META_IN))); + + if (m_tmp_meta_buf == NULL){ + DEBUG_PRINT_ERROR("Mem alloc failed for tmp meta buf\n"); + return OMX_ErrorInsufficientResources; + } + } + m_tmp_out_meta_buf = + (OMX_U8*)malloc(sizeof(OMX_U8)*OMX_AMR_OUTPUT_BUFFER_SIZE); + if ( m_tmp_out_meta_buf == NULL ){ + DEBUG_PRINT_ERROR("Mem alloc failed for out meta buf\n"); + return OMX_ErrorInsufficientResources; + } + + if(0 == pcm_input) + { + m_drv_fd = open("/dev/msm_amrnb_in",O_RDONLY); + DEBUG_PRINT("Driver in Tunnel mode open\n"); + } + else + { + m_drv_fd = open("/dev/msm_amrnb_in",O_RDWR); + DEBUG_PRINT("Driver in Non Tunnel mode open\n"); + } + if (m_drv_fd < 0) + { + DEBUG_PRINT_ERROR("Component_init Open Failed[%d] errno[%d]",\ + m_drv_fd,errno); + + return OMX_ErrorInsufficientResources; + } + if(ioctl(m_drv_fd, AUDIO_GET_SESSION_ID,&m_session_id) == -1) + { + DEBUG_PRINT_ERROR("AUDIO_GET_SESSION_ID FAILED\n"); + } + if(pcm_input) + { + if (!m_ipc_to_in_th) + { + m_ipc_to_in_th = omx_amr_thread_create(process_in_port_msg, + this, (char *)"INPUT_THREAD"); + if (!m_ipc_to_in_th) + { + DEBUG_PRINT_ERROR("ERROR!!! Failed to start \ + Input port thread\n"); + return OMX_ErrorInsufficientResources; + } + } + } + + if (!m_ipc_to_cmd_th) + { + m_ipc_to_cmd_th = omx_amr_thread_create(process_command_msg, + this, (char *)"CMD_THREAD"); + if (!m_ipc_to_cmd_th) + { + DEBUG_PRINT_ERROR("ERROR!!!Failed to start " + "command message thread\n"); + return OMX_ErrorInsufficientResources; + } + } + + if (!m_ipc_to_out_th) + { + m_ipc_to_out_th = omx_amr_thread_create(process_out_port_msg, + this, (char *)"OUTPUT_THREAD"); + if (!m_ipc_to_out_th) + { + DEBUG_PRINT_ERROR("ERROR!!! Failed to start output " + "port thread\n"); + return OMX_ErrorInsufficientResources; + } + } + return eRet; +} + +/** + + @brief member function to retrieve version of component + + + + @param hComp handle to this component instance + @param componentName name of component + @param componentVersion pointer to memory space which stores the + version number + @param specVersion pointer to memory sapce which stores version of + openMax specification + @param componentUUID + @return Error status + */ +OMX_ERRORTYPE omx_amr_aenc::get_component_version +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_OUT OMX_STRING componentName, + OMX_OUT OMX_VERSIONTYPE* componentVersion, + OMX_OUT OMX_VERSIONTYPE* specVersion, + OMX_OUT OMX_UUIDTYPE* componentUUID) +{ + if((hComp == NULL) || (componentName == NULL) || + (specVersion == NULL) || (componentUUID == NULL)) + { + componentVersion = NULL; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_state == OMX_StateInvalid) + { + DEBUG_PRINT_ERROR("Get Comp Version in Invalid State\n"); + return OMX_ErrorInvalidState; + } + componentVersion->nVersion = OMX_SPEC_VERSION; + specVersion->nVersion = OMX_SPEC_VERSION; + return OMX_ErrorNone; +} +/** + @brief member function handles command from IL client + + This function simply queue up commands from IL client. + Commands will be processed in command server thread context later + + @param hComp handle to component instance + @param cmd type of command + @param param1 parameters associated with the command type + @param cmdData + @return Error status +*/ +OMX_ERRORTYPE omx_amr_aenc::send_command(OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_COMMANDTYPE cmd, + OMX_IN OMX_U32 param1, + OMX_IN OMX_PTR cmdData) +{ + int portIndex = (int)param1; + + if(hComp == NULL) + { + cmdData = cmdData; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (OMX_StateInvalid == m_state) + { + return OMX_ErrorInvalidState; + } + if ( (cmd == OMX_CommandFlush) && (portIndex > 1) ) + { + return OMX_ErrorBadPortIndex; + } + post_command((unsigned)cmd,(unsigned)param1,OMX_COMPONENT_GENERATE_COMMAND); + DEBUG_PRINT("Send Command : returns with OMX_ErrorNone \n"); + DEBUG_PRINT("send_command : recieved state before semwait= %u\n",param1); + sem_wait (&sem_States); + DEBUG_PRINT("send_command : recieved state after semwait\n"); + return OMX_ErrorNone; +} + +/** + @brief member function performs actual processing of commands excluding + empty buffer call + + @param hComp handle to component + @param cmd command type + @param param1 parameter associated with the command + @param cmdData + + @return error status +*/ +OMX_ERRORTYPE omx_amr_aenc::send_command_proxy(OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_COMMANDTYPE cmd, + OMX_IN OMX_U32 param1, + OMX_IN OMX_PTR cmdData) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + // Handle only IDLE and executing + OMX_STATETYPE eState = (OMX_STATETYPE) param1; + int bFlag = 1; + nState = eState; + + if(hComp == NULL) + { + cmdData = cmdData; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (OMX_CommandStateSet == cmd) + { + /***************************/ + /* Current State is Loaded */ + /***************************/ + if (OMX_StateLoaded == m_state) + { + if (OMX_StateIdle == eState) + { + + if (allocate_done() || + (m_inp_bEnabled == OMX_FALSE + && m_out_bEnabled == OMX_FALSE)) + { + DEBUG_PRINT("SCP-->Allocate Done Complete\n"); + } + else + { + DEBUG_PRINT("SCP-->Loaded to Idle-Pending\n"); + BITMASK_SET(&m_flags, OMX_COMPONENT_IDLE_PENDING); + bFlag = 0; + } + + } else if (eState == OMX_StateLoaded) + { + DEBUG_PRINT("OMXCORE-SM: Loaded-->Loaded\n"); + m_cb.EventHandler(&this->m_cmp, + this->m_app_data, + OMX_EventError, + OMX_ErrorSameState, + 0, NULL ); + eRet = OMX_ErrorSameState; + } + + else if (eState == OMX_StateWaitForResources) + { + DEBUG_PRINT("OMXCORE-SM: Loaded-->WaitForResources\n"); + eRet = OMX_ErrorNone; + } + + else if (eState == OMX_StateExecuting) + { + DEBUG_PRINT("OMXCORE-SM: Loaded-->Executing\n"); + m_cb.EventHandler(&this->m_cmp, + this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } + + else if (eState == OMX_StatePause) + { + DEBUG_PRINT("OMXCORE-SM: Loaded-->Pause\n"); + m_cb.EventHandler(&this->m_cmp, + this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } + + else if (eState == OMX_StateInvalid) + { + DEBUG_PRINT("OMXCORE-SM: Loaded-->Invalid\n"); + m_cb.EventHandler(&this->m_cmp, + this->m_app_data, + OMX_EventError, + OMX_ErrorInvalidState, + 0, NULL ); + m_state = OMX_StateInvalid; + eRet = OMX_ErrorInvalidState; + } else + { + DEBUG_PRINT_ERROR("SCP-->Loaded to Invalid(%d))\n",eState); + eRet = OMX_ErrorBadParameter; + } + } + + /***************************/ + /* Current State is IDLE */ + /***************************/ + else if (OMX_StateIdle == m_state) + { + if (OMX_StateLoaded == eState) + { + if (release_done(-1)) + { + if (ioctl(m_drv_fd, AUDIO_STOP, 0) == -1) + { + DEBUG_PRINT_ERROR("SCP:Idle->Loaded,\ + ioctl stop failed %d\n", errno); + } + + nTimestamp=0; + ts = 0; + DEBUG_PRINT("SCP-->Idle to Loaded\n"); + } else + { + DEBUG_PRINT("SCP--> Idle to Loaded-Pending\n"); + BITMASK_SET(&m_flags, OMX_COMPONENT_LOADING_PENDING); + // Skip the event notification + bFlag = 0; + } + } + else if (OMX_StateExecuting == eState) + { + + struct msm_audio_amrnb_enc_config_v2 drv_amr_enc_config; + struct msm_audio_stream_config drv_stream_config; + struct msm_audio_buf_cfg buf_cfg; + struct msm_audio_config pcm_cfg; + + if(ioctl(m_drv_fd, AUDIO_GET_STREAM_CONFIG, &drv_stream_config) + == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_GET_STREAM_CONFIG failed, \ + errno[%d]\n", errno); + } + if(ioctl(m_drv_fd, AUDIO_SET_STREAM_CONFIG, &drv_stream_config) + == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_SET_STREAM_CONFIG failed, \ + errno[%d]\n", errno); + } + + if(ioctl(m_drv_fd, AUDIO_GET_AMRNB_ENC_CONFIG_V2, + &drv_amr_enc_config) == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_GET_AMRNB_ENC_CONFIG_V2 \ + failed, errno[%d]\n", errno); + } + drv_amr_enc_config.band_mode = m_amr_param.eAMRBandMode; + drv_amr_enc_config.dtx_enable = m_amr_param.eAMRDTXMode; + drv_amr_enc_config.frame_format = m_amr_param.eAMRFrameFormat; + if(ioctl(m_drv_fd, AUDIO_SET_AMRNB_ENC_CONFIG_V2, &drv_amr_enc_config) + == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_SET_AMRNB_ENC_CONFIG_V2 \ + failed, errno[%d]\n", errno); + } + if (ioctl(m_drv_fd, AUDIO_GET_BUF_CFG, &buf_cfg) == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_GET_BUF_CFG, errno[%d]\n", + errno); + } + buf_cfg.meta_info_enable = 1; + buf_cfg.frames_per_buf = NUMOFFRAMES; + if (ioctl(m_drv_fd, AUDIO_SET_BUF_CFG, &buf_cfg) == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_SET_BUF_CFG, errno[%d]\n", + errno); + } + if(pcm_input) + { + if (ioctl(m_drv_fd, AUDIO_GET_CONFIG, &pcm_cfg) == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_GET_CONFIG, errno[%d]\n", + errno); + } + pcm_cfg.channel_count = m_pcm_param.nChannels; + pcm_cfg.sample_rate = m_pcm_param.nSamplingRate; + DEBUG_PRINT("pcm config %u %u\n",m_pcm_param.nChannels, + m_pcm_param.nSamplingRate); + + if (ioctl(m_drv_fd, AUDIO_SET_CONFIG, &pcm_cfg) == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_SET_CONFIG, errno[%d]\n", + errno); + } + } + if(ioctl(m_drv_fd, AUDIO_START, 0) == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_START failed, errno[%d]\n", + errno); + m_state = OMX_StateInvalid; + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, OMX_ErrorInvalidState, + 0, NULL ); + eRet = OMX_ErrorInvalidState; + } + DEBUG_PRINT("SCP-->Idle to Executing\n"); + nState = eState; + } else if (eState == OMX_StateIdle) + { + DEBUG_PRINT("OMXCORE-SM: Idle-->Idle\n"); + m_cb.EventHandler(&this->m_cmp, + this->m_app_data, + OMX_EventError, + OMX_ErrorSameState, + 0, NULL ); + eRet = OMX_ErrorSameState; + } else if (eState == OMX_StateWaitForResources) + { + DEBUG_PRINT("OMXCORE-SM: Idle-->WaitForResources\n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } + + else if (eState == OMX_StatePause) + { + DEBUG_PRINT("OMXCORE-SM: Idle-->Pause\n"); + } + + else if (eState == OMX_StateInvalid) + { + DEBUG_PRINT("OMXCORE-SM: Idle-->Invalid\n"); + m_state = OMX_StateInvalid; + this->m_cb.EventHandler(&this->m_cmp, + this->m_app_data, + OMX_EventError, + OMX_ErrorInvalidState, + 0, NULL ); + eRet = OMX_ErrorInvalidState; + } else + { + DEBUG_PRINT_ERROR("SCP--> Idle to %d Not Handled\n",eState); + eRet = OMX_ErrorBadParameter; + } + } + + /******************************/ + /* Current State is Executing */ + /******************************/ + else if (OMX_StateExecuting == m_state) + { + if (OMX_StateIdle == eState) + { + DEBUG_PRINT("SCP-->Executing to Idle \n"); + if(pcm_input) + execute_omx_flush(-1,false); + else + execute_omx_flush(1,false); + + + } else if (OMX_StatePause == eState) + { + DEBUG_DETAIL("*************************\n"); + DEBUG_PRINT("SCP-->RXED PAUSE STATE\n"); + DEBUG_DETAIL("*************************\n"); + //ioctl(m_drv_fd, AUDIO_PAUSE, 0); + } else if (eState == OMX_StateLoaded) + { + DEBUG_PRINT("\n OMXCORE-SM: Executing --> Loaded \n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } else if (eState == OMX_StateWaitForResources) + { + DEBUG_PRINT("\n OMXCORE-SM: Executing --> WaitForResources \n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } else if (eState == OMX_StateExecuting) + { + DEBUG_PRINT("\n OMXCORE-SM: Executing --> Executing \n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, OMX_ErrorSameState, + 0, NULL ); + eRet = OMX_ErrorSameState; + } else if (eState == OMX_StateInvalid) + { + DEBUG_PRINT("\n OMXCORE-SM: Executing --> Invalid \n"); + m_state = OMX_StateInvalid; + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, OMX_ErrorInvalidState, + 0, NULL ); + eRet = OMX_ErrorInvalidState; + } else + { + DEBUG_PRINT_ERROR("SCP--> Executing to %d Not Handled\n", + eState); + eRet = OMX_ErrorBadParameter; + } + } + /***************************/ + /* Current State is Pause */ + /***************************/ + else if (OMX_StatePause == m_state) + { + if( (eState == OMX_StateExecuting || eState == OMX_StateIdle) ) + { + pthread_mutex_lock(&m_out_th_lock_1); + if(is_out_th_sleep) + { + DEBUG_DETAIL("PE: WAKING UP OUT THREAD\n"); + is_out_th_sleep = false; + out_th_wakeup(); + } + pthread_mutex_unlock(&m_out_th_lock_1); + } + if ( OMX_StateExecuting == eState ) + { + nState = eState; + } else if ( OMX_StateIdle == eState ) + { + DEBUG_PRINT("SCP-->Paused to Idle \n"); + DEBUG_PRINT ("\n Internal flush issued"); + pthread_mutex_lock(&m_flush_lock); + m_flush_cnt = 2; + pthread_mutex_unlock(&m_flush_lock); + if(pcm_input) + execute_omx_flush(-1,false); + else + execute_omx_flush(1,false); + + } else if ( eState == OMX_StateLoaded ) + { + DEBUG_PRINT("\n Pause --> loaded \n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } else if (eState == OMX_StateWaitForResources) + { + DEBUG_PRINT("\n Pause --> WaitForResources \n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } else if (eState == OMX_StatePause) + { + DEBUG_PRINT("\n Pause --> Pause \n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, OMX_ErrorSameState, + 0, NULL ); + eRet = OMX_ErrorSameState; + } else if (eState == OMX_StateInvalid) + { + DEBUG_PRINT("\n Pause --> Invalid \n"); + m_state = OMX_StateInvalid; + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, OMX_ErrorInvalidState, + 0, NULL ); + eRet = OMX_ErrorInvalidState; + } else + { + DEBUG_PRINT("SCP-->Paused to %d Not Handled\n",eState); + eRet = OMX_ErrorBadParameter; + } + } + /**************************************/ + /* Current State is WaitForResources */ + /**************************************/ + else if (m_state == OMX_StateWaitForResources) + { + if (eState == OMX_StateLoaded) + { + DEBUG_PRINT("OMXCORE-SM: WaitForResources-->Loaded\n"); + } else if (eState == OMX_StateWaitForResources) + { + DEBUG_PRINT("OMXCORE-SM: \ + WaitForResources-->WaitForResources\n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, OMX_ErrorSameState, + 0, NULL ); + eRet = OMX_ErrorSameState; + } else if (eState == OMX_StateExecuting) + { + DEBUG_PRINT("OMXCORE-SM: WaitForResources-->Executing\n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } else if (eState == OMX_StatePause) + { + DEBUG_PRINT("OMXCORE-SM: WaitForResources-->Pause\n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } else if (eState == OMX_StateInvalid) + { + DEBUG_PRINT("OMXCORE-SM: WaitForResources-->Invalid\n"); + m_state = OMX_StateInvalid; + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorInvalidState, + 0, NULL ); + eRet = OMX_ErrorInvalidState; + } else + { + DEBUG_PRINT_ERROR("SCP--> %d to %d(Not Handled)\n", + m_state,eState); + eRet = OMX_ErrorBadParameter; + } + } + /****************************/ + /* Current State is Invalid */ + /****************************/ + else if (m_state == OMX_StateInvalid) + { + if (OMX_StateLoaded == eState || OMX_StateWaitForResources == eState + || OMX_StateIdle == eState || OMX_StateExecuting == eState + || OMX_StatePause == eState || OMX_StateInvalid == eState) + { + DEBUG_PRINT("OMXCORE-SM: Invalid-->Loaded/Idle/Executing" + "/Pause/Invalid/WaitForResources\n"); + m_state = OMX_StateInvalid; + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, OMX_ErrorInvalidState, + 0, NULL ); + eRet = OMX_ErrorInvalidState; + } + } else + { + DEBUG_PRINT_ERROR("OMXCORE-SM: %d --> %d(Not Handled)\n",\ + m_state,eState); + eRet = OMX_ErrorBadParameter; + } + } else if (OMX_CommandFlush == cmd) + { + DEBUG_DETAIL("*************************\n"); + DEBUG_PRINT("SCP-->RXED FLUSH COMMAND port=%u\n",param1); + DEBUG_DETAIL("*************************\n"); + bFlag = 0; + if ( param1 == OMX_CORE_INPUT_PORT_INDEX || + param1 == OMX_CORE_OUTPUT_PORT_INDEX || + (signed)param1 == -1 ) + { + execute_omx_flush(param1); + } else + { + eRet = OMX_ErrorBadPortIndex; + m_cb.EventHandler(&m_cmp, m_app_data, OMX_EventError, + OMX_CommandFlush, OMX_ErrorBadPortIndex, NULL ); + } + } else if ( cmd == OMX_CommandPortDisable ) + { + bFlag = 0; + if ( param1 == OMX_CORE_INPUT_PORT_INDEX || param1 == OMX_ALL ) + { + DEBUG_PRINT("SCP: Disabling Input port Indx\n"); + m_inp_bEnabled = OMX_FALSE; + if ( (m_state == OMX_StateLoaded || m_state == OMX_StateIdle) + && release_done(0) ) + { + DEBUG_PRINT("send_command_proxy:OMX_CommandPortDisable:\ + OMX_CORE_INPUT_PORT_INDEX:release_done \n"); + DEBUG_PRINT("************* OMX_CommandPortDisable:\ + m_inp_bEnabled=%d********\n",m_inp_bEnabled); + + post_command(OMX_CommandPortDisable, + OMX_CORE_INPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + } + + else + { + if (m_state == OMX_StatePause ||m_state == OMX_StateExecuting) + { + DEBUG_PRINT("SCP: execute_omx_flush in Disable in "\ + " param1=%u m_state=%d \n",param1, m_state); + execute_omx_flush(param1); + } + DEBUG_PRINT("send_command_proxy:OMX_CommandPortDisable:\ + OMX_CORE_INPUT_PORT_INDEX \n"); + BITMASK_SET(&m_flags, OMX_COMPONENT_INPUT_DISABLE_PENDING); + // Skip the event notification + + } + + } + if (param1 == OMX_CORE_OUTPUT_PORT_INDEX || param1 == OMX_ALL) + { + + DEBUG_PRINT("SCP: Disabling Output port Indx\n"); + m_out_bEnabled = OMX_FALSE; + if ((m_state == OMX_StateLoaded || m_state == OMX_StateIdle) + && release_done(1)) + { + DEBUG_PRINT("send_command_proxy:OMX_CommandPortDisable:\ + OMX_CORE_OUTPUT_PORT_INDEX:release_done \n"); + DEBUG_PRINT("************* OMX_CommandPortDisable:\ + m_out_bEnabled=%d********\n",m_inp_bEnabled); + + post_command(OMX_CommandPortDisable, + OMX_CORE_OUTPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + } else + { + if (m_state == OMX_StatePause ||m_state == OMX_StateExecuting) + { + DEBUG_PRINT("SCP: execute_omx_flush in Disable out "\ + "param1=%u m_state=%d \n",param1, m_state); + execute_omx_flush(param1); + } + BITMASK_SET(&m_flags, OMX_COMPONENT_OUTPUT_DISABLE_PENDING); + // Skip the event notification + + } + } else + { + DEBUG_PRINT_ERROR("OMX_CommandPortDisable: disable wrong port ID"); + } + + } else if (cmd == OMX_CommandPortEnable) + { + bFlag = 0; + if (param1 == OMX_CORE_INPUT_PORT_INDEX || param1 == OMX_ALL) + { + m_inp_bEnabled = OMX_TRUE; + DEBUG_PRINT("SCP: Enabling Input port Indx\n"); + if ((m_state == OMX_StateLoaded + && !BITMASK_PRESENT(&m_flags,OMX_COMPONENT_IDLE_PENDING)) + || (m_state == OMX_StateWaitForResources) + || (m_inp_bPopulated == OMX_TRUE)) + { + post_command(OMX_CommandPortEnable, + OMX_CORE_INPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + + + } else + { + BITMASK_SET(&m_flags, OMX_COMPONENT_INPUT_ENABLE_PENDING); + // Skip the event notification + + } + } + + if (param1 == OMX_CORE_OUTPUT_PORT_INDEX || param1 == OMX_ALL) + { + DEBUG_PRINT("SCP: Enabling Output port Indx\n"); + m_out_bEnabled = OMX_TRUE; + if ((m_state == OMX_StateLoaded + && !BITMASK_PRESENT(&m_flags,OMX_COMPONENT_IDLE_PENDING)) + || (m_state == OMX_StateWaitForResources) + || (m_out_bPopulated == OMX_TRUE)) + { + post_command(OMX_CommandPortEnable, + OMX_CORE_OUTPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + } else + { + DEBUG_PRINT("send_command_proxy:OMX_CommandPortEnable:\ + OMX_CORE_OUTPUT_PORT_INDEX:release_done \n"); + BITMASK_SET(&m_flags, OMX_COMPONENT_OUTPUT_ENABLE_PENDING); + // Skip the event notification + + } + pthread_mutex_lock(&m_in_th_lock_1); + if(is_in_th_sleep) + { + is_in_th_sleep = false; + DEBUG_DETAIL("SCP:WAKING UP IN THREADS\n"); + in_th_wakeup(); + } + pthread_mutex_unlock(&m_in_th_lock_1); + pthread_mutex_lock(&m_out_th_lock_1); + if (is_out_th_sleep) + { + is_out_th_sleep = false; + DEBUG_PRINT("SCP:WAKING OUT THR, OMX_CommandPortEnable\n"); + out_th_wakeup(); + } + pthread_mutex_unlock(&m_out_th_lock_1); + } else + { + DEBUG_PRINT_ERROR("OMX_CommandPortEnable: disable wrong port ID"); + } + + } else + { + DEBUG_PRINT_ERROR("SCP-->ERROR: Invali Command [%d]\n",cmd); + eRet = OMX_ErrorNotImplemented; + } + DEBUG_PRINT("posting sem_States\n"); + sem_post (&sem_States); + if (eRet == OMX_ErrorNone && bFlag) + { + post_command(cmd,eState,OMX_COMPONENT_GENERATE_EVENT); + } + return eRet; +} + +/*============================================================================= +FUNCTION: + execute_omx_flush + +DESCRIPTION: + Function that flushes buffers that are pending to be written to driver + +INPUT/OUTPUT PARAMETERS: + [IN] param1 + [IN] cmd_cmpl + +RETURN VALUE: + true + false + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +bool omx_amr_aenc::execute_omx_flush(OMX_IN OMX_U32 param1, bool cmd_cmpl) +{ + bool bRet = true; + + DEBUG_PRINT("Execute_omx_flush Port[%u]", param1); + struct timespec abs_timeout; + abs_timeout.tv_sec = 1; + abs_timeout.tv_nsec = 0; + + if ((signed)param1 == -1) + { + bFlushinprogress = true; + DEBUG_PRINT("Execute flush for both I/p O/p port\n"); + pthread_mutex_lock(&m_flush_lock); + m_flush_cnt = 2; + pthread_mutex_unlock(&m_flush_lock); + + // Send Flush commands to input and output threads + post_input(OMX_CommandFlush, + OMX_CORE_INPUT_PORT_INDEX,OMX_COMPONENT_GENERATE_COMMAND); + post_output(OMX_CommandFlush, + OMX_CORE_OUTPUT_PORT_INDEX,OMX_COMPONENT_GENERATE_COMMAND); + // Send Flush to the kernel so that the in and out buffers are released + if (ioctl( m_drv_fd, AUDIO_FLUSH, 0) == -1) + DEBUG_PRINT_ERROR("FLush:ioctl flush failed errno=%d\n",errno); + DEBUG_DETAIL("****************************************"); + DEBUG_DETAIL("is_in_th_sleep=%d is_out_th_sleep=%d\n",\ + is_in_th_sleep,is_out_th_sleep); + DEBUG_DETAIL("****************************************"); + + pthread_mutex_lock(&m_in_th_lock_1); + if (is_in_th_sleep) + { + is_in_th_sleep = false; + DEBUG_DETAIL("For FLUSH-->WAKING UP IN THREADS\n"); + in_th_wakeup(); + } + pthread_mutex_unlock(&m_in_th_lock_1); + + pthread_mutex_lock(&m_out_th_lock_1); + if (is_out_th_sleep) + { + is_out_th_sleep = false; + DEBUG_DETAIL("For FLUSH-->WAKING UP OUT THREADS\n"); + out_th_wakeup(); + } + pthread_mutex_unlock(&m_out_th_lock_1); + + + // sleep till the FLUSH ACK are done by both the input and + // output threads + DEBUG_DETAIL("WAITING FOR FLUSH ACK's param1=%d",param1); + wait_for_event(); + + DEBUG_PRINT("RECIEVED BOTH FLUSH ACK's param1=%u cmd_cmpl=%d",\ + param1,cmd_cmpl); + + // If not going to idle state, Send FLUSH complete message + // to the Client, now that FLUSH ACK's have been recieved. + if (cmd_cmpl) + { + m_cb.EventHandler(&m_cmp, m_app_data, OMX_EventCmdComplete, + OMX_CommandFlush, OMX_CORE_INPUT_PORT_INDEX, + NULL ); + m_cb.EventHandler(&m_cmp, m_app_data, OMX_EventCmdComplete, + OMX_CommandFlush, OMX_CORE_OUTPUT_PORT_INDEX, + NULL ); + DEBUG_PRINT("Inside FLUSH.. sending FLUSH CMPL\n"); + } + bFlushinprogress = false; + } + else if (param1 == OMX_CORE_INPUT_PORT_INDEX) + { + DEBUG_PRINT("Execute FLUSH for I/p port\n"); + pthread_mutex_lock(&m_flush_lock); + m_flush_cnt = 1; + pthread_mutex_unlock(&m_flush_lock); + post_input(OMX_CommandFlush, + OMX_CORE_INPUT_PORT_INDEX,OMX_COMPONENT_GENERATE_COMMAND); + if (ioctl( m_drv_fd, AUDIO_FLUSH, 0) == -1) + DEBUG_PRINT_ERROR("Flush:Input port, ioctl flush failed %d\n", + errno); + DEBUG_DETAIL("****************************************"); + DEBUG_DETAIL("is_in_th_sleep=%d is_out_th_sleep=%d\n",\ + is_in_th_sleep,is_out_th_sleep); + DEBUG_DETAIL("****************************************"); + + if (is_in_th_sleep) + { + pthread_mutex_lock(&m_in_th_lock_1); + is_in_th_sleep = false; + pthread_mutex_unlock(&m_in_th_lock_1); + DEBUG_DETAIL("For FLUSH-->WAKING UP IN THREADS\n"); + in_th_wakeup(); + } + + if (is_out_th_sleep) + { + pthread_mutex_lock(&m_out_th_lock_1); + is_out_th_sleep = false; + pthread_mutex_unlock(&m_out_th_lock_1); + DEBUG_DETAIL("For FLUSH-->WAKING UP OUT THREADS\n"); + out_th_wakeup(); + } + + //sleep till the FLUSH ACK are done by both the input and output threads + DEBUG_DETAIL("Executing FLUSH for I/p port\n"); + DEBUG_DETAIL("WAITING FOR FLUSH ACK's param1=%d",param1); + wait_for_event(); + DEBUG_DETAIL(" RECIEVED FLUSH ACK FOR I/P PORT param1=%d",param1); + + // Send FLUSH complete message to the Client, + // now that FLUSH ACK's have been recieved. + if (cmd_cmpl) + { + m_cb.EventHandler(&m_cmp, m_app_data, OMX_EventCmdComplete, + OMX_CommandFlush, OMX_CORE_INPUT_PORT_INDEX, + NULL ); + } + } else if (OMX_CORE_OUTPUT_PORT_INDEX == param1) + { + DEBUG_PRINT("Executing FLUSH for O/p port\n"); + pthread_mutex_lock(&m_flush_lock); + m_flush_cnt = 1; + pthread_mutex_unlock(&m_flush_lock); + DEBUG_DETAIL("Executing FLUSH for O/p port\n"); + DEBUG_DETAIL("WAITING FOR FLUSH ACK's param1=%d",param1); + post_output(OMX_CommandFlush, + OMX_CORE_OUTPUT_PORT_INDEX,OMX_COMPONENT_GENERATE_COMMAND); + if (ioctl( m_drv_fd, AUDIO_FLUSH, 0) ==-1) + DEBUG_PRINT_ERROR("Flush:Output port, ioctl flush failed %d\n", + errno); + DEBUG_DETAIL("****************************************"); + DEBUG_DETAIL("is_in_th_sleep=%d is_out_th_sleep=%d\n",\ + is_in_th_sleep,is_out_th_sleep); + DEBUG_DETAIL("****************************************"); + if (is_in_th_sleep) + { + pthread_mutex_lock(&m_in_th_lock_1); + is_in_th_sleep = false; + pthread_mutex_unlock(&m_in_th_lock_1); + DEBUG_DETAIL("For FLUSH-->WAKING UP IN THREADS\n"); + in_th_wakeup(); + } + + if (is_out_th_sleep) + { + pthread_mutex_lock(&m_out_th_lock_1); + is_out_th_sleep = false; + pthread_mutex_unlock(&m_out_th_lock_1); + DEBUG_DETAIL("For FLUSH-->WAKING UP OUT THREADS\n"); + out_th_wakeup(); + } + + // sleep till the FLUSH ACK are done by both the input and + // output threads + wait_for_event(); + // Send FLUSH complete message to the Client, + // now that FLUSH ACK's have been recieved. + if (cmd_cmpl) + { + m_cb.EventHandler(&m_cmp, m_app_data, OMX_EventCmdComplete, + OMX_CommandFlush, OMX_CORE_OUTPUT_PORT_INDEX, + NULL ); + } + DEBUG_DETAIL("RECIEVED FLUSH ACK FOR O/P PORT param1=%d",param1); + } else + { + DEBUG_PRINT("Invalid Port ID[%u]",param1); + } + return bRet; +} + +/*============================================================================= +FUNCTION: + execute_input_omx_flush + +DESCRIPTION: + Function that flushes buffers that are pending to be written to driver + +INPUT/OUTPUT PARAMETERS: + None + +RETURN VALUE: + true + false + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +bool omx_amr_aenc::execute_input_omx_flush() +{ + OMX_BUFFERHEADERTYPE *omx_buf; + unsigned long p1 = 0; // Parameter - 1 + unsigned long p2 = 0; // Parameter - 2 + unsigned char ident = 0; + unsigned qsize=0; // qsize + unsigned tot_qsize=0; // qsize + + DEBUG_PRINT("Execute_omx_flush on input port"); + + pthread_mutex_lock(&m_lock); + do + { + qsize = m_input_q.m_size; + tot_qsize = qsize; + tot_qsize += m_input_ctrl_ebd_q.m_size; + + DEBUG_DETAIL("Input FLUSH-->flushq[%d] ebd[%d]dataq[%d]",\ + m_input_ctrl_cmd_q.m_size, + m_input_ctrl_ebd_q.m_size,qsize); + if (!tot_qsize) + { + DEBUG_DETAIL("Input-->BREAKING FROM execute_input_flush LOOP"); + pthread_mutex_unlock(&m_lock); + break; + } + if (qsize) + { + m_input_q.pop_entry(&p1, &p2, &ident); + if ((ident == OMX_COMPONENT_GENERATE_ETB) || + (ident == OMX_COMPONENT_GENERATE_BUFFER_DONE)) + { + omx_buf = (OMX_BUFFERHEADERTYPE *) p2; + DEBUG_DETAIL("Flush:Input dataq=%p \n", omx_buf); + omx_buf->nFilledLen = 0; + buffer_done_cb((OMX_BUFFERHEADERTYPE *)omx_buf); + } + } else if (m_input_ctrl_ebd_q.m_size) + { + m_input_ctrl_ebd_q.pop_entry(&p1, &p2, &ident); + if (ident == OMX_COMPONENT_GENERATE_BUFFER_DONE) + { + omx_buf = (OMX_BUFFERHEADERTYPE *) p2; + omx_buf->nFilledLen = 0; + DEBUG_DETAIL("Flush:ctrl dataq=%p \n", omx_buf); + buffer_done_cb((OMX_BUFFERHEADERTYPE *)omx_buf); + } + } else + { + } + }while (tot_qsize>0); + DEBUG_DETAIL("*************************\n"); + DEBUG_DETAIL("IN-->FLUSHING DONE\n"); + DEBUG_DETAIL("*************************\n"); + flush_ack(); + pthread_mutex_unlock(&m_lock); + return true; +} + +/*============================================================================= +FUNCTION: + execute_output_omx_flush + +DESCRIPTION: + Function that flushes buffers that are pending to be written to driver + +INPUT/OUTPUT PARAMETERS: + None + +RETURN VALUE: + true + false + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +bool omx_amr_aenc::execute_output_omx_flush() +{ + OMX_BUFFERHEADERTYPE *omx_buf; + unsigned long p1 = 0; // Parameter - 1 + unsigned long p2 = 0; // Parameter - 2 + unsigned char ident = 0; + unsigned qsize=0; // qsize + unsigned tot_qsize=0; // qsize + + DEBUG_PRINT("Execute_omx_flush on output port"); + + pthread_mutex_lock(&m_outputlock); + do + { + qsize = m_output_q.m_size; + DEBUG_DETAIL("OUT FLUSH-->flushq[%d] fbd[%d]dataq[%d]",\ + m_output_ctrl_cmd_q.m_size, + m_output_ctrl_fbd_q.m_size,qsize); + tot_qsize = qsize; + tot_qsize += m_output_ctrl_fbd_q.m_size; + if (!tot_qsize) + { + DEBUG_DETAIL("OUT-->BREAKING FROM execute_input_flush LOOP"); + pthread_mutex_unlock(&m_outputlock); + break; + } + if (qsize) + { + m_output_q.pop_entry(&p1,&p2,&ident); + if ( (OMX_COMPONENT_GENERATE_FTB == ident) || + (OMX_COMPONENT_GENERATE_FRAME_DONE == ident)) + { + omx_buf = (OMX_BUFFERHEADERTYPE *) p2; + DEBUG_DETAIL("Ouput Buf_Addr=%p TS[0x%x] \n",\ + omx_buf,nTimestamp); + omx_buf->nTimeStamp = nTimestamp; + omx_buf->nFilledLen = 0; + frame_done_cb((OMX_BUFFERHEADERTYPE *)omx_buf); + DEBUG_DETAIL("CALLING FBD FROM FLUSH"); + } + } else if ((qsize = m_output_ctrl_fbd_q.m_size)) + { + m_output_ctrl_fbd_q.pop_entry(&p1, &p2, &ident); + if (OMX_COMPONENT_GENERATE_FRAME_DONE == ident) + { + omx_buf = (OMX_BUFFERHEADERTYPE *) p2; + DEBUG_DETAIL("Ouput Buf_Addr=%p TS[0x%x] \n", \ + omx_buf,nTimestamp); + omx_buf->nTimeStamp = nTimestamp; + omx_buf->nFilledLen = 0; + frame_done_cb((OMX_BUFFERHEADERTYPE *)omx_buf); + DEBUG_DETAIL("CALLING FROM CTRL-FBDQ FROM FLUSH"); + } + } + }while (qsize>0); + DEBUG_DETAIL("*************************\n"); + DEBUG_DETAIL("OUT-->FLUSHING DONE\n"); + DEBUG_DETAIL("*************************\n"); + flush_ack(); + pthread_mutex_unlock(&m_outputlock); + return true; +} + +/*============================================================================= +FUNCTION: + post_input + +DESCRIPTION: + Function that posts command in the command queue + +INPUT/OUTPUT PARAMETERS: + [IN] p1 + [IN] p2 + [IN] id - command ID + [IN] lock - self-locking mode + +RETURN VALUE: + true + false + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +bool omx_amr_aenc::post_input(unsigned long p1, + unsigned long p2, + unsigned char id) +{ + bool bRet = false; + pthread_mutex_lock(&m_lock); + + if((OMX_COMPONENT_GENERATE_COMMAND == id) || (id == OMX_COMPONENT_SUSPEND)) + { + // insert flush message and ebd + m_input_ctrl_cmd_q.insert_entry(p1,p2,id); + } else if ((OMX_COMPONENT_GENERATE_BUFFER_DONE == id)) + { + // insert ebd + m_input_ctrl_ebd_q.insert_entry(p1,p2,id); + } else + { + // ETBS in this queue + m_input_q.insert_entry(p1,p2,id); + } + + if (m_ipc_to_in_th) + { + bRet = true; + omx_amr_post_msg(m_ipc_to_in_th, id); + } + + DEBUG_DETAIL("PostInput-->state[%d]id[%d]flushq[%d]ebdq[%d]dataq[%d] \n",\ + m_state, + id, + m_input_ctrl_cmd_q.m_size, + m_input_ctrl_ebd_q.m_size, + m_input_q.m_size); + + pthread_mutex_unlock(&m_lock); + return bRet; +} + +/*============================================================================= +FUNCTION: + post_command + +DESCRIPTION: + Function that posts command in the command queue + +INPUT/OUTPUT PARAMETERS: + [IN] p1 + [IN] p2 + [IN] id - command ID + [IN] lock - self-locking mode + +RETURN VALUE: + true + false + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +bool omx_amr_aenc::post_command(unsigned int p1, + unsigned int p2, + unsigned char id) +{ + bool bRet = false; + + pthread_mutex_lock(&m_commandlock); + + m_command_q.insert_entry(p1,p2,id); + + if (m_ipc_to_cmd_th) + { + bRet = true; + omx_amr_post_msg(m_ipc_to_cmd_th, id); + } + + DEBUG_DETAIL("PostCmd-->state[%d]id[%d]cmdq[%d]flags[%x]\n",\ + m_state, + id, + m_command_q.m_size, + m_flags >> 3); + + pthread_mutex_unlock(&m_commandlock); + return bRet; +} + +/*============================================================================= +FUNCTION: + post_output + +DESCRIPTION: + Function that posts command in the command queue + +INPUT/OUTPUT PARAMETERS: + [IN] p1 + [IN] p2 + [IN] id - command ID + [IN] lock - self-locking mode + +RETURN VALUE: + true + false + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +bool omx_amr_aenc::post_output(unsigned long p1, + unsigned long p2, + unsigned char id) +{ + bool bRet = false; + + pthread_mutex_lock(&m_outputlock); + if((OMX_COMPONENT_GENERATE_COMMAND == id) || (id == OMX_COMPONENT_SUSPEND) + || (id == OMX_COMPONENT_RESUME)) + { + // insert flush message and fbd + m_output_ctrl_cmd_q.insert_entry(p1,p2,id); + } else if ( (OMX_COMPONENT_GENERATE_FRAME_DONE == id) ) + { + // insert flush message and fbd + m_output_ctrl_fbd_q.insert_entry(p1,p2,id); + } else + { + m_output_q.insert_entry(p1,p2,id); + } + if ( m_ipc_to_out_th ) + { + bRet = true; + omx_amr_post_msg(m_ipc_to_out_th, id); + } + DEBUG_DETAIL("PostOutput-->state[%d]id[%d]flushq[%d]ebdq[%d]dataq[%d]\n",\ + m_state, + id, + m_output_ctrl_cmd_q.m_size, + m_output_ctrl_fbd_q.m_size, + m_output_q.m_size); + + pthread_mutex_unlock(&m_outputlock); + return bRet; +} +/** + @brief member function that return parameters to IL client + + @param hComp handle to component instance + @param paramIndex Parameter type + @param paramData pointer to memory space which would hold the + paramter + @return error status +*/ +OMX_ERRORTYPE omx_amr_aenc::get_parameter(OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_INDEXTYPE paramIndex, + OMX_INOUT OMX_PTR paramData) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_state == OMX_StateInvalid) + { + DEBUG_PRINT_ERROR("Get Param in Invalid State\n"); + return OMX_ErrorInvalidState; + } + if (paramData == NULL) + { + DEBUG_PRINT("get_parameter: paramData is NULL\n"); + return OMX_ErrorBadParameter; + } + + switch ((int)paramIndex) + { + case OMX_IndexParamPortDefinition: + { + OMX_PARAM_PORTDEFINITIONTYPE *portDefn; + portDefn = (OMX_PARAM_PORTDEFINITIONTYPE *) paramData; + + DEBUG_PRINT("OMX_IndexParamPortDefinition " \ + "portDefn->nPortIndex = %u\n", + portDefn->nPortIndex); + + portDefn->nVersion.nVersion = OMX_SPEC_VERSION; + portDefn->nSize = (OMX_U32)sizeof(portDefn); + portDefn->eDomain = OMX_PortDomainAudio; + + if (0 == portDefn->nPortIndex) + { + portDefn->eDir = OMX_DirInput; + portDefn->bEnabled = m_inp_bEnabled; + portDefn->bPopulated = m_inp_bPopulated; + portDefn->nBufferCountActual = m_inp_act_buf_count; + portDefn->nBufferCountMin = OMX_CORE_NUM_INPUT_BUFFERS; + portDefn->nBufferSize = input_buffer_size; + portDefn->format.audio.bFlagErrorConcealment = OMX_TRUE; + portDefn->format.audio.eEncoding = OMX_AUDIO_CodingPCM; + portDefn->format.audio.pNativeRender = 0; + } else if (1 == portDefn->nPortIndex) + { + portDefn->eDir = OMX_DirOutput; + portDefn->bEnabled = m_out_bEnabled; + portDefn->bPopulated = m_out_bPopulated; + portDefn->nBufferCountActual = m_out_act_buf_count; + portDefn->nBufferCountMin = OMX_CORE_NUM_OUTPUT_BUFFERS; + portDefn->nBufferSize = output_buffer_size; + portDefn->format.audio.bFlagErrorConcealment = OMX_TRUE; + portDefn->format.audio.eEncoding = OMX_AUDIO_CodingAMR; + portDefn->format.audio.pNativeRender = 0; + } else + { + portDefn->eDir = OMX_DirMax; + DEBUG_PRINT_ERROR("Bad Port idx %d\n",\ + (int)portDefn->nPortIndex); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + + case OMX_IndexParamAudioInit: + { + OMX_PORT_PARAM_TYPE *portParamType = + (OMX_PORT_PARAM_TYPE *) paramData; + DEBUG_PRINT("OMX_IndexParamAudioInit\n"); + + portParamType->nVersion.nVersion = OMX_SPEC_VERSION; + portParamType->nSize = (OMX_U32)sizeof(portParamType); + portParamType->nPorts = 2; + portParamType->nStartPortNumber = 0; + break; + } + + case OMX_IndexParamAudioPortFormat: + { + OMX_AUDIO_PARAM_PORTFORMATTYPE *portFormatType = + (OMX_AUDIO_PARAM_PORTFORMATTYPE *) paramData; + DEBUG_PRINT("OMX_IndexParamAudioPortFormat\n"); + portFormatType->nVersion.nVersion = OMX_SPEC_VERSION; + portFormatType->nSize = (OMX_U32)sizeof(portFormatType); + + if (OMX_CORE_INPUT_PORT_INDEX == portFormatType->nPortIndex) + { + + portFormatType->eEncoding = OMX_AUDIO_CodingPCM; + } else if (OMX_CORE_OUTPUT_PORT_INDEX== + portFormatType->nPortIndex) + { + DEBUG_PRINT("get_parameter: OMX_IndexParamAudioFormat: "\ + "%u\n", portFormatType->nIndex); + + portFormatType->eEncoding = OMX_AUDIO_CodingAMR; + } else + { + DEBUG_PRINT_ERROR("get_parameter: Bad port index %d\n", + (int)portFormatType->nPortIndex); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + + case OMX_IndexParamAudioAmr: + { + OMX_AUDIO_PARAM_AMRTYPE *amrParam = + (OMX_AUDIO_PARAM_AMRTYPE *) paramData; + DEBUG_PRINT("OMX_IndexParamAudioAmr\n"); + if (OMX_CORE_OUTPUT_PORT_INDEX== amrParam->nPortIndex) + { + memcpy(amrParam,&m_amr_param, + sizeof(OMX_AUDIO_PARAM_AMRTYPE)); + } else + { + DEBUG_PRINT_ERROR("get_parameter:OMX_IndexParamAudioAmr "\ + "OMX_ErrorBadPortIndex %d\n", \ + (int)amrParam->nPortIndex); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + case QOMX_IndexParamAudioSessionId: + { + QOMX_AUDIO_STREAM_INFO_DATA *streaminfoparam = + (QOMX_AUDIO_STREAM_INFO_DATA *) paramData; + streaminfoparam->sessionId = (OMX_U8)m_session_id; + break; + } + + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmparam = + (OMX_AUDIO_PARAM_PCMMODETYPE *) paramData; + + if (OMX_CORE_INPUT_PORT_INDEX== pcmparam->nPortIndex) + { + memcpy(pcmparam,&m_pcm_param,\ + sizeof(OMX_AUDIO_PARAM_PCMMODETYPE)); + DEBUG_PRINT("get_parameter: Sampling rate %u",\ + pcmparam->nSamplingRate); + DEBUG_PRINT("get_parameter: Number of channels %u",\ + pcmparam->nChannels); + } else + { + DEBUG_PRINT_ERROR("get_parameter:OMX_IndexParamAudioPcm "\ + "OMX_ErrorBadPortIndex %d\n", \ + (int)pcmparam->nPortIndex); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + case OMX_IndexParamComponentSuspended: + { + OMX_PARAM_SUSPENSIONTYPE *suspend= + (OMX_PARAM_SUSPENSIONTYPE *) paramData; + DEBUG_PRINT("get_parameter: OMX_IndexParamComponentSuspended %p\n", + suspend); + break; + } + case OMX_IndexParamVideoInit: + { + OMX_PORT_PARAM_TYPE *portParamType = + (OMX_PORT_PARAM_TYPE *) paramData; + DEBUG_PRINT("get_parameter: OMX_IndexParamVideoInit\n"); + portParamType->nVersion.nVersion = OMX_SPEC_VERSION; + portParamType->nSize = (OMX_U32)sizeof(portParamType); + portParamType->nPorts = 0; + portParamType->nStartPortNumber = 0; + break; + } + case OMX_IndexParamPriorityMgmt: + { + OMX_PRIORITYMGMTTYPE *priorityMgmtType = + (OMX_PRIORITYMGMTTYPE*)paramData; + DEBUG_PRINT("get_parameter: OMX_IndexParamPriorityMgmt\n"); + priorityMgmtType->nSize = (OMX_U32)sizeof(priorityMgmtType); + priorityMgmtType->nVersion.nVersion = OMX_SPEC_VERSION; + priorityMgmtType->nGroupID = m_priority_mgm.nGroupID; + priorityMgmtType->nGroupPriority = + m_priority_mgm.nGroupPriority; + break; + } + case OMX_IndexParamImageInit: + { + OMX_PORT_PARAM_TYPE *portParamType = + (OMX_PORT_PARAM_TYPE *) paramData; + DEBUG_PRINT("get_parameter: OMX_IndexParamImageInit\n"); + portParamType->nVersion.nVersion = OMX_SPEC_VERSION; + portParamType->nSize = (OMX_U32)sizeof(portParamType); + portParamType->nPorts = 0; + portParamType->nStartPortNumber = 0; + break; + } + + case OMX_IndexParamCompBufferSupplier: + { + DEBUG_PRINT("get_parameter: \ + OMX_IndexParamCompBufferSupplier\n"); + OMX_PARAM_BUFFERSUPPLIERTYPE *bufferSupplierType + = (OMX_PARAM_BUFFERSUPPLIERTYPE*) paramData; + DEBUG_PRINT("get_parameter: \ + OMX_IndexParamCompBufferSupplier\n"); + + bufferSupplierType->nSize = (OMX_U32)sizeof(bufferSupplierType); + bufferSupplierType->nVersion.nVersion = OMX_SPEC_VERSION; + if (OMX_CORE_INPUT_PORT_INDEX == + bufferSupplierType->nPortIndex) + { + bufferSupplierType->nPortIndex = + OMX_BufferSupplyUnspecified; + } else if (OMX_CORE_OUTPUT_PORT_INDEX == + bufferSupplierType->nPortIndex) + { + bufferSupplierType->nPortIndex = + OMX_BufferSupplyUnspecified; + } else + { + DEBUG_PRINT_ERROR("get_parameter:"\ + "OMX_IndexParamCompBufferSupplier eRet"\ + "%08x\n", eRet); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + + /*Component should support this port definition*/ + case OMX_IndexParamOtherInit: + { + OMX_PORT_PARAM_TYPE *portParamType = + (OMX_PORT_PARAM_TYPE *) paramData; + DEBUG_PRINT("get_parameter: OMX_IndexParamOtherInit\n"); + portParamType->nVersion.nVersion = OMX_SPEC_VERSION; + portParamType->nSize = (OMX_U32)sizeof(portParamType); + portParamType->nPorts = 0; + portParamType->nStartPortNumber = 0; + break; + } + case OMX_IndexParamStandardComponentRole: + { + OMX_PARAM_COMPONENTROLETYPE *componentRole; + componentRole = (OMX_PARAM_COMPONENTROLETYPE*)paramData; + componentRole->nSize = component_Role.nSize; + componentRole->nVersion = component_Role.nVersion; + strlcpy((char *)componentRole->cRole, + (const char*)component_Role.cRole, + sizeof(componentRole->cRole)); + DEBUG_PRINT_ERROR("nSize = %d , nVersion = %d, cRole = %s\n", + component_Role.nSize, + component_Role.nVersion, + component_Role.cRole); + break; + + } + default: + { + DEBUG_PRINT_ERROR("unknown param %08x\n", paramIndex); + eRet = OMX_ErrorUnsupportedIndex; + } + } + return eRet; + +} + +/** + @brief member function that set paramter from IL client + + @param hComp handle to component instance + @param paramIndex parameter type + @param paramData pointer to memory space which holds the paramter + @return error status + */ +OMX_ERRORTYPE omx_amr_aenc::set_parameter(OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_INDEXTYPE paramIndex, + OMX_IN OMX_PTR paramData) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_state != OMX_StateLoaded) + { + DEBUG_PRINT_ERROR("set_parameter is not in proper state\n"); + return OMX_ErrorIncorrectStateOperation; + } + if (paramData == NULL) + { + DEBUG_PRINT("param data is NULL"); + return OMX_ErrorBadParameter; + } + + switch (paramIndex) + { + case OMX_IndexParamAudioAmr: + { + DEBUG_PRINT("OMX_IndexParamAudioAmr"); + OMX_AUDIO_PARAM_AMRTYPE *amrparam + = (OMX_AUDIO_PARAM_AMRTYPE *) paramData; + memcpy(&m_amr_param,amrparam, + sizeof(OMX_AUDIO_PARAM_AMRTYPE)); + break; + } + case OMX_IndexParamPortDefinition: + { + OMX_PARAM_PORTDEFINITIONTYPE *portDefn; + portDefn = (OMX_PARAM_PORTDEFINITIONTYPE *) paramData; + + if (((m_state == OMX_StateLoaded)&& + !BITMASK_PRESENT(&m_flags,OMX_COMPONENT_IDLE_PENDING)) + || (m_state == OMX_StateWaitForResources && + ((OMX_DirInput == portDefn->eDir && + m_inp_bEnabled == true)|| + (OMX_DirInput == portDefn->eDir && + m_out_bEnabled == true))) + ||(((OMX_DirInput == portDefn->eDir && + m_inp_bEnabled == false)|| + (OMX_DirInput == portDefn->eDir && + m_out_bEnabled == false)) && + (m_state != OMX_StateWaitForResources))) + { + DEBUG_PRINT("Set Parameter called in valid state\n"); + } else + { + DEBUG_PRINT_ERROR("Set Parameter called in \ + Invalid State\n"); + return OMX_ErrorIncorrectStateOperation; + } + DEBUG_PRINT("OMX_IndexParamPortDefinition portDefn->nPortIndex " + "= %u\n",portDefn->nPortIndex); + if (OMX_CORE_INPUT_PORT_INDEX == portDefn->nPortIndex) + { + if ( portDefn->nBufferCountActual > + OMX_CORE_NUM_INPUT_BUFFERS ) + { + m_inp_act_buf_count = portDefn->nBufferCountActual; + } else + { + m_inp_act_buf_count =OMX_CORE_NUM_INPUT_BUFFERS; + } + input_buffer_size = portDefn->nBufferSize; + + } else if (OMX_CORE_OUTPUT_PORT_INDEX == portDefn->nPortIndex) + { + if ( portDefn->nBufferCountActual > + OMX_CORE_NUM_OUTPUT_BUFFERS ) + { + m_out_act_buf_count = portDefn->nBufferCountActual; + } else + { + m_out_act_buf_count =OMX_CORE_NUM_OUTPUT_BUFFERS; + } + output_buffer_size = portDefn->nBufferSize; + } else + { + DEBUG_PRINT(" set_parameter: Bad Port idx %d",\ + (int)portDefn->nPortIndex); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + case OMX_IndexParamPriorityMgmt: + { + DEBUG_PRINT("set_parameter: OMX_IndexParamPriorityMgmt\n"); + + if (m_state != OMX_StateLoaded) + { + DEBUG_PRINT_ERROR("Set Parameter called in \ + Invalid State\n"); + return OMX_ErrorIncorrectStateOperation; + } + OMX_PRIORITYMGMTTYPE *priorityMgmtype + = (OMX_PRIORITYMGMTTYPE*) paramData; + DEBUG_PRINT("set_parameter: OMX_IndexParamPriorityMgmt %u\n", + priorityMgmtype->nGroupID); + + DEBUG_PRINT("set_parameter: priorityMgmtype %u\n", + priorityMgmtype->nGroupPriority); + + m_priority_mgm.nGroupID = priorityMgmtype->nGroupID; + m_priority_mgm.nGroupPriority = priorityMgmtype->nGroupPriority; + + break; + } + case OMX_IndexParamAudioPortFormat: + { + + OMX_AUDIO_PARAM_PORTFORMATTYPE *portFormatType = + (OMX_AUDIO_PARAM_PORTFORMATTYPE *) paramData; + DEBUG_PRINT("set_parameter: OMX_IndexParamAudioPortFormat\n"); + + if (OMX_CORE_INPUT_PORT_INDEX== portFormatType->nPortIndex) + { + portFormatType->eEncoding = OMX_AUDIO_CodingPCM; + } else if (OMX_CORE_OUTPUT_PORT_INDEX == + portFormatType->nPortIndex) + { + DEBUG_PRINT("set_parameter: OMX_IndexParamAudioFormat:"\ + " %u\n", portFormatType->nIndex); + portFormatType->eEncoding = OMX_AUDIO_CodingAMR; + } else + { + DEBUG_PRINT_ERROR("set_parameter: Bad port index %d\n", \ + (int)portFormatType->nPortIndex); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + + + case OMX_IndexParamCompBufferSupplier: + { + DEBUG_PRINT("set_parameter: \ + OMX_IndexParamCompBufferSupplier\n"); + OMX_PARAM_BUFFERSUPPLIERTYPE *bufferSupplierType + = (OMX_PARAM_BUFFERSUPPLIERTYPE*) paramData; + DEBUG_PRINT("set_param: OMX_IndexParamCompBufferSupplier %d",\ + bufferSupplierType->eBufferSupplier); + + if (bufferSupplierType->nPortIndex == OMX_CORE_INPUT_PORT_INDEX + || bufferSupplierType->nPortIndex == + OMX_CORE_OUTPUT_PORT_INDEX) + { + DEBUG_PRINT("set_parameter:\ + OMX_IndexParamCompBufferSupplier\n"); + m_buffer_supplier.eBufferSupplier = + bufferSupplierType->eBufferSupplier; + } else + { + DEBUG_PRINT_ERROR("set_param:\ + IndexParamCompBufferSup %08x\n", eRet); + eRet = OMX_ErrorBadPortIndex; + } + + break; } + + case OMX_IndexParamAudioPcm: + { + DEBUG_PRINT("set_parameter: OMX_IndexParamAudioPcm\n"); + OMX_AUDIO_PARAM_PCMMODETYPE *pcmparam + = (OMX_AUDIO_PARAM_PCMMODETYPE *) paramData; + + if (OMX_CORE_INPUT_PORT_INDEX== pcmparam->nPortIndex) + { + memcpy(&m_pcm_param,pcmparam,\ + sizeof(OMX_AUDIO_PARAM_PCMMODETYPE)); + DEBUG_PRINT("set_pcm_parameter: %u %u",\ + m_pcm_param.nChannels, + m_pcm_param.nSamplingRate); + } else + { + DEBUG_PRINT_ERROR("Set_parameter:OMX_IndexParamAudioPcm " + "OMX_ErrorBadPortIndex %d\n", + (int)pcmparam->nPortIndex); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + case OMX_IndexParamSuspensionPolicy: + { + eRet = OMX_ErrorNotImplemented; + break; + } + case OMX_IndexParamStandardComponentRole: + { + OMX_PARAM_COMPONENTROLETYPE *componentRole; + componentRole = (OMX_PARAM_COMPONENTROLETYPE*)paramData; + component_Role.nSize = componentRole->nSize; + component_Role.nVersion = componentRole->nVersion; + strlcpy((char *)component_Role.cRole, + (const char*)componentRole->cRole, + sizeof(component_Role.cRole)); + break; + } + + default: + { + DEBUG_PRINT_ERROR("unknown param %d\n", paramIndex); + eRet = OMX_ErrorUnsupportedIndex; + } + } + return eRet; +} + +/* ====================================================================== +FUNCTION + omx_amr_aenc::GetConfig + +DESCRIPTION + OMX Get Config Method implementation. + +PARAMETERS + . + +RETURN VALUE + OMX Error None if successful. + +========================================================================== */ +OMX_ERRORTYPE omx_amr_aenc::get_config(OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_INDEXTYPE configIndex, + OMX_INOUT OMX_PTR configData) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_state == OMX_StateInvalid) + { + DEBUG_PRINT_ERROR("Get Config in Invalid State\n"); + return OMX_ErrorInvalidState; + } + + switch (configIndex) + { + case OMX_IndexConfigAudioVolume: + { + OMX_AUDIO_CONFIG_VOLUMETYPE *volume = + (OMX_AUDIO_CONFIG_VOLUMETYPE*) configData; + + if (OMX_CORE_INPUT_PORT_INDEX == volume->nPortIndex) + { + volume->nSize = (OMX_U32)sizeof(volume); + volume->nVersion.nVersion = OMX_SPEC_VERSION; + volume->bLinear = OMX_TRUE; + volume->sVolume.nValue = m_volume; + volume->sVolume.nMax = OMX_AENC_MAX; + volume->sVolume.nMin = OMX_AENC_MIN; + } else + { + eRet = OMX_ErrorBadPortIndex; + } + } + break; + + case OMX_IndexConfigAudioMute: + { + OMX_AUDIO_CONFIG_MUTETYPE *mute = + (OMX_AUDIO_CONFIG_MUTETYPE*) configData; + + if (OMX_CORE_INPUT_PORT_INDEX == mute->nPortIndex) + { + mute->nSize = (OMX_U32)sizeof(mute); + mute->nVersion.nVersion = OMX_SPEC_VERSION; + mute->bMute = (BITMASK_PRESENT(&m_flags, + OMX_COMPONENT_MUTED)?OMX_TRUE:OMX_FALSE); + } else + { + eRet = OMX_ErrorBadPortIndex; + } + } + break; + + default: + eRet = OMX_ErrorUnsupportedIndex; + break; + } + return eRet; +} + +/* ====================================================================== +FUNCTION + omx_amr_aenc::SetConfig + +DESCRIPTION + OMX Set Config method implementation + +PARAMETERS + . + +RETURN VALUE + OMX Error None if successful. +========================================================================== */ +OMX_ERRORTYPE omx_amr_aenc::set_config(OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_INDEXTYPE configIndex, + OMX_IN OMX_PTR configData) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_state == OMX_StateInvalid) + { + DEBUG_PRINT_ERROR("Set Config in Invalid State\n"); + return OMX_ErrorInvalidState; + } + if ( m_state == OMX_StateExecuting) + { + DEBUG_PRINT_ERROR("set_config:Ignore in Exe state\n"); + return OMX_ErrorInvalidState; + } + + switch (configIndex) + { + case OMX_IndexConfigAudioVolume: + { + OMX_AUDIO_CONFIG_VOLUMETYPE *vol = + (OMX_AUDIO_CONFIG_VOLUMETYPE*)configData; + if (vol->nPortIndex == OMX_CORE_INPUT_PORT_INDEX) + { + if ((vol->sVolume.nValue <= OMX_AENC_MAX) && + (vol->sVolume.nValue >= OMX_AENC_MIN)) + { + m_volume = vol->sVolume.nValue; + if (BITMASK_ABSENT(&m_flags, OMX_COMPONENT_MUTED)) + { + /* ioctl(m_drv_fd, AUDIO_VOLUME, + m_volume * OMX_AENC_VOLUME_STEP); */ + } + + } else + { + eRet = OMX_ErrorBadParameter; + } + } else + { + eRet = OMX_ErrorBadPortIndex; + } + } + break; + + case OMX_IndexConfigAudioMute: + { + OMX_AUDIO_CONFIG_MUTETYPE *mute = (OMX_AUDIO_CONFIG_MUTETYPE*) + configData; + if (mute->nPortIndex == OMX_CORE_INPUT_PORT_INDEX) + { + if (mute->bMute == OMX_TRUE) + { + BITMASK_SET(&m_flags, OMX_COMPONENT_MUTED); + /* ioctl(m_drv_fd, AUDIO_VOLUME, 0); */ + } else + { + BITMASK_CLEAR(&m_flags, OMX_COMPONENT_MUTED); + /* ioctl(m_drv_fd, AUDIO_VOLUME, + m_volume * OMX_AENC_VOLUME_STEP); */ + } + } else + { + eRet = OMX_ErrorBadPortIndex; + } + } + break; + + default: + eRet = OMX_ErrorUnsupportedIndex; + break; + } + return eRet; +} + +/* ====================================================================== +FUNCTION + omx_amr_aenc::GetExtensionIndex + +DESCRIPTION + OMX GetExtensionIndex method implementaion. + +PARAMETERS + . + +RETURN VALUE + OMX Error None if everything successful. + +========================================================================== */ +OMX_ERRORTYPE omx_amr_aenc::get_extension_index( + OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_STRING paramName, + OMX_OUT OMX_INDEXTYPE* indexType) +{ + if((hComp == NULL) || (paramName == NULL) || (indexType == NULL)) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_state == OMX_StateInvalid) + { + DEBUG_PRINT_ERROR("Get Extension Index in Invalid State\n"); + return OMX_ErrorInvalidState; + } + if(strncmp(paramName,"OMX.Qualcomm.index.audio.sessionId", + strlen("OMX.Qualcomm.index.audio.sessionId")) == 0) + { + *indexType =(OMX_INDEXTYPE)QOMX_IndexParamAudioSessionId; + DEBUG_PRINT("Extension index type - %d\n", *indexType); + + } + else + { + return OMX_ErrorBadParameter; + + } + return OMX_ErrorNone; +} + +/* ====================================================================== +FUNCTION + omx_amr_aenc::GetState + +DESCRIPTION + Returns the state information back to the caller. + +PARAMETERS + . + +RETURN VALUE + Error None if everything is successful. +========================================================================== */ +OMX_ERRORTYPE omx_amr_aenc::get_state(OMX_IN OMX_HANDLETYPE hComp, + OMX_OUT OMX_STATETYPE* state) +{ + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + *state = m_state; + DEBUG_PRINT("Returning the state %d\n",*state); + return OMX_ErrorNone; +} + +/* ====================================================================== +FUNCTION + omx_amr_aenc::ComponentTunnelRequest + +DESCRIPTION + OMX Component Tunnel Request method implementation. + +PARAMETERS + None. + +RETURN VALUE + OMX Error None if everything successful. + +========================================================================== */ +OMX_ERRORTYPE omx_amr_aenc::component_tunnel_request +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_U32 port, + OMX_IN OMX_HANDLETYPE peerComponent, + OMX_IN OMX_U32 peerPort, + OMX_INOUT OMX_TUNNELSETUPTYPE* tunnelSetup) +{ + DEBUG_PRINT_ERROR("Error: component_tunnel_request Not Implemented\n"); + + if((hComp == NULL) || (peerComponent == NULL) || (tunnelSetup == NULL)) + { + port = port; + peerPort = peerPort; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + return OMX_ErrorNotImplemented; +} + +/* ====================================================================== +FUNCTION + omx_amr_aenc::AllocateInputBuffer + +DESCRIPTION + Helper function for allocate buffer in the input pin + +PARAMETERS + None. + +RETURN VALUE + true/false + +========================================================================== */ +OMX_ERRORTYPE omx_amr_aenc::allocate_input_buffer +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE** bufferHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + OMX_BUFFERHEADERTYPE *bufHdr; + unsigned nBufSize = MAX(bytes, input_buffer_size); + char *buf_ptr; + if(m_inp_current_buf_count < m_inp_act_buf_count) + { + buf_ptr = (char *) calloc((nBufSize + \ + sizeof(OMX_BUFFERHEADERTYPE)+sizeof(META_IN)) , 1); + + if(hComp == NULL) + { + port = port; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + free(buf_ptr); + return OMX_ErrorBadParameter; + } + if (buf_ptr != NULL) + { + bufHdr = (OMX_BUFFERHEADERTYPE *) buf_ptr; + *bufferHdr = bufHdr; + memset(bufHdr,0,sizeof(OMX_BUFFERHEADERTYPE)); + + bufHdr->pBuffer = (OMX_U8 *)((buf_ptr) + sizeof(META_IN)+ + sizeof(OMX_BUFFERHEADERTYPE)); + bufHdr->nSize = (OMX_U32)sizeof(OMX_BUFFERHEADERTYPE); + bufHdr->nVersion.nVersion = OMX_SPEC_VERSION; + bufHdr->nAllocLen = nBufSize; + bufHdr->pAppPrivate = appData; + bufHdr->nInputPortIndex = OMX_CORE_INPUT_PORT_INDEX; + m_input_buf_hdrs.insert(bufHdr, NULL); + + m_inp_current_buf_count++; + DEBUG_PRINT("AIB:bufHdr %p bufHdr->pBuffer %p m_inp_buf_cnt=%d \ + bytes=%u", bufHdr, bufHdr->pBuffer,m_inp_current_buf_count, + bytes); + + } else + { + DEBUG_PRINT("Input buffer memory allocation failed 1 \n"); + eRet = OMX_ErrorInsufficientResources; + } + } + else + { + DEBUG_PRINT("Input buffer memory allocation failed 2\n"); + eRet = OMX_ErrorInsufficientResources; + } + return eRet; +} + +OMX_ERRORTYPE omx_amr_aenc::allocate_output_buffer +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE** bufferHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + OMX_BUFFERHEADERTYPE *bufHdr; + unsigned nBufSize = MAX(bytes,output_buffer_size); + char *buf_ptr; + + if(hComp == NULL) + { + port = port; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_out_current_buf_count < m_out_act_buf_count) + { + buf_ptr = (char *) calloc( (nBufSize + sizeof(OMX_BUFFERHEADERTYPE)),1); + + if (buf_ptr != NULL) + { + bufHdr = (OMX_BUFFERHEADERTYPE *) buf_ptr; + *bufferHdr = bufHdr; + memset(bufHdr,0,sizeof(OMX_BUFFERHEADERTYPE)); + + bufHdr->pBuffer = (OMX_U8 *)((buf_ptr) + + sizeof(OMX_BUFFERHEADERTYPE)); + bufHdr->nSize = (OMX_U32)sizeof(OMX_BUFFERHEADERTYPE); + bufHdr->nVersion.nVersion = OMX_SPEC_VERSION; + bufHdr->nAllocLen = nBufSize; + bufHdr->pAppPrivate = appData; + bufHdr->nOutputPortIndex = OMX_CORE_OUTPUT_PORT_INDEX; + m_output_buf_hdrs.insert(bufHdr, NULL); + m_out_current_buf_count++; + DEBUG_PRINT("AOB::bufHdr %p bufHdr->pBuffer %p m_out_buf_cnt=%d"\ + "bytes=%u",bufHdr, bufHdr->pBuffer,\ + m_out_current_buf_count, bytes); + } else + { + DEBUG_PRINT("Output buffer memory allocation failed 1 \n"); + eRet = OMX_ErrorInsufficientResources; + } + } else + { + DEBUG_PRINT("Output buffer memory allocation failed\n"); + eRet = OMX_ErrorInsufficientResources; + } + return eRet; +} + + +// AllocateBuffer -- API Call +/* ====================================================================== +FUNCTION + omx_amr_aenc::AllocateBuffer + +DESCRIPTION + Returns zero if all the buffers released.. + +PARAMETERS + None. + +RETURN VALUE + true/false + +========================================================================== */ +OMX_ERRORTYPE omx_amr_aenc::allocate_buffer +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE** bufferHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes) +{ + + OMX_ERRORTYPE eRet = OMX_ErrorNone; // OMX return type + + if (m_state == OMX_StateInvalid) + { + DEBUG_PRINT_ERROR("Allocate Buf in Invalid State\n"); + return OMX_ErrorInvalidState; + } + // What if the client calls again. + if (OMX_CORE_INPUT_PORT_INDEX == port) + { + eRet = allocate_input_buffer(hComp,bufferHdr,port,appData,bytes); + } else if (OMX_CORE_OUTPUT_PORT_INDEX == port) + { + eRet = allocate_output_buffer(hComp,bufferHdr,port,appData,bytes); + } else + { + DEBUG_PRINT_ERROR("Error: Invalid Port Index received %d\n", + (int)port); + eRet = OMX_ErrorBadPortIndex; + } + + if (eRet == OMX_ErrorNone) + { + DEBUG_PRINT("allocate_buffer: before allocate_done \n"); + if (allocate_done()) + { + DEBUG_PRINT("allocate_buffer: after allocate_done \n"); + if (BITMASK_PRESENT(&m_flags,OMX_COMPONENT_IDLE_PENDING)) + { + BITMASK_CLEAR(&m_flags, OMX_COMPONENT_IDLE_PENDING); + post_command(OMX_CommandStateSet,OMX_StateIdle, + OMX_COMPONENT_GENERATE_EVENT); + DEBUG_PRINT("allocate_buffer: post idle transition event \n"); + } + DEBUG_PRINT("allocate_buffer: complete \n"); + } + if (port == OMX_CORE_INPUT_PORT_INDEX && m_inp_bPopulated) + { + if (BITMASK_PRESENT(&m_flags,OMX_COMPONENT_INPUT_ENABLE_PENDING)) + { + BITMASK_CLEAR((&m_flags),OMX_COMPONENT_INPUT_ENABLE_PENDING); + post_command(OMX_CommandPortEnable, OMX_CORE_INPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + } + } + if (port == OMX_CORE_OUTPUT_PORT_INDEX && m_out_bPopulated) + { + if (BITMASK_PRESENT(&m_flags,OMX_COMPONENT_OUTPUT_ENABLE_PENDING)) + { + BITMASK_CLEAR((&m_flags),OMX_COMPONENT_OUTPUT_ENABLE_PENDING); + m_out_bEnabled = OMX_TRUE; + + DEBUG_PRINT("AllocBuf-->is_out_th_sleep=%d\n",is_out_th_sleep); + pthread_mutex_lock(&m_out_th_lock_1); + if (is_out_th_sleep) + { + is_out_th_sleep = false; + DEBUG_DETAIL("AllocBuf:WAKING UP OUT THREADS\n"); + out_th_wakeup(); + } + pthread_mutex_unlock(&m_out_th_lock_1); + pthread_mutex_lock(&m_in_th_lock_1); + if(is_in_th_sleep) + { + is_in_th_sleep = false; + DEBUG_DETAIL("AB:WAKING UP IN THREADS\n"); + in_th_wakeup(); + } + pthread_mutex_unlock(&m_in_th_lock_1); + post_command(OMX_CommandPortEnable, OMX_CORE_OUTPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + } + } + } + DEBUG_PRINT("Allocate Buffer exit with ret Code %d\n", eRet); + return eRet; +} + +/*============================================================================= +FUNCTION: + use_buffer + +DESCRIPTION: + OMX Use Buffer method implementation. + +INPUT/OUTPUT PARAMETERS: + [INOUT] bufferHdr + [IN] hComp + [IN] port + [IN] appData + [IN] bytes + [IN] buffer + +RETURN VALUE: + OMX_ERRORTYPE + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +OMX_ERRORTYPE omx_amr_aenc::use_buffer +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE** bufferHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes, + OMX_IN OMX_U8* buffer) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + + if (OMX_CORE_INPUT_PORT_INDEX == port) + { + eRet = use_input_buffer(hComp,bufferHdr,port,appData,bytes,buffer); + + } else if (OMX_CORE_OUTPUT_PORT_INDEX == port) + { + eRet = use_output_buffer(hComp,bufferHdr,port,appData,bytes,buffer); + } else + { + DEBUG_PRINT_ERROR("Error: Invalid Port Index received %d\n",(int)port); + eRet = OMX_ErrorBadPortIndex; + } + + if (eRet == OMX_ErrorNone) + { + DEBUG_PRINT("Checking for Output Allocate buffer Done"); + if (allocate_done()) + { + if (BITMASK_PRESENT(&m_flags,OMX_COMPONENT_IDLE_PENDING)) + { + BITMASK_CLEAR(&m_flags, OMX_COMPONENT_IDLE_PENDING); + post_command(OMX_CommandStateSet,OMX_StateIdle, + OMX_COMPONENT_GENERATE_EVENT); + } + } + if (port == OMX_CORE_INPUT_PORT_INDEX && m_inp_bPopulated) + { + if (BITMASK_PRESENT(&m_flags,OMX_COMPONENT_INPUT_ENABLE_PENDING)) + { + BITMASK_CLEAR((&m_flags),OMX_COMPONENT_INPUT_ENABLE_PENDING); + post_command(OMX_CommandPortEnable, OMX_CORE_INPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + + } + } + if (port == OMX_CORE_OUTPUT_PORT_INDEX && m_out_bPopulated) + { + if (BITMASK_PRESENT(&m_flags,OMX_COMPONENT_OUTPUT_ENABLE_PENDING)) + { + BITMASK_CLEAR((&m_flags),OMX_COMPONENT_OUTPUT_ENABLE_PENDING); + post_command(OMX_CommandPortEnable, OMX_CORE_OUTPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + pthread_mutex_lock(&m_out_th_lock_1); + if (is_out_th_sleep) + { + is_out_th_sleep = false; + DEBUG_DETAIL("UseBuf:WAKING UP OUT THREADS\n"); + out_th_wakeup(); + } + pthread_mutex_unlock(&m_out_th_lock_1); + pthread_mutex_lock(&m_in_th_lock_1); + if(is_in_th_sleep) + { + is_in_th_sleep = false; + DEBUG_DETAIL("UB:WAKING UP IN THREADS\n"); + in_th_wakeup(); + } + pthread_mutex_unlock(&m_in_th_lock_1); + } + } + } + DEBUG_PRINT("Use Buffer for port[%u] eRet[%d]\n", port,eRet); + return eRet; +} +/*============================================================================= +FUNCTION: + use_input_buffer + +DESCRIPTION: + Helper function for Use buffer in the input pin + +INPUT/OUTPUT PARAMETERS: + [INOUT] bufferHdr + [IN] hComp + [IN] port + [IN] appData + [IN] bytes + [IN] buffer + +RETURN VALUE: + OMX_ERRORTYPE + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +OMX_ERRORTYPE omx_amr_aenc::use_input_buffer +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE** bufferHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes, + OMX_IN OMX_U8* buffer) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + OMX_BUFFERHEADERTYPE *bufHdr; + unsigned nBufSize = MAX(bytes, input_buffer_size); + char *buf_ptr; + + if(hComp == NULL) + { + port = port; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if(bytes < input_buffer_size) + { + /* return if i\p buffer size provided by client + is less than min i\p buffer size supported by omx component*/ + return OMX_ErrorInsufficientResources; + } + if (m_inp_current_buf_count < m_inp_act_buf_count) + { + buf_ptr = (char *) calloc(sizeof(OMX_BUFFERHEADERTYPE), 1); + + if (buf_ptr != NULL) + { + bufHdr = (OMX_BUFFERHEADERTYPE *) buf_ptr; + *bufferHdr = bufHdr; + memset(bufHdr,0,sizeof(OMX_BUFFERHEADERTYPE)); + + bufHdr->pBuffer = (OMX_U8 *)(buffer); + DEBUG_PRINT("use_input_buffer:bufHdr %p bufHdr->pBuffer %p \ + bytes=%u", bufHdr, bufHdr->pBuffer,bytes); + bufHdr->nSize = (OMX_U32)sizeof(OMX_BUFFERHEADERTYPE); + bufHdr->nVersion.nVersion = OMX_SPEC_VERSION; + bufHdr->nAllocLen = nBufSize; + input_buffer_size = nBufSize; + bufHdr->pAppPrivate = appData; + bufHdr->nInputPortIndex = OMX_CORE_INPUT_PORT_INDEX; + bufHdr->nOffset = 0; + m_input_buf_hdrs.insert(bufHdr, NULL); + m_inp_current_buf_count++; + } else + { + DEBUG_PRINT("Input buffer memory allocation failed 1 \n"); + eRet = OMX_ErrorInsufficientResources; + } + } else + { + DEBUG_PRINT("Input buffer memory allocation failed\n"); + eRet = OMX_ErrorInsufficientResources; + } + return eRet; +} + +/*============================================================================= +FUNCTION: + use_output_buffer + +DESCRIPTION: + Helper function for Use buffer in the output pin + +INPUT/OUTPUT PARAMETERS: + [INOUT] bufferHdr + [IN] hComp + [IN] port + [IN] appData + [IN] bytes + [IN] buffer + +RETURN VALUE: + OMX_ERRORTYPE + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +OMX_ERRORTYPE omx_amr_aenc::use_output_buffer +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE** bufferHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes, + OMX_IN OMX_U8* buffer) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + OMX_BUFFERHEADERTYPE *bufHdr; + unsigned nBufSize = MAX(bytes,output_buffer_size); + char *buf_ptr; + + if(hComp == NULL) + { + port = port; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (bytes < output_buffer_size) + { + /* return if o\p buffer size provided by client + is less than min o\p buffer size supported by omx component*/ + return OMX_ErrorInsufficientResources; + } + + DEBUG_PRINT("Inside omx_amr_aenc::use_output_buffer"); + if (m_out_current_buf_count < m_out_act_buf_count) + { + + buf_ptr = (char *) calloc(sizeof(OMX_BUFFERHEADERTYPE), 1); + + if (buf_ptr != NULL) + { + bufHdr = (OMX_BUFFERHEADERTYPE *) buf_ptr; + DEBUG_PRINT("BufHdr=%p buffer=%p\n",bufHdr,buffer); + *bufferHdr = bufHdr; + memset(bufHdr,0,sizeof(OMX_BUFFERHEADERTYPE)); + + bufHdr->pBuffer = (OMX_U8 *)(buffer); + DEBUG_PRINT("use_output_buffer:bufHdr %p bufHdr->pBuffer %p \ + len=%u", bufHdr, bufHdr->pBuffer,bytes); + bufHdr->nSize = (OMX_U32)sizeof(OMX_BUFFERHEADERTYPE); + bufHdr->nVersion.nVersion = OMX_SPEC_VERSION; + bufHdr->nAllocLen = nBufSize; + output_buffer_size = nBufSize; + bufHdr->pAppPrivate = appData; + bufHdr->nOutputPortIndex = OMX_CORE_OUTPUT_PORT_INDEX; + bufHdr->nOffset = 0; + m_output_buf_hdrs.insert(bufHdr, NULL); + m_out_current_buf_count++; + + } else + { + DEBUG_PRINT("Output buffer memory allocation failed\n"); + eRet = OMX_ErrorInsufficientResources; + } + } else + { + DEBUG_PRINT("Output buffer memory allocation failed 2\n"); + eRet = OMX_ErrorInsufficientResources; + } + return eRet; +} +/** + @brief member function that searches for caller buffer + + @param buffer pointer to buffer header + @return bool value indicating whether buffer is found + */ +bool omx_amr_aenc::search_input_bufhdr(OMX_BUFFERHEADERTYPE *buffer) +{ + + bool eRet = false; + OMX_BUFFERHEADERTYPE *temp = NULL; + + //access only in IL client context + temp = m_input_buf_hdrs.find_ele(buffer); + if (buffer && temp) + { + DEBUG_DETAIL("search_input_bufhdr %p \n", buffer); + eRet = true; + } + return eRet; +} + +/** + @brief member function that searches for caller buffer + + @param buffer pointer to buffer header + @return bool value indicating whether buffer is found + */ +bool omx_amr_aenc::search_output_bufhdr(OMX_BUFFERHEADERTYPE *buffer) +{ + + bool eRet = false; + OMX_BUFFERHEADERTYPE *temp = NULL; + + //access only in IL client context + temp = m_output_buf_hdrs.find_ele(buffer); + if (buffer && temp) + { + DEBUG_DETAIL("search_output_bufhdr %p \n", buffer); + eRet = true; + } + return eRet; +} + +// Free Buffer - API call +/** + @brief member function that handles free buffer command from IL client + + This function is a block-call function that handles IL client request to + freeing the buffer + + @param hComp handle to component instance + @param port id of port which holds the buffer + @param buffer buffer header + @return Error status +*/ +OMX_ERRORTYPE omx_amr_aenc::free_buffer(OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_U32 port, + OMX_IN OMX_BUFFERHEADERTYPE* buffer) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + + DEBUG_PRINT("Free_Buffer buf %p\n", buffer); + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_state == OMX_StateIdle && + (BITMASK_PRESENT(&m_flags ,OMX_COMPONENT_LOADING_PENDING))) + { + DEBUG_PRINT(" free buffer while Component in Loading pending\n"); + } else if ((m_inp_bEnabled == OMX_FALSE && + port == OMX_CORE_INPUT_PORT_INDEX)|| + (m_out_bEnabled == OMX_FALSE && + port == OMX_CORE_OUTPUT_PORT_INDEX)) + { + DEBUG_PRINT("Free Buffer while port %u disabled\n", port); + } else if (m_state == OMX_StateExecuting || m_state == OMX_StatePause) + { + DEBUG_PRINT("Invalid state to free buffer,ports need to be disabled:\ + OMX_ErrorPortUnpopulated\n"); + post_command(OMX_EventError, + OMX_ErrorPortUnpopulated, + OMX_COMPONENT_GENERATE_EVENT); + + return eRet; + } else + { + DEBUG_PRINT("free_buffer: Invalid state to free buffer,ports need to be\ + disabled:OMX_ErrorPortUnpopulated\n"); + post_command(OMX_EventError, + OMX_ErrorPortUnpopulated, + OMX_COMPONENT_GENERATE_EVENT); + } + if (OMX_CORE_INPUT_PORT_INDEX == port) + { + if (m_inp_current_buf_count != 0) + { + m_inp_bPopulated = OMX_FALSE; + if (true == search_input_bufhdr(buffer)) + { + /* Buffer exist */ + //access only in IL client context + DEBUG_PRINT("Free_Buf:in_buffer[%p]\n",buffer); + m_input_buf_hdrs.erase(buffer); + free(buffer); + m_inp_current_buf_count--; + } else + { + DEBUG_PRINT_ERROR("Free_Buf:Error-->free_buffer, \ + Invalid Input buffer header\n"); + eRet = OMX_ErrorBadParameter; + } + } else + { + DEBUG_PRINT_ERROR("Error: free_buffer,Port Index calculation \ + came out Invalid\n"); + eRet = OMX_ErrorBadPortIndex; + } + if (BITMASK_PRESENT((&m_flags),OMX_COMPONENT_INPUT_DISABLE_PENDING) + && release_done(0)) + { + DEBUG_PRINT("INPUT PORT MOVING TO DISABLED STATE \n"); + BITMASK_CLEAR((&m_flags),OMX_COMPONENT_INPUT_DISABLE_PENDING); + post_command(OMX_CommandPortDisable, + OMX_CORE_INPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + } + } else if (OMX_CORE_OUTPUT_PORT_INDEX == port) + { + if (m_out_current_buf_count != 0) + { + m_out_bPopulated = OMX_FALSE; + if (true == search_output_bufhdr(buffer)) + { + /* Buffer exist */ + //access only in IL client context + DEBUG_PRINT("Free_Buf:out_buffer[%p]\n",buffer); + m_output_buf_hdrs.erase(buffer); + free(buffer); + m_out_current_buf_count--; + } else + { + DEBUG_PRINT("Free_Buf:Error-->free_buffer , \ + Invalid Output buffer header\n"); + eRet = OMX_ErrorBadParameter; + } + } else + { + eRet = OMX_ErrorBadPortIndex; + } + + if (BITMASK_PRESENT((&m_flags),OMX_COMPONENT_OUTPUT_DISABLE_PENDING) + && release_done(1)) + { + DEBUG_PRINT("OUTPUT PORT MOVING TO DISABLED STATE \n"); + BITMASK_CLEAR((&m_flags),OMX_COMPONENT_OUTPUT_DISABLE_PENDING); + post_command(OMX_CommandPortDisable, + OMX_CORE_OUTPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + + } + } else + { + eRet = OMX_ErrorBadPortIndex; + } + if ((OMX_ErrorNone == eRet) && + (BITMASK_PRESENT(&m_flags ,OMX_COMPONENT_LOADING_PENDING))) + { + if (release_done(-1)) + { + if(ioctl(m_drv_fd, AUDIO_STOP, 0) < 0) + DEBUG_PRINT_ERROR("AUDIO STOP in free buffer failed\n"); + else + DEBUG_PRINT("AUDIO STOP in free buffer passed\n"); + + + DEBUG_PRINT("Free_Buf: Free buffer\n"); + + + // Send the callback now + BITMASK_CLEAR((&m_flags),OMX_COMPONENT_LOADING_PENDING); + DEBUG_PRINT("Before OMX_StateLoaded \ + OMX_COMPONENT_GENERATE_EVENT\n"); + post_command(OMX_CommandStateSet, + OMX_StateLoaded,OMX_COMPONENT_GENERATE_EVENT); + DEBUG_PRINT("After OMX_StateLoaded OMX_COMPONENT_GENERATE_EVENT\n"); + + } + } + return eRet; +} + + +/** + @brief member function that that handles empty this buffer command + + This function meremly queue up the command and data would be consumed + in command server thread context + + @param hComp handle to component instance + @param buffer pointer to buffer header + @return error status + */ +OMX_ERRORTYPE omx_amr_aenc::empty_this_buffer( + OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_BUFFERHEADERTYPE* buffer) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + + DEBUG_PRINT("ETB:Buf:%p Len %u TS %lld numInBuf=%d\n", \ + buffer, buffer->nFilledLen, buffer->nTimeStamp, (nNumInputBuf)); + if (m_state == OMX_StateInvalid) + { + DEBUG_PRINT("Empty this buffer in Invalid State\n"); + return OMX_ErrorInvalidState; + } + if (!m_inp_bEnabled) + { + DEBUG_PRINT("empty_this_buffer OMX_ErrorIncorrectStateOperation "\ + "Port Status %d \n", m_inp_bEnabled); + return OMX_ErrorIncorrectStateOperation; + } + if (buffer->nSize != sizeof(OMX_BUFFERHEADERTYPE)) + { + DEBUG_PRINT("omx_amr_aenc::etb--> Buffer Size Invalid\n"); + return OMX_ErrorBadParameter; + } + if (buffer->nVersion.nVersion != OMX_SPEC_VERSION) + { + DEBUG_PRINT("omx_amr_aenc::etb--> OMX Version Invalid\n"); + return OMX_ErrorVersionMismatch; + } + + if (buffer->nInputPortIndex != OMX_CORE_INPUT_PORT_INDEX) + { + return OMX_ErrorBadPortIndex; + } + if ((m_state != OMX_StateExecuting) && + (m_state != OMX_StatePause)) + { + DEBUG_PRINT_ERROR("Invalid state\n"); + eRet = OMX_ErrorInvalidState; + } + if (OMX_ErrorNone == eRet) + { + if (search_input_bufhdr(buffer) == true) + { + post_input((unsigned long)hComp, + (unsigned long) buffer,OMX_COMPONENT_GENERATE_ETB); + } else + { + DEBUG_PRINT_ERROR("Bad header %p \n", buffer); + eRet = OMX_ErrorBadParameter; + } + } + pthread_mutex_lock(&in_buf_count_lock); + nNumInputBuf++; + m_amr_pb_stats.etb_cnt++; + pthread_mutex_unlock(&in_buf_count_lock); + return eRet; +} +/** + @brief member function that writes data to kernel driver + + @param hComp handle to component instance + @param buffer pointer to buffer header + @return error status + */ +OMX_ERRORTYPE omx_amr_aenc::empty_this_buffer_proxy +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE* buffer) +{ + OMX_STATETYPE state; + META_IN meta_in; + //Pointer to the starting location of the data to be transcoded + OMX_U8 *srcStart; + //The total length of the data to be transcoded + srcStart = buffer->pBuffer; + OMX_U8 *data = NULL; + PrintFrameHdr(OMX_COMPONENT_GENERATE_ETB,buffer); + memset(&meta_in,0,sizeof(meta_in)); + if ( search_input_bufhdr(buffer) == false ) + { + DEBUG_PRINT("ETBP: INVALID BUF HDR\n"); + buffer_done_cb((OMX_BUFFERHEADERTYPE *)buffer); + return OMX_ErrorBadParameter; + } + if (m_tmp_meta_buf) + { + data = m_tmp_meta_buf; + + // copy the metadata info from the BufHdr and insert to payload + meta_in.offsetVal = (OMX_U16)sizeof(META_IN); + meta_in.nTimeStamp.LowPart = + (unsigned int)((((OMX_BUFFERHEADERTYPE*)buffer)->nTimeStamp)& 0xFFFFFFFF); + meta_in.nTimeStamp.HighPart = + (unsigned int) (((((OMX_BUFFERHEADERTYPE*)buffer)->nTimeStamp) >> 32) & 0xFFFFFFFF); + meta_in.nFlags &= ~OMX_BUFFERFLAG_EOS; + if(buffer->nFlags & OMX_BUFFERFLAG_EOS) + { + DEBUG_PRINT("EOS OCCURED \n"); + meta_in.nFlags |= OMX_BUFFERFLAG_EOS; + } + memcpy(data,&meta_in, meta_in.offsetVal); + DEBUG_PRINT("meta_in.nFlags = %d\n",meta_in.nFlags); + } + + memcpy(&data[sizeof(META_IN)],buffer->pBuffer,buffer->nFilledLen); + write(m_drv_fd, data, buffer->nFilledLen+sizeof(META_IN)); + + pthread_mutex_lock(&m_state_lock); + get_state(&m_cmp, &state); + pthread_mutex_unlock(&m_state_lock); + + if (OMX_StateExecuting == state) + { + DEBUG_DETAIL("In Exe state, EBD CB"); + buffer_done_cb((OMX_BUFFERHEADERTYPE *)buffer); + } else + { + /* Assume empty this buffer function has already checked + validity of buffer */ + DEBUG_PRINT("Empty buffer %p to kernel driver\n", buffer); + post_input((unsigned long) & hComp,(unsigned long) buffer, + OMX_COMPONENT_GENERATE_BUFFER_DONE); + } + return OMX_ErrorNone; +} + +OMX_ERRORTYPE omx_amr_aenc::fill_this_buffer_proxy +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE* buffer) +{ + OMX_STATETYPE state; + ENC_META_OUT *meta_out = NULL; + ssize_t nReadbytes = 0; + + pthread_mutex_lock(&m_state_lock); + get_state(&m_cmp, &state); + pthread_mutex_unlock(&m_state_lock); + + if (true == search_output_bufhdr(buffer)) + { + DEBUG_PRINT("\nBefore Read..m_drv_fd = %d,\n",m_drv_fd); + nReadbytes = read(m_drv_fd,buffer->pBuffer,output_buffer_size ); + DEBUG_DETAIL("FTBP->Al_len[%lu]buf[%p]size[%d]numOutBuf[%d]\n",\ + buffer->nAllocLen,buffer->pBuffer, + nReadbytes,nNumOutputBuf); + if (nReadbytes <= 0) { + buffer->nFilledLen = 0; + buffer->nOffset = 0; + buffer->nTimeStamp = nTimestamp; + frame_done_cb((OMX_BUFFERHEADERTYPE *)buffer); + return OMX_ErrorNone; + } else + DEBUG_PRINT("Read bytes %d\n",nReadbytes); + // Buffer from Driver will have + // 1 byte => Nr of frame field + // (sizeof(ENC_META_OUT) * Nr of frame) bytes => meta_out->offset_to_frame + // Frame Size * Nr of frame => + + meta_out = (ENC_META_OUT *)(buffer->pBuffer + sizeof(unsigned char)); + buffer->nTimeStamp = (((OMX_TICKS)meta_out->msw_ts << 32)+ + meta_out->lsw_ts); + buffer->nFlags |= meta_out->nflags; + buffer->nOffset = (OMX_U32)(meta_out->offset_to_frame + + sizeof(unsigned char)); + buffer->nFilledLen = (OMX_U32)(nReadbytes - buffer->nOffset); + ts += FRAMEDURATION; + buffer->nTimeStamp = ts; + nTimestamp = buffer->nTimeStamp; + DEBUG_PRINT("nflags %d frame_size %d offset_to_frame %d \ + timestamp %lld\n", meta_out->nflags, meta_out->frame_size, + meta_out->offset_to_frame, buffer->nTimeStamp); + + if ((buffer->nFlags & OMX_BUFFERFLAG_EOS) == OMX_BUFFERFLAG_EOS ) + { + buffer->nFilledLen = 0; + buffer->nOffset = 0; + buffer->nTimeStamp = nTimestamp; + frame_done_cb((OMX_BUFFERHEADERTYPE *)buffer); + if ((buffer->nFlags & OMX_BUFFERFLAG_EOS) == OMX_BUFFERFLAG_EOS ) + { + DEBUG_PRINT("FTBP: Now, Send EOS flag to Client \n"); + m_cb.EventHandler(&m_cmp, + m_app_data, + OMX_EventBufferFlag, + 1, 1, NULL ); + } + + return OMX_ErrorNone; + } + DEBUG_PRINT("nState %d \n",nState ); + + pthread_mutex_lock(&m_state_lock); + get_state(&m_cmp, &state); + pthread_mutex_unlock(&m_state_lock); + + if (state == OMX_StatePause) + { + DEBUG_PRINT("FTBP:Post the FBD to event thread currstate=%d\n",\ + state); + post_output((unsigned long) & hComp,(unsigned long) buffer, + OMX_COMPONENT_GENERATE_FRAME_DONE); + } + else + { + frame_done_cb((OMX_BUFFERHEADERTYPE *)buffer); + + } + + } + else + DEBUG_PRINT("\n FTBP-->Invalid buffer in FTB \n"); + + + return OMX_ErrorNone; +} + +/* ====================================================================== +FUNCTION + omx_amr_aenc::FillThisBuffer + +DESCRIPTION + IL client uses this method to release the frame buffer + after displaying them. + + + +PARAMETERS + + None. + +RETURN VALUE + true/false + +========================================================================== */ +OMX_ERRORTYPE omx_amr_aenc::fill_this_buffer +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_BUFFERHEADERTYPE* buffer) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + if (buffer->nSize != sizeof(OMX_BUFFERHEADERTYPE)) + { + DEBUG_PRINT("omx_amr_aenc::ftb--> Buffer Size Invalid\n"); + return OMX_ErrorBadParameter; + } + if (m_out_bEnabled == OMX_FALSE) + { + return OMX_ErrorIncorrectStateOperation; + } + + if (buffer->nVersion.nVersion != OMX_SPEC_VERSION) + { + DEBUG_PRINT("omx_amr_aenc::ftb--> OMX Version Invalid\n"); + return OMX_ErrorVersionMismatch; + } + if (buffer->nOutputPortIndex != OMX_CORE_OUTPUT_PORT_INDEX) + { + return OMX_ErrorBadPortIndex; + } + pthread_mutex_lock(&out_buf_count_lock); + nNumOutputBuf++; + m_amr_pb_stats.ftb_cnt++; + DEBUG_DETAIL("FTB:nNumOutputBuf is %d", nNumOutputBuf); + pthread_mutex_unlock(&out_buf_count_lock); + post_output((unsigned long)hComp, + (unsigned long) buffer,OMX_COMPONENT_GENERATE_FTB); + return eRet; +} + +/* ====================================================================== +FUNCTION + omx_amr_aenc::SetCallbacks + +DESCRIPTION + Set the callbacks. + +PARAMETERS + None. + +RETURN VALUE + OMX Error None if everything successful. + +========================================================================== */ +OMX_ERRORTYPE omx_amr_aenc::set_callbacks(OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_CALLBACKTYPE* callbacks, + OMX_IN OMX_PTR appData) +{ + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + m_cb = *callbacks; + m_app_data = appData; + + return OMX_ErrorNone; +} + +/* ====================================================================== +FUNCTION + omx_amr_aenc::ComponentDeInit + +DESCRIPTION + Destroys the component and release memory allocated to the heap. + +PARAMETERS + . + +RETURN VALUE + OMX Error None if everything successful. + +========================================================================== */ +OMX_ERRORTYPE omx_amr_aenc::component_deinit(OMX_IN OMX_HANDLETYPE hComp) +{ + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (OMX_StateLoaded != m_state && OMX_StateInvalid != m_state) + { + DEBUG_PRINT_ERROR("Warning: Rxed DeInit when not in LOADED state %d\n", + m_state); + } + deinit_encoder(); + +DEBUG_PRINT_ERROR("%s:COMPONENT DEINIT...\n", __FUNCTION__); + return OMX_ErrorNone; +} + +/* ====================================================================== +FUNCTION + omx_amr_aenc::deinit_encoder + +DESCRIPTION + Closes all the threads and release memory allocated to the heap. + +PARAMETERS + None. + +RETURN VALUE + None. + +========================================================================== */ +void omx_amr_aenc::deinit_encoder() +{ + DEBUG_PRINT("Component-deinit being processed\n"); + DEBUG_PRINT("********************************\n"); + DEBUG_PRINT("STATS: in-buf-len[%u]out-buf-len[%u] tot-pb-time[%lld]",\ + m_amr_pb_stats.tot_in_buf_len, + m_amr_pb_stats.tot_out_buf_len, + m_amr_pb_stats.tot_pb_time); + DEBUG_PRINT("STATS: fbd-cnt[%u]ftb-cnt[%u]etb-cnt[%u]ebd-cnt[%u]",\ + m_amr_pb_stats.fbd_cnt,m_amr_pb_stats.ftb_cnt, + m_amr_pb_stats.etb_cnt, + m_amr_pb_stats.ebd_cnt); + memset(&m_amr_pb_stats,0,sizeof(AMR_PB_STATS)); + + if((OMX_StateLoaded != m_state) && (OMX_StateInvalid != m_state)) + { + DEBUG_PRINT_ERROR("%s,Deinit called in state[%d]\n",__FUNCTION__,\ + m_state); + // Get back any buffers from driver + if(pcm_input) + execute_omx_flush(-1,false); + else + execute_omx_flush(1,false); + // force state change to loaded so that all threads can be exited + pthread_mutex_lock(&m_state_lock); + m_state = OMX_StateLoaded; + pthread_mutex_unlock(&m_state_lock); + DEBUG_PRINT_ERROR("Freeing Buf:inp_current_buf_count[%d][%d]\n",\ + m_inp_current_buf_count, + m_input_buf_hdrs.size()); + m_input_buf_hdrs.eraseall(); + DEBUG_PRINT_ERROR("Freeing Buf:out_current_buf_count[%d][%d]\n",\ + m_out_current_buf_count, + m_output_buf_hdrs.size()); + m_output_buf_hdrs.eraseall(); + + } + if(pcm_input) + { + pthread_mutex_lock(&m_in_th_lock_1); + if (is_in_th_sleep) + { + is_in_th_sleep = false; + DEBUG_DETAIL("Deinit:WAKING UP IN THREADS\n"); + in_th_wakeup(); + } + pthread_mutex_unlock(&m_in_th_lock_1); + } + pthread_mutex_lock(&m_out_th_lock_1); + if (is_out_th_sleep) + { + is_out_th_sleep = false; + DEBUG_DETAIL("SCP:WAKING UP OUT THREADS\n"); + out_th_wakeup(); + } + pthread_mutex_unlock(&m_out_th_lock_1); + if(pcm_input) + { + if (m_ipc_to_in_th != NULL) + { + omx_amr_thread_stop(m_ipc_to_in_th); + m_ipc_to_in_th = NULL; + } + } + + if (m_ipc_to_cmd_th != NULL) + { + omx_amr_thread_stop(m_ipc_to_cmd_th); + m_ipc_to_cmd_th = NULL; + } + if (m_ipc_to_out_th != NULL) + { + DEBUG_DETAIL("Inside omx_amr_thread_stop\n"); + omx_amr_thread_stop(m_ipc_to_out_th); + m_ipc_to_out_th = NULL; + } + + + if(ioctl(m_drv_fd, AUDIO_STOP, 0) <0) + DEBUG_PRINT_ERROR("De-init: AUDIO_STOP FAILED\n"); + + if(pcm_input && m_tmp_meta_buf ) + { + free(m_tmp_meta_buf); + } + + if(m_tmp_out_meta_buf) + { + free(m_tmp_out_meta_buf); + } + nNumInputBuf = 0; + nNumOutputBuf = 0; + bFlushinprogress = 0; + + m_inp_current_buf_count=0; + m_out_current_buf_count=0; + m_out_act_buf_count = 0; + m_inp_act_buf_count = 0; + m_inp_bEnabled = OMX_FALSE; + m_out_bEnabled = OMX_FALSE; + m_inp_bPopulated = OMX_FALSE; + m_out_bPopulated = OMX_FALSE; + nTimestamp = 0; + ts = 0; + + if ( m_drv_fd >= 0 ) + { + if(close(m_drv_fd) < 0) + DEBUG_PRINT("De-init: Driver Close Failed \n"); + m_drv_fd = -1; + } + else + { + DEBUG_PRINT_ERROR(" AMR device already closed\n"); + } + m_comp_deinit=1; + m_is_out_th_sleep = 1; + m_is_in_th_sleep = 1; + DEBUG_PRINT("************************************\n"); + DEBUG_PRINT(" DEINIT COMPLETED"); + DEBUG_PRINT("************************************\n"); + +} + +/* ====================================================================== +FUNCTION + omx_amr_aenc::UseEGLImage + +DESCRIPTION + OMX Use EGL Image method implementation . + +PARAMETERS + . + +RETURN VALUE + Not Implemented error. + +========================================================================== */ +OMX_ERRORTYPE omx_amr_aenc::use_EGL_image +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE** bufferHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN void* eglImage) +{ + DEBUG_PRINT_ERROR("Error : use_EGL_image: Not Implemented \n"); + + if((hComp == NULL) || (appData == NULL) || (eglImage == NULL)) + { + bufferHdr = bufferHdr; + port = port; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + return OMX_ErrorNotImplemented; +} + +/* ====================================================================== +FUNCTION + omx_amr_aenc::ComponentRoleEnum + +DESCRIPTION + OMX Component Role Enum method implementation. + +PARAMETERS + . + +RETURN VALUE + OMX Error None if everything is successful. +========================================================================== */ +OMX_ERRORTYPE omx_amr_aenc::component_role_enum(OMX_IN OMX_HANDLETYPE hComp, + OMX_OUT OMX_U8* role, + OMX_IN OMX_U32 index) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + const char *cmp_role = "audio_encoder.amr"; + + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (index == 0 && role) + { + memcpy(role, cmp_role, strlen(cmp_role)); + *(((char *) role) + strlen(cmp_role) +1) = '\0'; + } else + { + eRet = OMX_ErrorNoMore; + } + return eRet; +} + + + + +/* ====================================================================== +FUNCTION + omx_amr_aenc::AllocateDone + +DESCRIPTION + Checks if entire buffer pool is allocated by IL Client or not. + Need this to move to IDLE state. + +PARAMETERS + None. + +RETURN VALUE + true/false. + +========================================================================== */ +bool omx_amr_aenc::allocate_done(void) +{ + OMX_BOOL bRet = OMX_FALSE; + if (pcm_input==1) + { + if ((m_inp_act_buf_count == m_inp_current_buf_count) + &&(m_out_act_buf_count == m_out_current_buf_count)) + { + bRet=OMX_TRUE; + + } + if ((m_inp_act_buf_count == m_inp_current_buf_count) && m_inp_bEnabled ) + { + m_inp_bPopulated = OMX_TRUE; + } + + if ((m_out_act_buf_count == m_out_current_buf_count) && m_out_bEnabled ) + { + m_out_bPopulated = OMX_TRUE; + } + } else if (pcm_input==0) + { + if (m_out_act_buf_count == m_out_current_buf_count) + { + bRet=OMX_TRUE; + + } + if ((m_out_act_buf_count == m_out_current_buf_count) && m_out_bEnabled ) + { + m_out_bPopulated = OMX_TRUE; + } + + } + return bRet; +} + + +/* ====================================================================== +FUNCTION + omx_amr_aenc::ReleaseDone + +DESCRIPTION + Checks if IL client has released all the buffers. + +PARAMETERS + None. + +RETURN VALUE + true/false + +========================================================================== */ +bool omx_amr_aenc::release_done(OMX_U32 param1) +{ + DEBUG_PRINT("Inside omx_amr_aenc::release_done"); + OMX_BOOL bRet = OMX_FALSE; + + if (param1 == OMX_ALL) + { + if ((0 == m_inp_current_buf_count)&&(0 == m_out_current_buf_count)) + { + bRet=OMX_TRUE; + } + } else if (param1 == OMX_CORE_INPUT_PORT_INDEX ) + { + if ((0 == m_inp_current_buf_count)) + { + bRet=OMX_TRUE; + } + } else if (param1 == OMX_CORE_OUTPUT_PORT_INDEX) + { + if ((0 == m_out_current_buf_count)) + { + bRet=OMX_TRUE; + } + } + return bRet; +} diff --git a/audio/mm-audio/aenc-amrnb/qdsp6/test/omx_amr_enc_test.c b/audio/mm-audio/aenc-amrnb/qdsp6/test/omx_amr_enc_test.c new file mode 100644 index 0000000..e509748 --- /dev/null +++ b/audio/mm-audio/aenc-amrnb/qdsp6/test/omx_amr_enc_test.c @@ -0,0 +1,1055 @@ + +/*-------------------------------------------------------------------------- +Copyright (c) 2010-2014, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of The Linux Foundation nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------*/ + + +/* + An Open max test application .... +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "OMX_Core.h" +#include "OMX_Component.h" +#include "pthread.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "QOMX_AudioExtensions.h" +#include "QOMX_AudioIndexExtensions.h" +#ifdef AUDIOV2 +#include "control.h" +#endif + + +#include + +typedef unsigned char uint8; +typedef unsigned char byte; +typedef unsigned int uint32; +typedef unsigned int uint16; +QOMX_AUDIO_STREAM_INFO_DATA streaminfoparam; +/* maximum ADTS frame header length */ +void Release_Encoder(); + +#ifdef AUDIOV2 +unsigned short session_id; +int device_id; +int control = 0; +const char *device="handset_tx"; +#define DIR_TX 2 +#endif + +uint32_t samplerate = 8000; +uint32_t channels = 1; +uint32_t bandmode = 7; +uint32_t dtxenable = 0; +uint32_t rectime = 0; +uint32_t recpath = 0; +uint32_t pcmplayback = 0; +uint32_t tunnel = 0; +uint32_t format = 1; +#define DEBUG_PRINT printf +unsigned to_idle_transition = 0; +unsigned long total_pcm_bytes; + +/************************************************************************/ +/* GLOBAL INIT */ +/************************************************************************/ + +/************************************************************************/ +/* #DEFINES */ +/************************************************************************/ +#define false 0 +#define true 1 + +#define CONFIG_VERSION_SIZE(param) \ + param.nVersion.nVersion = CURRENT_OMX_SPEC_VERSION;\ + param.nSize = sizeof(param); + +#define MIN_BITRATE 4 /* Bit rate 1 - 13.6 , 2 - 6.2 , 3 - 2.7 , 4 - 1.0 kbps*/ +#define MAX_BITRATE 4 +#define AMR_HEADER_SIZE 6 +#define FAILED(result) (result != OMX_ErrorNone) + +#define SUCCEEDED(result) (result == OMX_ErrorNone) + +/************************************************************************/ +/* GLOBAL DECLARATIONS */ +/************************************************************************/ + +pthread_mutex_t lock; +pthread_cond_t cond; +pthread_mutex_t elock; +pthread_cond_t econd; +pthread_cond_t fcond; +pthread_mutex_t etb_lock; +pthread_mutex_t etb_lock1; +pthread_cond_t etb_cond; +FILE * inputBufferFile; +FILE * outputBufferFile; +OMX_PARAM_PORTDEFINITIONTYPE inputportFmt; +OMX_PARAM_PORTDEFINITIONTYPE outputportFmt; +OMX_AUDIO_PARAM_AMRTYPE amrparam; +OMX_AUDIO_PARAM_PCMMODETYPE pcmparam; +OMX_PORT_PARAM_TYPE portParam; +OMX_PORT_PARAM_TYPE portFmt; +OMX_ERRORTYPE error; + + + + +#define ID_RIFF 0x46464952 +#define ID_WAVE 0x45564157 +#define ID_FMT 0x20746d66 +#define ID_DATA 0x61746164 + +#define FORMAT_PCM 1 + +struct wav_header { + uint32_t riff_id; + uint32_t riff_sz; + uint32_t riff_fmt; + uint32_t fmt_id; + uint32_t fmt_sz; + uint16_t audio_format; + uint16_t num_channels; + uint32_t sample_rate; + uint32_t byte_rate; /* sample_rate * num_channels * bps / 8 */ + uint16_t block_align; /* num_channels * bps / 8 */ + uint16_t bits_per_sample; + uint32_t data_id; + uint32_t data_sz; +}; +struct enc_meta_out{ + unsigned int offset_to_frame; + unsigned int frame_size; + unsigned int encoded_pcm_samples; + unsigned int msw_ts; + unsigned int lsw_ts; + unsigned int nflags; +} __attribute__ ((packed)); + +struct qcp_header { + /* RIFF Section */ + char riff[4]; + unsigned int s_riff; + char qlcm[4]; + + /* Format chunk */ + char fmt[4]; + unsigned int s_fmt; + char mjr; + char mnr; + unsigned int data1; /* UNIQUE ID of the codec */ + unsigned short data2; + unsigned short data3; + char data4[8]; + unsigned short ver; /* Codec Info */ + char name[80]; + unsigned short abps; /* average bits per sec of the codec */ + unsigned short bytes_per_pkt; + unsigned short samp_per_block; + unsigned short samp_per_sec; + unsigned short bits_per_samp; + unsigned char vr_num_of_rates; /* Rate Header fmt info */ + unsigned char rvd1[3]; + unsigned short vr_bytes_per_pkt[8]; + unsigned int rvd2[5]; + + /* Vrat chunk */ + unsigned char vrat[4]; + unsigned int s_vrat; + unsigned int v_rate; + unsigned int size_in_pkts; + + /* Data chunk */ + unsigned char data[4]; + unsigned int s_data; +} __attribute__ ((packed)); + +static int totaldatalen = 0; +static int framecnt = 0; +/************************************************************************/ +/* GLOBAL INIT */ +/************************************************************************/ + +unsigned int input_buf_cnt = 0; +unsigned int output_buf_cnt = 0; +int used_ip_buf_cnt = 0; +volatile int event_is_done = 0; +volatile int ebd_event_is_done = 0; +volatile int fbd_event_is_done = 0; +volatile int etb_event_is_done = 0; +int ebd_cnt; +int bInputEosReached = 0; +int bOutputEosReached = 0; +int bInputEosReached_tunnel = 0; +static int etb_done = 0; +int bFlushing = false; +int bPause = false; +const char *in_filename; +const char *out_filename; + +int timeStampLfile = 0; +int timestampInterval = 100; + +//* OMX Spec Version supported by the wrappers. Version = 1.1 */ +const OMX_U32 CURRENT_OMX_SPEC_VERSION = 0x00000101; +OMX_COMPONENTTYPE* amr_enc_handle = 0; + +OMX_BUFFERHEADERTYPE **pInputBufHdrs = NULL; +OMX_BUFFERHEADERTYPE **pOutputBufHdrs = NULL; + +/************************************************************************/ +/* GLOBAL FUNC DECL */ +/************************************************************************/ +int Init_Encoder(char*); +int Play_Encoder(); +OMX_STRING aud_comp; +/**************************************************************************/ +/* STATIC DECLARATIONS */ +/**************************************************************************/ + +static int open_audio_file (); +static int Read_Buffer(OMX_BUFFERHEADERTYPE *pBufHdr ); +static OMX_ERRORTYPE Allocate_Buffer ( OMX_COMPONENTTYPE *amr_enc_handle, + OMX_BUFFERHEADERTYPE ***pBufHdrs, + OMX_U32 nPortIndex, + unsigned int bufCntMin, unsigned int bufSize); + + +static OMX_ERRORTYPE EventHandler(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_EVENTTYPE eEvent, + OMX_IN OMX_U32 nData1, OMX_IN OMX_U32 nData2, + OMX_IN OMX_PTR pEventData); +static OMX_ERRORTYPE EmptyBufferDone(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer); + +static OMX_ERRORTYPE FillBufferDone(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer); +static OMX_ERRORTYPE parse_pcm_header(); +void wait_for_event(void) +{ + pthread_mutex_lock(&lock); + DEBUG_PRINT("%s: event_is_done=%d", __FUNCTION__, event_is_done); + while (event_is_done == 0) { + pthread_cond_wait(&cond, &lock); + } + event_is_done = 0; + pthread_mutex_unlock(&lock); +} + +void event_complete(void ) +{ + pthread_mutex_lock(&lock); + if (event_is_done == 0) { + event_is_done = 1; + pthread_cond_broadcast(&cond); + } + pthread_mutex_unlock(&lock); +} + +void etb_wait_for_event(void) +{ + pthread_mutex_lock(&etb_lock1); + DEBUG_PRINT("%s: etb_event_is_done=%d", __FUNCTION__, etb_event_is_done); + while (etb_event_is_done == 0) { + pthread_cond_wait(&etb_cond, &etb_lock1); + } + etb_event_is_done = 0; + pthread_mutex_unlock(&etb_lock1); +} + +void etb_event_complete(void ) +{ + pthread_mutex_lock(&etb_lock1); + if (etb_event_is_done == 0) { + etb_event_is_done = 1; + pthread_cond_broadcast(&etb_cond); + } + pthread_mutex_unlock(&etb_lock1); +} + + +OMX_ERRORTYPE EventHandler(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_EVENTTYPE eEvent, + OMX_IN OMX_U32 nData1, OMX_IN OMX_U32 nData2, + OMX_IN OMX_PTR pEventData) +{ + DEBUG_PRINT("Function %s \n", __FUNCTION__); + + /* To remove warning for unused variable to keep prototype same */ + (void)hComponent; + (void)pAppData; + (void)pEventData; + switch(eEvent) { + case OMX_EventCmdComplete: + DEBUG_PRINT("\n OMX_EventCmdComplete event=%d data1=%u data2=%u\n",(OMX_EVENTTYPE)eEvent, + nData1,nData2); + event_complete(); + break; + case OMX_EventError: + DEBUG_PRINT("\n OMX_EventError \n"); + break; + case OMX_EventBufferFlag: + DEBUG_PRINT("\n OMX_EventBufferFlag \n"); + bOutputEosReached = true; + event_complete(); + break; + case OMX_EventPortSettingsChanged: + DEBUG_PRINT("\n OMX_EventPortSettingsChanged \n"); + break; + default: + DEBUG_PRINT("\n Unknown Event \n"); + break; + } + return OMX_ErrorNone; +} + +OMX_ERRORTYPE FillBufferDone(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) +{ + size_t bytes_writen = 0; + size_t total_bytes_writen = 0; + size_t len = 0; + struct enc_meta_out *meta = NULL; + OMX_U8 *src = pBuffer->pBuffer; + unsigned int num_of_frames = 1; + + /* To remove warning for unused variable to keep prototype same */ + (void)pAppData; + + if(((pBuffer->nFlags & OMX_BUFFERFLAG_EOS) == OMX_BUFFERFLAG_EOS)) { + DEBUG_PRINT("FBD::EOS on output port\n "); + bOutputEosReached = true; + return OMX_ErrorNone; + } + if(bInputEosReached_tunnel || bOutputEosReached) + { + DEBUG_PRINT("EOS REACHED NO MORE PROCESSING OF BUFFERS\n"); + return OMX_ErrorNone; + } + if(num_of_frames != src[0]){ + + printf("Data corrupt\n"); + return OMX_ErrorNone; + } + /* Skip the first bytes */ + + + + src += sizeof(unsigned char); + meta = (struct enc_meta_out *)src; + while (num_of_frames > 0) { + meta = (struct enc_meta_out *)src; + /*printf("offset=%d framesize=%d encoded_pcm[%d] msw_ts[%d]lsw_ts[%d] nflags[%d]\n", + meta->offset_to_frame, + meta->frame_size, + meta->encoded_pcm_samples, meta->msw_ts, meta->lsw_ts, meta->nflags);*/ + len = meta->frame_size; + + bytes_writen = fwrite(pBuffer->pBuffer + sizeof(unsigned char) + meta->offset_to_frame,1,len,outputBufferFile); + if(bytes_writen < len) + { + DEBUG_PRINT("error: invalid AMR encoded data \n"); + return OMX_ErrorNone; + } + src += sizeof(struct enc_meta_out); + num_of_frames--; + total_bytes_writen += len; + } + DEBUG_PRINT(" FillBufferDone size writen to file %zu count %d\n",total_bytes_writen, framecnt); + totaldatalen = totaldatalen + (int)total_bytes_writen; + framecnt++; + + DEBUG_PRINT(" FBD calling FTB\n"); + OMX_FillThisBuffer(hComponent,pBuffer); + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE EmptyBufferDone(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) +{ + int readBytes =0; + /* To remove warning for unused variable to keep prototype same */ + (void)pAppData; + + ebd_cnt++; + used_ip_buf_cnt--; + pthread_mutex_lock(&etb_lock); + if(!etb_done) + { + DEBUG_PRINT("\n*********************************************\n"); + DEBUG_PRINT("Wait till first set of buffers are given to component\n"); + DEBUG_PRINT("\n*********************************************\n"); + etb_done++; + pthread_mutex_unlock(&etb_lock); + etb_wait_for_event(); + } + else + { + pthread_mutex_unlock(&etb_lock); + } + + + if(bInputEosReached) + { + DEBUG_PRINT("\n*********************************************\n"); + DEBUG_PRINT(" EBD::EOS on input port\n "); + DEBUG_PRINT("*********************************************\n"); + return OMX_ErrorNone; + }else if (bFlushing == true) { + DEBUG_PRINT("omx_amr_adec_test: bFlushing is set to TRUE used_ip_buf_cnt=%d\n",used_ip_buf_cnt); + if (used_ip_buf_cnt == 0) { + bFlushing = false; + } else { + DEBUG_PRINT("omx_amr_adec_test: more buffer to come back used_ip_buf_cnt=%d\n",used_ip_buf_cnt); + return OMX_ErrorNone; + } + } + + if((readBytes = Read_Buffer(pBuffer)) > 0) { + pBuffer->nFilledLen = (OMX_U32)readBytes; + used_ip_buf_cnt++; + OMX_EmptyThisBuffer(hComponent,pBuffer); + } + else{ + pBuffer->nFlags |= OMX_BUFFERFLAG_EOS; + used_ip_buf_cnt++; + bInputEosReached = true; + pBuffer->nFilledLen = 0; + OMX_EmptyThisBuffer(hComponent,pBuffer); + DEBUG_PRINT("EBD..Either EOS or Some Error while reading file\n"); + } + return OMX_ErrorNone; +} + +void signal_handler(int sig_id) { + + /* Flush */ + if (sig_id == SIGUSR1) { + DEBUG_PRINT("%s Initiate flushing\n", __FUNCTION__); + bFlushing = true; + OMX_SendCommand(amr_enc_handle, OMX_CommandFlush, OMX_ALL, NULL); + } else if (sig_id == SIGUSR2) { + if (bPause == true) { + DEBUG_PRINT("%s resume record\n", __FUNCTION__); + bPause = false; + OMX_SendCommand(amr_enc_handle, OMX_CommandStateSet, OMX_StateExecuting, NULL); + } else { + DEBUG_PRINT("%s pause record\n", __FUNCTION__); + bPause = true; + OMX_SendCommand(amr_enc_handle, OMX_CommandStateSet, OMX_StatePause, NULL); + } + } +} + +int main(int argc, char **argv) +{ + unsigned int bufCnt=0; + OMX_ERRORTYPE result; + + struct sigaction sa; + char amr_header[6] = {0x23, 0x21, 0x41, 0x4D, 0x52, 0x0A}; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = &signal_handler; + sigaction(SIGABRT, &sa, NULL); + sigaction(SIGUSR1, &sa, NULL); + sigaction(SIGUSR2, &sa, NULL); + + (void) signal(SIGINT, Release_Encoder); + + pthread_cond_init(&cond, 0); + pthread_mutex_init(&lock, 0); + pthread_cond_init(&etb_cond, 0); + pthread_mutex_init(&etb_lock, 0); + pthread_mutex_init(&etb_lock1, 0); + + if (argc >= 8) { + in_filename = argv[1]; + out_filename = argv[2]; + tunnel = (uint32_t)atoi(argv[3]); + bandmode = (uint32_t)atoi(argv[4]); + dtxenable = (uint32_t)atoi(argv[5]); + recpath = (uint32_t)atoi(argv[6]); // No configuration support yet.. + rectime = (uint32_t)atoi(argv[7]); + + } else { + DEBUG_PRINT(" invalid format: \n"); + DEBUG_PRINT("ex: ./mm-aenc-omxamr-test INPUTFILE OUTPUTFILE Tunnel BANDMODE DTXENABLE RECORDPATH RECORDTIME\n"); + DEBUG_PRINT("Bandmode 1-7, dtxenable 0-1\n"); + DEBUG_PRINT("RECORDPATH 0(TX),1(RX),2(BOTH),3(MIC)\n"); + DEBUG_PRINT("RECORDTIME in seconds for AST Automation\n"); + return 0; + } + if(recpath != 3) { + DEBUG_PRINT("For RECORDPATH Only MIC supported\n"); + return 0; + } + if(tunnel == 0) + aud_comp = "OMX.qcom.audio.encoder.amrnb"; + else + aud_comp = "OMX.qcom.audio.encoder.tunneled.amrnb"; + if(Init_Encoder(aud_comp)!= 0x00) + { + DEBUG_PRINT("Decoder Init failed\n"); + return -1; + } + + fcntl(0, F_SETFL, O_NONBLOCK); + + if(Play_Encoder() != 0x00) + { + DEBUG_PRINT("Play_Decoder failed\n"); + return -1; + } + + // Wait till EOS is reached... + if(rectime && tunnel) + { + sleep(rectime); + rectime = 0; + bInputEosReached_tunnel = 1; + DEBUG_PRINT("\EOS ON INPUT PORT\n"); + } + else + { + wait_for_event(); + } + + if((bInputEosReached_tunnel) || ((bOutputEosReached) && !tunnel)) + { + + DEBUG_PRINT("\nMoving the decoder to idle state \n"); + OMX_SendCommand(amr_enc_handle, OMX_CommandStateSet, OMX_StateIdle,0); + wait_for_event(); + + DEBUG_PRINT("\nMoving the encoder to loaded state \n"); + OMX_SendCommand(amr_enc_handle, OMX_CommandStateSet, OMX_StateLoaded,0); + sleep(1); + if (!tunnel) + { + DEBUG_PRINT("\nFillBufferDone: Deallocating i/p buffers \n"); + for(bufCnt=0; bufCnt < input_buf_cnt; ++bufCnt) { + OMX_FreeBuffer(amr_enc_handle, 0, pInputBufHdrs[bufCnt]); + } + } + + DEBUG_PRINT ("\nFillBufferDone: Deallocating o/p buffers \n"); + for(bufCnt=0; bufCnt < output_buf_cnt; ++bufCnt) { + OMX_FreeBuffer(amr_enc_handle, 1, pOutputBufHdrs[bufCnt]); + } + wait_for_event(); + fseek(outputBufferFile, 0,SEEK_SET); + fwrite(amr_header,1,AMR_HEADER_SIZE,outputBufferFile); + + result = OMX_FreeHandle(amr_enc_handle); + if (result != OMX_ErrorNone) { + DEBUG_PRINT ("\nOMX_FreeHandle error. Error code: %d\n", result); + } + + /* Deinit OpenMAX */ + if(tunnel) + { + #ifdef AUDIOV2 + if (msm_route_stream(DIR_TX,session_id,device_id, 0)) + { + DEBUG_PRINT("\ncould not set stream routing\n"); + return -1; + } + if (msm_en_device(device_id, 0)) + { + DEBUG_PRINT("\ncould not enable device\n"); + return -1; + } + msm_mixer_close(); + #endif + } + OMX_Deinit(); + ebd_cnt=0; + bOutputEosReached = false; + bInputEosReached_tunnel = false; + bInputEosReached = 0; + amr_enc_handle = NULL; + pthread_cond_destroy(&cond); + pthread_mutex_destroy(&lock); + fclose(outputBufferFile); + DEBUG_PRINT("*****************************************\n"); + DEBUG_PRINT("******...AMR ENC TEST COMPLETED...***************\n"); + DEBUG_PRINT("*****************************************\n"); + } + return 0; +} + +void Release_Encoder() +{ + static int cnt=0; + OMX_ERRORTYPE result; + + DEBUG_PRINT("END OF AMR ENCODING: EXITING PLEASE WAIT\n"); + bInputEosReached_tunnel = 1; + event_complete(); + cnt++; + if(cnt > 1) + { + /* FORCE RESET */ + amr_enc_handle = NULL; + ebd_cnt=0; + bInputEosReached_tunnel = false; + + result = OMX_FreeHandle(amr_enc_handle); + if (result != OMX_ErrorNone) { + DEBUG_PRINT ("\nOMX_FreeHandle error. Error code: %d\n", result); + } + + /* Deinit OpenMAX */ + + OMX_Deinit(); + + pthread_cond_destroy(&cond); + pthread_mutex_destroy(&lock); + DEBUG_PRINT("*****************************************\n"); + DEBUG_PRINT("******...AMR ENC TEST COMPLETED...***************\n"); + DEBUG_PRINT("*****************************************\n"); + exit(0); + } +} + +int Init_Encoder(OMX_STRING audio_component) +{ + DEBUG_PRINT("Inside %s \n", __FUNCTION__); + OMX_ERRORTYPE omxresult; + OMX_U32 total = 0; + typedef OMX_U8* OMX_U8_PTR; + char *role ="audio_encoder"; + + static OMX_CALLBACKTYPE call_back = { + &EventHandler,&EmptyBufferDone,&FillBufferDone + }; + + /* Init. the OpenMAX Core */ + DEBUG_PRINT("\nInitializing OpenMAX Core....\n"); + omxresult = OMX_Init(); + + if(OMX_ErrorNone != omxresult) { + DEBUG_PRINT("\n Failed to Init OpenMAX core"); + return -1; + } + else { + DEBUG_PRINT("\nOpenMAX Core Init Done\n"); + } + + /* Query for audio decoders*/ + DEBUG_PRINT("Amr_test: Before entering OMX_GetComponentOfRole"); + OMX_GetComponentsOfRole(role, &total, 0); + DEBUG_PRINT ("\nTotal components of role=%s :%u", role, total); + + + omxresult = OMX_GetHandle((OMX_HANDLETYPE*)(&amr_enc_handle), + (OMX_STRING)audio_component, NULL, &call_back); + if (FAILED(omxresult)) { + DEBUG_PRINT("\nFailed to Load the component:%s\n", audio_component); + return -1; + } + else + { + DEBUG_PRINT("\nComponent %s is in LOADED state\n", audio_component); + } + + /* Get the port information */ + CONFIG_VERSION_SIZE(portParam); + omxresult = OMX_GetParameter(amr_enc_handle, OMX_IndexParamAudioInit, + (OMX_PTR)&portParam); + + if(FAILED(omxresult)) { + DEBUG_PRINT("\nFailed to get Port Param\n"); + return -1; + } + else + { + DEBUG_PRINT("\nportParam.nPorts:%u\n", portParam.nPorts); + DEBUG_PRINT("\nportParam.nStartPortNumber:%u\n", + portParam.nStartPortNumber); + } + + if(OMX_ErrorNone != omxresult) + { + DEBUG_PRINT("Set parameter failed"); + } + + return 0; +} + +int Play_Encoder() +{ + unsigned int i; + int Size=0; + DEBUG_PRINT("Inside %s \n", __FUNCTION__); + OMX_ERRORTYPE ret; + OMX_INDEXTYPE index; +#ifdef __LP64__ + DEBUG_PRINT("sizeof[%ld]\n", sizeof(OMX_BUFFERHEADERTYPE)); +#else + DEBUG_PRINT("sizeof[%d]\n", sizeof(OMX_BUFFERHEADERTYPE)); +#endif + + /* open the i/p and o/p files based on the video file format passed */ + if(open_audio_file()) { + DEBUG_PRINT("\n Returning -1"); + return -1; + } + + /* Query the encoder input min buf requirements */ + CONFIG_VERSION_SIZE(inputportFmt); + + /* Port for which the Client needs to obtain info */ + inputportFmt.nPortIndex = portParam.nStartPortNumber; + + OMX_GetParameter(amr_enc_handle,OMX_IndexParamPortDefinition,&inputportFmt); + DEBUG_PRINT ("\nEnc Input Buffer Count %u\n", inputportFmt.nBufferCountMin); + DEBUG_PRINT ("\nEnc: Input Buffer Size %u\n", inputportFmt.nBufferSize); + + if(OMX_DirInput != inputportFmt.eDir) { + DEBUG_PRINT ("\nEnc: Expect Input Port\n"); + return -1; + } + + pcmparam.nPortIndex = 0; + pcmparam.nChannels = channels; + pcmparam.nSamplingRate = samplerate; + OMX_SetParameter(amr_enc_handle,OMX_IndexParamAudioPcm,&pcmparam); + + + /* Query the encoder outport's min buf requirements */ + CONFIG_VERSION_SIZE(outputportFmt); + /* Port for which the Client needs to obtain info */ + outputportFmt.nPortIndex = portParam.nStartPortNumber + 1; + + OMX_GetParameter(amr_enc_handle,OMX_IndexParamPortDefinition,&outputportFmt); + DEBUG_PRINT ("\nEnc: Output Buffer Count %u\n", outputportFmt.nBufferCountMin); + DEBUG_PRINT ("\nEnc: Output Buffer Size %u\n", outputportFmt.nBufferSize); + + if(OMX_DirOutput != outputportFmt.eDir) { + DEBUG_PRINT ("\nEnc: Expect Output Port\n"); + return -1; + } + + + CONFIG_VERSION_SIZE(amrparam); + + amrparam.nPortIndex = 1; + amrparam.nChannels = channels; //2 ; /* 1-> mono 2-> stereo*/ + amrparam.eAMRBandMode = bandmode; + amrparam.eAMRDTXMode = dtxenable; + OMX_SetParameter(amr_enc_handle,OMX_IndexParamAudioAmr,&amrparam); + OMX_GetExtensionIndex(amr_enc_handle,"OMX.Qualcomm.index.audio.sessionId",&index); + OMX_GetParameter(amr_enc_handle,index,&streaminfoparam); + if(tunnel) { + #ifdef AUDIOV2 + session_id = streaminfoparam.sessionId; + control = msm_mixer_open("/dev/snd/controlC0", 0); + if(control < 0) + printf("ERROR opening the device\n"); + device_id = msm_get_device(device); + DEBUG_PRINT ("\ndevice_id = %d\n",device_id); + DEBUG_PRINT("\nsession_id = %d\n",session_id); + if (msm_en_device(device_id, 1)) + { + perror("could not enable device\n"); + return -1; + } + if (msm_route_stream(DIR_TX,session_id,device_id, 1)) + { + perror("could not set stream routing\n"); + return -1; + } + #endif + } + + DEBUG_PRINT ("\nOMX_SendCommand Encoder -> IDLE\n"); + OMX_SendCommand(amr_enc_handle, OMX_CommandStateSet, OMX_StateIdle,0); + /* wait_for_event(); should not wait here event complete status will + not come until enough buffer are allocated */ + if (tunnel == 0) + { + input_buf_cnt = inputportFmt.nBufferCountActual; // inputportFmt.nBufferCountMin + 5; + DEBUG_PRINT("Transition to Idle State succesful...\n"); + /* Allocate buffer on decoder's i/p port */ + error = Allocate_Buffer(amr_enc_handle, &pInputBufHdrs, inputportFmt.nPortIndex, + input_buf_cnt, inputportFmt.nBufferSize); + if (error != OMX_ErrorNone || pInputBufHdrs == NULL ) { + DEBUG_PRINT ("\nOMX_AllocateBuffer Input buffer error\n"); + return -1; + } + else { + DEBUG_PRINT ("\nOMX_AllocateBuffer Input buffer success\n"); + } + } + output_buf_cnt = outputportFmt.nBufferCountMin ; + + /* Allocate buffer on encoder's O/Pp port */ + error = Allocate_Buffer(amr_enc_handle, &pOutputBufHdrs, outputportFmt.nPortIndex, + output_buf_cnt, outputportFmt.nBufferSize); + if (error != OMX_ErrorNone || pOutputBufHdrs == NULL ) { + DEBUG_PRINT ("\nOMX_AllocateBuffer Output buffer error\n"); + return -1; + } + else { + DEBUG_PRINT ("\nOMX_AllocateBuffer Output buffer success\n"); + } + + wait_for_event(); + + + if (tunnel == 1) + { + DEBUG_PRINT ("\nOMX_SendCommand to enable TUNNEL MODE during IDLE\n"); + OMX_SendCommand(amr_enc_handle, OMX_CommandPortDisable,0,0); // disable input port + wait_for_event(); + } + + DEBUG_PRINT ("\nOMX_SendCommand encoder -> Executing\n"); + OMX_SendCommand(amr_enc_handle, OMX_CommandStateSet, OMX_StateExecuting,0); + wait_for_event(); + + DEBUG_PRINT(" Start sending OMX_FILLthisbuffer\n"); + + for(i=0; i < output_buf_cnt; i++) { + DEBUG_PRINT ("\nOMX_FillThisBuffer on output buf no.%d\n",i); + pOutputBufHdrs[i]->nOutputPortIndex = 1; + pOutputBufHdrs[i]->nFlags = pOutputBufHdrs[i]->nFlags & (unsigned)~OMX_BUFFERFLAG_EOS; + ret = OMX_FillThisBuffer(amr_enc_handle, pOutputBufHdrs[i]); + if (OMX_ErrorNone != ret) { + DEBUG_PRINT("OMX_FillThisBuffer failed with result %d\n", ret); + } + else { + DEBUG_PRINT("OMX_FillThisBuffer success!\n"); + } + } + +if(tunnel == 0) +{ + DEBUG_PRINT(" Start sending OMX_emptythisbuffer\n"); + for (i = 0;i < input_buf_cnt;i++) { + DEBUG_PRINT ("\nOMX_EmptyThisBuffer on Input buf no.%d\n",i); + pInputBufHdrs[i]->nInputPortIndex = 0; + Size = Read_Buffer(pInputBufHdrs[i]); + if(Size <=0 ){ + DEBUG_PRINT("NO DATA READ\n"); + bInputEosReached = true; + pInputBufHdrs[i]->nFlags= OMX_BUFFERFLAG_EOS; + } + pInputBufHdrs[i]->nFilledLen = (OMX_U32)Size; + pInputBufHdrs[i]->nInputPortIndex = 0; + used_ip_buf_cnt++; + ret = OMX_EmptyThisBuffer(amr_enc_handle, pInputBufHdrs[i]); + if (OMX_ErrorNone != ret) { + DEBUG_PRINT("OMX_EmptyThisBuffer failed with result %d\n", ret); + } + else { + DEBUG_PRINT("OMX_EmptyThisBuffer success!\n"); + } + if(Size <=0 ){ + break;//eos reached + } + } + pthread_mutex_lock(&etb_lock); + if(etb_done) +{ + DEBUG_PRINT("Component is waiting for EBD to be released.\n"); + etb_event_complete(); + } + else + { + DEBUG_PRINT("\n****************************\n"); + DEBUG_PRINT("EBD not yet happened ...\n"); + DEBUG_PRINT("\n****************************\n"); + etb_done++; + } + pthread_mutex_unlock(&etb_lock); +} + + return 0; +} + + + +static OMX_ERRORTYPE Allocate_Buffer ( OMX_COMPONENTTYPE *avc_enc_handle, + OMX_BUFFERHEADERTYPE ***pBufHdrs, + OMX_U32 nPortIndex, + unsigned int bufCntMin, unsigned int bufSize) +{ + DEBUG_PRINT("Inside %s \n", __FUNCTION__); + OMX_ERRORTYPE error=OMX_ErrorNone; + unsigned int bufCnt=0; + + /* To remove warning for unused variable to keep prototype same */ + (void)avc_enc_handle; + *pBufHdrs= (OMX_BUFFERHEADERTYPE **) + malloc(sizeof(OMX_BUFFERHEADERTYPE*)*bufCntMin); + + for(bufCnt=0; bufCnt < bufCntMin; ++bufCnt) { + DEBUG_PRINT("\n OMX_AllocateBuffer No %d \n", bufCnt); + error = OMX_AllocateBuffer(amr_enc_handle, &((*pBufHdrs)[bufCnt]), + nPortIndex, NULL, bufSize); + } + + return error; +} + + + + +static int Read_Buffer (OMX_BUFFERHEADERTYPE *pBufHdr ) +{ + + size_t bytes_read=0; + + + pBufHdr->nFilledLen = 0; + pBufHdr->nFlags |= OMX_BUFFERFLAG_EOS; + + bytes_read = fread(pBufHdr->pBuffer, 1, pBufHdr->nAllocLen , inputBufferFile); + + pBufHdr->nFilledLen = (OMX_U32)bytes_read; + // Time stamp logic + ((OMX_BUFFERHEADERTYPE *)pBufHdr)->nTimeStamp = \ + + (OMX_TICKS) ((total_pcm_bytes * 1000)/(samplerate * channels *2)); + + DEBUG_PRINT ("\n--time stamp -- %ld\n", (unsigned long)((OMX_BUFFERHEADERTYPE *)pBufHdr)->nTimeStamp); + if(bytes_read == 0) + { + pBufHdr->nFlags |= OMX_BUFFERFLAG_EOS; + DEBUG_PRINT ("\nBytes read zero\n"); + } + else + { + pBufHdr->nFlags = pBufHdr->nFlags & (unsigned)~OMX_BUFFERFLAG_EOS; + + total_pcm_bytes = (unsigned)(total_pcm_bytes + bytes_read); + } + + return (int)bytes_read;; +} + + + +//In Encoder this Should Open a PCM or WAV file for input. + +static int open_audio_file () +{ + int error_code = 0; + + if (!tunnel) + { + DEBUG_PRINT("Inside %s filename=%s\n", __FUNCTION__, in_filename); + inputBufferFile = fopen (in_filename, "rb"); + if (inputBufferFile == NULL) { + DEBUG_PRINT("\ni/p file %s could NOT be opened\n", + in_filename); + error_code = -1; + } + if(parse_pcm_header() != 0x00) + { + DEBUG_PRINT("PCM parser failed \n"); + return -1; + } + } + + DEBUG_PRINT("Inside %s filename=%s\n", __FUNCTION__, out_filename); + outputBufferFile = fopen (out_filename, "wb"); + if (outputBufferFile == NULL) { + DEBUG_PRINT("\ni/p file %s could NOT be opened\n", + out_filename); + error_code = -1; + return error_code; + } + fseek(outputBufferFile, AMR_HEADER_SIZE, SEEK_SET); + return error_code; +} + +static OMX_ERRORTYPE parse_pcm_header() +{ + struct wav_header hdr; + + DEBUG_PRINT("\n***************************************************************\n"); + if(fread(&hdr, 1, sizeof(hdr),inputBufferFile)!=sizeof(hdr)) + { + DEBUG_PRINT("Wav file cannot read header\n"); + return -1; + } + + if ((hdr.riff_id != ID_RIFF) || + (hdr.riff_fmt != ID_WAVE)|| + (hdr.fmt_id != ID_FMT)) + { + DEBUG_PRINT("Wav file is not a riff/wave file\n"); + return -1; + } + + if (hdr.audio_format != FORMAT_PCM) + { + DEBUG_PRINT("Wav file is not adpcm format %d and fmt size is %d\n", + hdr.audio_format, hdr.fmt_sz); + return -1; + } + + DEBUG_PRINT("Samplerate is %d\n", hdr.sample_rate); + DEBUG_PRINT("Channel Count is %d\n", hdr.num_channels); + DEBUG_PRINT("\n***************************************************************\n"); + + samplerate = hdr.sample_rate; + channels = hdr.num_channels; + total_pcm_bytes = 0; + + return OMX_ErrorNone; +} diff --git a/audio/mm-audio/aenc-evrc/Android.mk b/audio/mm-audio/aenc-evrc/Android.mk new file mode 100644 index 0000000..d8d0818 --- /dev/null +++ b/audio/mm-audio/aenc-evrc/Android.mk @@ -0,0 +1,7 @@ +ifneq ($(filter arm aarch64 arm64, $(TARGET_ARCH)),) + +AENC_EVRC_PATH:= $(call my-dir) + +include $(AENC_EVRC_PATH)/qdsp6/Android.mk + +endif diff --git a/audio/mm-audio/aenc-evrc/Makefile b/audio/mm-audio/aenc-evrc/Makefile new file mode 100644 index 0000000..83d822b --- /dev/null +++ b/audio/mm-audio/aenc-evrc/Makefile @@ -0,0 +1,6 @@ +all: + @echo "invoking omxaudio make" + $(MAKE) -C qdsp6 + +install: + $(MAKE) -C qdsp6 install diff --git a/audio/mm-audio/aenc-evrc/qdsp6/Android.mk b/audio/mm-audio/aenc-evrc/qdsp6/Android.mk new file mode 100644 index 0000000..a9f556d --- /dev/null +++ b/audio/mm-audio/aenc-evrc/qdsp6/Android.mk @@ -0,0 +1,67 @@ +ifneq ($(BUILD_TINY_ANDROID),true) + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +# --------------------------------------------------------------------------------- +# Common definitons +# --------------------------------------------------------------------------------- + +libOmxEvrcEnc-def := -g -O3 +libOmxEvrcEnc-def += -DQC_MODIFIED +libOmxEvrcEnc-def += -D_ANDROID_ +libOmxEvrcEnc-def += -D_ENABLE_QC_MSG_LOG_ +libOmxEvrcEnc-def += -DVERBOSE +libOmxEvrcEnc-def += -D_DEBUG +libOmxEvrcEnc-def += -Wconversion +libOmxEvrcEnc-def += -DAUDIOV2 + +# --------------------------------------------------------------------------------- +# Make the Shared library (libOmxEvrcEnc) +# --------------------------------------------------------------------------------- + +include $(CLEAR_VARS) + +libOmxEvrcEnc-inc := $(LOCAL_PATH)/inc +libOmxEvrcEnc-inc += $(TARGET_OUT_HEADERS)/mm-core/omxcore + +LOCAL_MODULE := libOmxEvrcEnc +LOCAL_MODULE_TAGS := optional +LOCAL_CFLAGS := $(libOmxEvrcEnc-def) +LOCAL_C_INCLUDES := $(libOmxEvrcEnc-inc) +LOCAL_PRELINK_MODULE := false +LOCAL_SHARED_LIBRARIES := libutils liblog + +LOCAL_SRC_FILES := src/aenc_svr.c +LOCAL_SRC_FILES += src/omx_evrc_aenc.cpp + +include $(BUILD_SHARED_LIBRARY) + +# --------------------------------------------------------------------------------- +# Make the apps-test (mm-aenc-omxevrc-test) +# --------------------------------------------------------------------------------- + +include $(CLEAR_VARS) + +mm-evrc-enc-test-inc := $(LOCAL_PATH)/inc +mm-evrc-enc-test-inc += $(LOCAL_PATH)/test +mm-evrc-enc-test-inc += $(TARGET_OUT_HEADERS)/mm-core/omxcore +mm-evrc-enc-test-inc += $(TARGET_OUT_HEADERS)/mm-audio/audio-alsa +LOCAL_MODULE := mm-aenc-omxevrc-test +LOCAL_MODULE_TAGS := optional +LOCAL_CFLAGS := $(libOmxEvrcEnc-def) +LOCAL_C_INCLUDES := $(mm-evrc-enc-test-inc) +LOCAL_PRELINK_MODULE := false +LOCAL_SHARED_LIBRARIES := libmm-omxcore +LOCAL_SHARED_LIBRARIES += libOmxEvrcEnc +LOCAL_SHARED_LIBRARIES += libaudioalsa +LOCAL_SRC_FILES := test/omx_evrc_enc_test.c + +include $(BUILD_EXECUTABLE) + +endif + +# --------------------------------------------------------------------------------- +# END +# --------------------------------------------------------------------------------- + diff --git a/audio/mm-audio/aenc-evrc/qdsp6/Makefile b/audio/mm-audio/aenc-evrc/qdsp6/Makefile new file mode 100644 index 0000000..d0871de --- /dev/null +++ b/audio/mm-audio/aenc-evrc/qdsp6/Makefile @@ -0,0 +1,81 @@ +# --------------------------------------------------------------------------------- +# MM-AUDIO-OSS-8K-AENC-EVRC +# --------------------------------------------------------------------------------- + +# cross-compiler flags +CFLAGS += -Wall +CFLAGS += -Wundef +CFLAGS += -Wstrict-prototypes +CFLAGS += -Wno-trigraphs + +# cross-compile flags specific to shared objects +CFLAGS_SO += -fpic + +# required pre-processor flags +CPPFLAGS := -D__packed__= +CPPFLAGS += -DIMAGE_APPS_PROC +CPPFLAGS += -DFEATURE_Q_SINGLE_LINK +CPPFLAGS += -DFEATURE_Q_NO_SELF_QPTR +CPPFLAGS += -DFEATURE_LINUX +CPPFLAGS += -DFEATURE_NATIVELINUX +CPPFLAGS += -DFEATURE_DSM_DUP_ITEMS + +CPPFLAGS += -g +CPPFALGS += -D_DEBUG +CPPFLAGS += -Iinc + +# linker flags +LDFLAGS += -L$(SYSROOT)/usr/lib + +# linker flags for shared objects +LDFLAGS_SO := -shared + +# defintions +LIBMAJOR := $(basename $(basename $(LIBVER))) +LIBINSTALLDIR := $(DESTDIR)usr/lib +INCINSTALLDIR := $(DESTDIR)usr/include +BININSTALLDIR := $(DESTDIR)usr/bin + +# --------------------------------------------------------------------------------- +# BUILD +# --------------------------------------------------------------------------------- +all: libOmxEvrcEnc.so.$(LIBVER) mm-aenc-omxevrc-test + +install: + echo "intalling aenc-evrc in $(DESTDIR)" + if [ ! -d $(LIBINSTALLDIR) ]; then mkdir -p $(LIBINSTALLDIR); fi + if [ ! -d $(INCINSTALLDIR) ]; then mkdir -p $(INCINSTALLDIR); fi + if [ ! -d $(BININSTALLDIR) ]; then mkdir -p $(BININSTALLDIR); fi + install -m 555 libOmxEvrcEnc.so.$(LIBVER) $(LIBINSTALLDIR) + cd $(LIBINSTALLDIR) && ln -s libOmxEvrcEnc.so.$(LIBVER) libOmxEvrcEnc.so.$(LIBMAJOR) + cd $(LIBINSTALLDIR) && ln -s libOmxEvrcEnc.so.$(LIBMAJOR) libOmxEvrcEnc.so + install -m 555 mm-aenc-omxevrc-test $(BININSTALLDIR) + +# --------------------------------------------------------------------------------- +# COMPILE LIBRARY +# --------------------------------------------------------------------------------- +LDLIBS := -lpthread +LDLIBS += -lstdc++ +LDLIBS += -lOmxCore + +SRCS := src/omx_evrc_aenc.cpp +SRCS += src/aenc_svr.c + +libOmxEvrcEnc.so.$(LIBVER): $(SRCS) + $(CC) $(CPPFLAGS) $(CFLAGS_SO) $(LDFLAGS_SO) -Wl,-soname,libOmxEvrcEnc.so.$(LIBMAJOR) -o $@ $^ $(LDFLAGS) $(LDLIBS) + +# --------------------------------------------------------------------------------- +# COMPILE TEST APP +# --------------------------------------------------------------------------------- +TEST_LDLIBS := -lpthread +TEST_LDLIBS += -ldl +TEST_LDLIBS += -lOmxCore + +TEST_SRCS := test/omx_evrc_enc_test.c + +mm-aenc-omxevrc-test: libOmxEvrcEnc.so.$(LIBVER) $(TEST_SRCS) + $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $^ $(TEST_LDLIBS) + +# --------------------------------------------------------------------------------- +# END +# --------------------------------------------------------------------------------- diff --git a/audio/mm-audio/aenc-evrc/qdsp6/inc/Map.h b/audio/mm-audio/aenc-evrc/qdsp6/inc/Map.h new file mode 100644 index 0000000..aac96fd --- /dev/null +++ b/audio/mm-audio/aenc-evrc/qdsp6/inc/Map.h @@ -0,0 +1,244 @@ +/*-------------------------------------------------------------------------- +Copyright (c) 2010, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of The Linux Foundation nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------*/ +#ifndef _MAP_H_ +#define _MAP_H_ + +#include +using namespace std; + +template +class Map +{ + struct node + { + T data; + T2 data2; + node* prev; + node* next; + node(T t, T2 t2,node* p, node* n) : + data(t), data2(t2), prev(p), next(n) {} + }; + node* head; + node* tail; + node* tmp; + unsigned size_of_list; + static Map *m_self; +public: + Map() : head( NULL ), tail ( NULL ),tmp(head),size_of_list(0) {} + bool empty() const { return ( !head || !tail ); } + operator bool() const { return !empty(); } + void insert(T,T2); + void show(); + int size(); + T2 find(T); // Return VALUE + T find_ele(T);// Check if the KEY is present or not + T2 begin(); //give the first ele + bool erase(T); + bool eraseall(); + bool isempty(); + ~Map() + { + while(head) + { + node* temp(head); + head=head->next; + size_of_list--; + delete temp; + } + } +}; + +template +T2 Map::find(T d1) +{ + tmp = head; + while(tmp) + { + if(tmp->data == d1) + { + return tmp->data2; + } + tmp = tmp->next; + } + return 0; +} + +template +T Map::find_ele(T d1) +{ + tmp = head; + while(tmp) + { + if(tmp->data == d1) + { + return tmp->data; + } + tmp = tmp->next; + } + return 0; +} + +template +T2 Map::begin() +{ + tmp = head; + if(tmp) + { + return (tmp->data2); + } + return 0; +} + +template +void Map::show() +{ + tmp = head; + while(tmp) + { + printf("%d-->%d\n",tmp->data,tmp->data2); + tmp = tmp->next; + } +} + +template +int Map::size() +{ + int count =0; + tmp = head; + while(tmp) + { + tmp = tmp->next; + count++; + } + return count; +} + +template +void Map::insert(T data, T2 data2) +{ + tail = new node(data, data2,tail, NULL); + if( tail->prev ) + tail->prev->next = tail; + + if( empty() ) + { + head = tail; + tmp=head; + } + tmp = head; + size_of_list++; +} + +template +bool Map::erase(T d) +{ + bool found = false; + tmp = head; + node* prevnode = tmp; + node *tempnode; + + while(tmp) + { + if((head == tail) && (head->data == d)) + { + found = true; + tempnode = head; + head = tail = NULL; + delete tempnode; + break; + } + if((tmp ==head) && (tmp->data ==d)) + { + found = true; + tempnode = tmp; + tmp = tmp->next; + tmp->prev = NULL; + head = tmp; + tempnode->next = NULL; + delete tempnode; + break; + } + if((tmp == tail) && (tmp->data ==d)) + { + found = true; + tempnode = tmp; + prevnode->next = NULL; + tmp->prev = NULL; + tail = prevnode; + delete tempnode; + break; + } + if(tmp->data == d) + { + found = true; + prevnode->next = tmp->next; + tmp->next->prev = prevnode->next; + tempnode = tmp; + //tmp = tmp->next; + delete tempnode; + break; + } + prevnode = tmp; + tmp = tmp->next; + } + if(found)size_of_list--; + return found; +} + +template +bool Map::eraseall() +{ + // Be careful while using this method + // it not only removes the node but FREES(not delete) the allocated + // memory. + node *tempnode; + tmp = head; + while(head) + { + tempnode = head; + head = head->next; + tempnode->next = NULL; + if(tempnode->data) + free(tempnode->data); + if(tempnode->data2) + free(tempnode->data2); + delete tempnode; + } + tail = head = NULL; + return true; +} + + +template +bool Map::isempty() +{ + if(!size_of_list) return true; + else return false; +} + +#endif // _MAP_H_ diff --git a/audio/mm-audio/aenc-evrc/qdsp6/inc/aenc_svr.h b/audio/mm-audio/aenc-evrc/qdsp6/inc/aenc_svr.h new file mode 100644 index 0000000..46f40ee --- /dev/null +++ b/audio/mm-audio/aenc-evrc/qdsp6/inc/aenc_svr.h @@ -0,0 +1,122 @@ +/*-------------------------------------------------------------------------- +Copyright (c) 2010, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of The Linux Foundation nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------*/ +#ifndef AENC_SVR_H +#define AENC_SVR_H + +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#include + +#ifdef _ANDROID_ +#define LOG_TAG "QC_EVRCENC" +#endif + +#ifndef LOGE +#define LOGE ALOGE +#endif + +#ifndef LOGW +#define LOGW ALOGW +#endif + +#ifndef LOGD +#define LOGD ALOGD +#endif + +#ifndef LOGV +#define LOGV ALOGV +#endif + +#ifndef LOGI +#define LOGI ALOGI +#endif + +#define DEBUG_PRINT_ERROR LOGE +#define DEBUG_PRINT LOGI +#define DEBUG_DETAIL LOGV + +typedef void (*message_func)(void* client_data, unsigned char id); + +/** + @brief audio encoder ipc info structure + + */ +struct evrc_ipc_info +{ + pthread_t thr; + int pipe_in; + int pipe_out; + int dead; + message_func process_msg_cb; + void *client_data; + char thread_name[128]; +}; + +/** + @brief This function starts command server + + @param cb pointer to callback function from the client + @param client_data reference client wants to get back + through callback + @return handle to command server + */ +struct evrc_ipc_info *omx_evrc_thread_create(message_func cb, + void* client_data, + char *th_name); + +struct evrc_ipc_info *omx_evrc_event_thread_create(message_func cb, + void* client_data, + char *th_name); +/** + @brief This function stop command server + + @param svr handle to command server + @return none + */ +void omx_evrc_thread_stop(struct evrc_ipc_info *evrc_ipc); + + +/** + @brief This function post message in the command server + + @param svr handle to command server + @return none + */ +void omx_evrc_post_msg(struct evrc_ipc_info *evrc_ipc, + unsigned char id); + +void* omx_evrc_comp_timer_handler(void *); + +#ifdef __cplusplus +} +#endif + +#endif /* AENC_SVR */ diff --git a/audio/mm-audio/aenc-evrc/qdsp6/inc/omx_evrc_aenc.h b/audio/mm-audio/aenc-evrc/qdsp6/inc/omx_evrc_aenc.h new file mode 100644 index 0000000..09ffb2d --- /dev/null +++ b/audio/mm-audio/aenc-evrc/qdsp6/inc/omx_evrc_aenc.h @@ -0,0 +1,539 @@ +/*-------------------------------------------------------------------------- +Copyright (c) 2010,2014 The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of The Linux Foundation nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------*/ +#ifndef _EVRC_ENC_H_ +#define _EVRC_ENC_H_ +/*============================================================================ + Audio Encoder + +@file omx_evrc_aenc.h +This module contains the class definition for openMAX encoder component. + + + +============================================================================*/ + +////////////////////////////////////////////////////////////////////////////// +// Include Files +////////////////////////////////////////////////////////////////////////////// + +/* Uncomment out below line #define LOG_NDEBUG 0 if we want to see + * all DEBUG_PRINT or LOGV messaging */ +#include +#include +#include +#include +#include +#include +#include "QOMX_AudioExtensions.h" +#include "QOMX_AudioIndexExtensions.h" +#include "OMX_Core.h" +#include "OMX_Audio.h" +#include "aenc_svr.h" +#include "qc_omx_component.h" +#include "Map.h" +#include +#include +#include +extern "C" { + void * get_omx_component_factory_fn(void); +} + + +////////////////////////////////////////////////////////////////////////////// +// Module specific globals +////////////////////////////////////////////////////////////////////////////// + + + +#define OMX_SPEC_VERSION 0x00000101 +#define MIN(x,y) (((x) < (y)) ? (x) : (y)) +#define MAX(x,y) (x >= y?x:y) + +////////////////////////////////////////////////////////////////////////////// +// Macros +////////////////////////////////////////////////////////////////////////////// +// + + +#define PrintFrameHdr(i,bufHdr) \ + DEBUG_PRINT("i=%d OMX bufHdr[%p]buf[%p]size[%d]TS[%lld]nFlags[0x%x]\n",\ + i,\ + bufHdr, \ + ((OMX_BUFFERHEADERTYPE *)bufHdr)->pBuffer, \ + (unsigned)((OMX_BUFFERHEADERTYPE *)bufHdr)->nFilledLen,\ + ((OMX_BUFFERHEADERTYPE *)bufHdr)->nTimeStamp, \ + (unsigned)((OMX_BUFFERHEADERTYPE *)bufHdr)->nFlags) + + +// BitMask Management logic +#define BITS_PER_BYTE 8 +#define BITMASK_SIZE(mIndex) \ + (((mIndex) + BITS_PER_BYTE - 1)/BITS_PER_BYTE) +#define BITMASK_OFFSET(mIndex)\ + ((mIndex)/BITS_PER_BYTE) +#define BITMASK_FLAG(mIndex) \ + (1 << ((mIndex) % BITS_PER_BYTE)) +#define BITMASK_CLEAR(mArray,mIndex)\ + (mArray)[BITMASK_OFFSET(mIndex)] &= ~(BITMASK_FLAG(mIndex)) +#define BITMASK_SET(mArray,mIndex)\ + (mArray)[BITMASK_OFFSET(mIndex)] |= BITMASK_FLAG(mIndex) +#define BITMASK_PRESENT(mArray,mIndex)\ + ((mArray)[BITMASK_OFFSET(mIndex)] & BITMASK_FLAG(mIndex)) +#define BITMASK_ABSENT(mArray,mIndex)\ + (((mArray)[BITMASK_OFFSET(mIndex)] & \ + BITMASK_FLAG(mIndex)) == 0x0) + +#define OMX_CORE_NUM_INPUT_BUFFERS 2 +#define OMX_CORE_NUM_OUTPUT_BUFFERS 16 + +#define OMX_CORE_INPUT_BUFFER_SIZE 8160 // Multiple of 160 +#define OMX_CORE_CONTROL_CMDQ_SIZE 100 +#define OMX_AENC_VOLUME_STEP 0x147 +#define OMX_AENC_MIN 0 +#define OMX_AENC_MAX 100 +#define NON_TUNNEL 1 +#define TUNNEL 0 +#define IP_PORT_BITMASK 0x02 +#define OP_PORT_BITMASK 0x01 +#define IP_OP_PORT_BITMASK 0x03 + +#define OMX_EVRC_DEFAULT_SF 8000 +#define OMX_EVRC_DEFAULT_CH_CFG 1 +#define OMX_EVRC_DEFAULT_VOL 25 +// 14 bytes for input meta data +#define OMX_AENC_SIZEOF_META_BUF (OMX_CORE_INPUT_BUFFER_SIZE+14) + +#define TRUE 1 +#define FALSE 0 + +#define NUMOFFRAMES 1 +#define MAXFRAMELENGTH 25 +#define OMX_EVRC_OUTPUT_BUFFER_SIZE ((NUMOFFRAMES * (sizeof(ENC_META_OUT) + MAXFRAMELENGTH) \ + + 1)) + +#define OMX_EVRC_DEFAULT_MINRATE 4 +#define OMX_EVRC_DEFAULT_MAXRATE 4 + +class omx_evrc_aenc; + +// OMX EVRC audio encoder class +class omx_evrc_aenc: public qc_omx_component +{ +public: + omx_evrc_aenc(); // constructor + virtual ~omx_evrc_aenc(); // destructor + + OMX_ERRORTYPE allocate_buffer(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE **bufferHdr, + OMX_U32 port, + OMX_PTR appData, + OMX_U32 bytes); + + + OMX_ERRORTYPE component_deinit(OMX_HANDLETYPE hComp); + + OMX_ERRORTYPE component_init(OMX_STRING role); + + OMX_ERRORTYPE component_role_enum(OMX_HANDLETYPE hComp, + OMX_U8 *role, + OMX_U32 index); + + OMX_ERRORTYPE component_tunnel_request(OMX_HANDLETYPE hComp, + OMX_U32 port, + OMX_HANDLETYPE peerComponent, + OMX_U32 peerPort, + OMX_TUNNELSETUPTYPE *tunnelSetup); + + OMX_ERRORTYPE empty_this_buffer(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE *buffer); + + + OMX_ERRORTYPE empty_this_buffer_proxy(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE *buffer); + + + OMX_ERRORTYPE fill_this_buffer(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE *buffer); + + + OMX_ERRORTYPE free_buffer(OMX_HANDLETYPE hComp, + OMX_U32 port, + OMX_BUFFERHEADERTYPE *buffer); + + OMX_ERRORTYPE get_component_version(OMX_HANDLETYPE hComp, + OMX_STRING componentName, + OMX_VERSIONTYPE *componentVersion, + OMX_VERSIONTYPE * specVersion, + OMX_UUIDTYPE *componentUUID); + + OMX_ERRORTYPE get_config(OMX_HANDLETYPE hComp, + OMX_INDEXTYPE configIndex, + OMX_PTR configData); + + OMX_ERRORTYPE get_extension_index(OMX_HANDLETYPE hComp, + OMX_STRING paramName, + OMX_INDEXTYPE *indexType); + + OMX_ERRORTYPE get_parameter(OMX_HANDLETYPE hComp, + OMX_INDEXTYPE paramIndex, + OMX_PTR paramData); + + OMX_ERRORTYPE get_state(OMX_HANDLETYPE hComp, + OMX_STATETYPE *state); + + static void process_in_port_msg(void *client_data, + unsigned char id); + + static void process_out_port_msg(void *client_data, + unsigned char id); + + static void process_command_msg(void *client_data, + unsigned char id); + + static void process_event_cb(void *client_data, + unsigned char id); + + + OMX_ERRORTYPE set_callbacks(OMX_HANDLETYPE hComp, + OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData); + + OMX_ERRORTYPE set_config(OMX_HANDLETYPE hComp, + OMX_INDEXTYPE configIndex, + OMX_PTR configData); + + OMX_ERRORTYPE set_parameter(OMX_HANDLETYPE hComp, + OMX_INDEXTYPE paramIndex, + OMX_PTR paramData); + + OMX_ERRORTYPE use_buffer(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE **bufferHdr, + OMX_U32 port, + OMX_PTR appData, + OMX_U32 bytes, + OMX_U8 *buffer); + + OMX_ERRORTYPE use_EGL_image(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE **bufferHdr, + OMX_U32 port, + OMX_PTR appData, + void * eglImage); + + bool post_command(unsigned int p1, unsigned int p2, + unsigned char id); + + // Deferred callback identifiers + enum + { + //Event Callbacks from the component thread context + OMX_COMPONENT_GENERATE_EVENT = 0x1, + //Buffer Done callbacks from component thread context + OMX_COMPONENT_GENERATE_BUFFER_DONE = 0x2, + OMX_COMPONENT_GENERATE_ETB = 0x3, + //Command + OMX_COMPONENT_GENERATE_COMMAND = 0x4, + OMX_COMPONENT_GENERATE_FRAME_DONE = 0x05, + OMX_COMPONENT_GENERATE_FTB = 0x06, + OMX_COMPONENT_GENERATE_EOS = 0x07, + OMX_COMPONENT_PORTSETTINGS_CHANGED = 0x08, + OMX_COMPONENT_SUSPEND = 0x09, + OMX_COMPONENT_RESUME = 0x0a + }; +private: + + /////////////////////////////////////////////////////////// + // Type definitions + /////////////////////////////////////////////////////////// + // Bit Positions + enum flags_bit_positions + { + // Defer transition to IDLE + OMX_COMPONENT_IDLE_PENDING =0x1, + // Defer transition to LOADING + OMX_COMPONENT_LOADING_PENDING =0x2, + + OMX_COMPONENT_MUTED =0x3, + + // Defer transition to Enable + OMX_COMPONENT_INPUT_ENABLE_PENDING =0x4, + // Defer transition to Enable + OMX_COMPONENT_OUTPUT_ENABLE_PENDING =0x5, + // Defer transition to Disable + OMX_COMPONENT_INPUT_DISABLE_PENDING =0x6, + // Defer transition to Disable + OMX_COMPONENT_OUTPUT_DISABLE_PENDING =0x7 + }; + + + typedef Map + input_buffer_map; + + typedef Map + output_buffer_map; + + enum port_indexes + { + OMX_CORE_INPUT_PORT_INDEX =0, + OMX_CORE_OUTPUT_PORT_INDEX =1 + }; + + struct omx_event + { + unsigned long param1; + unsigned long param2; + unsigned char id; + }; + + struct omx_cmd_queue + { + omx_event m_q[OMX_CORE_CONTROL_CMDQ_SIZE]; + unsigned m_read; + unsigned m_write; + unsigned m_size; + + omx_cmd_queue(); + ~omx_cmd_queue(); + bool insert_entry(unsigned long p1, unsigned long p2, unsigned char id); + bool pop_entry(unsigned long *p1,unsigned long *p2, unsigned char *id); + bool get_msg_id(unsigned char *id); + bool get_msg_with_id(unsigned *p1,unsigned *p2, unsigned char id); + }; + + typedef struct TIMESTAMP + { + unsigned int LowPart; + unsigned int HighPart; + }__attribute__((packed)) TIMESTAMP; + + typedef struct metadata_input + { + unsigned short offsetVal; + TIMESTAMP nTimeStamp; + unsigned int nFlags; + }__attribute__((packed)) META_IN; + + typedef struct enc_meta_out + { + unsigned int offset_to_frame; + unsigned int frame_size; + unsigned int encoded_pcm_samples; + unsigned int msw_ts; + unsigned int lsw_ts; + unsigned int nflags; + } __attribute__ ((packed))ENC_META_OUT; + + typedef struct + { + OMX_U32 tot_in_buf_len; + OMX_U32 tot_out_buf_len; + OMX_TICKS tot_pb_time; + OMX_U32 fbd_cnt; + OMX_U32 ftb_cnt; + OMX_U32 etb_cnt; + OMX_U32 ebd_cnt; + }EVRC_PB_STATS; + + /////////////////////////////////////////////////////////// + // Member variables + /////////////////////////////////////////////////////////// + OMX_U8 *m_tmp_meta_buf; + OMX_U8 *m_tmp_out_meta_buf; + OMX_U8 m_flush_cnt ; + OMX_U8 m_comp_deinit; + + // the below var doesnt hold good if combo of use and alloc bufs are used + OMX_S32 m_volume;//Unit to be determined + OMX_PTR m_app_data;// Application data + int nNumInputBuf; + int nNumOutputBuf; + int m_drv_fd; // Kernel device node file handle + bool bFlushinprogress; + bool is_in_th_sleep; + bool is_out_th_sleep; + unsigned int m_flags; //encapsulate the waiting states. + OMX_TICKS nTimestamp; + unsigned int pcm_input; //tunnel or non-tunnel + unsigned int m_inp_act_buf_count; // Num of Input Buffers + unsigned int m_out_act_buf_count; // Numb of Output Buffers + unsigned int m_inp_current_buf_count; // Num of Input Buffers + unsigned int m_out_current_buf_count; // Numb of Output Buffers + unsigned int output_buffer_size; + unsigned int input_buffer_size; + unsigned short m_session_id; + // store I/P PORT state + OMX_BOOL m_inp_bEnabled; + // store O/P PORT state + OMX_BOOL m_out_bEnabled; + //Input port Populated + OMX_BOOL m_inp_bPopulated; + //Output port Populated + OMX_BOOL m_out_bPopulated; + sem_t sem_States; + sem_t sem_read_msg; + sem_t sem_write_msg; + + volatile int m_is_event_done; + volatile int m_is_in_th_sleep; + volatile int m_is_out_th_sleep; + input_buffer_map m_input_buf_hdrs; + output_buffer_map m_output_buf_hdrs; + omx_cmd_queue m_input_q; + omx_cmd_queue m_input_ctrl_cmd_q; + omx_cmd_queue m_input_ctrl_ebd_q; + omx_cmd_queue m_command_q; + omx_cmd_queue m_output_q; + omx_cmd_queue m_output_ctrl_cmd_q; + omx_cmd_queue m_output_ctrl_fbd_q; + pthread_mutexattr_t m_outputlock_attr; + pthread_mutexattr_t m_commandlock_attr; + pthread_mutexattr_t m_lock_attr; + pthread_mutexattr_t m_state_attr; + pthread_mutexattr_t m_flush_attr; + pthread_mutexattr_t m_in_th_attr_1; + pthread_mutexattr_t m_out_th_attr_1; + pthread_mutexattr_t m_event_attr; + pthread_mutexattr_t m_in_th_attr; + pthread_mutexattr_t m_out_th_attr; + pthread_mutexattr_t out_buf_count_lock_attr; + pthread_mutexattr_t in_buf_count_lock_attr; + pthread_cond_t cond; + pthread_cond_t in_cond; + pthread_cond_t out_cond; + pthread_mutex_t m_lock; + pthread_mutex_t m_commandlock; + pthread_mutex_t m_outputlock; + // Mutexes for state change + pthread_mutex_t m_state_lock; + // Mutexes for flush acks from input and output threads + pthread_mutex_t m_flush_lock; + pthread_mutex_t m_event_lock; + pthread_mutex_t m_in_th_lock; + pthread_mutex_t m_out_th_lock; + pthread_mutex_t m_in_th_lock_1; + pthread_mutex_t m_out_th_lock_1; + pthread_mutex_t out_buf_count_lock; + pthread_mutex_t in_buf_count_lock; + + OMX_STATETYPE m_state; // OMX State + OMX_STATETYPE nState; + OMX_CALLBACKTYPE m_cb; // Application callbacks + EVRC_PB_STATS m_evrc_pb_stats; + struct evrc_ipc_info *m_ipc_to_in_th; // for input thread + struct evrc_ipc_info *m_ipc_to_out_th; // for output thread + struct evrc_ipc_info *m_ipc_to_cmd_th; // for command thread + struct evrc_ipc_info *m_ipc_to_event_th; //for txco event thread + OMX_PRIORITYMGMTTYPE m_priority_mgm ; + OMX_AUDIO_PARAM_EVRCTYPE m_evrc_param; // Cache EVRC encoder parameter + OMX_AUDIO_PARAM_PCMMODETYPE m_pcm_param; // Cache pcm parameter + OMX_PARAM_COMPONENTROLETYPE component_Role; + OMX_PARAM_BUFFERSUPPLIERTYPE m_buffer_supplier; + + /////////////////////////////////////////////////////////// + // Private methods + /////////////////////////////////////////////////////////// + OMX_ERRORTYPE allocate_output_buffer(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE **bufferHdr, + OMX_U32 port,OMX_PTR appData, + OMX_U32 bytes); + + OMX_ERRORTYPE allocate_input_buffer(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE **bufferHdr, + OMX_U32 port, + OMX_PTR appData, + OMX_U32 bytes); + + OMX_ERRORTYPE use_input_buffer(OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE **bufHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes, + OMX_IN OMX_U8* buffer); + + OMX_ERRORTYPE use_output_buffer(OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE **bufHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes, + OMX_IN OMX_U8* buffer); + + OMX_ERRORTYPE fill_this_buffer_proxy(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE *buffer); + + OMX_ERRORTYPE send_command_proxy(OMX_HANDLETYPE hComp, + OMX_COMMANDTYPE cmd, + OMX_U32 param1, + OMX_PTR cmdData); + + OMX_ERRORTYPE send_command(OMX_HANDLETYPE hComp, + OMX_COMMANDTYPE cmd, + OMX_U32 param1, + OMX_PTR cmdData); + + bool allocate_done(void); + + bool release_done(OMX_U32 param1); + + bool execute_omx_flush(OMX_IN OMX_U32 param1, bool cmd_cmpl=true); + + bool execute_input_omx_flush(void); + + bool execute_output_omx_flush(void); + + bool search_input_bufhdr(OMX_BUFFERHEADERTYPE *buffer); + + bool search_output_bufhdr(OMX_BUFFERHEADERTYPE *buffer); + + bool post_input(unsigned long p1, unsigned long p2, + unsigned char id); + + bool post_output(unsigned long p1, unsigned long p2, + unsigned char id); + + void process_events(omx_evrc_aenc *client_data); + + void buffer_done_cb(OMX_BUFFERHEADERTYPE *bufHdr); + + void frame_done_cb(OMX_BUFFERHEADERTYPE *bufHdr); + + void wait_for_event(); + + void event_complete(); + + void in_th_goto_sleep(); + + void in_th_wakeup(); + + void out_th_goto_sleep(); + + void out_th_wakeup(); + + void flush_ack(); + void deinit_encoder(); + +}; +#endif diff --git a/audio/mm-audio/aenc-evrc/qdsp6/src/aenc_svr.c b/audio/mm-audio/aenc-evrc/qdsp6/src/aenc_svr.c new file mode 100644 index 0000000..4f828fc --- /dev/null +++ b/audio/mm-audio/aenc-evrc/qdsp6/src/aenc_svr.c @@ -0,0 +1,205 @@ +/*-------------------------------------------------------------------------- +Copyright (c) 2010, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of The Linux Foundation nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------*/ +#include +#include +#include + +#include +#include + +#include + +/** + @brief This function processes posted messages + + Once thread is being spawned, this function is run to + start processing commands posted by client + + @param info pointer to context + + */ +void *omx_evrc_msg(void *info) +{ + struct evrc_ipc_info *evrc_info = (struct evrc_ipc_info*)info; + unsigned char id; + ssize_t n; + + DEBUG_DETAIL("\n%s: message thread start\n", __FUNCTION__); + while (!evrc_info->dead) + { + n = read(evrc_info->pipe_in, &id, 1); + if (0 == n) break; + if (1 == n) + { + DEBUG_DETAIL("\n%s-->pipe_in=%d pipe_out=%d\n", + evrc_info->thread_name, + evrc_info->pipe_in, + evrc_info->pipe_out); + + evrc_info->process_msg_cb(evrc_info->client_data, id); + } + if ((n < 0) && (errno != EINTR)) break; + } + DEBUG_DETAIL("%s: message thread stop\n", __FUNCTION__); + + return 0; +} + +void *omx_evrc_events(void *info) +{ + struct evrc_ipc_info *evrc_info = (struct evrc_ipc_info*)info; + unsigned char id = 0; + + DEBUG_DETAIL("%s: message thread start\n", evrc_info->thread_name); + evrc_info->process_msg_cb(evrc_info->client_data, id); + DEBUG_DETAIL("%s: message thread stop\n", evrc_info->thread_name); + return 0; +} + +/** + @brief This function starts command server + + @param cb pointer to callback function from the client + @param client_data reference client wants to get back + through callback + @return handle to msging thread + */ +struct evrc_ipc_info *omx_evrc_thread_create( + message_func cb, + void* client_data, + char* th_name) +{ + int r; + int fds[2]; + struct evrc_ipc_info *evrc_info; + + evrc_info = calloc(1, sizeof(struct evrc_ipc_info)); + if (!evrc_info) + { + return 0; + } + + evrc_info->client_data = client_data; + evrc_info->process_msg_cb = cb; + strlcpy(evrc_info->thread_name, th_name, sizeof(evrc_info->thread_name)); + + if (pipe(fds)) + { + DEBUG_PRINT_ERROR("\n%s: pipe creation failed\n", __FUNCTION__); + goto fail_pipe; + } + + evrc_info->pipe_in = fds[0]; + evrc_info->pipe_out = fds[1]; + + r = pthread_create(&evrc_info->thr, 0, omx_evrc_msg, evrc_info); + if (r < 0) goto fail_thread; + + DEBUG_DETAIL("Created thread for %s \n", evrc_info->thread_name); + return evrc_info; + + +fail_thread: + close(evrc_info->pipe_in); + close(evrc_info->pipe_out); + +fail_pipe: + free(evrc_info); + + return 0; +} + +/** + * @brief This function starts command server + * + * @param cb pointer to callback function from the client + * @param client_data reference client wants to get back + * through callback + * @return handle to msging thread + * */ +struct evrc_ipc_info *omx_evrc_event_thread_create( + message_func cb, + void* client_data, + char* th_name) +{ + int r; + int fds[2]; + struct evrc_ipc_info *evrc_info; + + evrc_info = calloc(1, sizeof(struct evrc_ipc_info)); + if (!evrc_info) + { + return 0; + } + + evrc_info->client_data = client_data; + evrc_info->process_msg_cb = cb; + strlcpy(evrc_info->thread_name, th_name, sizeof(evrc_info->thread_name)); + + if (pipe(fds)) + { + DEBUG_PRINT("\n%s: pipe creation failed\n", __FUNCTION__); + goto fail_pipe; + } + + evrc_info->pipe_in = fds[0]; + evrc_info->pipe_out = fds[1]; + + r = pthread_create(&evrc_info->thr, 0, omx_evrc_events, evrc_info); + if (r < 0) goto fail_thread; + + DEBUG_DETAIL("Created thread for %s \n", evrc_info->thread_name); + return evrc_info; + + +fail_thread: + close(evrc_info->pipe_in); + close(evrc_info->pipe_out); + +fail_pipe: + free(evrc_info); + + return 0; +} + +void omx_evrc_thread_stop(struct evrc_ipc_info *evrc_info) { + DEBUG_DETAIL("%s stop server\n", __FUNCTION__); + close(evrc_info->pipe_in); + close(evrc_info->pipe_out); + pthread_join(evrc_info->thr,NULL); + evrc_info->pipe_out = -1; + evrc_info->pipe_in = -1; + DEBUG_DETAIL("%s: message thread close fds%d %d\n", evrc_info->thread_name, + evrc_info->pipe_in,evrc_info->pipe_out); + free(evrc_info); +} + +void omx_evrc_post_msg(struct evrc_ipc_info *evrc_info, unsigned char id) { + DEBUG_DETAIL("\n%s id=%d\n", __FUNCTION__,id); + write(evrc_info->pipe_out, &id, 1); +} diff --git a/audio/mm-audio/aenc-evrc/qdsp6/src/omx_evrc_aenc.cpp b/audio/mm-audio/aenc-evrc/qdsp6/src/omx_evrc_aenc.cpp new file mode 100644 index 0000000..8200365 --- /dev/null +++ b/audio/mm-audio/aenc-evrc/qdsp6/src/omx_evrc_aenc.cpp @@ -0,0 +1,4531 @@ +/*-------------------------------------------------------------------------- +Copyright (c) 2010, 2014 The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of The Linux Foundation nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------*/ +/*============================================================================ +@file omx_aenc_evrc.c + This module contains the implementation of the OpenMAX core & component. + +*//*========================================================================*/ +////////////////////////////////////////////////////////////////////////////// +// Include Files +////////////////////////////////////////////////////////////////////////////// + + +#include +#include +#include +#include "omx_evrc_aenc.h" +#include + +using namespace std; +#define SLEEP_MS 100 + +// omx_cmd_queue destructor +omx_evrc_aenc::omx_cmd_queue::~omx_cmd_queue() +{ + // Nothing to do +} + +// omx cmd queue constructor +omx_evrc_aenc::omx_cmd_queue::omx_cmd_queue(): m_read(0),m_write(0),m_size(0) +{ + memset(m_q, 0,sizeof(omx_event)*OMX_CORE_CONTROL_CMDQ_SIZE); +} + +// omx cmd queue insert +bool omx_evrc_aenc::omx_cmd_queue::insert_entry(unsigned long p1, + unsigned long p2, + unsigned char id) +{ + bool ret = true; + if (m_size < OMX_CORE_CONTROL_CMDQ_SIZE) + { + m_q[m_write].id = id; + m_q[m_write].param1 = p1; + m_q[m_write].param2 = p2; + m_write++; + m_size ++; + if (m_write >= OMX_CORE_CONTROL_CMDQ_SIZE) + { + m_write = 0; + } + } else + { + ret = false; + DEBUG_PRINT_ERROR("ERROR!!! Command Queue Full"); + } + return ret; +} + +bool omx_evrc_aenc::omx_cmd_queue::pop_entry(unsigned long *p1, + unsigned long *p2, unsigned char *id) +{ + bool ret = true; + if (m_size > 0) + { + *id = m_q[m_read].id; + *p1 = m_q[m_read].param1; + *p2 = m_q[m_read].param2; + // Move the read pointer ahead + ++m_read; + --m_size; + if (m_read >= OMX_CORE_CONTROL_CMDQ_SIZE) + { + m_read = 0; + + } + } else + { + ret = false; + DEBUG_PRINT_ERROR("ERROR Delete!!! Command Queue Empty"); + } + return ret; +} + +// factory function executed by the core to create instances +void *get_omx_component_factory_fn(void) +{ + return(new omx_evrc_aenc); +} +bool omx_evrc_aenc::omx_cmd_queue::get_msg_id(unsigned char *id) +{ + if(m_size > 0) + { + *id = m_q[m_read].id; + DEBUG_PRINT("get_msg_id=%d\n",*id); + } + else{ + return false; + } + return true; +} +/*============================================================================= +FUNCTION: + wait_for_event + +DESCRIPTION: + waits for a particular event + +INPUT/OUTPUT PARAMETERS: + None + +RETURN VALUE: + None + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +void omx_evrc_aenc::wait_for_event() +{ + int rc; + struct timespec ts; + pthread_mutex_lock(&m_event_lock); + while (0 == m_is_event_done) + { + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += (SLEEP_MS/1000); + ts.tv_nsec += ((SLEEP_MS%1000) * 1000000); + rc = pthread_cond_timedwait(&cond, &m_event_lock, &ts); + if (rc == ETIMEDOUT && !m_is_event_done) { + DEBUG_PRINT("Timed out waiting for flush"); + if (ioctl( m_drv_fd, AUDIO_FLUSH, 0) == -1) + DEBUG_PRINT_ERROR("Flush:Input port, ioctl flush failed %d\n", + errno); + } + } + m_is_event_done = 0; + pthread_mutex_unlock(&m_event_lock); +} + +/*============================================================================= +FUNCTION: + event_complete + +DESCRIPTION: + informs about the occurance of an event + +INPUT/OUTPUT PARAMETERS: + None + +RETURN VALUE: + None + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +void omx_evrc_aenc::event_complete() +{ + pthread_mutex_lock(&m_event_lock); + if (0 == m_is_event_done) + { + m_is_event_done = 1; + pthread_cond_signal(&cond); + } + pthread_mutex_unlock(&m_event_lock); +} + +// All this non-sense because of a single evrc object +void omx_evrc_aenc::in_th_goto_sleep() +{ + pthread_mutex_lock(&m_in_th_lock); + while (0 == m_is_in_th_sleep) + { + pthread_cond_wait(&in_cond, &m_in_th_lock); + } + m_is_in_th_sleep = 0; + pthread_mutex_unlock(&m_in_th_lock); +} + +void omx_evrc_aenc::in_th_wakeup() +{ + pthread_mutex_lock(&m_in_th_lock); + if (0 == m_is_in_th_sleep) + { + m_is_in_th_sleep = 1; + pthread_cond_signal(&in_cond); + } + pthread_mutex_unlock(&m_in_th_lock); +} + +void omx_evrc_aenc::out_th_goto_sleep() +{ + + pthread_mutex_lock(&m_out_th_lock); + while (0 == m_is_out_th_sleep) + { + pthread_cond_wait(&out_cond, &m_out_th_lock); + } + m_is_out_th_sleep = 0; + pthread_mutex_unlock(&m_out_th_lock); +} + +void omx_evrc_aenc::out_th_wakeup() +{ + pthread_mutex_lock(&m_out_th_lock); + if (0 == m_is_out_th_sleep) + { + m_is_out_th_sleep = 1; + pthread_cond_signal(&out_cond); + } + pthread_mutex_unlock(&m_out_th_lock); +} +/* ====================================================================== +FUNCTION + omx_evrc_aenc::omx_evrc_aenc + +DESCRIPTION + Constructor + +PARAMETERS + None + +RETURN VALUE + None. +========================================================================== */ +omx_evrc_aenc::omx_evrc_aenc(): m_tmp_meta_buf(NULL), + m_tmp_out_meta_buf(NULL), + m_flush_cnt(255), + m_comp_deinit(0), + m_volume(25), + m_app_data(NULL), + nNumInputBuf(0), + nNumOutputBuf(0), + m_drv_fd(-1), + bFlushinprogress(0), + is_in_th_sleep(false), + is_out_th_sleep(false), + m_flags(0), + nTimestamp(0), + m_inp_act_buf_count (OMX_CORE_NUM_INPUT_BUFFERS), + m_out_act_buf_count (OMX_CORE_NUM_OUTPUT_BUFFERS), + m_inp_current_buf_count(0), + m_out_current_buf_count(0), + output_buffer_size((OMX_U32)OMX_EVRC_OUTPUT_BUFFER_SIZE), + input_buffer_size(OMX_CORE_INPUT_BUFFER_SIZE), + m_inp_bEnabled(OMX_TRUE), + m_out_bEnabled(OMX_TRUE), + m_inp_bPopulated(OMX_FALSE), + m_out_bPopulated(OMX_FALSE), + m_is_event_done(0), + m_state(OMX_StateInvalid), + m_ipc_to_in_th(NULL), + m_ipc_to_out_th(NULL), + m_ipc_to_cmd_th(NULL) +{ + int cond_ret = 0; + memset(&m_cmp, 0, sizeof(m_cmp)); + memset(&m_cb, 0, sizeof(m_cb)); + memset(&m_evrc_param, 0, sizeof(m_evrc_param)); + memset(&m_buffer_supplier, 0, sizeof(m_buffer_supplier)); + memset(&m_evrc_pb_stats, 0, sizeof(m_evrc_pb_stats)); + memset(&m_pcm_param, 0, sizeof(m_pcm_param)); + memset(&m_priority_mgm, 0, sizeof(m_priority_mgm)); + + pthread_mutexattr_init(&m_lock_attr); + pthread_mutex_init(&m_lock, &m_lock_attr); + pthread_mutexattr_init(&m_commandlock_attr); + pthread_mutex_init(&m_commandlock, &m_commandlock_attr); + + pthread_mutexattr_init(&m_outputlock_attr); + pthread_mutex_init(&m_outputlock, &m_outputlock_attr); + + pthread_mutexattr_init(&m_state_attr); + pthread_mutex_init(&m_state_lock, &m_state_attr); + + pthread_mutexattr_init(&m_event_attr); + pthread_mutex_init(&m_event_lock, &m_event_attr); + + pthread_mutexattr_init(&m_flush_attr); + pthread_mutex_init(&m_flush_lock, &m_flush_attr); + + pthread_mutexattr_init(&m_event_attr); + pthread_mutex_init(&m_event_lock, &m_event_attr); + + pthread_mutexattr_init(&m_in_th_attr); + pthread_mutex_init(&m_in_th_lock, &m_in_th_attr); + + pthread_mutexattr_init(&m_out_th_attr); + pthread_mutex_init(&m_out_th_lock, &m_out_th_attr); + + pthread_mutexattr_init(&m_in_th_attr_1); + pthread_mutex_init(&m_in_th_lock_1, &m_in_th_attr_1); + + pthread_mutexattr_init(&m_out_th_attr_1); + pthread_mutex_init(&m_out_th_lock_1, &m_out_th_attr_1); + + pthread_mutexattr_init(&out_buf_count_lock_attr); + pthread_mutex_init(&out_buf_count_lock, &out_buf_count_lock_attr); + + pthread_mutexattr_init(&in_buf_count_lock_attr); + pthread_mutex_init(&in_buf_count_lock, &in_buf_count_lock_attr); + if ((cond_ret = pthread_cond_init (&cond, NULL)) != 0) + { + DEBUG_PRINT_ERROR("pthread_cond_init returns non zero for cond\n"); + if (cond_ret == EAGAIN) + DEBUG_PRINT_ERROR("The system lacked necessary \ + resources(other than mem)\n"); + else if (cond_ret == ENOMEM) + DEBUG_PRINT_ERROR("Insufficient memory to initialise \ + condition variable\n"); + } + if ((cond_ret = pthread_cond_init (&in_cond, NULL)) != 0) + { + DEBUG_PRINT_ERROR("pthread_cond_init returns non zero for in_cond\n"); + if (cond_ret == EAGAIN) + DEBUG_PRINT_ERROR("The system lacked necessary \ + resources(other than mem)\n"); + else if (cond_ret == ENOMEM) + DEBUG_PRINT_ERROR("Insufficient memory to initialise \ + condition variable\n"); + } + if ((cond_ret = pthread_cond_init (&out_cond, NULL)) != 0) + { + DEBUG_PRINT_ERROR("pthread_cond_init returns non zero for out_cond\n"); + if (cond_ret == EAGAIN) + DEBUG_PRINT_ERROR("The system lacked necessary \ + resources(other than mem)\n"); + else if (cond_ret == ENOMEM) + DEBUG_PRINT_ERROR("Insufficient memory to initialise \ + condition variable\n"); + } + + sem_init(&sem_read_msg,0, 0); + sem_init(&sem_write_msg,0, 0); + sem_init(&sem_States,0, 0); + return; +} + + +/* ====================================================================== +FUNCTION + omx_evrc_aenc::~omx_evrc_aenc + +DESCRIPTION + Destructor + +PARAMETERS + None + +RETURN VALUE + None. +========================================================================== */ +omx_evrc_aenc::~omx_evrc_aenc() +{ + DEBUG_PRINT_ERROR("EVRC Object getting destroyed comp-deinit=%d\n", + m_comp_deinit); + if ( !m_comp_deinit ) + { + deinit_encoder(); + } + pthread_mutexattr_destroy(&m_lock_attr); + pthread_mutex_destroy(&m_lock); + + pthread_mutexattr_destroy(&m_commandlock_attr); + pthread_mutex_destroy(&m_commandlock); + + pthread_mutexattr_destroy(&m_outputlock_attr); + pthread_mutex_destroy(&m_outputlock); + + pthread_mutexattr_destroy(&m_state_attr); + pthread_mutex_destroy(&m_state_lock); + + pthread_mutexattr_destroy(&m_event_attr); + pthread_mutex_destroy(&m_event_lock); + + pthread_mutexattr_destroy(&m_flush_attr); + pthread_mutex_destroy(&m_flush_lock); + + pthread_mutexattr_destroy(&m_in_th_attr); + pthread_mutex_destroy(&m_in_th_lock); + + pthread_mutexattr_destroy(&m_out_th_attr); + pthread_mutex_destroy(&m_out_th_lock); + + pthread_mutexattr_destroy(&out_buf_count_lock_attr); + pthread_mutex_destroy(&out_buf_count_lock); + + pthread_mutexattr_destroy(&in_buf_count_lock_attr); + pthread_mutex_destroy(&in_buf_count_lock); + + pthread_mutexattr_destroy(&m_in_th_attr_1); + pthread_mutex_destroy(&m_in_th_lock_1); + + pthread_mutexattr_destroy(&m_out_th_attr_1); + pthread_mutex_destroy(&m_out_th_lock_1); + pthread_mutex_destroy(&out_buf_count_lock); + pthread_mutex_destroy(&in_buf_count_lock); + pthread_cond_destroy(&cond); + pthread_cond_destroy(&in_cond); + pthread_cond_destroy(&out_cond); + sem_destroy (&sem_read_msg); + sem_destroy (&sem_write_msg); + sem_destroy (&sem_States); + DEBUG_PRINT_ERROR("OMX EVRC component destroyed\n"); + return; +} + +/** + @brief memory function for sending EmptyBufferDone event + back to IL client + + @param bufHdr OMX buffer header to be passed back to IL client + @return none + */ +void omx_evrc_aenc::buffer_done_cb(OMX_BUFFERHEADERTYPE *bufHdr) +{ + if (m_cb.EmptyBufferDone) + { + PrintFrameHdr(OMX_COMPONENT_GENERATE_BUFFER_DONE,bufHdr); + bufHdr->nFilledLen = 0; + + m_cb.EmptyBufferDone(&m_cmp, m_app_data, bufHdr); + pthread_mutex_lock(&in_buf_count_lock); + m_evrc_pb_stats.ebd_cnt++; + nNumInputBuf--; + DEBUG_DETAIL("EBD CB:: in_buf_len=%d nNumInputBuf=%d %d ebd_cnt %d \n",\ + m_evrc_pb_stats.tot_in_buf_len, + nNumInputBuf, m_evrc_pb_stats.ebd_cnt); + pthread_mutex_unlock(&in_buf_count_lock); + } + + return; +} + +/*============================================================================= +FUNCTION: + flush_ack + +DESCRIPTION: + + +INPUT/OUTPUT PARAMETERS: + None + +RETURN VALUE: + None + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +void omx_evrc_aenc::flush_ack() +{ + // Decrement the FLUSH ACK count and notify the waiting recepients + pthread_mutex_lock(&m_flush_lock); + --m_flush_cnt; + if (0 == m_flush_cnt) + { + event_complete(); + } + DEBUG_PRINT("Rxed FLUSH ACK cnt=%d\n",m_flush_cnt); + pthread_mutex_unlock(&m_flush_lock); +} +void omx_evrc_aenc::frame_done_cb(OMX_BUFFERHEADERTYPE *bufHdr) +{ + if (m_cb.FillBufferDone) + { + PrintFrameHdr(OMX_COMPONENT_GENERATE_FRAME_DONE,bufHdr); + m_evrc_pb_stats.fbd_cnt++; + pthread_mutex_lock(&out_buf_count_lock); + nNumOutputBuf--; + DEBUG_PRINT("FBD CB:: nNumOutputBuf=%d out_buf_len=%u fbd_cnt=%u\n",\ + nNumOutputBuf, + m_evrc_pb_stats.tot_out_buf_len, + m_evrc_pb_stats.fbd_cnt); + m_evrc_pb_stats.tot_out_buf_len += bufHdr->nFilledLen; + m_evrc_pb_stats.tot_pb_time = bufHdr->nTimeStamp; + DEBUG_PRINT("FBD:in_buf_len=%u out_buf_len=%u\n", + m_evrc_pb_stats.tot_in_buf_len, + m_evrc_pb_stats.tot_out_buf_len); + + pthread_mutex_unlock(&out_buf_count_lock); + m_cb.FillBufferDone(&m_cmp, m_app_data, bufHdr); + } + return; +} + +/*============================================================================= +FUNCTION: + process_out_port_msg + +DESCRIPTION: + Function for handling all commands from IL client +IL client commands are processed and callbacks are generated through +this routine Audio Command Server provides the thread context for this routine + +INPUT/OUTPUT PARAMETERS: + [INOUT] client_data + [IN] id + +RETURN VALUE: + None + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +void omx_evrc_aenc::process_out_port_msg(void *client_data, unsigned char id) +{ + unsigned long p1 = 0; // Parameter - 1 + unsigned long p2 = 0; // Parameter - 2 + unsigned char ident = 0; + unsigned qsize = 0; // qsize + unsigned tot_qsize = 0; + omx_evrc_aenc *pThis = (omx_evrc_aenc *) client_data; + OMX_STATETYPE state; + +loopback_out: + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + if ( state == OMX_StateLoaded ) + { + DEBUG_PRINT(" OUT: IN LOADED STATE RETURN\n"); + return; + } + pthread_mutex_lock(&pThis->m_outputlock); + + qsize = pThis->m_output_ctrl_cmd_q.m_size; + tot_qsize = pThis->m_output_ctrl_cmd_q.m_size; + tot_qsize += pThis->m_output_ctrl_fbd_q.m_size; + tot_qsize += pThis->m_output_q.m_size; + + if ( 0 == tot_qsize ) + { + pthread_mutex_unlock(&pThis->m_outputlock); + DEBUG_DETAIL("OUT-->BREAK FROM LOOP...%d\n",tot_qsize); + return; + } + if ( (state != OMX_StateExecuting) && !qsize ) + { + pthread_mutex_unlock(&pThis->m_outputlock); + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + if ( state == OMX_StateLoaded ) + return; + + DEBUG_DETAIL("OUT:1.SLEEPING OUT THREAD\n"); + pthread_mutex_lock(&pThis->m_out_th_lock_1); + pThis->is_out_th_sleep = true; + pthread_mutex_unlock(&pThis->m_out_th_lock_1); + pThis->out_th_goto_sleep(); + + /* Get the updated state */ + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + } + + if ( ((!pThis->m_output_ctrl_cmd_q.m_size) && !pThis->m_out_bEnabled) ) + { + // case where no port reconfig and nothing in the flush q + DEBUG_DETAIL("No flush/port reconfig qsize=%d tot_qsize=%d",\ + qsize,tot_qsize); + pthread_mutex_unlock(&pThis->m_outputlock); + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + if ( state == OMX_StateLoaded ) + return; + + if(pThis->m_output_ctrl_cmd_q.m_size || !(pThis->bFlushinprogress)) + { + DEBUG_PRINT("OUT:2. SLEEPING OUT THREAD \n"); + pthread_mutex_lock(&pThis->m_out_th_lock_1); + pThis->is_out_th_sleep = true; + pthread_mutex_unlock(&pThis->m_out_th_lock_1); + pThis->out_th_goto_sleep(); + } + /* Get the updated state */ + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + } + qsize = pThis->m_output_ctrl_cmd_q.m_size; + tot_qsize = pThis->m_output_ctrl_cmd_q.m_size; + tot_qsize += pThis->m_output_ctrl_fbd_q.m_size; + tot_qsize += pThis->m_output_q.m_size; + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + DEBUG_DETAIL("OUT-->QSIZE-flush=%d,fbd=%d QSIZE=%d state=%d\n",\ + pThis->m_output_ctrl_cmd_q.m_size, + pThis->m_output_ctrl_fbd_q.m_size, + pThis->m_output_q.m_size,state); + + + if (qsize) + { + // process FLUSH message + pThis->m_output_ctrl_cmd_q.pop_entry(&p1,&p2,&ident); + } else if ( (qsize = pThis->m_output_ctrl_fbd_q.m_size) && + (pThis->m_out_bEnabled) && (state == OMX_StateExecuting) ) + { + // then process EBD's + pThis->m_output_ctrl_fbd_q.pop_entry(&p1,&p2,&ident); + } else if ( (qsize = pThis->m_output_q.m_size) && + (pThis->m_out_bEnabled) && (state == OMX_StateExecuting) ) + { + // if no FLUSH and FBD's then process FTB's + pThis->m_output_q.pop_entry(&p1,&p2,&ident); + } else if ( state == OMX_StateLoaded ) + { + pthread_mutex_unlock(&pThis->m_outputlock); + DEBUG_PRINT("IN: ***in OMX_StateLoaded so exiting\n"); + return ; + } else + { + qsize = 0; + DEBUG_PRINT("OUT--> Empty Queue state=%d %d %d %d\n",state, + pThis->m_output_ctrl_cmd_q.m_size, + pThis->m_output_ctrl_fbd_q.m_size, + pThis->m_output_q.m_size); + + if(state == OMX_StatePause) + { + DEBUG_DETAIL("OUT: SLEEPING AGAIN OUT THREAD\n"); + pthread_mutex_lock(&pThis->m_out_th_lock_1); + pThis->is_out_th_sleep = true; + pthread_mutex_unlock(&pThis->m_out_th_lock_1); + pthread_mutex_unlock(&pThis->m_outputlock); + pThis->out_th_goto_sleep(); + goto loopback_out; + } + } + pthread_mutex_unlock(&pThis->m_outputlock); + + if ( qsize > 0 ) + { + id = ident; + ident = 0; + DEBUG_DETAIL("OUT->state[%d]ident[%d]flushq[%d]fbd[%d]dataq[%d]\n",\ + pThis->m_state, + ident, + pThis->m_output_ctrl_cmd_q.m_size, + pThis->m_output_ctrl_fbd_q.m_size, + pThis->m_output_q.m_size); + + if ( OMX_COMPONENT_GENERATE_FRAME_DONE == id ) + { + pThis->frame_done_cb((OMX_BUFFERHEADERTYPE *)p2); + } else if ( OMX_COMPONENT_GENERATE_FTB == id ) + { + pThis->fill_this_buffer_proxy((OMX_HANDLETYPE)p1, + (OMX_BUFFERHEADERTYPE *)p2); + } else if ( OMX_COMPONENT_GENERATE_EOS == id ) + { + pThis->m_cb.EventHandler(&pThis->m_cmp, + pThis->m_app_data, + OMX_EventBufferFlag, + 1, 1, NULL ); + + } + else if(id == OMX_COMPONENT_RESUME) + { + DEBUG_PRINT("RESUMED...\n"); + } + else if(id == OMX_COMPONENT_GENERATE_COMMAND) + { + // Execute FLUSH command + if ( OMX_CommandFlush == p1 ) + { + DEBUG_DETAIL("Executing FLUSH command on Output port\n"); + pThis->execute_output_omx_flush(); + } else + { + DEBUG_DETAIL("Invalid command[%lu]\n",p1); + } + } else + { + DEBUG_PRINT_ERROR("ERROR:OUT-->Invalid Id[%d]\n",id); + } + } else + { + DEBUG_DETAIL("ERROR: OUT--> Empty OUTPUTQ\n"); + } + + return; +} + +/*============================================================================= +FUNCTION: + process_command_msg + +DESCRIPTION: + + +INPUT/OUTPUT PARAMETERS: + [INOUT] client_data + [IN] id + +RETURN VALUE: + None + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +void omx_evrc_aenc::process_command_msg(void *client_data, unsigned char id) +{ + unsigned long p1 = 0; // Parameter - 1 + unsigned long p2 = 0; // Parameter - 2 + unsigned char ident = 0; + unsigned qsize = 0; + omx_evrc_aenc *pThis = (omx_evrc_aenc*)client_data; + pthread_mutex_lock(&pThis->m_commandlock); + + qsize = pThis->m_command_q.m_size; + DEBUG_DETAIL("CMD-->QSIZE=%d state=%d\n",pThis->m_command_q.m_size, + pThis->m_state); + + if (!qsize) + { + DEBUG_DETAIL("CMD-->BREAKING FROM LOOP\n"); + pthread_mutex_unlock(&pThis->m_commandlock); + return; + } else + { + pThis->m_command_q.pop_entry(&p1,&p2,&ident); + } + pthread_mutex_unlock(&pThis->m_commandlock); + + id = ident; + DEBUG_DETAIL("CMD->state[%d]id[%d]cmdq[%d]n",\ + pThis->m_state,ident, \ + pThis->m_command_q.m_size); + + if (OMX_COMPONENT_GENERATE_EVENT == id) + { + if (pThis->m_cb.EventHandler) + { + if (OMX_CommandStateSet == p1) + { + pthread_mutex_lock(&pThis->m_state_lock); + pThis->m_state = (OMX_STATETYPE) p2; + pthread_mutex_unlock(&pThis->m_state_lock); + DEBUG_PRINT("CMD:Process->state set to %d \n", \ + pThis->m_state); + + if (pThis->m_state == OMX_StateExecuting || + pThis->m_state == OMX_StateLoaded) + { + + pthread_mutex_lock(&pThis->m_in_th_lock_1); + if (pThis->is_in_th_sleep) + { + pThis->is_in_th_sleep = false; + DEBUG_DETAIL("CMD:WAKING UP IN THREADS\n"); + pThis->in_th_wakeup(); + } + pthread_mutex_unlock(&pThis->m_in_th_lock_1); + + pthread_mutex_lock(&pThis->m_out_th_lock_1); + if (pThis->is_out_th_sleep) + { + DEBUG_DETAIL("CMD:WAKING UP OUT THREADS\n"); + pThis->is_out_th_sleep = false; + pThis->out_th_wakeup(); + } + pthread_mutex_unlock(&pThis->m_out_th_lock_1); + } + } + if (OMX_StateInvalid == pThis->m_state) + { + pThis->m_cb.EventHandler(&pThis->m_cmp, + pThis->m_app_data, + OMX_EventError, + OMX_ErrorInvalidState, + 0, NULL ); + } else if ((signed)p2 == OMX_ErrorPortUnpopulated) + { + pThis->m_cb.EventHandler(&pThis->m_cmp, + pThis->m_app_data, + OMX_EventError, + (OMX_U32)p2, + 0, + 0 ); + } else + { + pThis->m_cb.EventHandler(&pThis->m_cmp, + pThis->m_app_data, + OMX_EventCmdComplete, + (OMX_U32)p1, (OMX_U32)p2, NULL ); + } + } else + { + DEBUG_PRINT_ERROR("ERROR:CMD-->EventHandler NULL \n"); + } + } else if (OMX_COMPONENT_GENERATE_COMMAND == id) + { + pThis->send_command_proxy(&pThis->m_cmp, + (OMX_COMMANDTYPE)p1, + (OMX_U32)p2,(OMX_PTR)NULL); + } else if (OMX_COMPONENT_PORTSETTINGS_CHANGED == id) + { + DEBUG_DETAIL("CMD-->RXED PORTSETTINGS_CHANGED"); + pThis->m_cb.EventHandler(&pThis->m_cmp, + pThis->m_app_data, + OMX_EventPortSettingsChanged, + 1, 1, NULL ); + } + else + { + DEBUG_PRINT_ERROR("CMD->state[%d]id[%d]\n",pThis->m_state,ident); + } + return; +} + +/*============================================================================= +FUNCTION: + process_in_port_msg + +DESCRIPTION: + + +INPUT/OUTPUT PARAMETERS: + [INOUT] client_data + [IN] id + +RETURN VALUE: + None + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +void omx_evrc_aenc::process_in_port_msg(void *client_data, unsigned char id) +{ + unsigned long p1 = 0; // Parameter - 1 + unsigned long p2 = 0; // Parameter - 2 + unsigned char ident = 0; + unsigned qsize = 0; + unsigned tot_qsize = 0; + omx_evrc_aenc *pThis = (omx_evrc_aenc *) client_data; + OMX_STATETYPE state; + + if (!pThis) + { + DEBUG_PRINT_ERROR("ERROR:IN--> Invalid Obj \n"); + return; + } +loopback_in: + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + if ( state == OMX_StateLoaded ) + { + DEBUG_PRINT(" IN: IN LOADED STATE RETURN\n"); + return; + } + // Protect the shared queue data structure + pthread_mutex_lock(&pThis->m_lock); + + qsize = pThis->m_input_ctrl_cmd_q.m_size; + tot_qsize = qsize; + tot_qsize += pThis->m_input_ctrl_ebd_q.m_size; + tot_qsize += pThis->m_input_q.m_size; + + if ( 0 == tot_qsize ) + { + DEBUG_DETAIL("IN-->BREAKING FROM IN LOOP"); + pthread_mutex_unlock(&pThis->m_lock); + return; + } + + if ( (state != OMX_StateExecuting) && ! (pThis->m_input_ctrl_cmd_q.m_size)) + { + pthread_mutex_unlock(&pThis->m_lock); + DEBUG_DETAIL("SLEEPING IN THREAD\n"); + pthread_mutex_lock(&pThis->m_in_th_lock_1); + pThis->is_in_th_sleep = true; + pthread_mutex_unlock(&pThis->m_in_th_lock_1); + pThis->in_th_goto_sleep(); + + /* Get the updated state */ + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + } + else if ((state == OMX_StatePause)) + { + if(!(pThis->m_input_ctrl_cmd_q.m_size)) + { + pthread_mutex_unlock(&pThis->m_lock); + + DEBUG_DETAIL("IN: SLEEPING IN THREAD\n"); + pthread_mutex_lock(&pThis->m_in_th_lock_1); + pThis->is_in_th_sleep = true; + pthread_mutex_unlock(&pThis->m_in_th_lock_1); + pThis->in_th_goto_sleep(); + + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + } + } + + qsize = pThis->m_input_ctrl_cmd_q.m_size; + tot_qsize = qsize; + tot_qsize += pThis->m_input_ctrl_ebd_q.m_size; + tot_qsize += pThis->m_input_q.m_size; + + DEBUG_DETAIL("Input-->QSIZE-flush=%d,ebd=%d QSIZE=%d state=%d\n",\ + pThis->m_input_ctrl_cmd_q.m_size, + pThis->m_input_ctrl_ebd_q.m_size, + pThis->m_input_q.m_size, state); + + + if ( qsize ) + { + // process FLUSH message + pThis->m_input_ctrl_cmd_q.pop_entry(&p1,&p2,&ident); + } else if ( (qsize = pThis->m_input_ctrl_ebd_q.m_size) && + (state == OMX_StateExecuting) ) + { + // then process EBD's + pThis->m_input_ctrl_ebd_q.pop_entry(&p1,&p2,&ident); + } else if ((qsize = pThis->m_input_q.m_size) && + (state == OMX_StateExecuting)) + { + // if no FLUSH and EBD's then process ETB's + pThis->m_input_q.pop_entry(&p1, &p2, &ident); + } else if ( state == OMX_StateLoaded ) + { + pthread_mutex_unlock(&pThis->m_lock); + DEBUG_PRINT("IN: ***in OMX_StateLoaded so exiting\n"); + return ; + } else + { + qsize = 0; + DEBUG_PRINT("IN-->state[%d]cmdq[%d]ebdq[%d]in[%d]\n",\ + state,pThis->m_input_ctrl_cmd_q.m_size, + pThis->m_input_ctrl_ebd_q.m_size, + pThis->m_input_q.m_size); + + if(state == OMX_StatePause) + { + DEBUG_DETAIL("IN: SLEEPING AGAIN IN THREAD\n"); + pthread_mutex_lock(&pThis->m_in_th_lock_1); + pThis->is_in_th_sleep = true; + pthread_mutex_unlock(&pThis->m_in_th_lock_1); + pthread_mutex_unlock(&pThis->m_lock); + pThis->in_th_goto_sleep(); + goto loopback_in; + } + } + pthread_mutex_unlock(&pThis->m_lock); + + if ( qsize > 0 ) + { + id = ident; + DEBUG_DETAIL("Input->state[%d]id[%d]flushq[%d]ebdq[%d]dataq[%d]\n",\ + pThis->m_state, + ident, + pThis->m_input_ctrl_cmd_q.m_size, + pThis->m_input_ctrl_ebd_q.m_size, + pThis->m_input_q.m_size); + if ( OMX_COMPONENT_GENERATE_BUFFER_DONE == id ) + { + pThis->buffer_done_cb((OMX_BUFFERHEADERTYPE *)p2); + } + else if(id == OMX_COMPONENT_GENERATE_EOS) + { + pThis->m_cb.EventHandler(&pThis->m_cmp, pThis->m_app_data, + OMX_EventBufferFlag, 0, 1, NULL ); + } else if ( OMX_COMPONENT_GENERATE_ETB == id ) + { + pThis->empty_this_buffer_proxy((OMX_HANDLETYPE)p1, + (OMX_BUFFERHEADERTYPE *)p2); + } else if ( OMX_COMPONENT_GENERATE_COMMAND == id ) + { + // Execute FLUSH command + if ( OMX_CommandFlush == p1 ) + { + DEBUG_DETAIL(" Executing FLUSH command on Input port\n"); + pThis->execute_input_omx_flush(); + } else + { + DEBUG_DETAIL("Invalid command[%lu]\n",p1); + } + } + else + { + DEBUG_PRINT_ERROR("ERROR:IN-->Invalid Id[%u]\n",id); + } + } else + { + DEBUG_DETAIL("ERROR:IN-->Empty INPUT Q\n"); + } + return; +} + +/** + @brief member function for performing component initialization + + @param role C string mandating role of this component + @return Error status + */ +OMX_ERRORTYPE omx_evrc_aenc::component_init(OMX_STRING role) +{ + + OMX_ERRORTYPE eRet = OMX_ErrorNone; + m_state = OMX_StateLoaded; + + /* DSP does not give information about the bitstream + randomly assign the value right now. Query will result in + incorrect param */ + memset(&m_evrc_param, 0, sizeof(m_evrc_param)); + m_evrc_param.nSize = (OMX_U32)sizeof(m_evrc_param); + m_evrc_param.nChannels = OMX_EVRC_DEFAULT_CH_CFG; + //Current DSP does not have config + m_evrc_param.eCDMARate = OMX_AUDIO_CDMARateFull; + m_evrc_param.nMinBitRate = OMX_EVRC_DEFAULT_MINRATE; + m_evrc_param.nMaxBitRate = OMX_EVRC_DEFAULT_MAXRATE; + m_volume = OMX_EVRC_DEFAULT_VOL; /* Close to unity gain */ + memset(&m_evrc_pb_stats,0,sizeof(EVRC_PB_STATS)); + memset(&m_pcm_param, 0, sizeof(m_pcm_param)); + m_pcm_param.nSize = (OMX_U32)sizeof(m_pcm_param); + m_pcm_param.nChannels = OMX_EVRC_DEFAULT_CH_CFG; + m_pcm_param.nSamplingRate = OMX_EVRC_DEFAULT_SF; + nTimestamp = 0; + + + nNumInputBuf = 0; + nNumOutputBuf = 0; + m_ipc_to_in_th = NULL; // Command server instance + m_ipc_to_out_th = NULL; // Client server instance + m_ipc_to_cmd_th = NULL; // command instance + m_is_out_th_sleep = 0; + m_is_in_th_sleep = 0; + is_out_th_sleep= false; + + is_in_th_sleep=false; + + memset(&m_priority_mgm, 0, sizeof(m_priority_mgm)); + m_priority_mgm.nGroupID =0; + m_priority_mgm.nGroupPriority=0; + + memset(&m_buffer_supplier, 0, sizeof(m_buffer_supplier)); + m_buffer_supplier.nPortIndex=OMX_BufferSupplyUnspecified; + + DEBUG_PRINT_ERROR(" component init: role = %s\n",role); + + DEBUG_PRINT(" component init: role = %s\n",role); + component_Role.nVersion.nVersion = OMX_SPEC_VERSION; + if (!strcmp(role,"OMX.qcom.audio.encoder.evrc")) + { + pcm_input = 1; + component_Role.nSize = (OMX_U32)sizeof(role); + strlcpy((char *)component_Role.cRole, + (const char*)role, sizeof(component_Role.cRole)); + DEBUG_PRINT("\ncomponent_init: Component %s LOADED \n", role); + } else if (!strcmp(role,"OMX.qcom.audio.encoder.tunneled.evrc")) + { + pcm_input = 0; + component_Role.nSize = (OMX_U32)sizeof(role); + strlcpy((char *)component_Role.cRole, + (const char*)role, sizeof(component_Role.cRole)); + DEBUG_PRINT("\ncomponent_init: Component %s LOADED \n", role); + } else + { + component_Role.nSize = (OMX_U32)sizeof("\0"); + strlcpy((char *)component_Role.cRole, + (const char*)"\0",sizeof(component_Role.cRole)); + DEBUG_PRINT("\ncomponent_init: Component %s LOADED is invalid\n", role); + } + if(pcm_input) + { + + + m_tmp_meta_buf = (OMX_U8*) malloc(sizeof(OMX_U8) * + (OMX_CORE_INPUT_BUFFER_SIZE + sizeof(META_IN))); + + if (m_tmp_meta_buf == NULL){ + DEBUG_PRINT_ERROR("Mem alloc failed for in meta buf\n"); + return OMX_ErrorInsufficientResources; + } + } + m_tmp_out_meta_buf = + (OMX_U8*)malloc(sizeof(OMX_U8)*OMX_EVRC_OUTPUT_BUFFER_SIZE); + if ( m_tmp_out_meta_buf == NULL ) { + DEBUG_PRINT_ERROR("Mem alloc failed for out meta buf\n"); + return OMX_ErrorInsufficientResources; + } + + if(0 == pcm_input) + { + m_drv_fd = open("/dev/msm_evrc_in",O_RDONLY); + DEBUG_PRINT("Driver in Tunnel mode open\n"); + } + else + { + m_drv_fd = open("/dev/msm_evrc_in",O_RDWR); + DEBUG_PRINT("Driver in Non Tunnel mode open\n"); + } + if (m_drv_fd < 0) + { + DEBUG_PRINT_ERROR("Component_init Open Failed[%d] errno[%d]",\ + m_drv_fd,errno); + + return OMX_ErrorInsufficientResources; + } + if(ioctl(m_drv_fd, AUDIO_GET_SESSION_ID,&m_session_id) == -1) + { + DEBUG_PRINT_ERROR("AUDIO_GET_SESSION_ID FAILED\n"); + } + if(pcm_input) + { + if (!m_ipc_to_in_th) + { + m_ipc_to_in_th = omx_evrc_thread_create(process_in_port_msg, + this, (char *)"INPUT_THREAD"); + if (!m_ipc_to_in_th) + { + DEBUG_PRINT_ERROR("ERROR!!! Failed to start \ + Input port thread\n"); + return OMX_ErrorInsufficientResources; + } + } + } + + if (!m_ipc_to_cmd_th) + { + m_ipc_to_cmd_th = omx_evrc_thread_create(process_command_msg, + this, (char *)"CMD_THREAD"); + if (!m_ipc_to_cmd_th) + { + DEBUG_PRINT_ERROR("ERROR!!!Failed to start " + "command message thread\n"); + return OMX_ErrorInsufficientResources; + } + } + + if (!m_ipc_to_out_th) + { + m_ipc_to_out_th = omx_evrc_thread_create(process_out_port_msg, + this, (char *)"OUTPUT_THREAD"); + if (!m_ipc_to_out_th) + { + DEBUG_PRINT_ERROR("ERROR!!! Failed to start output " + "port thread\n"); + return OMX_ErrorInsufficientResources; + } + } + return eRet; +} + +/** + + @brief member function to retrieve version of component + + + + @param hComp handle to this component instance + @param componentName name of component + @param componentVersion pointer to memory space which stores the + version number + @param specVersion pointer to memory sapce which stores version of + openMax specification + @param componentUUID + @return Error status + */ +OMX_ERRORTYPE omx_evrc_aenc::get_component_version +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_OUT OMX_STRING componentName, + OMX_OUT OMX_VERSIONTYPE* componentVersion, + OMX_OUT OMX_VERSIONTYPE* specVersion, + OMX_OUT OMX_UUIDTYPE* componentUUID) +{ + if((hComp == NULL) || (componentName == NULL) || + (specVersion == NULL) || (componentUUID == NULL)) + { + componentVersion = NULL; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_state == OMX_StateInvalid) + { + DEBUG_PRINT_ERROR("Get Comp Version in Invalid State\n"); + return OMX_ErrorInvalidState; + } + componentVersion->nVersion = OMX_SPEC_VERSION; + specVersion->nVersion = OMX_SPEC_VERSION; + return OMX_ErrorNone; +} +/** + @brief member function handles command from IL client + + This function simply queue up commands from IL client. + Commands will be processed in command server thread context later + + @param hComp handle to component instance + @param cmd type of command + @param param1 parameters associated with the command type + @param cmdData + @return Error status +*/ +OMX_ERRORTYPE omx_evrc_aenc::send_command(OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_COMMANDTYPE cmd, + OMX_IN OMX_U32 param1, + OMX_IN OMX_PTR cmdData) +{ + int portIndex = (int)param1; + + if(hComp == NULL) + { + cmdData = cmdData; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (OMX_StateInvalid == m_state) + { + return OMX_ErrorInvalidState; + } + if ( (cmd == OMX_CommandFlush) && (portIndex > 1) ) + { + return OMX_ErrorBadPortIndex; + } + post_command((unsigned)cmd,(unsigned)param1,OMX_COMPONENT_GENERATE_COMMAND); + DEBUG_PRINT("Send Command : returns with OMX_ErrorNone \n"); + DEBUG_PRINT("send_command : recieved state before semwait= %u\n",param1); + sem_wait (&sem_States); + DEBUG_PRINT("send_command : recieved state after semwait\n"); + return OMX_ErrorNone; +} + +/** + @brief member function performs actual processing of commands excluding + empty buffer call + + @param hComp handle to component + @param cmd command type + @param param1 parameter associated with the command + @param cmdData + + @return error status +*/ +OMX_ERRORTYPE omx_evrc_aenc::send_command_proxy(OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_COMMANDTYPE cmd, + OMX_IN OMX_U32 param1, + OMX_IN OMX_PTR cmdData) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + // Handle only IDLE and executing + OMX_STATETYPE eState = (OMX_STATETYPE) param1; + int bFlag = 1; + nState = eState; + + if(hComp == NULL) + { + cmdData = cmdData; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (OMX_CommandStateSet == cmd) + { + /***************************/ + /* Current State is Loaded */ + /***************************/ + if (OMX_StateLoaded == m_state) + { + if (OMX_StateIdle == eState) + { + + if (allocate_done() || + (m_inp_bEnabled == OMX_FALSE + && m_out_bEnabled == OMX_FALSE)) + { + DEBUG_PRINT("SCP-->Allocate Done Complete\n"); + } + else + { + DEBUG_PRINT("SCP-->Loaded to Idle-Pending\n"); + BITMASK_SET(&m_flags, OMX_COMPONENT_IDLE_PENDING); + bFlag = 0; + } + + } else if (eState == OMX_StateLoaded) + { + DEBUG_PRINT("OMXCORE-SM: Loaded-->Loaded\n"); + m_cb.EventHandler(&this->m_cmp, + this->m_app_data, + OMX_EventError, + OMX_ErrorSameState, + 0, NULL ); + eRet = OMX_ErrorSameState; + } + + else if (eState == OMX_StateWaitForResources) + { + DEBUG_PRINT("OMXCORE-SM: Loaded-->WaitForResources\n"); + eRet = OMX_ErrorNone; + } + + else if (eState == OMX_StateExecuting) + { + DEBUG_PRINT("OMXCORE-SM: Loaded-->Executing\n"); + m_cb.EventHandler(&this->m_cmp, + this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } + + else if (eState == OMX_StatePause) + { + DEBUG_PRINT("OMXCORE-SM: Loaded-->Pause\n"); + m_cb.EventHandler(&this->m_cmp, + this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } + + else if (eState == OMX_StateInvalid) + { + DEBUG_PRINT("OMXCORE-SM: Loaded-->Invalid\n"); + m_cb.EventHandler(&this->m_cmp, + this->m_app_data, + OMX_EventError, + OMX_ErrorInvalidState, + 0, NULL ); + m_state = OMX_StateInvalid; + eRet = OMX_ErrorInvalidState; + } else + { + DEBUG_PRINT_ERROR("SCP-->Loaded to Invalid(%d))\n",eState); + eRet = OMX_ErrorBadParameter; + } + } + + /***************************/ + /* Current State is IDLE */ + /***************************/ + else if (OMX_StateIdle == m_state) + { + if (OMX_StateLoaded == eState) + { + if (release_done(-1)) + { + if (ioctl(m_drv_fd, AUDIO_STOP, 0) == -1) + { + DEBUG_PRINT_ERROR("SCP:Idle->Loaded,\ + ioctl stop failed %d\n", errno); + } + + nTimestamp=0; + + DEBUG_PRINT("SCP-->Idle to Loaded\n"); + } else + { + DEBUG_PRINT("SCP--> Idle to Loaded-Pending\n"); + BITMASK_SET(&m_flags, OMX_COMPONENT_LOADING_PENDING); + // Skip the event notification + bFlag = 0; + } + } + else if (OMX_StateExecuting == eState) + { + + struct msm_audio_evrc_enc_config drv_evrc_enc_config; + struct msm_audio_stream_config drv_stream_config; + struct msm_audio_buf_cfg buf_cfg; + struct msm_audio_config pcm_cfg; + + if(ioctl(m_drv_fd, AUDIO_GET_STREAM_CONFIG, &drv_stream_config) + == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_GET_STREAM_CONFIG failed, \ + errno[%d]\n", errno); + } + if(ioctl(m_drv_fd, AUDIO_SET_STREAM_CONFIG, &drv_stream_config) + == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_SET_STREAM_CONFIG failed, \ + errno[%d]\n", errno); + } + + if(ioctl(m_drv_fd, AUDIO_GET_EVRC_ENC_CONFIG, + &drv_evrc_enc_config) == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_GET_EVRC_ENC_CONFIG failed,\ + errno[%d]\n", errno); + } + drv_evrc_enc_config.min_bit_rate = m_evrc_param.nMinBitRate; + drv_evrc_enc_config.max_bit_rate = m_evrc_param.nMaxBitRate; + if(ioctl(m_drv_fd, AUDIO_SET_EVRC_ENC_CONFIG, &drv_evrc_enc_config) + == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_SET_EVRC_ENC_CONFIG failed,\ + errno[%d]\n", errno); + } + if (ioctl(m_drv_fd, AUDIO_GET_BUF_CFG, &buf_cfg) == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_GET_BUF_CFG, errno[%d]\n", + errno); + } + buf_cfg.meta_info_enable = 1; + buf_cfg.frames_per_buf = NUMOFFRAMES; + if (ioctl(m_drv_fd, AUDIO_SET_BUF_CFG, &buf_cfg) == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_SET_BUF_CFG, errno[%d]\n", + errno); + } + if(pcm_input) + { + if (ioctl(m_drv_fd, AUDIO_GET_CONFIG, &pcm_cfg) == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_GET_CONFIG, errno[%d]\n", + errno); + } + pcm_cfg.channel_count = m_pcm_param.nChannels; + pcm_cfg.sample_rate = m_pcm_param.nSamplingRate; + DEBUG_PRINT("pcm config %u %u\n",m_pcm_param.nChannels, + m_pcm_param.nSamplingRate); + + if (ioctl(m_drv_fd, AUDIO_SET_CONFIG, &pcm_cfg) == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_SET_CONFIG, errno[%d]\n", + errno); + } + } + if(ioctl(m_drv_fd, AUDIO_START, 0) == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_START failed, errno[%d]\n", + errno); + m_state = OMX_StateInvalid; + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, OMX_ErrorInvalidState, + 0, NULL ); + eRet = OMX_ErrorInvalidState; + + } + DEBUG_PRINT("SCP-->Idle to Executing\n"); + nState = eState; + } else if (eState == OMX_StateIdle) + { + DEBUG_PRINT("OMXCORE-SM: Idle-->Idle\n"); + m_cb.EventHandler(&this->m_cmp, + this->m_app_data, + OMX_EventError, + OMX_ErrorSameState, + 0, NULL ); + eRet = OMX_ErrorSameState; + } else if (eState == OMX_StateWaitForResources) + { + DEBUG_PRINT("OMXCORE-SM: Idle-->WaitForResources\n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } + + else if (eState == OMX_StatePause) + { + DEBUG_PRINT("OMXCORE-SM: Idle-->Pause\n"); + } + + else if (eState == OMX_StateInvalid) + { + DEBUG_PRINT("OMXCORE-SM: Idle-->Invalid\n"); + m_state = OMX_StateInvalid; + this->m_cb.EventHandler(&this->m_cmp, + this->m_app_data, + OMX_EventError, + OMX_ErrorInvalidState, + 0, NULL ); + eRet = OMX_ErrorInvalidState; + } else + { + DEBUG_PRINT_ERROR("SCP--> Idle to %d Not Handled\n",eState); + eRet = OMX_ErrorBadParameter; + } + } + + /******************************/ + /* Current State is Executing */ + /******************************/ + else if (OMX_StateExecuting == m_state) + { + if (OMX_StateIdle == eState) + { + DEBUG_PRINT("SCP-->Executing to Idle \n"); + if(pcm_input) + execute_omx_flush(-1,false); + else + execute_omx_flush(1,false); + + + } else if (OMX_StatePause == eState) + { + DEBUG_DETAIL("*************************\n"); + DEBUG_PRINT("SCP-->RXED PAUSE STATE\n"); + DEBUG_DETAIL("*************************\n"); + //ioctl(m_drv_fd, AUDIO_PAUSE, 0); + } else if (eState == OMX_StateLoaded) + { + DEBUG_PRINT("\n OMXCORE-SM: Executing --> Loaded \n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } else if (eState == OMX_StateWaitForResources) + { + DEBUG_PRINT("\n OMXCORE-SM: Executing --> WaitForResources \n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } else if (eState == OMX_StateExecuting) + { + DEBUG_PRINT("\n OMXCORE-SM: Executing --> Executing \n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, OMX_ErrorSameState, + 0, NULL ); + eRet = OMX_ErrorSameState; + } else if (eState == OMX_StateInvalid) + { + DEBUG_PRINT("\n OMXCORE-SM: Executing --> Invalid \n"); + m_state = OMX_StateInvalid; + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, OMX_ErrorInvalidState, + 0, NULL ); + eRet = OMX_ErrorInvalidState; + } else + { + DEBUG_PRINT_ERROR("SCP--> Executing to %d Not Handled\n", + eState); + eRet = OMX_ErrorBadParameter; + } + } + /***************************/ + /* Current State is Pause */ + /***************************/ + else if (OMX_StatePause == m_state) + { + if( (eState == OMX_StateExecuting || eState == OMX_StateIdle) ) + { + pthread_mutex_lock(&m_out_th_lock_1); + if(is_out_th_sleep) + { + DEBUG_DETAIL("PE: WAKING UP OUT THREAD\n"); + is_out_th_sleep = false; + out_th_wakeup(); + } + pthread_mutex_unlock(&m_out_th_lock_1); + } + if ( OMX_StateExecuting == eState ) + { + nState = eState; + } else if ( OMX_StateIdle == eState ) + { + DEBUG_PRINT("SCP-->Paused to Idle \n"); + DEBUG_PRINT ("\n Internal flush issued"); + pthread_mutex_lock(&m_flush_lock); + m_flush_cnt = 2; + pthread_mutex_unlock(&m_flush_lock); + if(pcm_input) + execute_omx_flush(-1,false); + else + execute_omx_flush(1,false); + + } else if ( eState == OMX_StateLoaded ) + { + DEBUG_PRINT("\n Pause --> loaded \n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } else if (eState == OMX_StateWaitForResources) + { + DEBUG_PRINT("\n Pause --> WaitForResources \n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } else if (eState == OMX_StatePause) + { + DEBUG_PRINT("\n Pause --> Pause \n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, OMX_ErrorSameState, + 0, NULL ); + eRet = OMX_ErrorSameState; + } else if (eState == OMX_StateInvalid) + { + DEBUG_PRINT("\n Pause --> Invalid \n"); + m_state = OMX_StateInvalid; + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, OMX_ErrorInvalidState, + 0, NULL ); + eRet = OMX_ErrorInvalidState; + } else + { + DEBUG_PRINT("SCP-->Paused to %d Not Handled\n",eState); + eRet = OMX_ErrorBadParameter; + } + } + /**************************************/ + /* Current State is WaitForResources */ + /**************************************/ + else if (m_state == OMX_StateWaitForResources) + { + if (eState == OMX_StateLoaded) + { + DEBUG_PRINT("OMXCORE-SM: WaitForResources-->Loaded\n"); + } else if (eState == OMX_StateWaitForResources) + { + DEBUG_PRINT("OMXCORE-SM: \ + WaitForResources-->WaitForResources\n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, OMX_ErrorSameState, + 0, NULL ); + eRet = OMX_ErrorSameState; + } else if (eState == OMX_StateExecuting) + { + DEBUG_PRINT("OMXCORE-SM: WaitForResources-->Executing\n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } else if (eState == OMX_StatePause) + { + DEBUG_PRINT("OMXCORE-SM: WaitForResources-->Pause\n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } else if (eState == OMX_StateInvalid) + { + DEBUG_PRINT("OMXCORE-SM: WaitForResources-->Invalid\n"); + m_state = OMX_StateInvalid; + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorInvalidState, + 0, NULL ); + eRet = OMX_ErrorInvalidState; + } else + { + DEBUG_PRINT_ERROR("SCP--> %d to %d(Not Handled)\n", + m_state,eState); + eRet = OMX_ErrorBadParameter; + } + } + /****************************/ + /* Current State is Invalid */ + /****************************/ + else if (m_state == OMX_StateInvalid) + { + if (OMX_StateLoaded == eState || OMX_StateWaitForResources == eState + || OMX_StateIdle == eState || OMX_StateExecuting == eState + || OMX_StatePause == eState || OMX_StateInvalid == eState) + { + DEBUG_PRINT("OMXCORE-SM: Invalid-->Loaded/Idle/Executing" + "/Pause/Invalid/WaitForResources\n"); + m_state = OMX_StateInvalid; + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, OMX_ErrorInvalidState, + 0, NULL ); + eRet = OMX_ErrorInvalidState; + } + } else + { + DEBUG_PRINT_ERROR("OMXCORE-SM: %d --> %d(Not Handled)\n",\ + m_state,eState); + eRet = OMX_ErrorBadParameter; + } + } else if (OMX_CommandFlush == cmd) + { + DEBUG_DETAIL("*************************\n"); + DEBUG_PRINT("SCP-->RXED FLUSH COMMAND port=%u\n",param1); + DEBUG_DETAIL("*************************\n"); + bFlag = 0; + if ( param1 == OMX_CORE_INPUT_PORT_INDEX || + param1 == OMX_CORE_OUTPUT_PORT_INDEX || + (signed)param1 == -1 ) + { + execute_omx_flush(param1); + } else + { + eRet = OMX_ErrorBadPortIndex; + m_cb.EventHandler(&m_cmp, m_app_data, OMX_EventError, + OMX_CommandFlush, OMX_ErrorBadPortIndex, NULL ); + } + } else if ( cmd == OMX_CommandPortDisable ) + { + bFlag = 0; + if ( param1 == OMX_CORE_INPUT_PORT_INDEX || param1 == OMX_ALL ) + { + DEBUG_PRINT("SCP: Disabling Input port Indx\n"); + m_inp_bEnabled = OMX_FALSE; + if ( (m_state == OMX_StateLoaded || m_state == OMX_StateIdle) + && release_done(0) ) + { + DEBUG_PRINT("send_command_proxy:OMX_CommandPortDisable:\ + OMX_CORE_INPUT_PORT_INDEX:release_done \n"); + DEBUG_PRINT("************* OMX_CommandPortDisable:\ + m_inp_bEnabled = %d********\n",m_inp_bEnabled); + + post_command(OMX_CommandPortDisable, + OMX_CORE_INPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + } + + else + { + if (m_state == OMX_StatePause ||m_state == OMX_StateExecuting) + { + DEBUG_PRINT("SCP: execute_omx_flush in Disable in "\ + " param1=%u m_state=%d \n",param1, m_state); + execute_omx_flush(param1); + } + DEBUG_PRINT("send_command_proxy:OMX_CommandPortDisable:\ + OMX_CORE_INPUT_PORT_INDEX \n"); + BITMASK_SET(&m_flags, OMX_COMPONENT_INPUT_DISABLE_PENDING); + // Skip the event notification + + } + + } + if (param1 == OMX_CORE_OUTPUT_PORT_INDEX || param1 == OMX_ALL) + { + + DEBUG_PRINT("SCP: Disabling Output port Indx\n"); + m_out_bEnabled = OMX_FALSE; + if ((m_state == OMX_StateLoaded || m_state == OMX_StateIdle) + && release_done(1)) + { + DEBUG_PRINT("send_command_proxy:OMX_CommandPortDisable:\ + OMX_CORE_OUTPUT_PORT_INDEX:release_done \n"); + DEBUG_PRINT("************* OMX_CommandPortDisable:\ + m_out_bEnabled = %d********\n",m_inp_bEnabled); + + post_command(OMX_CommandPortDisable, + OMX_CORE_OUTPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + } else + { + if (m_state == OMX_StatePause ||m_state == OMX_StateExecuting) + { + DEBUG_PRINT("SCP: execute_omx_flush in Disable out "\ + "param1=%u m_state=%d \n",param1, m_state); + execute_omx_flush(param1); + } + BITMASK_SET(&m_flags, OMX_COMPONENT_OUTPUT_DISABLE_PENDING); + // Skip the event notification + + } + } else + { + DEBUG_PRINT_ERROR("OMX_CommandPortDisable: disable wrong port ID"); + } + + } else if (cmd == OMX_CommandPortEnable) + { + bFlag = 0; + if (param1 == OMX_CORE_INPUT_PORT_INDEX || param1 == OMX_ALL) + { + m_inp_bEnabled = OMX_TRUE; + DEBUG_PRINT("SCP: Enabling Input port Indx\n"); + if ((m_state == OMX_StateLoaded + && !BITMASK_PRESENT(&m_flags,OMX_COMPONENT_IDLE_PENDING)) + || (m_state == OMX_StateWaitForResources) + || (m_inp_bPopulated == OMX_TRUE)) + { + post_command(OMX_CommandPortEnable, + OMX_CORE_INPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + + + } else + { + BITMASK_SET(&m_flags, OMX_COMPONENT_INPUT_ENABLE_PENDING); + // Skip the event notification + + } + } + + if (param1 == OMX_CORE_OUTPUT_PORT_INDEX || param1 == OMX_ALL) + { + DEBUG_PRINT("SCP: Enabling Output port Indx\n"); + m_out_bEnabled = OMX_TRUE; + if ((m_state == OMX_StateLoaded + && !BITMASK_PRESENT(&m_flags,OMX_COMPONENT_IDLE_PENDING)) + || (m_state == OMX_StateWaitForResources) + || (m_out_bPopulated == OMX_TRUE)) + { + post_command(OMX_CommandPortEnable, + OMX_CORE_OUTPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + } else + { + DEBUG_PRINT("send_command_proxy:OMX_CommandPortEnable:\ + OMX_CORE_OUTPUT_PORT_INDEX:release_done \n"); + BITMASK_SET(&m_flags, OMX_COMPONENT_OUTPUT_ENABLE_PENDING); + // Skip the event notification + + } + pthread_mutex_lock(&m_in_th_lock_1); + if(is_in_th_sleep) + { + is_in_th_sleep = false; + DEBUG_DETAIL("SCP:WAKING UP IN THREADS\n"); + in_th_wakeup(); + } + pthread_mutex_unlock(&m_in_th_lock_1); + pthread_mutex_lock(&m_out_th_lock_1); + if (is_out_th_sleep) + { + is_out_th_sleep = false; + DEBUG_PRINT("SCP:WAKING OUT THR, OMX_CommandPortEnable\n"); + out_th_wakeup(); + } + pthread_mutex_unlock(&m_out_th_lock_1); + } else + { + DEBUG_PRINT_ERROR("OMX_CommandPortEnable: disable wrong port ID"); + } + + } else + { + DEBUG_PRINT_ERROR("SCP-->ERROR: Invali Command [%d]\n",cmd); + eRet = OMX_ErrorNotImplemented; + } + DEBUG_PRINT("posting sem_States\n"); + sem_post (&sem_States); + if (eRet == OMX_ErrorNone && bFlag) + { + post_command(cmd,eState,OMX_COMPONENT_GENERATE_EVENT); + } + return eRet; +} + +/*============================================================================= +FUNCTION: + execute_omx_flush + +DESCRIPTION: + Function that flushes buffers that are pending to be written to driver + +INPUT/OUTPUT PARAMETERS: + [IN] param1 + [IN] cmd_cmpl + +RETURN VALUE: + true + false + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +bool omx_evrc_aenc::execute_omx_flush(OMX_IN OMX_U32 param1, bool cmd_cmpl) +{ + bool bRet = true; + + DEBUG_PRINT("Execute_omx_flush Port[%u]", param1); + struct timespec abs_timeout; + abs_timeout.tv_sec = 1; + abs_timeout.tv_nsec = 0; + + if ((signed)param1 == -1) + { + bFlushinprogress = true; + DEBUG_PRINT("Execute flush for both I/p O/p port\n"); + pthread_mutex_lock(&m_flush_lock); + m_flush_cnt = 2; + pthread_mutex_unlock(&m_flush_lock); + + // Send Flush commands to input and output threads + post_input(OMX_CommandFlush, + OMX_CORE_INPUT_PORT_INDEX,OMX_COMPONENT_GENERATE_COMMAND); + post_output(OMX_CommandFlush, + OMX_CORE_OUTPUT_PORT_INDEX,OMX_COMPONENT_GENERATE_COMMAND); + // Send Flush to the kernel so that the in and out buffers are released + if (ioctl( m_drv_fd, AUDIO_FLUSH, 0) == -1) + DEBUG_PRINT_ERROR("FLush:ioctl flush failed errno=%d\n",errno); + DEBUG_DETAIL("****************************************"); + DEBUG_DETAIL("is_in_th_sleep=%d is_out_th_sleep=%d\n",\ + is_in_th_sleep,is_out_th_sleep); + DEBUG_DETAIL("****************************************"); + + pthread_mutex_lock(&m_in_th_lock_1); + if (is_in_th_sleep) + { + is_in_th_sleep = false; + DEBUG_DETAIL("For FLUSH-->WAKING UP IN THREADS\n"); + in_th_wakeup(); + } + pthread_mutex_unlock(&m_in_th_lock_1); + + pthread_mutex_lock(&m_out_th_lock_1); + if (is_out_th_sleep) + { + is_out_th_sleep = false; + DEBUG_DETAIL("For FLUSH-->WAKING UP OUT THREADS\n"); + out_th_wakeup(); + } + pthread_mutex_unlock(&m_out_th_lock_1); + + + // sleep till the FLUSH ACK are done by both the input and + // output threads + DEBUG_DETAIL("WAITING FOR FLUSH ACK's param1=%d",param1); + wait_for_event(); + + DEBUG_PRINT("RECIEVED BOTH FLUSH ACK's param1=%u cmd_cmpl=%d",\ + param1,cmd_cmpl); + + // If not going to idle state, Send FLUSH complete message + // to the Client, now that FLUSH ACK's have been recieved. + if (cmd_cmpl) + { + m_cb.EventHandler(&m_cmp, m_app_data, OMX_EventCmdComplete, + OMX_CommandFlush, OMX_CORE_INPUT_PORT_INDEX, + NULL ); + m_cb.EventHandler(&m_cmp, m_app_data, OMX_EventCmdComplete, + OMX_CommandFlush, OMX_CORE_OUTPUT_PORT_INDEX, + NULL ); + DEBUG_PRINT("Inside FLUSH.. sending FLUSH CMPL\n"); + } + bFlushinprogress = false; + } + else if (param1 == OMX_CORE_INPUT_PORT_INDEX) + { + DEBUG_PRINT("Execute FLUSH for I/p port\n"); + pthread_mutex_lock(&m_flush_lock); + m_flush_cnt = 1; + pthread_mutex_unlock(&m_flush_lock); + post_input(OMX_CommandFlush, + OMX_CORE_INPUT_PORT_INDEX,OMX_COMPONENT_GENERATE_COMMAND); + if (ioctl( m_drv_fd, AUDIO_FLUSH, 0) == -1) + DEBUG_PRINT_ERROR("Flush:Input port, ioctl flush failed %d\n", + errno); + DEBUG_DETAIL("****************************************"); + DEBUG_DETAIL("is_in_th_sleep=%d is_out_th_sleep=%d\n",\ + is_in_th_sleep,is_out_th_sleep); + DEBUG_DETAIL("****************************************"); + + if (is_in_th_sleep) + { + pthread_mutex_lock(&m_in_th_lock_1); + is_in_th_sleep = false; + pthread_mutex_unlock(&m_in_th_lock_1); + DEBUG_DETAIL("For FLUSH-->WAKING UP IN THREADS\n"); + in_th_wakeup(); + } + + if (is_out_th_sleep) + { + pthread_mutex_lock(&m_out_th_lock_1); + is_out_th_sleep = false; + pthread_mutex_unlock(&m_out_th_lock_1); + DEBUG_DETAIL("For FLUSH-->WAKING UP OUT THREADS\n"); + out_th_wakeup(); + } + + //sleep till the FLUSH ACK are done by both the input and output threads + DEBUG_DETAIL("Executing FLUSH for I/p port\n"); + DEBUG_DETAIL("WAITING FOR FLUSH ACK's param1=%d",param1); + wait_for_event(); + DEBUG_DETAIL(" RECIEVED FLUSH ACK FOR I/P PORT param1=%d",param1); + + // Send FLUSH complete message to the Client, + // now that FLUSH ACK's have been recieved. + if (cmd_cmpl) + { + m_cb.EventHandler(&m_cmp, m_app_data, OMX_EventCmdComplete, + OMX_CommandFlush, OMX_CORE_INPUT_PORT_INDEX, + NULL ); + } + } else if (OMX_CORE_OUTPUT_PORT_INDEX == param1) + { + DEBUG_PRINT("Executing FLUSH for O/p port\n"); + pthread_mutex_lock(&m_flush_lock); + m_flush_cnt = 1; + pthread_mutex_unlock(&m_flush_lock); + DEBUG_DETAIL("Executing FLUSH for O/p port\n"); + DEBUG_DETAIL("WAITING FOR FLUSH ACK's param1=%d",param1); + post_output(OMX_CommandFlush, + OMX_CORE_OUTPUT_PORT_INDEX,OMX_COMPONENT_GENERATE_COMMAND); + if (ioctl( m_drv_fd, AUDIO_FLUSH, 0) ==-1) + DEBUG_PRINT_ERROR("Flush:Output port, ioctl flush failed %d\n", + errno); + DEBUG_DETAIL("****************************************"); + DEBUG_DETAIL("is_in_th_sleep=%d is_out_th_sleep=%d\n",\ + is_in_th_sleep,is_out_th_sleep); + DEBUG_DETAIL("****************************************"); + if (is_in_th_sleep) + { + pthread_mutex_lock(&m_in_th_lock_1); + is_in_th_sleep = false; + pthread_mutex_unlock(&m_in_th_lock_1); + DEBUG_DETAIL("For FLUSH-->WAKING UP IN THREADS\n"); + in_th_wakeup(); + } + + if (is_out_th_sleep) + { + pthread_mutex_lock(&m_out_th_lock_1); + is_out_th_sleep = false; + pthread_mutex_unlock(&m_out_th_lock_1); + DEBUG_DETAIL("For FLUSH-->WAKING UP OUT THREADS\n"); + out_th_wakeup(); + } + + // sleep till the FLUSH ACK are done by both the input and + // output threads + wait_for_event(); + // Send FLUSH complete message to the Client, + // now that FLUSH ACK's have been recieved. + if (cmd_cmpl) + { + m_cb.EventHandler(&m_cmp, m_app_data, OMX_EventCmdComplete, + OMX_CommandFlush, OMX_CORE_OUTPUT_PORT_INDEX, + NULL ); + } + DEBUG_DETAIL("RECIEVED FLUSH ACK FOR O/P PORT param1=%d",param1); + } else + { + DEBUG_PRINT("Invalid Port ID[%u]",param1); + } + return bRet; +} + +/*============================================================================= +FUNCTION: + execute_input_omx_flush + +DESCRIPTION: + Function that flushes buffers that are pending to be written to driver + +INPUT/OUTPUT PARAMETERS: + None + +RETURN VALUE: + true + false + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +bool omx_evrc_aenc::execute_input_omx_flush() +{ + OMX_BUFFERHEADERTYPE *omx_buf; + unsigned long p1 = 0; // Parameter - 1 + unsigned long p2 = 0; // Parameter - 2 + unsigned char ident = 0; + unsigned qsize=0; // qsize + unsigned tot_qsize=0; // qsize + + DEBUG_PRINT("Execute_omx_flush on input port"); + + pthread_mutex_lock(&m_lock); + do + { + qsize = m_input_q.m_size; + tot_qsize = qsize; + tot_qsize += m_input_ctrl_ebd_q.m_size; + + DEBUG_DETAIL("Input FLUSH-->flushq[%d] ebd[%d]dataq[%d]",\ + m_input_ctrl_cmd_q.m_size, + m_input_ctrl_ebd_q.m_size,qsize); + if (!tot_qsize) + { + DEBUG_DETAIL("Input-->BREAKING FROM execute_input_flush LOOP"); + pthread_mutex_unlock(&m_lock); + break; + } + if (qsize) + { + m_input_q.pop_entry(&p1, &p2, &ident); + if ((ident == OMX_COMPONENT_GENERATE_ETB) || + (ident == OMX_COMPONENT_GENERATE_BUFFER_DONE)) + { + omx_buf = (OMX_BUFFERHEADERTYPE *) p2; + DEBUG_DETAIL("Flush:Input dataq=%p \n", omx_buf); + omx_buf->nFilledLen = 0; + buffer_done_cb((OMX_BUFFERHEADERTYPE *)omx_buf); + } + } else if (m_input_ctrl_ebd_q.m_size) + { + m_input_ctrl_ebd_q.pop_entry(&p1, &p2, &ident); + if (ident == OMX_COMPONENT_GENERATE_BUFFER_DONE) + { + omx_buf = (OMX_BUFFERHEADERTYPE *) p2; + omx_buf->nFilledLen = 0; + DEBUG_DETAIL("Flush:ctrl dataq=%p \n", omx_buf); + buffer_done_cb((OMX_BUFFERHEADERTYPE *)omx_buf); + } + } else + { + } + }while (tot_qsize>0); + DEBUG_DETAIL("*************************\n"); + DEBUG_DETAIL("IN-->FLUSHING DONE\n"); + DEBUG_DETAIL("*************************\n"); + flush_ack(); + pthread_mutex_unlock(&m_lock); + return true; +} + +/*============================================================================= +FUNCTION: + execute_output_omx_flush + +DESCRIPTION: + Function that flushes buffers that are pending to be written to driver + +INPUT/OUTPUT PARAMETERS: + None + +RETURN VALUE: + true + false + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +bool omx_evrc_aenc::execute_output_omx_flush() +{ + OMX_BUFFERHEADERTYPE *omx_buf; + unsigned long p1; // Parameter - 1 + unsigned long p2; // Parameter - 2 + unsigned char ident = 0; + unsigned qsize=0; // qsize + unsigned tot_qsize=0; // qsize + + DEBUG_PRINT("Execute_omx_flush on output port"); + + pthread_mutex_lock(&m_outputlock); + do + { + qsize = m_output_q.m_size; + DEBUG_DETAIL("OUT FLUSH-->flushq[%d] fbd[%d]dataq[%d]",\ + m_output_ctrl_cmd_q.m_size, + m_output_ctrl_fbd_q.m_size,qsize); + tot_qsize = qsize; + tot_qsize += m_output_ctrl_fbd_q.m_size; + if (!tot_qsize) + { + DEBUG_DETAIL("OUT-->BREAKING FROM execute_input_flush LOOP"); + pthread_mutex_unlock(&m_outputlock); + break; + } + if (qsize) + { + m_output_q.pop_entry(&p1,&p2,&ident); + if ( (OMX_COMPONENT_GENERATE_FTB == ident) || + (OMX_COMPONENT_GENERATE_FRAME_DONE == ident)) + { + omx_buf = (OMX_BUFFERHEADERTYPE *) p2; + DEBUG_DETAIL("Ouput Buf_Addr=%p TS[0x%x] \n",\ + omx_buf,nTimestamp); + omx_buf->nTimeStamp = nTimestamp; + omx_buf->nFilledLen = 0; + frame_done_cb((OMX_BUFFERHEADERTYPE *)omx_buf); + DEBUG_DETAIL("CALLING FBD FROM FLUSH"); + } + } else if ((qsize = m_output_ctrl_fbd_q.m_size)) + { + m_output_ctrl_fbd_q.pop_entry(&p1, &p2, &ident); + if (OMX_COMPONENT_GENERATE_FRAME_DONE == ident) + { + omx_buf = (OMX_BUFFERHEADERTYPE *) p2; + DEBUG_DETAIL("Ouput Buf_Addr=%p TS[0x%x] \n", \ + omx_buf,nTimestamp); + omx_buf->nTimeStamp = nTimestamp; + omx_buf->nFilledLen = 0; + frame_done_cb((OMX_BUFFERHEADERTYPE *)omx_buf); + DEBUG_DETAIL("CALLING FROM CTRL-FBDQ FROM FLUSH"); + } + } + }while (qsize>0); + DEBUG_DETAIL("*************************\n"); + DEBUG_DETAIL("OUT-->FLUSHING DONE\n"); + DEBUG_DETAIL("*************************\n"); + flush_ack(); + pthread_mutex_unlock(&m_outputlock); + return true; +} + +/*============================================================================= +FUNCTION: + post_input + +DESCRIPTION: + Function that posts command in the command queue + +INPUT/OUTPUT PARAMETERS: + [IN] p1 + [IN] p2 + [IN] id - command ID + [IN] lock - self-locking mode + +RETURN VALUE: + true + false + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +bool omx_evrc_aenc::post_input(unsigned long p1, + unsigned long p2, + unsigned char id) +{ + bool bRet = false; + pthread_mutex_lock(&m_lock); + + if((OMX_COMPONENT_GENERATE_COMMAND == id) || (id == OMX_COMPONENT_SUSPEND)) + { + // insert flush message and ebd + m_input_ctrl_cmd_q.insert_entry(p1,p2,id); + } else if ((OMX_COMPONENT_GENERATE_BUFFER_DONE == id)) + { + // insert ebd + m_input_ctrl_ebd_q.insert_entry(p1,p2,id); + } else + { + // ETBS in this queue + m_input_q.insert_entry(p1,p2,id); + } + + if (m_ipc_to_in_th) + { + bRet = true; + omx_evrc_post_msg(m_ipc_to_in_th, id); + } + + DEBUG_DETAIL("PostInput-->state[%d]id[%d]flushq[%d]ebdq[%d]dataq[%d] \n",\ + m_state, + id, + m_input_ctrl_cmd_q.m_size, + m_input_ctrl_ebd_q.m_size, + m_input_q.m_size); + + pthread_mutex_unlock(&m_lock); + return bRet; +} + +/*============================================================================= +FUNCTION: + post_command + +DESCRIPTION: + Function that posts command in the command queue + +INPUT/OUTPUT PARAMETERS: + [IN] p1 + [IN] p2 + [IN] id - command ID + [IN] lock - self-locking mode + +RETURN VALUE: + true + false + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +bool omx_evrc_aenc::post_command(unsigned int p1, + unsigned int p2, + unsigned char id) +{ + bool bRet = false; + + pthread_mutex_lock(&m_commandlock); + + m_command_q.insert_entry(p1,p2,id); + + if (m_ipc_to_cmd_th) + { + bRet = true; + omx_evrc_post_msg(m_ipc_to_cmd_th, id); + } + + DEBUG_DETAIL("PostCmd-->state[%d]id[%d]cmdq[%d]flags[%x]\n",\ + m_state, + id, + m_command_q.m_size, + m_flags >> 3); + + pthread_mutex_unlock(&m_commandlock); + return bRet; +} + +/*============================================================================= +FUNCTION: + post_output + +DESCRIPTION: + Function that posts command in the command queue + +INPUT/OUTPUT PARAMETERS: + [IN] p1 + [IN] p2 + [IN] id - command ID + [IN] lock - self-locking mode + +RETURN VALUE: + true + false + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +bool omx_evrc_aenc::post_output(unsigned long p1, + unsigned long p2, + unsigned char id) +{ + bool bRet = false; + + pthread_mutex_lock(&m_outputlock); + if((OMX_COMPONENT_GENERATE_COMMAND == id) || (id == OMX_COMPONENT_SUSPEND) + || (id == OMX_COMPONENT_RESUME)) + { + // insert flush message and fbd + m_output_ctrl_cmd_q.insert_entry(p1,p2,id); + } else if ( (OMX_COMPONENT_GENERATE_FRAME_DONE == id) ) + { + // insert flush message and fbd + m_output_ctrl_fbd_q.insert_entry(p1,p2,id); + } else + { + m_output_q.insert_entry(p1,p2,id); + } + if ( m_ipc_to_out_th ) + { + bRet = true; + omx_evrc_post_msg(m_ipc_to_out_th, id); + } + DEBUG_DETAIL("PostOutput-->state[%d]id[%d]flushq[%d]ebdq[%d]dataq[%d]\n",\ + m_state, + id, + m_output_ctrl_cmd_q.m_size, + m_output_ctrl_fbd_q.m_size, + m_output_q.m_size); + + pthread_mutex_unlock(&m_outputlock); + return bRet; +} +/** + @brief member function that return parameters to IL client + + @param hComp handle to component instance + @param paramIndex Parameter type + @param paramData pointer to memory space which would hold the + paramter + @return error status +*/ +OMX_ERRORTYPE omx_evrc_aenc::get_parameter(OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_INDEXTYPE paramIndex, + OMX_INOUT OMX_PTR paramData) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_state == OMX_StateInvalid) + { + DEBUG_PRINT_ERROR("Get Param in Invalid State\n"); + return OMX_ErrorInvalidState; + } + if (paramData == NULL) + { + DEBUG_PRINT("get_parameter: paramData is NULL\n"); + return OMX_ErrorBadParameter; + } + + switch ((int)paramIndex) + { + case OMX_IndexParamPortDefinition: + { + OMX_PARAM_PORTDEFINITIONTYPE *portDefn; + portDefn = (OMX_PARAM_PORTDEFINITIONTYPE *) paramData; + + DEBUG_PRINT("OMX_IndexParamPortDefinition " \ + "portDefn->nPortIndex = %u\n", + portDefn->nPortIndex); + + portDefn->nVersion.nVersion = OMX_SPEC_VERSION; + portDefn->nSize = (OMX_U32)sizeof(portDefn); + portDefn->eDomain = OMX_PortDomainAudio; + + if (0 == portDefn->nPortIndex) + { + portDefn->eDir = OMX_DirInput; + portDefn->bEnabled = m_inp_bEnabled; + portDefn->bPopulated = m_inp_bPopulated; + portDefn->nBufferCountActual = m_inp_act_buf_count; + portDefn->nBufferCountMin = OMX_CORE_NUM_INPUT_BUFFERS; + portDefn->nBufferSize = input_buffer_size; + portDefn->format.audio.bFlagErrorConcealment = OMX_TRUE; + portDefn->format.audio.eEncoding = OMX_AUDIO_CodingPCM; + portDefn->format.audio.pNativeRender = 0; + } else if (1 == portDefn->nPortIndex) + { + portDefn->eDir = OMX_DirOutput; + portDefn->bEnabled = m_out_bEnabled; + portDefn->bPopulated = m_out_bPopulated; + portDefn->nBufferCountActual = m_out_act_buf_count; + portDefn->nBufferCountMin = OMX_CORE_NUM_OUTPUT_BUFFERS; + portDefn->nBufferSize = output_buffer_size; + portDefn->format.audio.bFlagErrorConcealment = OMX_TRUE; + portDefn->format.audio.eEncoding = OMX_AUDIO_CodingEVRC; + portDefn->format.audio.pNativeRender = 0; + } else + { + portDefn->eDir = OMX_DirMax; + DEBUG_PRINT_ERROR("Bad Port idx %d\n",\ + (int)portDefn->nPortIndex); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + + case OMX_IndexParamAudioInit: + { + OMX_PORT_PARAM_TYPE *portParamType = + (OMX_PORT_PARAM_TYPE *) paramData; + DEBUG_PRINT("OMX_IndexParamAudioInit\n"); + + portParamType->nVersion.nVersion = OMX_SPEC_VERSION; + portParamType->nSize = (OMX_U32)sizeof(portParamType); + portParamType->nPorts = 2; + portParamType->nStartPortNumber = 0; + break; + } + + case OMX_IndexParamAudioPortFormat: + { + OMX_AUDIO_PARAM_PORTFORMATTYPE *portFormatType = + (OMX_AUDIO_PARAM_PORTFORMATTYPE *) paramData; + DEBUG_PRINT("OMX_IndexParamAudioPortFormat\n"); + portFormatType->nVersion.nVersion = OMX_SPEC_VERSION; + portFormatType->nSize = (OMX_U32)sizeof(portFormatType); + + if (OMX_CORE_INPUT_PORT_INDEX == portFormatType->nPortIndex) + { + + portFormatType->eEncoding = OMX_AUDIO_CodingPCM; + } else if (OMX_CORE_OUTPUT_PORT_INDEX== + portFormatType->nPortIndex) + { + DEBUG_PRINT("get_parameter: OMX_IndexParamAudioFormat: "\ + "%u\n", portFormatType->nIndex); + + portFormatType->eEncoding = OMX_AUDIO_CodingEVRC; + } else + { + DEBUG_PRINT_ERROR("get_parameter: Bad port index %d\n", + (int)portFormatType->nPortIndex); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + + case OMX_IndexParamAudioEvrc: + { + OMX_AUDIO_PARAM_EVRCTYPE *evrcParam = + (OMX_AUDIO_PARAM_EVRCTYPE *) paramData; + DEBUG_PRINT("OMX_IndexParamAudioEvrc\n"); + if (OMX_CORE_OUTPUT_PORT_INDEX== evrcParam->nPortIndex) + { + memcpy(evrcParam,&m_evrc_param, + sizeof(OMX_AUDIO_PARAM_EVRCTYPE)); + } else + { + DEBUG_PRINT_ERROR("get_parameter:OMX_IndexParamAudioEvrc "\ + "OMX_ErrorBadPortIndex %d\n", \ + (int)evrcParam->nPortIndex); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + case QOMX_IndexParamAudioSessionId: + { + QOMX_AUDIO_STREAM_INFO_DATA *streaminfoparam = + (QOMX_AUDIO_STREAM_INFO_DATA *) paramData; + streaminfoparam->sessionId = (OMX_U8)m_session_id; + break; + } + + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmparam = + (OMX_AUDIO_PARAM_PCMMODETYPE *) paramData; + + if (OMX_CORE_INPUT_PORT_INDEX== pcmparam->nPortIndex) + { + memcpy(pcmparam,&m_pcm_param,\ + sizeof(OMX_AUDIO_PARAM_PCMMODETYPE)); + DEBUG_PRINT("get_parameter: Sampling rate %u",\ + pcmparam->nSamplingRate); + DEBUG_PRINT("get_parameter: Number of channels %u",\ + pcmparam->nChannels); + } else + { + DEBUG_PRINT_ERROR("get_parameter:OMX_IndexParamAudioPcm "\ + "OMX_ErrorBadPortIndex %d\n", \ + (int)pcmparam->nPortIndex); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + case OMX_IndexParamComponentSuspended: + { + OMX_PARAM_SUSPENSIONTYPE *suspend = + (OMX_PARAM_SUSPENSIONTYPE *) paramData; + DEBUG_PRINT("get_parameter: OMX_IndexParamComponentSuspended %p\n", + suspend); + break; + } + case OMX_IndexParamVideoInit: + { + OMX_PORT_PARAM_TYPE *portParamType = + (OMX_PORT_PARAM_TYPE *) paramData; + DEBUG_PRINT("get_parameter: OMX_IndexParamVideoInit\n"); + portParamType->nVersion.nVersion = OMX_SPEC_VERSION; + portParamType->nSize = (OMX_U32)sizeof(portParamType); + portParamType->nPorts = 0; + portParamType->nStartPortNumber = 0; + break; + } + case OMX_IndexParamPriorityMgmt: + { + OMX_PRIORITYMGMTTYPE *priorityMgmtType = + (OMX_PRIORITYMGMTTYPE*)paramData; + DEBUG_PRINT("get_parameter: OMX_IndexParamPriorityMgmt\n"); + priorityMgmtType->nSize = (OMX_U32)sizeof(priorityMgmtType); + priorityMgmtType->nVersion.nVersion = OMX_SPEC_VERSION; + priorityMgmtType->nGroupID = m_priority_mgm.nGroupID; + priorityMgmtType->nGroupPriority = + m_priority_mgm.nGroupPriority; + break; + } + case OMX_IndexParamImageInit: + { + OMX_PORT_PARAM_TYPE *portParamType = + (OMX_PORT_PARAM_TYPE *) paramData; + DEBUG_PRINT("get_parameter: OMX_IndexParamImageInit\n"); + portParamType->nVersion.nVersion = OMX_SPEC_VERSION; + portParamType->nSize = (OMX_U32)sizeof(portParamType); + portParamType->nPorts = 0; + portParamType->nStartPortNumber = 0; + break; + } + + case OMX_IndexParamCompBufferSupplier: + { + DEBUG_PRINT("get_parameter: \ + OMX_IndexParamCompBufferSupplier\n"); + OMX_PARAM_BUFFERSUPPLIERTYPE *bufferSupplierType + = (OMX_PARAM_BUFFERSUPPLIERTYPE*) paramData; + DEBUG_PRINT("get_parameter: \ + OMX_IndexParamCompBufferSupplier\n"); + + bufferSupplierType->nSize = (OMX_U32)sizeof(bufferSupplierType); + bufferSupplierType->nVersion.nVersion = OMX_SPEC_VERSION; + if (OMX_CORE_INPUT_PORT_INDEX == + bufferSupplierType->nPortIndex) + { + bufferSupplierType->nPortIndex = + OMX_BufferSupplyUnspecified; + } else if (OMX_CORE_OUTPUT_PORT_INDEX == + bufferSupplierType->nPortIndex) + { + bufferSupplierType->nPortIndex = + OMX_BufferSupplyUnspecified; + } else + { + DEBUG_PRINT_ERROR("get_parameter:"\ + "OMX_IndexParamCompBufferSupplier eRet"\ + "%08x\n", eRet); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + + /*Component should support this port definition*/ + case OMX_IndexParamOtherInit: + { + OMX_PORT_PARAM_TYPE *portParamType = + (OMX_PORT_PARAM_TYPE *) paramData; + DEBUG_PRINT("get_parameter: OMX_IndexParamOtherInit\n"); + portParamType->nVersion.nVersion = OMX_SPEC_VERSION; + portParamType->nSize = (OMX_U32)sizeof(portParamType); + portParamType->nPorts = 0; + portParamType->nStartPortNumber = 0; + break; + } + case OMX_IndexParamStandardComponentRole: + { + OMX_PARAM_COMPONENTROLETYPE *componentRole; + componentRole = (OMX_PARAM_COMPONENTROLETYPE*)paramData; + componentRole->nSize = component_Role.nSize; + componentRole->nVersion = component_Role.nVersion; + strlcpy((char *)componentRole->cRole, + (const char*)component_Role.cRole, + sizeof(componentRole->cRole)); + DEBUG_PRINT_ERROR("nSize = %d , nVersion = %d, cRole = %s\n", + component_Role.nSize, + component_Role.nVersion, + component_Role.cRole); + break; + + } + default: + { + DEBUG_PRINT_ERROR("unknown param %08x\n", paramIndex); + eRet = OMX_ErrorUnsupportedIndex; + } + } + return eRet; + +} + +/** + @brief member function that set paramter from IL client + + @param hComp handle to component instance + @param paramIndex parameter type + @param paramData pointer to memory space which holds the paramter + @return error status + */ +OMX_ERRORTYPE omx_evrc_aenc::set_parameter(OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_INDEXTYPE paramIndex, + OMX_IN OMX_PTR paramData) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_state != OMX_StateLoaded) + { + DEBUG_PRINT_ERROR("set_parameter is not in proper state\n"); + return OMX_ErrorIncorrectStateOperation; + } + if (paramData == NULL) + { + DEBUG_PRINT("param data is NULL"); + return OMX_ErrorBadParameter; + } + + switch (paramIndex) + { + case OMX_IndexParamAudioEvrc: + { + DEBUG_PRINT("OMX_IndexParamAudioEvrc"); + OMX_AUDIO_PARAM_AMRTYPE *evrcparam + = (OMX_AUDIO_PARAM_AMRTYPE *) paramData; + memcpy(&m_evrc_param,evrcparam, + sizeof(OMX_AUDIO_PARAM_EVRCTYPE)); + break; + } + case OMX_IndexParamPortDefinition: + { + OMX_PARAM_PORTDEFINITIONTYPE *portDefn; + portDefn = (OMX_PARAM_PORTDEFINITIONTYPE *) paramData; + + if (((m_state == OMX_StateLoaded)&& + !BITMASK_PRESENT(&m_flags,OMX_COMPONENT_IDLE_PENDING)) + || (m_state == OMX_StateWaitForResources && + ((OMX_DirInput == portDefn->eDir && + m_inp_bEnabled == true)|| + (OMX_DirInput == portDefn->eDir && + m_out_bEnabled == true))) + ||(((OMX_DirInput == portDefn->eDir && + m_inp_bEnabled == false)|| + (OMX_DirInput == portDefn->eDir && + m_out_bEnabled == false)) && + (m_state != OMX_StateWaitForResources))) + { + DEBUG_PRINT("Set Parameter called in valid state\n"); + } else + { + DEBUG_PRINT_ERROR("Set Parameter called in \ + Invalid State\n"); + return OMX_ErrorIncorrectStateOperation; + } + DEBUG_PRINT("OMX_IndexParamPortDefinition portDefn->nPortIndex " + "= %u\n",portDefn->nPortIndex); + if (OMX_CORE_INPUT_PORT_INDEX == portDefn->nPortIndex) + { + if ( portDefn->nBufferCountActual > + OMX_CORE_NUM_INPUT_BUFFERS ) + { + m_inp_act_buf_count = portDefn->nBufferCountActual; + } else + { + m_inp_act_buf_count =OMX_CORE_NUM_INPUT_BUFFERS; + } + input_buffer_size = portDefn->nBufferSize; + + } else if (OMX_CORE_OUTPUT_PORT_INDEX == portDefn->nPortIndex) + { + if ( portDefn->nBufferCountActual > + OMX_CORE_NUM_OUTPUT_BUFFERS ) + { + m_out_act_buf_count = portDefn->nBufferCountActual; + } else + { + m_out_act_buf_count =OMX_CORE_NUM_OUTPUT_BUFFERS; + } + output_buffer_size = portDefn->nBufferSize; + } else + { + DEBUG_PRINT(" set_parameter: Bad Port idx %d",\ + (int)portDefn->nPortIndex); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + case OMX_IndexParamPriorityMgmt: + { + DEBUG_PRINT("set_parameter: OMX_IndexParamPriorityMgmt\n"); + + if (m_state != OMX_StateLoaded) + { + DEBUG_PRINT_ERROR("Set Parameter called in \ + Invalid State\n"); + return OMX_ErrorIncorrectStateOperation; + } + OMX_PRIORITYMGMTTYPE *priorityMgmtype + = (OMX_PRIORITYMGMTTYPE*) paramData; + DEBUG_PRINT("set_parameter: OMX_IndexParamPriorityMgmt %u\n", + priorityMgmtype->nGroupID); + + DEBUG_PRINT("set_parameter: priorityMgmtype %u\n", + priorityMgmtype->nGroupPriority); + + m_priority_mgm.nGroupID = priorityMgmtype->nGroupID; + m_priority_mgm.nGroupPriority = priorityMgmtype->nGroupPriority; + + break; + } + case OMX_IndexParamAudioPortFormat: + { + + OMX_AUDIO_PARAM_PORTFORMATTYPE *portFormatType = + (OMX_AUDIO_PARAM_PORTFORMATTYPE *) paramData; + DEBUG_PRINT("set_parameter: OMX_IndexParamAudioPortFormat\n"); + + if (OMX_CORE_INPUT_PORT_INDEX== portFormatType->nPortIndex) + { + portFormatType->eEncoding = OMX_AUDIO_CodingPCM; + } else if (OMX_CORE_OUTPUT_PORT_INDEX == + portFormatType->nPortIndex) + { + DEBUG_PRINT("set_parameter: OMX_IndexParamAudioFormat:"\ + " %u\n", portFormatType->nIndex); + portFormatType->eEncoding = OMX_AUDIO_CodingEVRC; + } else + { + DEBUG_PRINT_ERROR("set_parameter: Bad port index %d\n", \ + (int)portFormatType->nPortIndex); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + + + case OMX_IndexParamCompBufferSupplier: + { + DEBUG_PRINT("set_parameter: \ + OMX_IndexParamCompBufferSupplier\n"); + OMX_PARAM_BUFFERSUPPLIERTYPE *bufferSupplierType + = (OMX_PARAM_BUFFERSUPPLIERTYPE*) paramData; + DEBUG_PRINT("set_param: OMX_IndexParamCompBufferSupplier %d",\ + bufferSupplierType->eBufferSupplier); + + if (bufferSupplierType->nPortIndex == OMX_CORE_INPUT_PORT_INDEX + || bufferSupplierType->nPortIndex == + OMX_CORE_OUTPUT_PORT_INDEX) + { + DEBUG_PRINT("set_parameter:\ + OMX_IndexParamCompBufferSupplier\n"); + m_buffer_supplier.eBufferSupplier = + bufferSupplierType->eBufferSupplier; + } else + { + DEBUG_PRINT_ERROR("set_param:\ + IndexParamCompBufferSup %08x\n", eRet); + eRet = OMX_ErrorBadPortIndex; + } + + break; } + + case OMX_IndexParamAudioPcm: + { + DEBUG_PRINT("set_parameter: OMX_IndexParamAudioPcm\n"); + OMX_AUDIO_PARAM_PCMMODETYPE *pcmparam + = (OMX_AUDIO_PARAM_PCMMODETYPE *) paramData; + + if (OMX_CORE_INPUT_PORT_INDEX== pcmparam->nPortIndex) + { + memcpy(&m_pcm_param,pcmparam,\ + sizeof(OMX_AUDIO_PARAM_PCMMODETYPE)); + DEBUG_PRINT("set_pcm_parameter: %u %u",\ + m_pcm_param.nChannels, + m_pcm_param.nSamplingRate); + } else + { + DEBUG_PRINT_ERROR("Set_parameter:OMX_IndexParamAudioPcm " + "OMX_ErrorBadPortIndex %d\n", + (int)pcmparam->nPortIndex); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + case OMX_IndexParamSuspensionPolicy: + { + eRet = OMX_ErrorNotImplemented; + break; + } + case OMX_IndexParamStandardComponentRole: + { + OMX_PARAM_COMPONENTROLETYPE *componentRole; + componentRole = (OMX_PARAM_COMPONENTROLETYPE*)paramData; + component_Role.nSize = componentRole->nSize; + component_Role.nVersion = componentRole->nVersion; + strlcpy((char *)component_Role.cRole, + (const char*)componentRole->cRole, + sizeof(component_Role.cRole)); + break; + } + + default: + { + DEBUG_PRINT_ERROR("unknown param %d\n", paramIndex); + eRet = OMX_ErrorUnsupportedIndex; + } + } + return eRet; +} + +/* ====================================================================== +FUNCTION + omx_evrc_aenc::GetConfig + +DESCRIPTION + OMX Get Config Method implementation. + +PARAMETERS + . + +RETURN VALUE + OMX Error None if successful. + +========================================================================== */ +OMX_ERRORTYPE omx_evrc_aenc::get_config(OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_INDEXTYPE configIndex, + OMX_INOUT OMX_PTR configData) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_state == OMX_StateInvalid) + { + DEBUG_PRINT_ERROR("Get Config in Invalid State\n"); + return OMX_ErrorInvalidState; + } + + switch (configIndex) + { + case OMX_IndexConfigAudioVolume: + { + OMX_AUDIO_CONFIG_VOLUMETYPE *volume = + (OMX_AUDIO_CONFIG_VOLUMETYPE*) configData; + + if (OMX_CORE_INPUT_PORT_INDEX == volume->nPortIndex) + { + volume->nSize = (OMX_U32)sizeof(volume); + volume->nVersion.nVersion = OMX_SPEC_VERSION; + volume->bLinear = OMX_TRUE; + volume->sVolume.nValue = m_volume; + volume->sVolume.nMax = OMX_AENC_MAX; + volume->sVolume.nMin = OMX_AENC_MIN; + } else + { + eRet = OMX_ErrorBadPortIndex; + } + } + break; + + case OMX_IndexConfigAudioMute: + { + OMX_AUDIO_CONFIG_MUTETYPE *mute = + (OMX_AUDIO_CONFIG_MUTETYPE*) configData; + + if (OMX_CORE_INPUT_PORT_INDEX == mute->nPortIndex) + { + mute->nSize = (OMX_U32)sizeof(mute); + mute->nVersion.nVersion = OMX_SPEC_VERSION; + mute->bMute = (BITMASK_PRESENT(&m_flags, + OMX_COMPONENT_MUTED)?OMX_TRUE:OMX_FALSE); + } else + { + eRet = OMX_ErrorBadPortIndex; + } + } + break; + + default: + eRet = OMX_ErrorUnsupportedIndex; + break; + } + return eRet; +} + +/* ====================================================================== +FUNCTION + omx_evrc_aenc::SetConfig + +DESCRIPTION + OMX Set Config method implementation + +PARAMETERS + . + +RETURN VALUE + OMX Error None if successful. +========================================================================== */ +OMX_ERRORTYPE omx_evrc_aenc::set_config(OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_INDEXTYPE configIndex, + OMX_IN OMX_PTR configData) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_state == OMX_StateInvalid) + { + DEBUG_PRINT_ERROR("Set Config in Invalid State\n"); + return OMX_ErrorInvalidState; + } + if ( m_state == OMX_StateExecuting) + { + DEBUG_PRINT_ERROR("set_config:Ignore in Exe state\n"); + return OMX_ErrorInvalidState; + } + + switch (configIndex) + { + case OMX_IndexConfigAudioVolume: + { + OMX_AUDIO_CONFIG_VOLUMETYPE *vol = + (OMX_AUDIO_CONFIG_VOLUMETYPE*)configData; + if (vol->nPortIndex == OMX_CORE_INPUT_PORT_INDEX) + { + if ((vol->sVolume.nValue <= OMX_AENC_MAX) && + (vol->sVolume.nValue >= OMX_AENC_MIN)) + { + m_volume = vol->sVolume.nValue; + if (BITMASK_ABSENT(&m_flags, OMX_COMPONENT_MUTED)) + { + /* ioctl(m_drv_fd, AUDIO_VOLUME, + m_volume * OMX_AENC_VOLUME_STEP); */ + } + + } else + { + eRet = OMX_ErrorBadParameter; + } + } else + { + eRet = OMX_ErrorBadPortIndex; + } + } + break; + + case OMX_IndexConfigAudioMute: + { + OMX_AUDIO_CONFIG_MUTETYPE *mute = (OMX_AUDIO_CONFIG_MUTETYPE*) + configData; + if (mute->nPortIndex == OMX_CORE_INPUT_PORT_INDEX) + { + if (mute->bMute == OMX_TRUE) + { + BITMASK_SET(&m_flags, OMX_COMPONENT_MUTED); + /* ioctl(m_drv_fd, AUDIO_VOLUME, 0); */ + } else + { + BITMASK_CLEAR(&m_flags, OMX_COMPONENT_MUTED); + /* ioctl(m_drv_fd, AUDIO_VOLUME, + m_volume * OMX_AENC_VOLUME_STEP); */ + } + } else + { + eRet = OMX_ErrorBadPortIndex; + } + } + break; + + default: + eRet = OMX_ErrorUnsupportedIndex; + break; + } + return eRet; +} + +/* ====================================================================== +FUNCTION + omx_evrc_aenc::GetExtensionIndex + +DESCRIPTION + OMX GetExtensionIndex method implementaion. + +PARAMETERS + . + +RETURN VALUE + OMX Error None if everything successful. + +========================================================================== */ +OMX_ERRORTYPE omx_evrc_aenc::get_extension_index( + OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_STRING paramName, + OMX_OUT OMX_INDEXTYPE* indexType) +{ + if((hComp == NULL) || (paramName == NULL) || (indexType == NULL)) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_state == OMX_StateInvalid) + { + DEBUG_PRINT_ERROR("Get Extension Index in Invalid State\n"); + return OMX_ErrorInvalidState; + } + if(strncmp(paramName,"OMX.Qualcomm.index.audio.sessionId", + strlen("OMX.Qualcomm.index.audio.sessionId")) == 0) + { + *indexType =(OMX_INDEXTYPE)QOMX_IndexParamAudioSessionId; + DEBUG_PRINT("Extension index type - %d\n", *indexType); + + } + else + { + return OMX_ErrorBadParameter; + + } + return OMX_ErrorNone; +} + +/* ====================================================================== +FUNCTION + omx_evrc_aenc::GetState + +DESCRIPTION + Returns the state information back to the caller. + +PARAMETERS + . + +RETURN VALUE + Error None if everything is successful. +========================================================================== */ +OMX_ERRORTYPE omx_evrc_aenc::get_state(OMX_IN OMX_HANDLETYPE hComp, + OMX_OUT OMX_STATETYPE* state) +{ + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + *state = m_state; + DEBUG_PRINT("Returning the state %d\n",*state); + return OMX_ErrorNone; +} + +/* ====================================================================== +FUNCTION + omx_evrc_aenc::ComponentTunnelRequest + +DESCRIPTION + OMX Component Tunnel Request method implementation. + +PARAMETERS + None. + +RETURN VALUE + OMX Error None if everything successful. + +========================================================================== */ +OMX_ERRORTYPE omx_evrc_aenc::component_tunnel_request +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_U32 port, + OMX_IN OMX_HANDLETYPE peerComponent, + OMX_IN OMX_U32 peerPort, + OMX_INOUT OMX_TUNNELSETUPTYPE* tunnelSetup) +{ + DEBUG_PRINT_ERROR("Error: component_tunnel_request Not Implemented\n"); + + if((hComp == NULL) || (peerComponent == NULL) || (tunnelSetup == NULL)) + { + port = port; + peerPort = peerPort; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + return OMX_ErrorNotImplemented; +} + +/* ====================================================================== +FUNCTION + omx_evrc_aenc::AllocateInputBuffer + +DESCRIPTION + Helper function for allocate buffer in the input pin + +PARAMETERS + None. + +RETURN VALUE + true/false + +========================================================================== */ +OMX_ERRORTYPE omx_evrc_aenc::allocate_input_buffer +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE** bufferHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + OMX_BUFFERHEADERTYPE *bufHdr; + unsigned nBufSize = MAX(bytes, input_buffer_size); + char *buf_ptr; + if(m_inp_current_buf_count < m_inp_act_buf_count) + { + buf_ptr = (char *) calloc((nBufSize + \ + sizeof(OMX_BUFFERHEADERTYPE)+sizeof(META_IN)) , 1); + + if(hComp == NULL) + { + port = port; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + free(buf_ptr); + return OMX_ErrorBadParameter; + } + if (buf_ptr != NULL) + { + bufHdr = (OMX_BUFFERHEADERTYPE *) buf_ptr; + *bufferHdr = bufHdr; + memset(bufHdr,0,sizeof(OMX_BUFFERHEADERTYPE)); + + bufHdr->pBuffer = (OMX_U8 *)((buf_ptr) + sizeof(META_IN)+ + sizeof(OMX_BUFFERHEADERTYPE)); + bufHdr->nSize = (OMX_U32)sizeof(OMX_BUFFERHEADERTYPE); + bufHdr->nVersion.nVersion = OMX_SPEC_VERSION; + bufHdr->nAllocLen = nBufSize; + bufHdr->pAppPrivate = appData; + bufHdr->nInputPortIndex = OMX_CORE_INPUT_PORT_INDEX; + m_input_buf_hdrs.insert(bufHdr, NULL); + + m_inp_current_buf_count++; + DEBUG_PRINT("AIB:bufHdr %p bufHdr->pBuffer %p m_inp_buf_cnt=%u \ + bytes=%u", bufHdr, bufHdr->pBuffer, + m_inp_current_buf_count, bytes); + + } else + { + DEBUG_PRINT("Input buffer memory allocation failed 1 \n"); + eRet = OMX_ErrorInsufficientResources; + } + } + else + { + DEBUG_PRINT("Input buffer memory allocation failed 2\n"); + eRet = OMX_ErrorInsufficientResources; + } + return eRet; +} + +OMX_ERRORTYPE omx_evrc_aenc::allocate_output_buffer +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE** bufferHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + OMX_BUFFERHEADERTYPE *bufHdr; + unsigned nBufSize = MAX(bytes,output_buffer_size); + char *buf_ptr; + + if(hComp == NULL) + { + port = port; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_out_current_buf_count < m_out_act_buf_count) + { + buf_ptr = (char *) calloc( (nBufSize + sizeof(OMX_BUFFERHEADERTYPE)),1); + + if (buf_ptr != NULL) + { + bufHdr = (OMX_BUFFERHEADERTYPE *) buf_ptr; + *bufferHdr = bufHdr; + memset(bufHdr,0,sizeof(OMX_BUFFERHEADERTYPE)); + + bufHdr->pBuffer = (OMX_U8 *)((buf_ptr) + + sizeof(OMX_BUFFERHEADERTYPE)); + bufHdr->nSize = (OMX_U32)sizeof(OMX_BUFFERHEADERTYPE); + bufHdr->nVersion.nVersion = OMX_SPEC_VERSION; + bufHdr->nAllocLen = nBufSize; + bufHdr->pAppPrivate = appData; + bufHdr->nOutputPortIndex = OMX_CORE_OUTPUT_PORT_INDEX; + m_output_buf_hdrs.insert(bufHdr, NULL); + m_out_current_buf_count++; + DEBUG_PRINT("AOB::bufHdr %p bufHdr->pBuffer %p m_out_buf_cnt=%d "\ + "bytes=%u",bufHdr, bufHdr->pBuffer,\ + m_out_current_buf_count, bytes); + } else + { + DEBUG_PRINT("Output buffer memory allocation failed 1 \n"); + eRet = OMX_ErrorInsufficientResources; + } + } else + { + DEBUG_PRINT("Output buffer memory allocation failed\n"); + eRet = OMX_ErrorInsufficientResources; + } + return eRet; +} + + +// AllocateBuffer -- API Call +/* ====================================================================== +FUNCTION + omx_evrc_aenc::AllocateBuffer + +DESCRIPTION + Returns zero if all the buffers released.. + +PARAMETERS + None. + +RETURN VALUE + true/false + +========================================================================== */ +OMX_ERRORTYPE omx_evrc_aenc::allocate_buffer +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE** bufferHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes) +{ + + OMX_ERRORTYPE eRet = OMX_ErrorNone; // OMX return type + + if (m_state == OMX_StateInvalid) + { + DEBUG_PRINT_ERROR("Allocate Buf in Invalid State\n"); + return OMX_ErrorInvalidState; + } + // What if the client calls again. + if (OMX_CORE_INPUT_PORT_INDEX == port) + { + eRet = allocate_input_buffer(hComp,bufferHdr,port,appData,bytes); + } else if (OMX_CORE_OUTPUT_PORT_INDEX == port) + { + eRet = allocate_output_buffer(hComp,bufferHdr,port,appData,bytes); + } else + { + DEBUG_PRINT_ERROR("Error: Invalid Port Index received %d\n", + (int)port); + eRet = OMX_ErrorBadPortIndex; + } + + if (eRet == OMX_ErrorNone) + { + DEBUG_PRINT("allocate_buffer: before allocate_done \n"); + if (allocate_done()) + { + DEBUG_PRINT("allocate_buffer: after allocate_done \n"); + if (BITMASK_PRESENT(&m_flags,OMX_COMPONENT_IDLE_PENDING)) + { + BITMASK_CLEAR(&m_flags, OMX_COMPONENT_IDLE_PENDING); + post_command(OMX_CommandStateSet,OMX_StateIdle, + OMX_COMPONENT_GENERATE_EVENT); + DEBUG_PRINT("allocate_buffer: post idle transition event \n"); + } + DEBUG_PRINT("allocate_buffer: complete \n"); + } + if (port == OMX_CORE_INPUT_PORT_INDEX && m_inp_bPopulated) + { + if (BITMASK_PRESENT(&m_flags,OMX_COMPONENT_INPUT_ENABLE_PENDING)) + { + BITMASK_CLEAR((&m_flags),OMX_COMPONENT_INPUT_ENABLE_PENDING); + post_command(OMX_CommandPortEnable, OMX_CORE_INPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + } + } + if (port == OMX_CORE_OUTPUT_PORT_INDEX && m_out_bPopulated) + { + if (BITMASK_PRESENT(&m_flags,OMX_COMPONENT_OUTPUT_ENABLE_PENDING)) + { + BITMASK_CLEAR((&m_flags),OMX_COMPONENT_OUTPUT_ENABLE_PENDING); + m_out_bEnabled = OMX_TRUE; + + DEBUG_PRINT("AllocBuf-->is_out_th_sleep=%d\n",is_out_th_sleep); + pthread_mutex_lock(&m_out_th_lock_1); + if (is_out_th_sleep) + { + is_out_th_sleep = false; + DEBUG_DETAIL("AllocBuf:WAKING UP OUT THREADS\n"); + out_th_wakeup(); + } + pthread_mutex_unlock(&m_out_th_lock_1); + pthread_mutex_lock(&m_in_th_lock_1); + if(is_in_th_sleep) + { + is_in_th_sleep = false; + DEBUG_DETAIL("AB:WAKING UP IN THREADS\n"); + in_th_wakeup(); + } + pthread_mutex_unlock(&m_in_th_lock_1); + post_command(OMX_CommandPortEnable, OMX_CORE_OUTPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + } + } + } + DEBUG_PRINT("Allocate Buffer exit with ret Code %d\n", eRet); + return eRet; +} + +/*============================================================================= +FUNCTION: + use_buffer + +DESCRIPTION: + OMX Use Buffer method implementation. + +INPUT/OUTPUT PARAMETERS: + [INOUT] bufferHdr + [IN] hComp + [IN] port + [IN] appData + [IN] bytes + [IN] buffer + +RETURN VALUE: + OMX_ERRORTYPE + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +OMX_ERRORTYPE omx_evrc_aenc::use_buffer +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE** bufferHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes, + OMX_IN OMX_U8* buffer) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + + if (OMX_CORE_INPUT_PORT_INDEX == port) + { + eRet = use_input_buffer(hComp,bufferHdr,port,appData,bytes,buffer); + + } else if (OMX_CORE_OUTPUT_PORT_INDEX == port) + { + eRet = use_output_buffer(hComp,bufferHdr,port,appData,bytes,buffer); + } else + { + DEBUG_PRINT_ERROR("Error: Invalid Port Index received %d\n",(int)port); + eRet = OMX_ErrorBadPortIndex; + } + + if (eRet == OMX_ErrorNone) + { + DEBUG_PRINT("Checking for Output Allocate buffer Done"); + if (allocate_done()) + { + if (BITMASK_PRESENT(&m_flags,OMX_COMPONENT_IDLE_PENDING)) + { + BITMASK_CLEAR(&m_flags, OMX_COMPONENT_IDLE_PENDING); + post_command(OMX_CommandStateSet,OMX_StateIdle, + OMX_COMPONENT_GENERATE_EVENT); + } + } + if (port == OMX_CORE_INPUT_PORT_INDEX && m_inp_bPopulated) + { + if (BITMASK_PRESENT(&m_flags,OMX_COMPONENT_INPUT_ENABLE_PENDING)) + { + BITMASK_CLEAR((&m_flags),OMX_COMPONENT_INPUT_ENABLE_PENDING); + post_command(OMX_CommandPortEnable, OMX_CORE_INPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + + } + } + if (port == OMX_CORE_OUTPUT_PORT_INDEX && m_out_bPopulated) + { + if (BITMASK_PRESENT(&m_flags,OMX_COMPONENT_OUTPUT_ENABLE_PENDING)) + { + BITMASK_CLEAR((&m_flags),OMX_COMPONENT_OUTPUT_ENABLE_PENDING); + post_command(OMX_CommandPortEnable, OMX_CORE_OUTPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + pthread_mutex_lock(&m_out_th_lock_1); + if (is_out_th_sleep) + { + is_out_th_sleep = false; + DEBUG_DETAIL("UseBuf:WAKING UP OUT THREADS\n"); + out_th_wakeup(); + } + pthread_mutex_unlock(&m_out_th_lock_1); + pthread_mutex_lock(&m_in_th_lock_1); + if(is_in_th_sleep) + { + is_in_th_sleep = false; + DEBUG_DETAIL("UB:WAKING UP IN THREADS\n"); + in_th_wakeup(); + } + pthread_mutex_unlock(&m_in_th_lock_1); + } + } + } + DEBUG_PRINT("Use Buffer for port[%u] eRet[%d]\n", port,eRet); + return eRet; +} +/*============================================================================= +FUNCTION: + use_input_buffer + +DESCRIPTION: + Helper function for Use buffer in the input pin + +INPUT/OUTPUT PARAMETERS: + [INOUT] bufferHdr + [IN] hComp + [IN] port + [IN] appData + [IN] bytes + [IN] buffer + +RETURN VALUE: + OMX_ERRORTYPE + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +OMX_ERRORTYPE omx_evrc_aenc::use_input_buffer +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE** bufferHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes, + OMX_IN OMX_U8* buffer) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + OMX_BUFFERHEADERTYPE *bufHdr; + unsigned nBufSize = MAX(bytes, input_buffer_size); + char *buf_ptr; + + if(hComp == NULL) + { + port = port; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if(bytes < input_buffer_size) + { + /* return if i\p buffer size provided by client + is less than min i\p buffer size supported by omx component*/ + return OMX_ErrorInsufficientResources; + } + if (m_inp_current_buf_count < m_inp_act_buf_count) + { + buf_ptr = (char *) calloc(sizeof(OMX_BUFFERHEADERTYPE), 1); + + if (buf_ptr != NULL) + { + bufHdr = (OMX_BUFFERHEADERTYPE *) buf_ptr; + *bufferHdr = bufHdr; + memset(bufHdr,0,sizeof(OMX_BUFFERHEADERTYPE)); + + bufHdr->pBuffer = (OMX_U8 *)(buffer); + DEBUG_PRINT("use_input_buffer:bufHdr %p bufHdr->pBuffer %p \ + bytes=%u", bufHdr, bufHdr->pBuffer,bytes); + bufHdr->nSize = (OMX_U32)sizeof(OMX_BUFFERHEADERTYPE); + bufHdr->nVersion.nVersion = OMX_SPEC_VERSION; + bufHdr->nAllocLen = nBufSize; + input_buffer_size = nBufSize; + bufHdr->pAppPrivate = appData; + bufHdr->nInputPortIndex = OMX_CORE_INPUT_PORT_INDEX; + bufHdr->nOffset = 0; + m_input_buf_hdrs.insert(bufHdr, NULL); + m_inp_current_buf_count++; + } else + { + DEBUG_PRINT("Input buffer memory allocation failed 1 \n"); + eRet = OMX_ErrorInsufficientResources; + } + } else + { + DEBUG_PRINT("Input buffer memory allocation failed\n"); + eRet = OMX_ErrorInsufficientResources; + } + return eRet; +} + +/*============================================================================= +FUNCTION: + use_output_buffer + +DESCRIPTION: + Helper function for Use buffer in the output pin + +INPUT/OUTPUT PARAMETERS: + [INOUT] bufferHdr + [IN] hComp + [IN] port + [IN] appData + [IN] bytes + [IN] buffer + +RETURN VALUE: + OMX_ERRORTYPE + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +OMX_ERRORTYPE omx_evrc_aenc::use_output_buffer +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE** bufferHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes, + OMX_IN OMX_U8* buffer) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + OMX_BUFFERHEADERTYPE *bufHdr; + unsigned nBufSize = MAX(bytes,output_buffer_size); + char *buf_ptr; + + if(hComp == NULL) + { + port = port; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (bytes < output_buffer_size) + { + /* return if o\p buffer size provided by client + is less than min o\p buffer size supported by omx component*/ + return OMX_ErrorInsufficientResources; + } + + DEBUG_PRINT("Inside omx_evrc_aenc::use_output_buffer"); + if (m_out_current_buf_count < m_out_act_buf_count) + { + + buf_ptr = (char *) calloc(sizeof(OMX_BUFFERHEADERTYPE), 1); + + if (buf_ptr != NULL) + { + bufHdr = (OMX_BUFFERHEADERTYPE *) buf_ptr; + DEBUG_PRINT("BufHdr=%p buffer=%p\n",bufHdr,buffer); + *bufferHdr = bufHdr; + memset(bufHdr,0,sizeof(OMX_BUFFERHEADERTYPE)); + + bufHdr->pBuffer = (OMX_U8 *)(buffer); + DEBUG_PRINT("use_output_buffer:bufHdr %p bufHdr->pBuffer %p \ + len=%u\n", bufHdr, bufHdr->pBuffer,bytes); + bufHdr->nSize = (OMX_U32)sizeof(OMX_BUFFERHEADERTYPE); + bufHdr->nVersion.nVersion = OMX_SPEC_VERSION; + bufHdr->nAllocLen = nBufSize; + output_buffer_size = nBufSize; + bufHdr->pAppPrivate = appData; + bufHdr->nOutputPortIndex = OMX_CORE_OUTPUT_PORT_INDEX; + bufHdr->nOffset = 0; + m_output_buf_hdrs.insert(bufHdr, NULL); + m_out_current_buf_count++; + + } else + { + DEBUG_PRINT("Output buffer memory allocation failed\n"); + eRet = OMX_ErrorInsufficientResources; + } + } else + { + DEBUG_PRINT("Output buffer memory allocation failed 2\n"); + eRet = OMX_ErrorInsufficientResources; + } + return eRet; +} +/** + @brief member function that searches for caller buffer + + @param buffer pointer to buffer header + @return bool value indicating whether buffer is found + */ +bool omx_evrc_aenc::search_input_bufhdr(OMX_BUFFERHEADERTYPE *buffer) +{ + + bool eRet = false; + OMX_BUFFERHEADERTYPE *temp = NULL; + + //access only in IL client context + temp = m_input_buf_hdrs.find_ele(buffer); + if (buffer && temp) + { + DEBUG_DETAIL("search_input_bufhdr %p \n", buffer); + eRet = true; + } + return eRet; +} + +/** + @brief member function that searches for caller buffer + + @param buffer pointer to buffer header + @return bool value indicating whether buffer is found + */ +bool omx_evrc_aenc::search_output_bufhdr(OMX_BUFFERHEADERTYPE *buffer) +{ + + bool eRet = false; + OMX_BUFFERHEADERTYPE *temp = NULL; + + //access only in IL client context + temp = m_output_buf_hdrs.find_ele(buffer); + if (buffer && temp) + { + DEBUG_DETAIL("search_output_bufhdr %p \n", buffer); + eRet = true; + } + return eRet; +} + +// Free Buffer - API call +/** + @brief member function that handles free buffer command from IL client + + This function is a block-call function that handles IL client request to + freeing the buffer + + @param hComp handle to component instance + @param port id of port which holds the buffer + @param buffer buffer header + @return Error status +*/ +OMX_ERRORTYPE omx_evrc_aenc::free_buffer(OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_U32 port, + OMX_IN OMX_BUFFERHEADERTYPE* buffer) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + + DEBUG_PRINT("Free_Buffer buf %p\n", buffer); + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_state == OMX_StateIdle && + (BITMASK_PRESENT(&m_flags ,OMX_COMPONENT_LOADING_PENDING))) + { + DEBUG_PRINT(" free buffer while Component in Loading pending\n"); + } else if ((m_inp_bEnabled == OMX_FALSE && + port == OMX_CORE_INPUT_PORT_INDEX)|| + (m_out_bEnabled == OMX_FALSE && + port == OMX_CORE_OUTPUT_PORT_INDEX)) + { + DEBUG_PRINT("Free Buffer while port %u disabled\n", port); + } else if (m_state == OMX_StateExecuting || m_state == OMX_StatePause) + { + DEBUG_PRINT("Invalid state to free buffer,ports need to be disabled:\ + OMX_ErrorPortUnpopulated\n"); + post_command(OMX_EventError, + OMX_ErrorPortUnpopulated, + OMX_COMPONENT_GENERATE_EVENT); + + return eRet; + } else + { + DEBUG_PRINT("free_buffer: Invalid state to free buffer,ports need to be\ + disabled:OMX_ErrorPortUnpopulated\n"); + post_command(OMX_EventError, + OMX_ErrorPortUnpopulated, + OMX_COMPONENT_GENERATE_EVENT); + } + if (OMX_CORE_INPUT_PORT_INDEX == port) + { + if (m_inp_current_buf_count != 0) + { + m_inp_bPopulated = OMX_FALSE; + if (true == search_input_bufhdr(buffer)) + { + /* Buffer exist */ + //access only in IL client context + DEBUG_PRINT("Free_Buf:in_buffer[%p]\n",buffer); + m_input_buf_hdrs.erase(buffer); + free(buffer); + m_inp_current_buf_count--; + } else + { + DEBUG_PRINT_ERROR("Free_Buf:Error-->free_buffer, \ + Invalid Input buffer header\n"); + eRet = OMX_ErrorBadParameter; + } + } else + { + DEBUG_PRINT_ERROR("Error: free_buffer,Port Index calculation \ + came out Invalid\n"); + eRet = OMX_ErrorBadPortIndex; + } + if (BITMASK_PRESENT((&m_flags),OMX_COMPONENT_INPUT_DISABLE_PENDING) + && release_done(0)) + { + DEBUG_PRINT("INPUT PORT MOVING TO DISABLED STATE \n"); + BITMASK_CLEAR((&m_flags),OMX_COMPONENT_INPUT_DISABLE_PENDING); + post_command(OMX_CommandPortDisable, + OMX_CORE_INPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + } + } else if (OMX_CORE_OUTPUT_PORT_INDEX == port) + { + if (m_out_current_buf_count != 0) + { + m_out_bPopulated = OMX_FALSE; + if (true == search_output_bufhdr(buffer)) + { + /* Buffer exist */ + //access only in IL client context + DEBUG_PRINT("Free_Buf:out_buffer[%p]\n",buffer); + m_output_buf_hdrs.erase(buffer); + free(buffer); + m_out_current_buf_count--; + } else + { + DEBUG_PRINT("Free_Buf:Error-->free_buffer , \ + Invalid Output buffer header\n"); + eRet = OMX_ErrorBadParameter; + } + } else + { + eRet = OMX_ErrorBadPortIndex; + } + + if (BITMASK_PRESENT((&m_flags),OMX_COMPONENT_OUTPUT_DISABLE_PENDING) + && release_done(1)) + { + DEBUG_PRINT("OUTPUT PORT MOVING TO DISABLED STATE \n"); + BITMASK_CLEAR((&m_flags),OMX_COMPONENT_OUTPUT_DISABLE_PENDING); + post_command(OMX_CommandPortDisable, + OMX_CORE_OUTPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + + } + } else + { + eRet = OMX_ErrorBadPortIndex; + } + if ((OMX_ErrorNone == eRet) && + (BITMASK_PRESENT(&m_flags ,OMX_COMPONENT_LOADING_PENDING))) + { + if (release_done(-1)) + { + if(ioctl(m_drv_fd, AUDIO_STOP, 0) < 0) + DEBUG_PRINT_ERROR("AUDIO STOP in free buffer failed\n"); + else + DEBUG_PRINT("AUDIO STOP in free buffer passed\n"); + + + DEBUG_PRINT("Free_Buf: Free buffer\n"); + + + // Send the callback now + BITMASK_CLEAR((&m_flags),OMX_COMPONENT_LOADING_PENDING); + DEBUG_PRINT("Before OMX_StateLoaded \ + OMX_COMPONENT_GENERATE_EVENT\n"); + post_command(OMX_CommandStateSet, + OMX_StateLoaded,OMX_COMPONENT_GENERATE_EVENT); + DEBUG_PRINT("After OMX_StateLoaded OMX_COMPONENT_GENERATE_EVENT\n"); + + } + } + return eRet; +} + + +/** + @brief member function that that handles empty this buffer command + + This function meremly queue up the command and data would be consumed + in command server thread context + + @param hComp handle to component instance + @param buffer pointer to buffer header + @return error status + */ +OMX_ERRORTYPE omx_evrc_aenc::empty_this_buffer( + OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_BUFFERHEADERTYPE* buffer) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + + DEBUG_PRINT("ETB:Buf:%p Len %u TS %lld numInBuf=%d\n", \ + buffer, buffer->nFilledLen, buffer->nTimeStamp, (nNumInputBuf)); + if (m_state == OMX_StateInvalid) + { + DEBUG_PRINT("Empty this buffer in Invalid State\n"); + return OMX_ErrorInvalidState; + } + if (!m_inp_bEnabled) + { + DEBUG_PRINT("empty_this_buffer OMX_ErrorIncorrectStateOperation "\ + "Port Status %d \n", m_inp_bEnabled); + return OMX_ErrorIncorrectStateOperation; + } + if (buffer->nSize != sizeof(OMX_BUFFERHEADERTYPE)) + { + DEBUG_PRINT("omx_evrc_aenc::etb--> Buffer Size Invalid\n"); + return OMX_ErrorBadParameter; + } + if (buffer->nVersion.nVersion != OMX_SPEC_VERSION) + { + DEBUG_PRINT("omx_evrc_aenc::etb--> OMX Version Invalid\n"); + return OMX_ErrorVersionMismatch; + } + + if (buffer->nInputPortIndex != OMX_CORE_INPUT_PORT_INDEX) + { + return OMX_ErrorBadPortIndex; + } + if ((m_state != OMX_StateExecuting) && + (m_state != OMX_StatePause)) + { + DEBUG_PRINT_ERROR("Invalid state\n"); + eRet = OMX_ErrorInvalidState; + } + if (OMX_ErrorNone == eRet) + { + if (search_input_bufhdr(buffer) == true) + { + post_input((unsigned long)hComp, + (unsigned long) buffer,OMX_COMPONENT_GENERATE_ETB); + } else + { + DEBUG_PRINT_ERROR("Bad header %p \n", buffer); + eRet = OMX_ErrorBadParameter; + } + } + pthread_mutex_lock(&in_buf_count_lock); + nNumInputBuf++; + m_evrc_pb_stats.etb_cnt++; + pthread_mutex_unlock(&in_buf_count_lock); + return eRet; +} +/** + @brief member function that writes data to kernel driver + + @param hComp handle to component instance + @param buffer pointer to buffer header + @return error status + */ +OMX_ERRORTYPE omx_evrc_aenc::empty_this_buffer_proxy +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE* buffer) +{ + OMX_STATETYPE state; + META_IN meta_in; + //Pointer to the starting location of the data to be transcoded + OMX_U8 *srcStart; + //The total length of the data to be transcoded + srcStart = buffer->pBuffer; + OMX_U8 *data = NULL; + PrintFrameHdr(OMX_COMPONENT_GENERATE_ETB,buffer); + memset(&meta_in,0,sizeof(meta_in)); + if ( search_input_bufhdr(buffer) == false ) + { + DEBUG_PRINT("ETBP: INVALID BUF HDR\n"); + buffer_done_cb((OMX_BUFFERHEADERTYPE *)buffer); + return OMX_ErrorBadParameter; + } + if (m_tmp_meta_buf) + { + data = m_tmp_meta_buf; + + // copy the metadata info from the BufHdr and insert to payload + meta_in.offsetVal = (OMX_U16)sizeof(META_IN); + meta_in.nTimeStamp.LowPart = + (unsigned int) ((((OMX_BUFFERHEADERTYPE*)buffer)->nTimeStamp) & 0xFFFFFFFF); + meta_in.nTimeStamp.HighPart = + (unsigned int) (((((OMX_BUFFERHEADERTYPE*)buffer)->nTimeStamp) >> 32) & 0xFFFFFFFF); + meta_in.nFlags &= ~OMX_BUFFERFLAG_EOS; + if(buffer->nFlags & OMX_BUFFERFLAG_EOS) + { + DEBUG_PRINT("EOS OCCURED \n"); + meta_in.nFlags |= OMX_BUFFERFLAG_EOS; + } + memcpy(data,&meta_in, meta_in.offsetVal); + DEBUG_PRINT("meta_in.nFlags = %d\n",meta_in.nFlags); + } + + memcpy(&data[sizeof(META_IN)],buffer->pBuffer,buffer->nFilledLen); + write(m_drv_fd, data, buffer->nFilledLen+sizeof(META_IN)); + + pthread_mutex_lock(&m_state_lock); + get_state(&m_cmp, &state); + pthread_mutex_unlock(&m_state_lock); + + if (OMX_StateExecuting == state) + { + DEBUG_DETAIL("In Exe state, EBD CB"); + buffer_done_cb((OMX_BUFFERHEADERTYPE *)buffer); + } else + { + /* Assume empty this buffer function has already checked + validity of buffer */ + DEBUG_PRINT("Empty buffer %p to kernel driver\n", buffer); + post_input((unsigned long) & hComp,(unsigned long) buffer, + OMX_COMPONENT_GENERATE_BUFFER_DONE); + } + return OMX_ErrorNone; +} + +OMX_ERRORTYPE omx_evrc_aenc::fill_this_buffer_proxy +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE* buffer) +{ + OMX_STATETYPE state; + ENC_META_OUT *meta_out = NULL; + ssize_t nReadbytes = 0; + + pthread_mutex_lock(&m_state_lock); + get_state(&m_cmp, &state); + pthread_mutex_unlock(&m_state_lock); + + if (true == search_output_bufhdr(buffer)) + { + DEBUG_PRINT("\nBefore Read..m_drv_fd = %d,\n",m_drv_fd); + nReadbytes = read(m_drv_fd,buffer->pBuffer,output_buffer_size ); + DEBUG_DETAIL("FTBP->Al_len[%lu]buf[%p]size[%d]numOutBuf[%d]\n",\ + buffer->nAllocLen,buffer->pBuffer, + nReadbytes,nNumOutputBuf); + if (nReadbytes <= 0) { + buffer->nFilledLen = 0; + buffer->nOffset = 0; + buffer->nTimeStamp = nTimestamp; + frame_done_cb((OMX_BUFFERHEADERTYPE *)buffer); + return OMX_ErrorNone; + } else + DEBUG_PRINT("Read bytes %d\n",nReadbytes); + // Buffer from Driver will have + // 1 byte => Nr of frame field + // (sizeof(ENC_META_OUT) * Nr of frame) bytes => meta_out->offset_to_frame + // Frame Size * Nr of frame => + + meta_out = (ENC_META_OUT *)(buffer->pBuffer + sizeof(unsigned char)); + buffer->nTimeStamp = (((OMX_TICKS) meta_out->msw_ts << 32)+ + meta_out->lsw_ts); + buffer->nFlags |= meta_out->nflags; + buffer->nOffset = (OMX_U32)(meta_out->offset_to_frame + + sizeof(unsigned char)); + buffer->nFilledLen = (OMX_U32)(nReadbytes - buffer->nOffset); + nTimestamp = buffer->nTimeStamp; + DEBUG_PRINT("nflags %d frame_size %d offset_to_frame %d \ + timestamp %lld\n", meta_out->nflags, + meta_out->frame_size, meta_out->offset_to_frame, + buffer->nTimeStamp); + + if ((buffer->nFlags & OMX_BUFFERFLAG_EOS) == OMX_BUFFERFLAG_EOS ) + { + buffer->nFilledLen = 0; + buffer->nOffset = 0; + buffer->nTimeStamp = nTimestamp; + frame_done_cb((OMX_BUFFERHEADERTYPE *)buffer); + if ((buffer->nFlags & OMX_BUFFERFLAG_EOS) == OMX_BUFFERFLAG_EOS ) + { + DEBUG_PRINT("FTBP: Now, Send EOS flag to Client \n"); + m_cb.EventHandler(&m_cmp, + m_app_data, + OMX_EventBufferFlag, + 1, 1, NULL ); + } + + return OMX_ErrorNone; + } + DEBUG_PRINT("nState %d \n",nState ); + + pthread_mutex_lock(&m_state_lock); + get_state(&m_cmp, &state); + pthread_mutex_unlock(&m_state_lock); + + if (state == OMX_StatePause) + { + DEBUG_PRINT("FTBP:Post the FBD to event thread currstate=%d\n",\ + state); + post_output((unsigned long) & hComp,(unsigned long) buffer, + OMX_COMPONENT_GENERATE_FRAME_DONE); + } + else + { + frame_done_cb((OMX_BUFFERHEADERTYPE *)buffer); + + } + + } + else + DEBUG_PRINT("\n FTBP-->Invalid buffer in FTB \n"); + + + return OMX_ErrorNone; +} + +/* ====================================================================== +FUNCTION + omx_evrc_aenc::FillThisBuffer + +DESCRIPTION + IL client uses this method to release the frame buffer + after displaying them. + + + +PARAMETERS + + None. + +RETURN VALUE + true/false + +========================================================================== */ +OMX_ERRORTYPE omx_evrc_aenc::fill_this_buffer +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_BUFFERHEADERTYPE* buffer) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + if (buffer->nSize != sizeof(OMX_BUFFERHEADERTYPE)) + { + DEBUG_PRINT("omx_evrc_aenc::ftb--> Buffer Size Invalid\n"); + return OMX_ErrorBadParameter; + } + if (m_out_bEnabled == OMX_FALSE) + { + return OMX_ErrorIncorrectStateOperation; + } + + if (buffer->nVersion.nVersion != OMX_SPEC_VERSION) + { + DEBUG_PRINT("omx_evrc_aenc::ftb--> OMX Version Invalid\n"); + return OMX_ErrorVersionMismatch; + } + if (buffer->nOutputPortIndex != OMX_CORE_OUTPUT_PORT_INDEX) + { + return OMX_ErrorBadPortIndex; + } + pthread_mutex_lock(&out_buf_count_lock); + nNumOutputBuf++; + m_evrc_pb_stats.ftb_cnt++; + DEBUG_DETAIL("FTB:nNumOutputBuf is %d", nNumOutputBuf); + pthread_mutex_unlock(&out_buf_count_lock); + post_output((unsigned long)hComp, + (unsigned long) buffer,OMX_COMPONENT_GENERATE_FTB); + return eRet; +} + +/* ====================================================================== +FUNCTION + omx_evrc_aenc::SetCallbacks + +DESCRIPTION + Set the callbacks. + +PARAMETERS + None. + +RETURN VALUE + OMX Error None if everything successful. + +========================================================================== */ +OMX_ERRORTYPE omx_evrc_aenc::set_callbacks(OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_CALLBACKTYPE* callbacks, + OMX_IN OMX_PTR appData) +{ + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + m_cb = *callbacks; + m_app_data = appData; + + return OMX_ErrorNone; +} + +/* ====================================================================== +FUNCTION + omx_evrc_aenc::ComponentDeInit + +DESCRIPTION + Destroys the component and release memory allocated to the heap. + +PARAMETERS + . + +RETURN VALUE + OMX Error None if everything successful. + +========================================================================== */ +OMX_ERRORTYPE omx_evrc_aenc::component_deinit(OMX_IN OMX_HANDLETYPE hComp) +{ + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (OMX_StateLoaded != m_state && OMX_StateInvalid != m_state) + { + DEBUG_PRINT_ERROR("Warning: Rxed DeInit when not in LOADED state %d\n", + m_state); + } + deinit_encoder(); + +DEBUG_PRINT_ERROR("%s:COMPONENT DEINIT...\n", __FUNCTION__); + return OMX_ErrorNone; +} + +/* ====================================================================== +FUNCTION + omx_evrc_aenc::deinit_encoder + +DESCRIPTION + Closes all the threads and release memory allocated to the heap. + +PARAMETERS + None. + +RETURN VALUE + None. + +========================================================================== */ +void omx_evrc_aenc::deinit_encoder() +{ + DEBUG_PRINT("Component-deinit being processed\n"); + DEBUG_PRINT("********************************\n"); + DEBUG_PRINT("STATS: in-buf-len[%u]out-buf-len[%u] tot-pb-time[%lld]",\ + m_evrc_pb_stats.tot_in_buf_len, + m_evrc_pb_stats.tot_out_buf_len, + m_evrc_pb_stats.tot_pb_time); + DEBUG_PRINT("STATS: fbd-cnt[%u]ftb-cnt[%u]etb-cnt[%u]ebd-cnt[%u]",\ + m_evrc_pb_stats.fbd_cnt,m_evrc_pb_stats.ftb_cnt, + m_evrc_pb_stats.etb_cnt, + m_evrc_pb_stats.ebd_cnt); + memset(&m_evrc_pb_stats,0,sizeof(EVRC_PB_STATS)); + + if((OMX_StateLoaded != m_state) && (OMX_StateInvalid != m_state)) + { + DEBUG_PRINT_ERROR("%s,Deinit called in state[%d]\n",__FUNCTION__,\ + m_state); + // Get back any buffers from driver + if(pcm_input) + execute_omx_flush(-1,false); + else + execute_omx_flush(1,false); + // force state change to loaded so that all threads can be exited + pthread_mutex_lock(&m_state_lock); + m_state = OMX_StateLoaded; + pthread_mutex_unlock(&m_state_lock); + DEBUG_PRINT_ERROR("Freeing Buf:inp_current_buf_count[%d][%d]\n",\ + m_inp_current_buf_count, + m_input_buf_hdrs.size()); + m_input_buf_hdrs.eraseall(); + DEBUG_PRINT_ERROR("Freeing Buf:out_current_buf_count[%d][%d]\n",\ + m_out_current_buf_count, + m_output_buf_hdrs.size()); + m_output_buf_hdrs.eraseall(); + + } + if(pcm_input) + { + pthread_mutex_lock(&m_in_th_lock_1); + if (is_in_th_sleep) + { + is_in_th_sleep = false; + DEBUG_DETAIL("Deinit:WAKING UP IN THREADS\n"); + in_th_wakeup(); + } + pthread_mutex_unlock(&m_in_th_lock_1); + } + pthread_mutex_lock(&m_out_th_lock_1); + if (is_out_th_sleep) + { + is_out_th_sleep = false; + DEBUG_DETAIL("SCP:WAKING UP OUT THREADS\n"); + out_th_wakeup(); + } + pthread_mutex_unlock(&m_out_th_lock_1); + if(pcm_input) + { + if (m_ipc_to_in_th != NULL) + { + omx_evrc_thread_stop(m_ipc_to_in_th); + m_ipc_to_in_th = NULL; + } + } + + if (m_ipc_to_cmd_th != NULL) + { + omx_evrc_thread_stop(m_ipc_to_cmd_th); + m_ipc_to_cmd_th = NULL; + } + if (m_ipc_to_out_th != NULL) + { + DEBUG_DETAIL("Inside omx_evrc_thread_stop\n"); + omx_evrc_thread_stop(m_ipc_to_out_th); + m_ipc_to_out_th = NULL; + } + + + if(ioctl(m_drv_fd, AUDIO_STOP, 0) <0) + DEBUG_PRINT_ERROR("De-init: AUDIO_STOP FAILED\n"); + + if(pcm_input && m_tmp_meta_buf ) + { + free(m_tmp_meta_buf); + } + + if(m_tmp_out_meta_buf) + { + free(m_tmp_out_meta_buf); + } + nNumInputBuf = 0; + nNumOutputBuf = 0; + bFlushinprogress = 0; + + m_inp_current_buf_count=0; + m_out_current_buf_count=0; + m_out_act_buf_count = 0; + m_inp_act_buf_count = 0; + m_inp_bEnabled = OMX_FALSE; + m_out_bEnabled = OMX_FALSE; + m_inp_bPopulated = OMX_FALSE; + m_out_bPopulated = OMX_FALSE; + + if ( m_drv_fd >= 0 ) + { + if(close(m_drv_fd) < 0) + DEBUG_PRINT("De-init: Driver Close Failed \n"); + m_drv_fd = -1; + } + else + { + DEBUG_PRINT_ERROR(" EVRC device already closed\n"); + } + m_comp_deinit=1; + m_is_out_th_sleep = 1; + m_is_in_th_sleep = 1; + DEBUG_PRINT("************************************\n"); + DEBUG_PRINT(" DEINIT COMPLETED"); + DEBUG_PRINT("************************************\n"); + +} + +/* ====================================================================== +FUNCTION + omx_evrc_aenc::UseEGLImage + +DESCRIPTION + OMX Use EGL Image method implementation . + +PARAMETERS + . + +RETURN VALUE + Not Implemented error. + +========================================================================== */ +OMX_ERRORTYPE omx_evrc_aenc::use_EGL_image +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE** bufferHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN void* eglImage) +{ + DEBUG_PRINT_ERROR("Error : use_EGL_image: Not Implemented \n"); + + if((hComp == NULL) || (appData == NULL) || (eglImage == NULL)) + { + bufferHdr = bufferHdr; + port = port; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + return OMX_ErrorNotImplemented; +} + +/* ====================================================================== +FUNCTION + omx_evrc_aenc::ComponentRoleEnum + +DESCRIPTION + OMX Component Role Enum method implementation. + +PARAMETERS + . + +RETURN VALUE + OMX Error None if everything is successful. +========================================================================== */ +OMX_ERRORTYPE omx_evrc_aenc::component_role_enum(OMX_IN OMX_HANDLETYPE hComp, + OMX_OUT OMX_U8* role, + OMX_IN OMX_U32 index) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + const char *cmp_role = "audio_encoder.evrc"; + + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (index == 0 && role) + { + memcpy(role, cmp_role, strlen(cmp_role)); + *(((char *) role) + strlen(cmp_role) + 1) = '\0'; + } else + { + eRet = OMX_ErrorNoMore; + } + return eRet; +} + + + + +/* ====================================================================== +FUNCTION + omx_evrc_aenc::AllocateDone + +DESCRIPTION + Checks if entire buffer pool is allocated by IL Client or not. + Need this to move to IDLE state. + +PARAMETERS + None. + +RETURN VALUE + true/false. + +========================================================================== */ +bool omx_evrc_aenc::allocate_done(void) +{ + OMX_BOOL bRet = OMX_FALSE; + if (pcm_input==1) + { + if ((m_inp_act_buf_count == m_inp_current_buf_count) + &&(m_out_act_buf_count == m_out_current_buf_count)) + { + bRet=OMX_TRUE; + + } + if ((m_inp_act_buf_count == m_inp_current_buf_count) && m_inp_bEnabled ) + { + m_inp_bPopulated = OMX_TRUE; + } + + if ((m_out_act_buf_count == m_out_current_buf_count) && m_out_bEnabled ) + { + m_out_bPopulated = OMX_TRUE; + } + } else if (pcm_input==0) + { + if (m_out_act_buf_count == m_out_current_buf_count) + { + bRet=OMX_TRUE; + + } + if ((m_out_act_buf_count == m_out_current_buf_count) && m_out_bEnabled ) + { + m_out_bPopulated = OMX_TRUE; + } + + } + return bRet; +} + + +/* ====================================================================== +FUNCTION + omx_evrc_aenc::ReleaseDone + +DESCRIPTION + Checks if IL client has released all the buffers. + +PARAMETERS + None. + +RETURN VALUE + true/false + +========================================================================== */ +bool omx_evrc_aenc::release_done(OMX_U32 param1) +{ + DEBUG_PRINT("Inside omx_evrc_aenc::release_done"); + OMX_BOOL bRet = OMX_FALSE; + + if (param1 == OMX_ALL) + { + if ((0 == m_inp_current_buf_count)&&(0 == m_out_current_buf_count)) + { + bRet=OMX_TRUE; + } + } else if (param1 == OMX_CORE_INPUT_PORT_INDEX ) + { + if ((0 == m_inp_current_buf_count)) + { + bRet=OMX_TRUE; + } + } else if (param1 == OMX_CORE_OUTPUT_PORT_INDEX) + { + if ((0 == m_out_current_buf_count)) + { + bRet=OMX_TRUE; + } + } + return bRet; +} diff --git a/audio/mm-audio/aenc-evrc/qdsp6/test/omx_evrc_enc_test.c b/audio/mm-audio/aenc-evrc/qdsp6/test/omx_evrc_enc_test.c new file mode 100644 index 0000000..63d953b --- /dev/null +++ b/audio/mm-audio/aenc-evrc/qdsp6/test/omx_evrc_enc_test.c @@ -0,0 +1,1098 @@ + +/*-------------------------------------------------------------------------- +Copyright (c) 2010-2014, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of The Linux Foundation nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------*/ + + +/* + An Open max test application .... +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "OMX_Core.h" +#include "OMX_Component.h" +#include "pthread.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "QOMX_AudioExtensions.h" +#include "QOMX_AudioIndexExtensions.h" +#ifdef AUDIOV2 +#include "control.h" +#endif + + +#include + +typedef unsigned char uint8; +typedef unsigned char byte; +typedef unsigned int uint32; +typedef unsigned int uint16; +QOMX_AUDIO_STREAM_INFO_DATA streaminfoparam; +/* maximum ADTS frame header length */ +void Release_Encoder(); + +#ifdef AUDIOV2 +unsigned short session_id; +int device_id; +int control = 0; +const char *device="handset_tx"; +#define DIR_TX 2 +#endif + +uint32_t samplerate = 8000; +uint32_t channels = 1; +uint32_t min_bitrate = 0; +uint32_t max_bitrate = 0; +uint32_t cdmarate = 0; +uint32_t rectime = 0; +uint32_t recpath = 0; +int32_t pcmplayback = 0; +uint32_t tunnel = 0; +uint32_t format = 1; +#define DEBUG_PRINT printf +unsigned to_idle_transition = 0; +unsigned long total_pcm_bytes; + +/************************************************************************/ +/* GLOBAL INIT */ +/************************************************************************/ + +/************************************************************************/ +/* #DEFINES */ +/************************************************************************/ +#define false 0 +#define true 1 + +#define CONFIG_VERSION_SIZE(param) \ + param.nVersion.nVersion = CURRENT_OMX_SPEC_VERSION;\ + param.nSize = sizeof(param); + +#define QCP_HEADER_SIZE sizeof(struct qcp_header) +#define MIN_BITRATE 4 /* Bit rate 1 - 13.6 , 2 - 6.2 , 3 - 2.7 , 4 - 1.0 kbps*/ +#define MAX_BITRATE 4 + +#define FAILED(result) (result != OMX_ErrorNone) + +#define SUCCEEDED(result) (result == OMX_ErrorNone) + +/************************************************************************/ +/* GLOBAL DECLARATIONS */ +/************************************************************************/ + +pthread_mutex_t lock; +pthread_cond_t cond; +pthread_mutex_t elock; +pthread_cond_t econd; +pthread_cond_t fcond; +pthread_mutex_t etb_lock; +pthread_mutex_t etb_lock1; +pthread_cond_t etb_cond; +FILE * inputBufferFile; +FILE * outputBufferFile; +OMX_PARAM_PORTDEFINITIONTYPE inputportFmt; +OMX_PARAM_PORTDEFINITIONTYPE outputportFmt; +OMX_AUDIO_PARAM_EVRCTYPE evrcparam; +OMX_AUDIO_PARAM_PCMMODETYPE pcmparam; +OMX_PORT_PARAM_TYPE portParam; +OMX_PORT_PARAM_TYPE portFmt; +OMX_ERRORTYPE error; + + + + +#define ID_RIFF 0x46464952 +#define ID_WAVE 0x45564157 +#define ID_FMT 0x20746d66 +#define ID_DATA 0x61746164 + +#define FORMAT_PCM 1 + +struct wav_header { + uint32_t riff_id; + uint32_t riff_sz; + uint32_t riff_fmt; + uint32_t fmt_id; + uint32_t fmt_sz; + uint16_t audio_format; + uint16_t num_channels; + uint32_t sample_rate; + uint32_t byte_rate; /* sample_rate * num_channels * bps / 8 */ + uint16_t block_align; /* num_channels * bps / 8 */ + uint16_t bits_per_sample; + uint32_t data_id; + uint32_t data_sz; +}; +struct enc_meta_out{ + unsigned int offset_to_frame; + unsigned int frame_size; + unsigned int encoded_pcm_samples; + unsigned int msw_ts; + unsigned int lsw_ts; + unsigned int nflags; +} __attribute__ ((packed)); + +struct qcp_header { + /* RIFF Section */ + char riff[4]; + unsigned int s_riff; + char qlcm[4]; + + /* Format chunk */ + char fmt[4]; + unsigned int s_fmt; + char mjr; + char mnr; + unsigned int data1; /* UNIQUE ID of the codec */ + unsigned short data2; + unsigned short data3; + char data4[8]; + unsigned short ver; /* Codec Info */ + char name[80]; + unsigned short abps; /* average bits per sec of the codec */ + unsigned short bytes_per_pkt; + unsigned short samp_per_block; + unsigned short samp_per_sec; + unsigned short bits_per_samp; + unsigned char vr_num_of_rates; /* Rate Header fmt info */ + unsigned char rvd1[3]; + unsigned short vr_bytes_per_pkt[8]; + unsigned int rvd2[5]; + + /* Vrat chunk */ + unsigned char vrat[4]; + unsigned int s_vrat; + unsigned int v_rate; + unsigned int size_in_pkts; + + /* Data chunk */ + unsigned char data[4]; + unsigned int s_data; +} __attribute__ ((packed)); + + /* Common part */ + static struct qcp_header append_header = { + {'R', 'I', 'F', 'F'}, 0, {'Q', 'L', 'C', 'M'}, + {'f', 'm', 't', ' '}, 150, 1, 0, 0, 0, 0,{0}, 0, {0},0,0,160,8000,16,0,{0},{0},{0}, + {'v','r','a','t'},0, 0, 0,{'d','a','t','a'},0 + }; + +static int totaldatalen = 0; +static int framecnt = 0; +/************************************************************************/ +/* GLOBAL INIT */ +/************************************************************************/ + +unsigned int input_buf_cnt = 0; +unsigned int output_buf_cnt = 0; +int used_ip_buf_cnt = 0; +volatile int event_is_done = 0; +volatile int ebd_event_is_done = 0; +volatile int fbd_event_is_done = 0; +volatile int etb_event_is_done = 0; +int ebd_cnt; +int bInputEosReached = 0; +int bOutputEosReached = 0; +int bInputEosReached_tunnel = 0; +static int etb_done = 0; +int bFlushing = false; +int bPause = false; +const char *in_filename; +const char *out_filename; + +int timeStampLfile = 0; +int timestampInterval = 100; + +//* OMX Spec Version supported by the wrappers. Version = 1.1 */ +const OMX_U32 CURRENT_OMX_SPEC_VERSION = 0x00000101; +OMX_COMPONENTTYPE* evrc_enc_handle = 0; + +OMX_BUFFERHEADERTYPE **pInputBufHdrs = NULL; +OMX_BUFFERHEADERTYPE **pOutputBufHdrs = NULL; + +/************************************************************************/ +/* GLOBAL FUNC DECL */ +/************************************************************************/ +int Init_Encoder(char*); +int Play_Encoder(); +OMX_STRING aud_comp; +/**************************************************************************/ +/* STATIC DECLARATIONS */ +/**************************************************************************/ + +static int open_audio_file (); +static int Read_Buffer(OMX_BUFFERHEADERTYPE *pBufHdr ); +static OMX_ERRORTYPE Allocate_Buffer ( OMX_COMPONENTTYPE *evrc_enc_handle, + OMX_BUFFERHEADERTYPE ***pBufHdrs, + OMX_U32 nPortIndex, + unsigned int bufCntMin, unsigned int bufSize); + + +static OMX_ERRORTYPE EventHandler(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_EVENTTYPE eEvent, + OMX_IN OMX_U32 nData1, OMX_IN OMX_U32 nData2, + OMX_IN OMX_PTR pEventData); +static OMX_ERRORTYPE EmptyBufferDone(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer); + +static OMX_ERRORTYPE FillBufferDone(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer); +static OMX_ERRORTYPE parse_pcm_header(); +void wait_for_event(void) +{ + pthread_mutex_lock(&lock); + DEBUG_PRINT("%s: event_is_done=%d", __FUNCTION__, event_is_done); + while (event_is_done == 0) { + pthread_cond_wait(&cond, &lock); + } + event_is_done = 0; + pthread_mutex_unlock(&lock); +} + +void event_complete(void ) +{ + pthread_mutex_lock(&lock); + if (event_is_done == 0) { + event_is_done = 1; + pthread_cond_broadcast(&cond); + } + pthread_mutex_unlock(&lock); +} + +void etb_wait_for_event(void) +{ + pthread_mutex_lock(&etb_lock1); + DEBUG_PRINT("%s: etb_event_is_done=%d", __FUNCTION__, etb_event_is_done); + while (etb_event_is_done == 0) { + pthread_cond_wait(&etb_cond, &etb_lock1); + } + etb_event_is_done = 0; + pthread_mutex_unlock(&etb_lock1); +} + +void etb_event_complete(void ) +{ + pthread_mutex_lock(&etb_lock1); + if (etb_event_is_done == 0) { + etb_event_is_done = 1; + pthread_cond_broadcast(&etb_cond); + } + pthread_mutex_unlock(&etb_lock1); +} + +static void create_qcp_header(int Datasize, int Frames) +{ + append_header.s_riff = (unsigned)(Datasize + (int)QCP_HEADER_SIZE - 8); + /* exclude riff id and size field */ + append_header.data1 = 0xe689d48d; + append_header.data2 = 0x9076; + append_header.data3 = 0x46b5; + append_header.data4[0] = 0x91; + append_header.data4[1] = 0xef; + append_header.data4[2] = 0x73; + append_header.data4[3] = 0x6a; + append_header.data4[4] = 0x51; + append_header.data4[5] = 0x00; + append_header.data4[6] = 0xce; + append_header.data4[7] = 0xb4; + append_header.ver = 0x0001; + memcpy(append_header.name, "TIA IS-127 Enhanced Variable Rate Codec, Speech Service Option 3", 64); + append_header.abps = 9600; + append_header.bytes_per_pkt = 23; + append_header.vr_num_of_rates = 4; + append_header.vr_bytes_per_pkt[0] = 0x0416; + append_header.vr_bytes_per_pkt[1] = 0x030a; + append_header.vr_bytes_per_pkt[2] = 0x0200; + append_header.vr_bytes_per_pkt[3] = 0x0102; + append_header.s_vrat = 0x00000008; + append_header.v_rate = 0x00000001; + append_header.size_in_pkts = (unsigned)Frames; + append_header.s_data = (unsigned)Datasize; + return; +} + +OMX_ERRORTYPE EventHandler(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_EVENTTYPE eEvent, + OMX_IN OMX_U32 nData1, OMX_IN OMX_U32 nData2, + OMX_IN OMX_PTR pEventData) +{ + DEBUG_PRINT("Function %s \n", __FUNCTION__); + + /* To remove warning for unused variable to keep prototype same */ + (void)hComponent; + (void)pAppData; + (void)pEventData; + switch(eEvent) { + case OMX_EventCmdComplete: + DEBUG_PRINT("\n OMX_EventCmdComplete event=%d data1=%u data2=%u\n",(OMX_EVENTTYPE)eEvent, + nData1,nData2); + event_complete(); + break; + case OMX_EventError: + DEBUG_PRINT("\n OMX_EventError \n"); + break; + case OMX_EventBufferFlag: + DEBUG_PRINT("\n OMX_EventBufferFlag \n"); + bOutputEosReached = true; + event_complete(); + break; + case OMX_EventPortSettingsChanged: + DEBUG_PRINT("\n OMX_EventPortSettingsChanged \n"); + break; + default: + DEBUG_PRINT("\n Unknown Event \n"); + break; + } + return OMX_ErrorNone; +} + +OMX_ERRORTYPE FillBufferDone(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) +{ + size_t bytes_writen = 0; + size_t total_bytes_writen = 0; + size_t len = 0; + struct enc_meta_out *meta = NULL; + OMX_U8 *src = pBuffer->pBuffer; + unsigned int num_of_frames = 1; + + /* To remove warning for unused variable to keep prototype same */ + (void)pAppData; + + if(((pBuffer->nFlags & OMX_BUFFERFLAG_EOS) == OMX_BUFFERFLAG_EOS)) { + DEBUG_PRINT("FBD::EOS on output port\n "); + bOutputEosReached = true; + return OMX_ErrorNone; + } + if(bInputEosReached_tunnel || bOutputEosReached) + { + DEBUG_PRINT("EOS REACHED NO MORE PROCESSING OF BUFFERS\n"); + return OMX_ErrorNone; + } + if(num_of_frames != src[0]){ + + printf("Data corrupt\n"); + return OMX_ErrorNone; + } + /* Skip the first bytes */ + + + + src += sizeof(unsigned char); + meta = (struct enc_meta_out *)src; + while (num_of_frames > 0) { + meta = (struct enc_meta_out *)src; + /*printf("offset=%d framesize=%d encoded_pcm[%d] msw_ts[%d]lsw_ts[%d] nflags[%d]\n", + meta->offset_to_frame, + meta->frame_size, + meta->encoded_pcm_samples, meta->msw_ts, meta->lsw_ts, meta->nflags);*/ + len = meta->frame_size; + + bytes_writen = fwrite(pBuffer->pBuffer + sizeof(unsigned char) + meta->offset_to_frame,1,len,outputBufferFile); + if(bytes_writen < len) + { + DEBUG_PRINT("error: invalid EVRC encoded data \n"); + return OMX_ErrorNone; + } + src += sizeof(struct enc_meta_out); + num_of_frames--; + total_bytes_writen += len; + } + DEBUG_PRINT(" FillBufferDone size writen to file %zu count %d\n",total_bytes_writen, framecnt); + totaldatalen = totaldatalen + (int)total_bytes_writen; + framecnt++; + + DEBUG_PRINT(" FBD calling FTB\n"); + OMX_FillThisBuffer(hComponent,pBuffer); + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE EmptyBufferDone(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) +{ + int readBytes =0; + + /* To remove warning for unused variable to keep prototype same */ + (void)pAppData; + + ebd_cnt++; + used_ip_buf_cnt--; + pthread_mutex_lock(&etb_lock); + if(!etb_done) + { + DEBUG_PRINT("\n*********************************************\n"); + DEBUG_PRINT("Wait till first set of buffers are given to component\n"); + DEBUG_PRINT("\n*********************************************\n"); + etb_done++; + pthread_mutex_unlock(&etb_lock); + etb_wait_for_event(); + } + else + { + pthread_mutex_unlock(&etb_lock); + } + + + if(bInputEosReached) + { + DEBUG_PRINT("\n*********************************************\n"); + DEBUG_PRINT(" EBD::EOS on input port\n "); + DEBUG_PRINT("*********************************************\n"); + return OMX_ErrorNone; + }else if (bFlushing == true) { + DEBUG_PRINT("omx_evrc13_adec_test: bFlushing is set to TRUE used_ip_buf_cnt=%d\n",used_ip_buf_cnt); + if (used_ip_buf_cnt == 0) { + bFlushing = false; + } else { + DEBUG_PRINT("omx_evrc13_adec_test: more buffer to come back used_ip_buf_cnt=%d\n",used_ip_buf_cnt); + return OMX_ErrorNone; + } + } + + if((readBytes = Read_Buffer(pBuffer)) > 0) { + pBuffer->nFilledLen = (OMX_U32)readBytes; + used_ip_buf_cnt++; + OMX_EmptyThisBuffer(hComponent,pBuffer); + } + else{ + pBuffer->nFlags |= OMX_BUFFERFLAG_EOS; + used_ip_buf_cnt++; + bInputEosReached = true; + pBuffer->nFilledLen = 0; + OMX_EmptyThisBuffer(hComponent,pBuffer); + DEBUG_PRINT("EBD..Either EOS or Some Error while reading file\n"); + } + return OMX_ErrorNone; +} + +void signal_handler(int sig_id) { + + /* Flush */ + if (sig_id == SIGUSR1) { + DEBUG_PRINT("%s Initiate flushing\n", __FUNCTION__); + bFlushing = true; + OMX_SendCommand(evrc_enc_handle, OMX_CommandFlush, OMX_ALL, NULL); + } else if (sig_id == SIGUSR2) { + if (bPause == true) { + DEBUG_PRINT("%s resume record\n", __FUNCTION__); + bPause = false; + OMX_SendCommand(evrc_enc_handle, OMX_CommandStateSet, OMX_StateExecuting, NULL); + } else { + DEBUG_PRINT("%s pause record\n", __FUNCTION__); + bPause = true; + OMX_SendCommand(evrc_enc_handle, OMX_CommandStateSet, OMX_StatePause, NULL); + } + } +} + +int main(int argc, char **argv) +{ + unsigned int bufCnt=0; + OMX_ERRORTYPE result; + + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = &signal_handler; + sigaction(SIGABRT, &sa, NULL); + sigaction(SIGUSR1, &sa, NULL); + sigaction(SIGUSR2, &sa, NULL); + + (void) signal(SIGINT, Release_Encoder); + + pthread_cond_init(&cond, 0); + pthread_mutex_init(&lock, 0); + pthread_cond_init(&etb_cond, 0); + pthread_mutex_init(&etb_lock, 0); + pthread_mutex_init(&etb_lock1, 0); + + if (argc >= 9) { + in_filename = argv[1]; + out_filename = argv[2]; + tunnel = (uint32_t)atoi(argv[3]); + min_bitrate = (uint32_t)atoi(argv[4]); + max_bitrate = (uint32_t)atoi(argv[5]); + cdmarate = (uint32_t)atoi(argv[6]); + recpath = (uint32_t)atoi(argv[7]); // No configuration support yet.. + rectime = (uint32_t)atoi(argv[8]); + + } else { + DEBUG_PRINT(" invalid format: \n"); + DEBUG_PRINT("ex: ./mm-aenc-omxevrc-test INPUTFILE OUTPUTFILE Tunnel MINRATE MAXRATE CDMARATE RECORDPATH RECORDTIME\n"); + DEBUG_PRINT("MINRATE MAXRATE and CDMARATE 1 to 4\n"); + DEBUG_PRINT("RECORDPATH 0(TX),1(RX),2(BOTH),3(MIC)\n"); + DEBUG_PRINT("RECORDTIME in seconds for AST Automation\n"); + return 0; + } + if(recpath != 3) { + DEBUG_PRINT("For RECORDPATH Only MIC supported\n"); + return 0; + } + if(tunnel == 0) + aud_comp = "OMX.qcom.audio.encoder.evrc"; + else + aud_comp = "OMX.qcom.audio.encoder.tunneled.evrc"; + if(Init_Encoder(aud_comp)!= 0x00) + { + DEBUG_PRINT("Decoder Init failed\n"); + return -1; + } + + fcntl(0, F_SETFL, O_NONBLOCK); + + if(Play_Encoder() != 0x00) + { + DEBUG_PRINT("Play_Decoder failed\n"); + return -1; + } + + // Wait till EOS is reached... + if(rectime && tunnel) + { + sleep(rectime); + rectime = 0; + bInputEosReached_tunnel = 1; + DEBUG_PRINT("\EOS ON INPUT PORT\n"); + } + else + { + wait_for_event(); + } + + if((bInputEosReached_tunnel) || ((bOutputEosReached) && !tunnel)) + { + + DEBUG_PRINT("\nMoving the decoder to idle state \n"); + OMX_SendCommand(evrc_enc_handle, OMX_CommandStateSet, OMX_StateIdle,0); + wait_for_event(); + + DEBUG_PRINT("\nMoving the encoder to loaded state \n"); + OMX_SendCommand(evrc_enc_handle, OMX_CommandStateSet, OMX_StateLoaded,0); + sleep(1); + if (!tunnel) + { + DEBUG_PRINT("\nFillBufferDone: Deallocating i/p buffers \n"); + for(bufCnt=0; bufCnt < input_buf_cnt; ++bufCnt) { + OMX_FreeBuffer(evrc_enc_handle, 0, pInputBufHdrs[bufCnt]); + } + } + + DEBUG_PRINT ("\nFillBufferDone: Deallocating o/p buffers \n"); + for(bufCnt=0; bufCnt < output_buf_cnt; ++bufCnt) { + OMX_FreeBuffer(evrc_enc_handle, 1, pOutputBufHdrs[bufCnt]); + } + wait_for_event(); + create_qcp_header(totaldatalen, framecnt); + fseek(outputBufferFile, 0,SEEK_SET); + fwrite(&append_header,1,QCP_HEADER_SIZE,outputBufferFile); + + + result = OMX_FreeHandle(evrc_enc_handle); + if (result != OMX_ErrorNone) { + DEBUG_PRINT ("\nOMX_FreeHandle error. Error code: %d\n", result); + } + + /* Deinit OpenMAX */ + if(tunnel) + { + #ifdef AUDIOV2 + if (msm_route_stream(DIR_TX,session_id,device_id, 0)) + { + DEBUG_PRINT("\ncould not set stream routing\n"); + return -1; + } + if (msm_en_device(device_id, 0)) + { + DEBUG_PRINT("\ncould not enable device\n"); + return -1; + } + msm_mixer_close(); + #endif + } + OMX_Deinit(); + ebd_cnt=0; + bOutputEosReached = false; + bInputEosReached_tunnel = false; + bInputEosReached = 0; + evrc_enc_handle = NULL; + pthread_cond_destroy(&cond); + pthread_mutex_destroy(&lock); + fclose(outputBufferFile); + DEBUG_PRINT("*****************************************\n"); + DEBUG_PRINT("******...EVRC ENC TEST COMPLETED...***************\n"); + DEBUG_PRINT("*****************************************\n"); + } + return 0; +} + +void Release_Encoder() +{ + static int cnt=0; + OMX_ERRORTYPE result; + + DEBUG_PRINT("END OF EVRC ENCODING: EXITING PLEASE WAIT\n"); + bInputEosReached_tunnel = 1; + event_complete(); + cnt++; + if(cnt > 1) + { + /* FORCE RESET */ + evrc_enc_handle = NULL; + ebd_cnt=0; + bInputEosReached_tunnel = false; + + result = OMX_FreeHandle(evrc_enc_handle); + if (result != OMX_ErrorNone) { + DEBUG_PRINT ("\nOMX_FreeHandle error. Error code: %d\n", result); + } + + /* Deinit OpenMAX */ + + OMX_Deinit(); + + pthread_cond_destroy(&cond); + pthread_mutex_destroy(&lock); + DEBUG_PRINT("*****************************************\n"); + DEBUG_PRINT("******...EVRC ENC TEST COMPLETED...***************\n"); + DEBUG_PRINT("*****************************************\n"); + exit(0); + } +} + +int Init_Encoder(OMX_STRING audio_component) +{ + DEBUG_PRINT("Inside %s \n", __FUNCTION__); + OMX_ERRORTYPE omxresult; + OMX_U32 total = 0; + typedef OMX_U8* OMX_U8_PTR; + char *role ="audio_encoder"; + + static OMX_CALLBACKTYPE call_back = { + &EventHandler,&EmptyBufferDone,&FillBufferDone + }; + + /* Init. the OpenMAX Core */ + DEBUG_PRINT("\nInitializing OpenMAX Core....\n"); + omxresult = OMX_Init(); + + if(OMX_ErrorNone != omxresult) { + DEBUG_PRINT("\n Failed to Init OpenMAX core"); + return -1; + } + else { + DEBUG_PRINT("\nOpenMAX Core Init Done\n"); + } + + /* Query for audio decoders*/ + DEBUG_PRINT("Evrc_test: Before entering OMX_GetComponentOfRole"); + OMX_GetComponentsOfRole(role, &total, 0); + DEBUG_PRINT ("\nTotal components of role=%s :%u", role, total); + + + omxresult = OMX_GetHandle((OMX_HANDLETYPE*)(&evrc_enc_handle), + (OMX_STRING)audio_component, NULL, &call_back); + if (FAILED(omxresult)) { + DEBUG_PRINT("\nFailed to Load the component:%s\n", audio_component); + return -1; + } + else + { + DEBUG_PRINT("\nComponent %s is in LOADED state\n", audio_component); + } + + /* Get the port information */ + CONFIG_VERSION_SIZE(portParam); + omxresult = OMX_GetParameter(evrc_enc_handle, OMX_IndexParamAudioInit, + (OMX_PTR)&portParam); + + if(FAILED(omxresult)) { + DEBUG_PRINT("\nFailed to get Port Param\n"); + return -1; + } + else + { + DEBUG_PRINT("\nportParam.nPorts:%u\n", portParam.nPorts); + DEBUG_PRINT("\nportParam.nStartPortNumber:%u\n", + portParam.nStartPortNumber); + } + + if(OMX_ErrorNone != omxresult) + { + DEBUG_PRINT("Set parameter failed"); + } + + return 0; +} + +int Play_Encoder() +{ + unsigned int i; + int Size=0; + DEBUG_PRINT("Inside %s \n", __FUNCTION__); + OMX_ERRORTYPE ret; + OMX_INDEXTYPE index; +#ifdef __LP64__ + DEBUG_PRINT("sizeof[%ld]\n", sizeof(OMX_BUFFERHEADERTYPE)); +#else + DEBUG_PRINT("sizeof[%d]\n", sizeof(OMX_BUFFERHEADERTYPE)); +#endif + + /* open the i/p and o/p files based on the video file format passed */ + if(open_audio_file()) { + DEBUG_PRINT("\n Returning -1"); + return -1; + } + + /* Query the encoder input min buf requirements */ + CONFIG_VERSION_SIZE(inputportFmt); + + /* Port for which the Client needs to obtain info */ + inputportFmt.nPortIndex = portParam.nStartPortNumber; + + OMX_GetParameter(evrc_enc_handle,OMX_IndexParamPortDefinition,&inputportFmt); + DEBUG_PRINT ("\nEnc Input Buffer Count %u\n", inputportFmt.nBufferCountMin); + DEBUG_PRINT ("\nEnc: Input Buffer Size %u\n", inputportFmt.nBufferSize); + + if(OMX_DirInput != inputportFmt.eDir) { + DEBUG_PRINT ("\nEnc: Expect Input Port\n"); + return -1; + } + + pcmparam.nPortIndex = 0; + pcmparam.nChannels = channels; + pcmparam.nSamplingRate = samplerate; + OMX_SetParameter(evrc_enc_handle,OMX_IndexParamAudioPcm,&pcmparam); + + + /* Query the encoder outport's min buf requirements */ + CONFIG_VERSION_SIZE(outputportFmt); + /* Port for which the Client needs to obtain info */ + outputportFmt.nPortIndex = portParam.nStartPortNumber + 1; + + OMX_GetParameter(evrc_enc_handle,OMX_IndexParamPortDefinition,&outputportFmt); + DEBUG_PRINT ("\nEnc: Output Buffer Count %u\n", outputportFmt.nBufferCountMin); + DEBUG_PRINT ("\nEnc: Output Buffer Size %u\n", outputportFmt.nBufferSize); + + if(OMX_DirOutput != outputportFmt.eDir) { + DEBUG_PRINT ("\nEnc: Expect Output Port\n"); + return -1; + } + + + CONFIG_VERSION_SIZE(evrcparam); + + evrcparam.nPortIndex = 1; + evrcparam.nChannels = channels; //2 ; /* 1-> mono 2-> stereo*/ + evrcparam.nMinBitRate = min_bitrate; + evrcparam.nMaxBitRate = max_bitrate; + OMX_SetParameter(evrc_enc_handle,OMX_IndexParamAudioEvrc,&evrcparam); + OMX_GetExtensionIndex(evrc_enc_handle,"OMX.Qualcomm.index.audio.sessionId",&index); + OMX_GetParameter(evrc_enc_handle,index,&streaminfoparam); + if(tunnel) { + #ifdef AUDIOV2 + session_id = streaminfoparam.sessionId; + control = msm_mixer_open("/dev/snd/controlC0", 0); + if(control < 0) + printf("ERROR opening the device\n"); + device_id = msm_get_device(device); + DEBUG_PRINT ("\ndevice_id = %d\n",device_id); + DEBUG_PRINT("\nsession_id = %d\n",session_id); + if (msm_en_device(device_id, 1)) + { + perror("could not enable device\n"); + return -1; + } + if (msm_route_stream(DIR_TX,session_id,device_id, 1)) + { + perror("could not set stream routing\n"); + return -1; + } + #endif + } + + DEBUG_PRINT ("\nOMX_SendCommand Encoder -> IDLE\n"); + OMX_SendCommand(evrc_enc_handle, OMX_CommandStateSet, OMX_StateIdle,0); + /* wait_for_event(); should not wait here event complete status will + not come until enough buffer are allocated */ + if (tunnel == 0) + { + input_buf_cnt = inputportFmt.nBufferCountActual; // inputportFmt.nBufferCountMin + 5; + DEBUG_PRINT("Transition to Idle State succesful...\n"); + /* Allocate buffer on decoder's i/p port */ + error = Allocate_Buffer(evrc_enc_handle, &pInputBufHdrs, inputportFmt.nPortIndex, + input_buf_cnt, inputportFmt.nBufferSize); + if (error != OMX_ErrorNone || pInputBufHdrs == NULL) { + DEBUG_PRINT ("\nOMX_AllocateBuffer Input buffer error\n"); + return -1; + } + else { + DEBUG_PRINT ("\nOMX_AllocateBuffer Input buffer success\n"); + } + } + output_buf_cnt = outputportFmt.nBufferCountMin ; + + /* Allocate buffer on encoder's O/Pp port */ + error = Allocate_Buffer(evrc_enc_handle, &pOutputBufHdrs, outputportFmt.nPortIndex, + output_buf_cnt, outputportFmt.nBufferSize); + if (error != OMX_ErrorNone || pOutputBufHdrs == NULL) { + DEBUG_PRINT ("\nOMX_AllocateBuffer Output buffer error\n"); + return -1; + } + else { + DEBUG_PRINT ("\nOMX_AllocateBuffer Output buffer success\n"); + } + + wait_for_event(); + + + if (tunnel == 1) + { + DEBUG_PRINT ("\nOMX_SendCommand to enable TUNNEL MODE during IDLE\n"); + OMX_SendCommand(evrc_enc_handle, OMX_CommandPortDisable,0,0); // disable input port + wait_for_event(); + } + + DEBUG_PRINT ("\nOMX_SendCommand encoder -> Executing\n"); + OMX_SendCommand(evrc_enc_handle, OMX_CommandStateSet, OMX_StateExecuting,0); + wait_for_event(); + + DEBUG_PRINT(" Start sending OMX_FILLthisbuffer\n"); + + for(i=0; i < output_buf_cnt; i++) { + DEBUG_PRINT ("\nOMX_FillThisBuffer on output buf no.%d\n",i); + pOutputBufHdrs[i]->nOutputPortIndex = 1; + pOutputBufHdrs[i]->nFlags = pOutputBufHdrs[i]->nFlags & (unsigned)~OMX_BUFFERFLAG_EOS; + ret = OMX_FillThisBuffer(evrc_enc_handle, pOutputBufHdrs[i]); + if (OMX_ErrorNone != ret) { + DEBUG_PRINT("OMX_FillThisBuffer failed with result %d\n", ret); + } + else { + DEBUG_PRINT("OMX_FillThisBuffer success!\n"); + } + } + +if(tunnel == 0) +{ + DEBUG_PRINT(" Start sending OMX_emptythisbuffer\n"); + for (i = 0;i < input_buf_cnt;i++) { + DEBUG_PRINT ("\nOMX_EmptyThisBuffer on Input buf no.%d\n",i); + pInputBufHdrs[i]->nInputPortIndex = 0; + Size = Read_Buffer(pInputBufHdrs[i]); + if(Size <=0 ){ + DEBUG_PRINT("NO DATA READ\n"); + bInputEosReached = true; + pInputBufHdrs[i]->nFlags= OMX_BUFFERFLAG_EOS; + } + pInputBufHdrs[i]->nFilledLen = (OMX_U32)Size; + pInputBufHdrs[i]->nInputPortIndex = 0; + used_ip_buf_cnt++; + ret = OMX_EmptyThisBuffer(evrc_enc_handle, pInputBufHdrs[i]); + if (OMX_ErrorNone != ret) { + DEBUG_PRINT("OMX_EmptyThisBuffer failed with result %d\n", ret); + } + else { + DEBUG_PRINT("OMX_EmptyThisBuffer success!\n"); + } + if(Size <=0 ){ + break;//eos reached + } + } + pthread_mutex_lock(&etb_lock); + if(etb_done) +{ + DEBUG_PRINT("Component is waiting for EBD to be released.\n"); + etb_event_complete(); + } + else + { + DEBUG_PRINT("\n****************************\n"); + DEBUG_PRINT("EBD not yet happened ...\n"); + DEBUG_PRINT("\n****************************\n"); + etb_done++; + } + pthread_mutex_unlock(&etb_lock); +} + + return 0; +} + + + +static OMX_ERRORTYPE Allocate_Buffer ( OMX_COMPONENTTYPE *avc_enc_handle, + OMX_BUFFERHEADERTYPE ***pBufHdrs, + OMX_U32 nPortIndex, + unsigned int bufCntMin, unsigned int bufSize) +{ + DEBUG_PRINT("Inside %s \n", __FUNCTION__); + OMX_ERRORTYPE error=OMX_ErrorNone; + unsigned int bufCnt=0; + + /* To remove warning for unused variable to keep prototype same */ + (void)avc_enc_handle; + *pBufHdrs= (OMX_BUFFERHEADERTYPE **) + malloc(sizeof(OMX_BUFFERHEADERTYPE*)*bufCntMin); + + for(bufCnt=0; bufCnt < bufCntMin; ++bufCnt) { + DEBUG_PRINT("\n OMX_AllocateBuffer No %d \n", bufCnt); + error = OMX_AllocateBuffer(evrc_enc_handle, &((*pBufHdrs)[bufCnt]), + nPortIndex, NULL, bufSize); + } + + return error; +} + + + + +static int Read_Buffer (OMX_BUFFERHEADERTYPE *pBufHdr ) +{ + + size_t bytes_read=0; + + + pBufHdr->nFilledLen = 0; + pBufHdr->nFlags |= OMX_BUFFERFLAG_EOS; + + bytes_read = fread(pBufHdr->pBuffer, 1, pBufHdr->nAllocLen , inputBufferFile); + + pBufHdr->nFilledLen = (OMX_U32)bytes_read; + // Time stamp logic + ((OMX_BUFFERHEADERTYPE *)pBufHdr)->nTimeStamp = \ + + (OMX_TICKS) ((total_pcm_bytes * 1000)/(samplerate * channels *2)); + + DEBUG_PRINT ("\n--time stamp -- %ld\n", (unsigned long)((OMX_BUFFERHEADERTYPE *)pBufHdr)->nTimeStamp); + if(bytes_read == 0) + { + pBufHdr->nFlags |= OMX_BUFFERFLAG_EOS; + DEBUG_PRINT ("\nBytes read zero\n"); + } + else + { + pBufHdr->nFlags = pBufHdr->nFlags & (unsigned)~OMX_BUFFERFLAG_EOS; + + total_pcm_bytes = (unsigned)(total_pcm_bytes + bytes_read); + } + + return (int)bytes_read;; +} + + + +//In Encoder this Should Open a PCM or WAV file for input. + +static int open_audio_file () +{ + int error_code = 0; + + if (!tunnel) + { + DEBUG_PRINT("Inside %s filename=%s\n", __FUNCTION__, in_filename); + inputBufferFile = fopen (in_filename, "rb"); + if (inputBufferFile == NULL) { + DEBUG_PRINT("\ni/p file %s could NOT be opened\n", + in_filename); + error_code = -1; + } + if(parse_pcm_header() != 0x00) + { + DEBUG_PRINT("PCM parser failed \n"); + return -1; + } + } + + DEBUG_PRINT("Inside %s filename=%s\n", __FUNCTION__, out_filename); + outputBufferFile = fopen (out_filename, "wb"); + if (outputBufferFile == NULL) { + DEBUG_PRINT("\ni/p file %s could NOT be opened\n", + out_filename); + error_code = -1; + return error_code; + } + fseek(outputBufferFile, QCP_HEADER_SIZE, SEEK_SET); + return error_code; +} + +static OMX_ERRORTYPE parse_pcm_header() +{ + struct wav_header hdr; + + DEBUG_PRINT("\n***************************************************************\n"); + if(fread(&hdr, 1, sizeof(hdr),inputBufferFile)!=sizeof(hdr)) + { + DEBUG_PRINT("Wav file cannot read header\n"); + return -1; + } + + if ((hdr.riff_id != ID_RIFF) || + (hdr.riff_fmt != ID_WAVE)|| + (hdr.fmt_id != ID_FMT)) + { + DEBUG_PRINT("Wav file is not a riff/wave file\n"); + return -1; + } + + if (hdr.audio_format != FORMAT_PCM) + { + DEBUG_PRINT("Wav file is not adpcm format %d and fmt size is %d\n", + hdr.audio_format, hdr.fmt_sz); + return -1; + } + + DEBUG_PRINT("Samplerate is %d\n", hdr.sample_rate); + DEBUG_PRINT("Channel Count is %d\n", hdr.num_channels); + DEBUG_PRINT("\n***************************************************************\n"); + + samplerate = hdr.sample_rate; + channels = hdr.num_channels; + total_pcm_bytes = 0; + + return OMX_ErrorNone; +} diff --git a/audio/mm-audio/aenc-qcelp13/Android.mk b/audio/mm-audio/aenc-qcelp13/Android.mk new file mode 100644 index 0000000..58d0456 --- /dev/null +++ b/audio/mm-audio/aenc-qcelp13/Android.mk @@ -0,0 +1,7 @@ +ifneq ($(filter arm aarch64 arm64, $(TARGET_ARCH)),) + +AENC_QCELP13_PATH:= $(call my-dir) + +include $(AENC_QCELP13_PATH)/qdsp6/Android.mk + +endif diff --git a/audio/mm-audio/aenc-qcelp13/Makefile b/audio/mm-audio/aenc-qcelp13/Makefile new file mode 100644 index 0000000..83d822b --- /dev/null +++ b/audio/mm-audio/aenc-qcelp13/Makefile @@ -0,0 +1,6 @@ +all: + @echo "invoking omxaudio make" + $(MAKE) -C qdsp6 + +install: + $(MAKE) -C qdsp6 install diff --git a/audio/mm-audio/aenc-qcelp13/qdsp6/Android.mk b/audio/mm-audio/aenc-qcelp13/qdsp6/Android.mk new file mode 100644 index 0000000..006a275 --- /dev/null +++ b/audio/mm-audio/aenc-qcelp13/qdsp6/Android.mk @@ -0,0 +1,69 @@ +ifneq ($(BUILD_TINY_ANDROID),true) + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +# --------------------------------------------------------------------------------- +# Common definitons +# --------------------------------------------------------------------------------- + +libOmxQcelp13Enc-def := -g -O3 +libOmxQcelp13Enc-def += -DQC_MODIFIED +libOmxQcelp13Enc-def += -D_ANDROID_ +libOmxQcelp13Enc-def += -D_ENABLE_QC_MSG_LOG_ +libOmxQcelp13Enc-def += -DVERBOSE +libOmxQcelp13Enc-def += -D_DEBUG +libOmxQcelp13Enc-def += -Wconversion +libOmxQcelp13Enc-def += -DAUDIOV2 + +# --------------------------------------------------------------------------------- +# Make the Shared library (libOmxQcelp13Enc) +# --------------------------------------------------------------------------------- + +include $(CLEAR_VARS) + +libOmxQcelp13Enc-inc := $(LOCAL_PATH)/inc +libOmxQcelp13Enc-inc += $(TARGET_OUT_HEADERS)/mm-core/omxcore + +LOCAL_MODULE := libOmxQcelp13Enc +LOCAL_MODULE_TAGS := optional +LOCAL_CFLAGS := $(libOmxQcelp13Enc-def) +LOCAL_C_INCLUDES := $(libOmxQcelp13Enc-inc) +LOCAL_PRELINK_MODULE := false +LOCAL_SHARED_LIBRARIES := libutils liblog + +LOCAL_SRC_FILES := src/aenc_svr.c +LOCAL_SRC_FILES += src/omx_qcelp13_aenc.cpp + +include $(BUILD_SHARED_LIBRARY) + + +# --------------------------------------------------------------------------------- +# Make the apps-test (mm-aenc-omxqcelp13-test) +# --------------------------------------------------------------------------------- + +include $(CLEAR_VARS) + +mm-qcelp13-enc-test-inc := $(LOCAL_PATH)/inc +mm-qcelp13-enc-test-inc += $(LOCAL_PATH)/test + +mm-qcelp13-enc-test-inc += $(TARGET_OUT_HEADERS)/mm-core/omxcore +mm-qcelp13-enc-test-inc += $(TARGET_OUT_HEADERS)/mm-audio/audio-alsa +LOCAL_MODULE := mm-aenc-omxqcelp13-test +LOCAL_MODULE_TAGS := optional +LOCAL_CFLAGS := $(libOmxQcelp13Enc-def) +LOCAL_C_INCLUDES := $(mm-qcelp13-enc-test-inc) +LOCAL_PRELINK_MODULE := false +LOCAL_SHARED_LIBRARIES := libmm-omxcore +LOCAL_SHARED_LIBRARIES += libOmxQcelp13Enc +LOCAL_SHARED_LIBRARIES += libaudioalsa +LOCAL_SRC_FILES := test/omx_qcelp13_enc_test.c + +include $(BUILD_EXECUTABLE) + +endif + +# --------------------------------------------------------------------------------- +# END +# --------------------------------------------------------------------------------- + diff --git a/audio/mm-audio/aenc-qcelp13/qdsp6/Makefile b/audio/mm-audio/aenc-qcelp13/qdsp6/Makefile new file mode 100644 index 0000000..b14655b --- /dev/null +++ b/audio/mm-audio/aenc-qcelp13/qdsp6/Makefile @@ -0,0 +1,81 @@ +# --------------------------------------------------------------------------------- +# MM-AUDIO-OSS-8K-AENC-QCELP13 +# --------------------------------------------------------------------------------- + +# cross-compiler flags +CFLAGS += -Wall +CFLAGS += -Wundef +CFLAGS += -Wstrict-prototypes +CFLAGS += -Wno-trigraphs + +# cross-compile flags specific to shared objects +CFLAGS_SO += -fpic + +# required pre-processor flags +CPPFLAGS := -D__packed__= +CPPFLAGS += -DIMAGE_APPS_PROC +CPPFLAGS += -DFEATURE_Q_SINGLE_LINK +CPPFLAGS += -DFEATURE_Q_NO_SELF_QPTR +CPPFLAGS += -DFEATURE_LINUX +CPPFLAGS += -DFEATURE_NATIVELINUX +CPPFLAGS += -DFEATURE_DSM_DUP_ITEMS + +CPPFLAGS += -g +CPPFALGS += -D_DEBUG +CPPFLAGS += -Iinc + +# linker flags +LDFLAGS += -L$(SYSROOT)/usr/lib + +# linker flags for shared objects +LDFLAGS_SO := -shared + +# defintions +LIBMAJOR := $(basename $(basename $(LIBVER))) +LIBINSTALLDIR := $(DESTDIR)usr/lib +INCINSTALLDIR := $(DESTDIR)usr/include +BININSTALLDIR := $(DESTDIR)usr/bin + +# --------------------------------------------------------------------------------- +# BUILD +# --------------------------------------------------------------------------------- +all: libOmxQcelp13Enc.so.$(LIBVER) mm-aenc-omxqcelp13-test + +install: + echo "intalling aenc-qcelp13 in $(DESTDIR)" + if [ ! -d $(LIBINSTALLDIR) ]; then mkdir -p $(LIBINSTALLDIR); fi + if [ ! -d $(INCINSTALLDIR) ]; then mkdir -p $(INCINSTALLDIR); fi + if [ ! -d $(BININSTALLDIR) ]; then mkdir -p $(BININSTALLDIR); fi + install -m 555 libOmxQcelp13Enc.so.$(LIBVER) $(LIBINSTALLDIR) + cd $(LIBINSTALLDIR) && ln -s libOmxQcelp13Enc.so.$(LIBVER) libOmxQcelp13Enc.so.$(LIBMAJOR) + cd $(LIBINSTALLDIR) && ln -s libOmxQcelp13Enc.so.$(LIBMAJOR) libOmxQcelp13Enc.so + install -m 555 mm-aenc-omxqcelp13-test $(BININSTALLDIR) + +# --------------------------------------------------------------------------------- +# COMPILE LIBRARY +# --------------------------------------------------------------------------------- +LDLIBS := -lpthread +LDLIBS += -lstdc++ +LDLIBS += -lOmxCore + +SRCS := src/omx_qcelp13_aenc.cpp +SRCS += src/aenc_svr.c + +libOmxQcelp13Enc.so.$(LIBVER): $(SRCS) + $(CC) $(CPPFLAGS) $(CFLAGS_SO) $(LDFLAGS_SO) -Wl,-soname,libOmxQcelp13Enc.so.$(LIBMAJOR) -o $@ $^ $(LDFLAGS) $(LDLIBS) + +# --------------------------------------------------------------------------------- +# COMPILE TEST APP +# --------------------------------------------------------------------------------- +TEST_LDLIBS := -lpthread +TEST_LDLIBS += -ldl +TEST_LDLIBS += -lOmxCore + +TEST_SRCS := test/omx_qcelp13_enc_test.c + +mm-aenc-omxqcelp13-test: libOmxQcelp13Enc.so.$(LIBVER) $(TEST_SRCS) + $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $^ $(TEST_LDLIBS) + +# --------------------------------------------------------------------------------- +# END +# --------------------------------------------------------------------------------- diff --git a/audio/mm-audio/aenc-qcelp13/qdsp6/inc/Map.h b/audio/mm-audio/aenc-qcelp13/qdsp6/inc/Map.h new file mode 100644 index 0000000..aac96fd --- /dev/null +++ b/audio/mm-audio/aenc-qcelp13/qdsp6/inc/Map.h @@ -0,0 +1,244 @@ +/*-------------------------------------------------------------------------- +Copyright (c) 2010, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of The Linux Foundation nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------*/ +#ifndef _MAP_H_ +#define _MAP_H_ + +#include +using namespace std; + +template +class Map +{ + struct node + { + T data; + T2 data2; + node* prev; + node* next; + node(T t, T2 t2,node* p, node* n) : + data(t), data2(t2), prev(p), next(n) {} + }; + node* head; + node* tail; + node* tmp; + unsigned size_of_list; + static Map *m_self; +public: + Map() : head( NULL ), tail ( NULL ),tmp(head),size_of_list(0) {} + bool empty() const { return ( !head || !tail ); } + operator bool() const { return !empty(); } + void insert(T,T2); + void show(); + int size(); + T2 find(T); // Return VALUE + T find_ele(T);// Check if the KEY is present or not + T2 begin(); //give the first ele + bool erase(T); + bool eraseall(); + bool isempty(); + ~Map() + { + while(head) + { + node* temp(head); + head=head->next; + size_of_list--; + delete temp; + } + } +}; + +template +T2 Map::find(T d1) +{ + tmp = head; + while(tmp) + { + if(tmp->data == d1) + { + return tmp->data2; + } + tmp = tmp->next; + } + return 0; +} + +template +T Map::find_ele(T d1) +{ + tmp = head; + while(tmp) + { + if(tmp->data == d1) + { + return tmp->data; + } + tmp = tmp->next; + } + return 0; +} + +template +T2 Map::begin() +{ + tmp = head; + if(tmp) + { + return (tmp->data2); + } + return 0; +} + +template +void Map::show() +{ + tmp = head; + while(tmp) + { + printf("%d-->%d\n",tmp->data,tmp->data2); + tmp = tmp->next; + } +} + +template +int Map::size() +{ + int count =0; + tmp = head; + while(tmp) + { + tmp = tmp->next; + count++; + } + return count; +} + +template +void Map::insert(T data, T2 data2) +{ + tail = new node(data, data2,tail, NULL); + if( tail->prev ) + tail->prev->next = tail; + + if( empty() ) + { + head = tail; + tmp=head; + } + tmp = head; + size_of_list++; +} + +template +bool Map::erase(T d) +{ + bool found = false; + tmp = head; + node* prevnode = tmp; + node *tempnode; + + while(tmp) + { + if((head == tail) && (head->data == d)) + { + found = true; + tempnode = head; + head = tail = NULL; + delete tempnode; + break; + } + if((tmp ==head) && (tmp->data ==d)) + { + found = true; + tempnode = tmp; + tmp = tmp->next; + tmp->prev = NULL; + head = tmp; + tempnode->next = NULL; + delete tempnode; + break; + } + if((tmp == tail) && (tmp->data ==d)) + { + found = true; + tempnode = tmp; + prevnode->next = NULL; + tmp->prev = NULL; + tail = prevnode; + delete tempnode; + break; + } + if(tmp->data == d) + { + found = true; + prevnode->next = tmp->next; + tmp->next->prev = prevnode->next; + tempnode = tmp; + //tmp = tmp->next; + delete tempnode; + break; + } + prevnode = tmp; + tmp = tmp->next; + } + if(found)size_of_list--; + return found; +} + +template +bool Map::eraseall() +{ + // Be careful while using this method + // it not only removes the node but FREES(not delete) the allocated + // memory. + node *tempnode; + tmp = head; + while(head) + { + tempnode = head; + head = head->next; + tempnode->next = NULL; + if(tempnode->data) + free(tempnode->data); + if(tempnode->data2) + free(tempnode->data2); + delete tempnode; + } + tail = head = NULL; + return true; +} + + +template +bool Map::isempty() +{ + if(!size_of_list) return true; + else return false; +} + +#endif // _MAP_H_ diff --git a/audio/mm-audio/aenc-qcelp13/qdsp6/inc/aenc_svr.h b/audio/mm-audio/aenc-qcelp13/qdsp6/inc/aenc_svr.h new file mode 100644 index 0000000..940d863 --- /dev/null +++ b/audio/mm-audio/aenc-qcelp13/qdsp6/inc/aenc_svr.h @@ -0,0 +1,120 @@ +/*-------------------------------------------------------------------------- +Copyright (c) 2010, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of The Linux Foundation nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------*/ +#ifndef AENC_SVR_H +#define AENC_SVR_H + +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#include + +#ifdef _ANDROID_ +#define LOG_TAG "QC_QCELP13ENC" +#endif + +#ifndef LOGE +#define LOGE ALOGE +#endif + +#ifndef LOGW +#define LOGW ALOGW +#endif + +#ifndef LOGD +#define LOGD ALOGD +#endif + +#ifndef LOGV +#define LOGV ALOGV +#endif + +#ifndef LOGI +#define LOGI ALOGI +#endif + +#define DEBUG_PRINT_ERROR LOGE +#define DEBUG_PRINT LOGI +#define DEBUG_DETAIL LOGV + +typedef void (*message_func)(void* client_data, unsigned char id); + +/** + @brief audio encoder ipc info structure + + */ +struct qcelp13_ipc_info +{ + pthread_t thr; + int pipe_in; + int pipe_out; + int dead; + message_func process_msg_cb; + void *client_data; + char thread_name[128]; +}; + +/** + @brief This function starts command server + + @param cb pointer to callback function from the client + @param client_data reference client wants to get back + through callback + @return handle to command server + */ +struct qcelp13_ipc_info *omx_qcelp13_thread_create(message_func cb, + void* client_data, + char *th_name); + +struct qcelp13_ipc_info *omx_qcelp13_event_thread_create(message_func cb, + void* client_data, + char *th_name); +/** + @brief This function stop command server + + @param svr handle to command server + @return none + */ +void omx_qcelp13_thread_stop(struct qcelp13_ipc_info *qcelp13_ipc); + + +/** + @brief This function post message in the command server + + @param svr handle to command server + @return none + */ +void omx_qcelp13_post_msg(struct qcelp13_ipc_info *qcelp13_ipc, + unsigned char id); + +#ifdef __cplusplus +} +#endif + +#endif /* AENC_SVR */ diff --git a/audio/mm-audio/aenc-qcelp13/qdsp6/inc/omx_qcelp13_aenc.h b/audio/mm-audio/aenc-qcelp13/qdsp6/inc/omx_qcelp13_aenc.h new file mode 100644 index 0000000..22cc9ed --- /dev/null +++ b/audio/mm-audio/aenc-qcelp13/qdsp6/inc/omx_qcelp13_aenc.h @@ -0,0 +1,539 @@ +/*-------------------------------------------------------------------------- +Copyright (c) 2010, 2014 The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of The Linux Foundation nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------*/ +#ifndef _QCELP13_ENC_H_ +#define _QCELP13_ENC_H_ +/*============================================================================ + Audio Encoder + +@file omx_qcelp13_aenc.h +This module contains the class definition for openMAX encoder component. + + + +============================================================================*/ + +////////////////////////////////////////////////////////////////////////////// +// Include Files +////////////////////////////////////////////////////////////////////////////// + +/* Uncomment out below line #define LOG_NDEBUG 0 if we want to see + * all DEBUG_PRINT or LOGV messaging */ +#include +#include +#include +#include +#include +#include +#include "QOMX_AudioExtensions.h" +#include "QOMX_AudioIndexExtensions.h" +#include "OMX_Core.h" +#include "OMX_Audio.h" +#include "aenc_svr.h" +#include "qc_omx_component.h" +#include "Map.h" +#include +#include +#include +extern "C" { + void * get_omx_component_factory_fn(void); +} + + +////////////////////////////////////////////////////////////////////////////// +// Module specific globals +////////////////////////////////////////////////////////////////////////////// + + + +#define OMX_SPEC_VERSION 0x00000101 +#define MIN(x,y) (((x) < (y)) ? (x) : (y)) +#define MAX(x,y) (x >= y?x:y) + +////////////////////////////////////////////////////////////////////////////// +// Macros +////////////////////////////////////////////////////////////////////////////// +// + + +#define PrintFrameHdr(i,bufHdr) \ + DEBUG_PRINT("i=%d OMX bufHdr[%p]buf[%p]size[%d]TS[%lld]nFlags[0x%x]\n",\ + i,\ + bufHdr, \ + ((OMX_BUFFERHEADERTYPE *)bufHdr)->pBuffer, \ + (unsigned)((OMX_BUFFERHEADERTYPE *)bufHdr)->nFilledLen,\ + ((OMX_BUFFERHEADERTYPE *)bufHdr)->nTimeStamp, \ + (unsigned)((OMX_BUFFERHEADERTYPE *)bufHdr)->nFlags) + + +// BitMask Management logic +#define BITS_PER_BYTE 8 +#define BITMASK_SIZE(mIndex) \ + (((mIndex) + BITS_PER_BYTE - 1)/BITS_PER_BYTE) +#define BITMASK_OFFSET(mIndex)\ + ((mIndex)/BITS_PER_BYTE) +#define BITMASK_FLAG(mIndex) \ + (1 << ((mIndex) % BITS_PER_BYTE)) +#define BITMASK_CLEAR(mArray,mIndex)\ + (mArray)[BITMASK_OFFSET(mIndex)] &= ~(BITMASK_FLAG(mIndex)) +#define BITMASK_SET(mArray,mIndex)\ + (mArray)[BITMASK_OFFSET(mIndex)] |= BITMASK_FLAG(mIndex) +#define BITMASK_PRESENT(mArray,mIndex)\ + ((mArray)[BITMASK_OFFSET(mIndex)] & BITMASK_FLAG(mIndex)) +#define BITMASK_ABSENT(mArray,mIndex)\ + (((mArray)[BITMASK_OFFSET(mIndex)] & \ + BITMASK_FLAG(mIndex)) == 0x0) + +#define OMX_CORE_NUM_INPUT_BUFFERS 2 +#define OMX_CORE_NUM_OUTPUT_BUFFERS 16 + +#define OMX_CORE_INPUT_BUFFER_SIZE 8160 // Multiple of 160 +#define OMX_CORE_CONTROL_CMDQ_SIZE 100 +#define OMX_AENC_VOLUME_STEP 0x147 +#define OMX_AENC_MIN 0 +#define OMX_AENC_MAX 100 +#define NON_TUNNEL 1 +#define TUNNEL 0 +#define IP_PORT_BITMASK 0x02 +#define OP_PORT_BITMASK 0x01 +#define IP_OP_PORT_BITMASK 0x03 + +#define OMX_QCELP13_DEFAULT_SF 8000 +#define OMX_QCELP13_DEFAULT_CH_CFG 1 +#define OMX_QCELP13_DEFAULT_VOL 25 +// 14 bytes for input meta data +#define OMX_AENC_SIZEOF_META_BUF (OMX_CORE_INPUT_BUFFER_SIZE+14) + +#define TRUE 1 +#define FALSE 0 + +#define NUMOFFRAMES 1 +#define MAXFRAMELENGTH 35 +#define OMX_QCELP13_OUTPUT_BUFFER_SIZE ((NUMOFFRAMES * (sizeof(ENC_META_OUT) + MAXFRAMELENGTH) \ + + 1)) + +#define OMX_QCELP13_DEFAULT_MINRATE 4 +#define OMX_QCELP13_DEFAULT_MAXRATE 4 + +class omx_qcelp13_aenc; + +// OMX mo3 audio encoder class +class omx_qcelp13_aenc: public qc_omx_component +{ +public: + omx_qcelp13_aenc(); // constructor + virtual ~omx_qcelp13_aenc(); // destructor + + OMX_ERRORTYPE allocate_buffer(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE **bufferHdr, + OMX_U32 port, + OMX_PTR appData, + OMX_U32 bytes); + + + OMX_ERRORTYPE component_deinit(OMX_HANDLETYPE hComp); + + OMX_ERRORTYPE component_init(OMX_STRING role); + + OMX_ERRORTYPE component_role_enum(OMX_HANDLETYPE hComp, + OMX_U8 *role, + OMX_U32 index); + + OMX_ERRORTYPE component_tunnel_request(OMX_HANDLETYPE hComp, + OMX_U32 port, + OMX_HANDLETYPE peerComponent, + OMX_U32 peerPort, + OMX_TUNNELSETUPTYPE *tunnelSetup); + + OMX_ERRORTYPE empty_this_buffer(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE *buffer); + + + OMX_ERRORTYPE empty_this_buffer_proxy(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE *buffer); + + + OMX_ERRORTYPE fill_this_buffer(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE *buffer); + + + OMX_ERRORTYPE free_buffer(OMX_HANDLETYPE hComp, + OMX_U32 port, + OMX_BUFFERHEADERTYPE *buffer); + + OMX_ERRORTYPE get_component_version(OMX_HANDLETYPE hComp, + OMX_STRING componentName, + OMX_VERSIONTYPE *componentVersion, + OMX_VERSIONTYPE * specVersion, + OMX_UUIDTYPE *componentUUID); + + OMX_ERRORTYPE get_config(OMX_HANDLETYPE hComp, + OMX_INDEXTYPE configIndex, + OMX_PTR configData); + + OMX_ERRORTYPE get_extension_index(OMX_HANDLETYPE hComp, + OMX_STRING paramName, + OMX_INDEXTYPE *indexType); + + OMX_ERRORTYPE get_parameter(OMX_HANDLETYPE hComp, + OMX_INDEXTYPE paramIndex, + OMX_PTR paramData); + + OMX_ERRORTYPE get_state(OMX_HANDLETYPE hComp, + OMX_STATETYPE *state); + + static void process_in_port_msg(void *client_data, + unsigned char id); + + static void process_out_port_msg(void *client_data, + unsigned char id); + + static void process_command_msg(void *client_data, + unsigned char id); + + static void process_event_cb(void *client_data, + unsigned char id); + + + OMX_ERRORTYPE set_callbacks(OMX_HANDLETYPE hComp, + OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData); + + OMX_ERRORTYPE set_config(OMX_HANDLETYPE hComp, + OMX_INDEXTYPE configIndex, + OMX_PTR configData); + + OMX_ERRORTYPE set_parameter(OMX_HANDLETYPE hComp, + OMX_INDEXTYPE paramIndex, + OMX_PTR paramData); + + OMX_ERRORTYPE use_buffer(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE **bufferHdr, + OMX_U32 port, + OMX_PTR appData, + OMX_U32 bytes, + OMX_U8 *buffer); + + OMX_ERRORTYPE use_EGL_image(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE **bufferHdr, + OMX_U32 port, + OMX_PTR appData, + void * eglImage); + + bool post_command(unsigned int p1, unsigned int p2, + unsigned char id); + + // Deferred callback identifiers + enum + { + //Event Callbacks from the component thread context + OMX_COMPONENT_GENERATE_EVENT = 0x1, + //Buffer Done callbacks from component thread context + OMX_COMPONENT_GENERATE_BUFFER_DONE = 0x2, + OMX_COMPONENT_GENERATE_ETB = 0x3, + //Command + OMX_COMPONENT_GENERATE_COMMAND = 0x4, + OMX_COMPONENT_GENERATE_FRAME_DONE = 0x05, + OMX_COMPONENT_GENERATE_FTB = 0x06, + OMX_COMPONENT_GENERATE_EOS = 0x07, + OMX_COMPONENT_PORTSETTINGS_CHANGED = 0x08, + OMX_COMPONENT_SUSPEND = 0x09, + OMX_COMPONENT_RESUME = 0x0a + }; +private: + + /////////////////////////////////////////////////////////// + // Type definitions + /////////////////////////////////////////////////////////// + // Bit Positions + enum flags_bit_positions + { + // Defer transition to IDLE + OMX_COMPONENT_IDLE_PENDING =0x1, + // Defer transition to LOADING + OMX_COMPONENT_LOADING_PENDING =0x2, + + OMX_COMPONENT_MUTED =0x3, + + // Defer transition to Enable + OMX_COMPONENT_INPUT_ENABLE_PENDING =0x4, + // Defer transition to Enable + OMX_COMPONENT_OUTPUT_ENABLE_PENDING =0x5, + // Defer transition to Disable + OMX_COMPONENT_INPUT_DISABLE_PENDING =0x6, + // Defer transition to Disable + OMX_COMPONENT_OUTPUT_DISABLE_PENDING =0x7 + }; + + + typedef Map + input_buffer_map; + + typedef Map + output_buffer_map; + + enum port_indexes + { + OMX_CORE_INPUT_PORT_INDEX =0, + OMX_CORE_OUTPUT_PORT_INDEX =1 + }; + + struct omx_event + { + unsigned long param1; + unsigned long param2; + unsigned char id; + }; + + struct omx_cmd_queue + { + omx_event m_q[OMX_CORE_CONTROL_CMDQ_SIZE]; + unsigned m_read; + unsigned m_write; + unsigned m_size; + + omx_cmd_queue(); + ~omx_cmd_queue(); + bool insert_entry(unsigned long p1, unsigned long p2, unsigned char id); + bool pop_entry(unsigned long *p1,unsigned long *p2, unsigned char *id); + bool get_msg_id(unsigned char *id); + bool get_msg_with_id(unsigned *p1,unsigned *p2, unsigned id); + }; + + typedef struct TIMESTAMP + { + unsigned int LowPart; + unsigned int HighPart; + }__attribute__((packed)) TIMESTAMP; + + typedef struct metadata_input + { + unsigned short offsetVal; + TIMESTAMP nTimeStamp; + unsigned int nFlags; + }__attribute__((packed)) META_IN; + + typedef struct enc_meta_out + { + unsigned int offset_to_frame; + unsigned int frame_size; + unsigned int encoded_pcm_samples; + unsigned int msw_ts; + unsigned int lsw_ts; + unsigned int nflags; + } __attribute__ ((packed))ENC_META_OUT; + + typedef struct + { + OMX_U32 tot_in_buf_len; + OMX_U32 tot_out_buf_len; + OMX_TICKS tot_pb_time; + OMX_U32 fbd_cnt; + OMX_U32 ftb_cnt; + OMX_U32 etb_cnt; + OMX_U32 ebd_cnt; + }QCELP13_PB_STATS; + + /////////////////////////////////////////////////////////// + // Member variables + /////////////////////////////////////////////////////////// + OMX_U8 *m_tmp_meta_buf; + OMX_U8 *m_tmp_out_meta_buf; + OMX_U8 m_flush_cnt ; + OMX_U8 m_comp_deinit; + + // the below var doesnt hold good if combo of use and alloc bufs are used + OMX_S32 m_volume;//Unit to be determined + OMX_PTR m_app_data;// Application data + int nNumInputBuf; + int nNumOutputBuf; + int m_drv_fd; // Kernel device node file handle + bool bFlushinprogress; + bool is_in_th_sleep; + bool is_out_th_sleep; + unsigned int m_flags; //encapsulate the waiting states. + OMX_TICKS nTimestamp; + unsigned int pcm_input; //tunnel or non-tunnel + unsigned int m_inp_act_buf_count; // Num of Input Buffers + unsigned int m_out_act_buf_count; // Numb of Output Buffers + unsigned int m_inp_current_buf_count; // Num of Input Buffers + unsigned int m_out_current_buf_count; // Numb of Output Buffers + unsigned int output_buffer_size; + unsigned int input_buffer_size; + unsigned short m_session_id; + // store I/P PORT state + OMX_BOOL m_inp_bEnabled; + // store O/P PORT state + OMX_BOOL m_out_bEnabled; + //Input port Populated + OMX_BOOL m_inp_bPopulated; + //Output port Populated + OMX_BOOL m_out_bPopulated; + sem_t sem_States; + sem_t sem_read_msg; + sem_t sem_write_msg; + + volatile int m_is_event_done; + volatile int m_is_in_th_sleep; + volatile int m_is_out_th_sleep; + input_buffer_map m_input_buf_hdrs; + output_buffer_map m_output_buf_hdrs; + omx_cmd_queue m_input_q; + omx_cmd_queue m_input_ctrl_cmd_q; + omx_cmd_queue m_input_ctrl_ebd_q; + omx_cmd_queue m_command_q; + omx_cmd_queue m_output_q; + omx_cmd_queue m_output_ctrl_cmd_q; + omx_cmd_queue m_output_ctrl_fbd_q; + pthread_mutexattr_t m_outputlock_attr; + pthread_mutexattr_t m_commandlock_attr; + pthread_mutexattr_t m_lock_attr; + pthread_mutexattr_t m_state_attr; + pthread_mutexattr_t m_flush_attr; + pthread_mutexattr_t m_in_th_attr_1; + pthread_mutexattr_t m_out_th_attr_1; + pthread_mutexattr_t m_event_attr; + pthread_mutexattr_t m_in_th_attr; + pthread_mutexattr_t m_out_th_attr; + pthread_mutexattr_t out_buf_count_lock_attr; + pthread_mutexattr_t in_buf_count_lock_attr; + pthread_cond_t cond; + pthread_cond_t in_cond; + pthread_cond_t out_cond; + pthread_mutex_t m_lock; + pthread_mutex_t m_commandlock; + pthread_mutex_t m_outputlock; + // Mutexes for state change + pthread_mutex_t m_state_lock; + // Mutexes for flush acks from input and output threads + pthread_mutex_t m_flush_lock; + pthread_mutex_t m_event_lock; + pthread_mutex_t m_in_th_lock; + pthread_mutex_t m_out_th_lock; + pthread_mutex_t m_in_th_lock_1; + pthread_mutex_t m_out_th_lock_1; + pthread_mutex_t out_buf_count_lock; + pthread_mutex_t in_buf_count_lock; + + OMX_STATETYPE m_state; // OMX State + OMX_STATETYPE nState; + OMX_CALLBACKTYPE m_cb; // Application callbacks + QCELP13_PB_STATS m_qcelp13_pb_stats; + struct qcelp13_ipc_info *m_ipc_to_in_th; // for input thread + struct qcelp13_ipc_info *m_ipc_to_out_th; // for output thread + struct qcelp13_ipc_info *m_ipc_to_cmd_th; // for command thread + struct qcelp13_ipc_info *m_ipc_to_event_th; //for txco event thread + OMX_PRIORITYMGMTTYPE m_priority_mgm ; + OMX_AUDIO_PARAM_QCELP13TYPE m_qcelp13_param; // Cache QCELP13 encoder parameter + OMX_AUDIO_PARAM_PCMMODETYPE m_pcm_param; // Cache pcm parameter + OMX_PARAM_COMPONENTROLETYPE component_Role; + OMX_PARAM_BUFFERSUPPLIERTYPE m_buffer_supplier; + + /////////////////////////////////////////////////////////// + // Private methods + /////////////////////////////////////////////////////////// + OMX_ERRORTYPE allocate_output_buffer(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE **bufferHdr, + OMX_U32 port,OMX_PTR appData, + OMX_U32 bytes); + + OMX_ERRORTYPE allocate_input_buffer(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE **bufferHdr, + OMX_U32 port, + OMX_PTR appData, + OMX_U32 bytes); + + OMX_ERRORTYPE use_input_buffer(OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE **bufHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes, + OMX_IN OMX_U8* buffer); + + OMX_ERRORTYPE use_output_buffer(OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE **bufHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes, + OMX_IN OMX_U8* buffer); + + OMX_ERRORTYPE fill_this_buffer_proxy(OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE *buffer); + + OMX_ERRORTYPE send_command_proxy(OMX_HANDLETYPE hComp, + OMX_COMMANDTYPE cmd, + OMX_U32 param1, + OMX_PTR cmdData); + + OMX_ERRORTYPE send_command(OMX_HANDLETYPE hComp, + OMX_COMMANDTYPE cmd, + OMX_U32 param1, + OMX_PTR cmdData); + + bool allocate_done(void); + + bool release_done(OMX_U32 param1); + + bool execute_omx_flush(OMX_IN OMX_U32 param1, bool cmd_cmpl=true); + + bool execute_input_omx_flush(void); + + bool execute_output_omx_flush(void); + + bool search_input_bufhdr(OMX_BUFFERHEADERTYPE *buffer); + + bool search_output_bufhdr(OMX_BUFFERHEADERTYPE *buffer); + + bool post_input(unsigned long p1, unsigned long p2, + unsigned char id); + + bool post_output(unsigned long p1, unsigned long p2, + unsigned char id); + + void process_events(omx_qcelp13_aenc *client_data); + + void buffer_done_cb(OMX_BUFFERHEADERTYPE *bufHdr); + + void frame_done_cb(OMX_BUFFERHEADERTYPE *bufHdr); + + void wait_for_event(); + + void event_complete(); + + void in_th_goto_sleep(); + + void in_th_wakeup(); + + void out_th_goto_sleep(); + + void out_th_wakeup(); + + void flush_ack(); + void deinit_encoder(); + +}; +#endif diff --git a/audio/mm-audio/aenc-qcelp13/qdsp6/src/aenc_svr.c b/audio/mm-audio/aenc-qcelp13/qdsp6/src/aenc_svr.c new file mode 100644 index 0000000..bdc96ac --- /dev/null +++ b/audio/mm-audio/aenc-qcelp13/qdsp6/src/aenc_svr.c @@ -0,0 +1,208 @@ +/*-------------------------------------------------------------------------- +Copyright (c) 2010, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of The Linux Foundation nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------*/ +#include +#include +#include + +#include +#include + +#include + +/** + @brief This function processes posted messages + + Once thread is being spawned, this function is run to + start processing commands posted by client + + @param info pointer to context + + */ +void *omx_qcelp13_msg(void *info) +{ + struct qcelp13_ipc_info *qcelp13_info = (struct qcelp13_ipc_info*)info; + unsigned char id; + ssize_t n; + + DEBUG_DETAIL("\n%s: message thread start\n", __FUNCTION__); + while (!qcelp13_info->dead) + { + n = read(qcelp13_info->pipe_in, &id, 1); + if (0 == n) break; + if (1 == n) + { + DEBUG_DETAIL("\n%s-->pipe_in=%d pipe_out=%d\n", + qcelp13_info->thread_name, + qcelp13_info->pipe_in, + qcelp13_info->pipe_out); + + qcelp13_info->process_msg_cb(qcelp13_info->client_data, id); + } + if ((n < 0) && (errno != EINTR)) break; + } + DEBUG_DETAIL("%s: message thread stop\n", __FUNCTION__); + + return 0; +} + +void *omx_qcelp13_events(void *info) +{ + struct qcelp13_ipc_info *qcelp13_info = (struct qcelp13_ipc_info*)info; + unsigned char id = 0; + + DEBUG_DETAIL("%s: message thread start\n", qcelp13_info->thread_name); + qcelp13_info->process_msg_cb(qcelp13_info->client_data, id); + DEBUG_DETAIL("%s: message thread stop\n", qcelp13_info->thread_name); + return 0; +} + +/** + @brief This function starts command server + + @param cb pointer to callback function from the client + @param client_data reference client wants to get back + through callback + @return handle to msging thread + */ +struct qcelp13_ipc_info *omx_qcelp13_thread_create( + message_func cb, + void* client_data, + char* th_name) +{ + int r; + int fds[2]; + struct qcelp13_ipc_info *qcelp13_info; + + qcelp13_info = calloc(1, sizeof(struct qcelp13_ipc_info)); + if (!qcelp13_info) + { + return 0; + } + + qcelp13_info->client_data = client_data; + qcelp13_info->process_msg_cb = cb; + strlcpy(qcelp13_info->thread_name, th_name, + sizeof(qcelp13_info->thread_name)); + + if (pipe(fds)) + { + DEBUG_PRINT_ERROR("\n%s: pipe creation failed\n", __FUNCTION__); + goto fail_pipe; + } + + qcelp13_info->pipe_in = fds[0]; + qcelp13_info->pipe_out = fds[1]; + + r = pthread_create(&qcelp13_info->thr, 0, omx_qcelp13_msg, qcelp13_info); + if (r < 0) goto fail_thread; + + DEBUG_DETAIL("Created thread for %s \n", qcelp13_info->thread_name); + return qcelp13_info; + + +fail_thread: + close(qcelp13_info->pipe_in); + close(qcelp13_info->pipe_out); + +fail_pipe: + free(qcelp13_info); + + return 0; +} + +/** + * @brief This function starts command server + * + * @param cb pointer to callback function from the client + * @param client_data reference client wants to get back + * through callback + * @return handle to msging thread + * */ +struct qcelp13_ipc_info *omx_qcelp13_event_thread_create( + message_func cb, + void* client_data, + char* th_name) +{ + int r; + int fds[2]; + struct qcelp13_ipc_info *qcelp13_info; + + qcelp13_info = calloc(1, sizeof(struct qcelp13_ipc_info)); + if (!qcelp13_info) + { + return 0; + } + + qcelp13_info->client_data = client_data; + qcelp13_info->process_msg_cb = cb; + strlcpy(qcelp13_info->thread_name, th_name, + sizeof(qcelp13_info->thread_name)); + + if (pipe(fds)) + { + DEBUG_PRINT("\n%s: pipe creation failed\n", __FUNCTION__); + goto fail_pipe; + } + + qcelp13_info->pipe_in = fds[0]; + qcelp13_info->pipe_out = fds[1]; + + r = pthread_create(&qcelp13_info->thr, 0, omx_qcelp13_events, qcelp13_info); + if (r < 0) goto fail_thread; + + DEBUG_DETAIL("Created thread for %s \n", qcelp13_info->thread_name); + return qcelp13_info; + + +fail_thread: + close(qcelp13_info->pipe_in); + close(qcelp13_info->pipe_out); + +fail_pipe: + free(qcelp13_info); + + return 0; +} + +void omx_qcelp13_thread_stop(struct qcelp13_ipc_info *qcelp13_info) { + DEBUG_DETAIL("%s stop server\n", __FUNCTION__); + close(qcelp13_info->pipe_in); + close(qcelp13_info->pipe_out); + pthread_join(qcelp13_info->thr,NULL); + qcelp13_info->pipe_out = -1; + qcelp13_info->pipe_in = -1; + DEBUG_DETAIL("%s: message thread close fds%d %d\n", qcelp13_info->thread_name, + qcelp13_info->pipe_in,qcelp13_info->pipe_out); + free(qcelp13_info); +} + +void omx_qcelp13_post_msg(struct qcelp13_ipc_info *qcelp13_info, unsigned char id) { + DEBUG_DETAIL("\n%s id=%d\n", __FUNCTION__,id); + + write(qcelp13_info->pipe_out, &id, 1); +} diff --git a/audio/mm-audio/aenc-qcelp13/qdsp6/src/omx_qcelp13_aenc.cpp b/audio/mm-audio/aenc-qcelp13/qdsp6/src/omx_qcelp13_aenc.cpp new file mode 100644 index 0000000..399b8cf --- /dev/null +++ b/audio/mm-audio/aenc-qcelp13/qdsp6/src/omx_qcelp13_aenc.cpp @@ -0,0 +1,4532 @@ +/*-------------------------------------------------------------------------- +Copyright (c) 2010, 2014 The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of The Linux Foundation nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------*/ +/*============================================================================ +@file omx_aenc_qcelp13.c + This module contains the implementation of the OpenMAX core & component. + +*//*========================================================================*/ +////////////////////////////////////////////////////////////////////////////// +// Include Files +////////////////////////////////////////////////////////////////////////////// + + +#include +#include +#include +#include "omx_qcelp13_aenc.h" +#include + +using namespace std; +#define SLEEP_MS 100 + +// omx_cmd_queue destructor +omx_qcelp13_aenc::omx_cmd_queue::~omx_cmd_queue() +{ + // Nothing to do +} + +// omx cmd queue constructor +omx_qcelp13_aenc::omx_cmd_queue::omx_cmd_queue(): m_read(0),m_write(0),m_size(0) +{ + memset(m_q, 0,sizeof(omx_event)*OMX_CORE_CONTROL_CMDQ_SIZE); +} + +// omx cmd queue insert +bool omx_qcelp13_aenc::omx_cmd_queue::insert_entry(unsigned long p1, + unsigned long p2, + unsigned char id) +{ + bool ret = true; + if (m_size < OMX_CORE_CONTROL_CMDQ_SIZE) + { + m_q[m_write].id = id; + m_q[m_write].param1 = p1; + m_q[m_write].param2 = p2; + m_write++; + m_size ++; + if (m_write >= OMX_CORE_CONTROL_CMDQ_SIZE) + { + m_write = 0; + } + } else + { + ret = false; + DEBUG_PRINT_ERROR("ERROR!!! Command Queue Full"); + } + return ret; +} + +bool omx_qcelp13_aenc::omx_cmd_queue::pop_entry(unsigned long *p1, + unsigned long *p2, unsigned char *id) +{ + bool ret = true; + if (m_size > 0) + { + *id = m_q[m_read].id; + *p1 = m_q[m_read].param1; + *p2 = m_q[m_read].param2; + // Move the read pointer ahead + ++m_read; + --m_size; + if (m_read >= OMX_CORE_CONTROL_CMDQ_SIZE) + { + m_read = 0; + + } + } else + { + ret = false; + DEBUG_PRINT_ERROR("ERROR Delete!!! Command Queue Empty"); + } + return ret; +} + +// factory function executed by the core to create instances +void *get_omx_component_factory_fn(void) +{ + return(new omx_qcelp13_aenc); +} +bool omx_qcelp13_aenc::omx_cmd_queue::get_msg_id(unsigned char *id) +{ + if(m_size > 0) + { + *id = m_q[m_read].id; + DEBUG_PRINT("get_msg_id=%d\n",*id); + } + else{ + return false; + } + return true; +} +/*============================================================================= +FUNCTION: + wait_for_event + +DESCRIPTION: + waits for a particular event + +INPUT/OUTPUT PARAMETERS: + None + +RETURN VALUE: + None + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +void omx_qcelp13_aenc::wait_for_event() +{ + int rc; + struct timespec ts; + pthread_mutex_lock(&m_event_lock); + while (0 == m_is_event_done) + { + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += (SLEEP_MS/1000); + ts.tv_nsec += ((SLEEP_MS%1000) * 1000000); + rc = pthread_cond_timedwait(&cond, &m_event_lock, &ts); + if (rc == ETIMEDOUT && !m_is_event_done) { + DEBUG_PRINT("Timed out waiting for flush"); + if (ioctl( m_drv_fd, AUDIO_FLUSH, 0) == -1) + DEBUG_PRINT_ERROR("Flush:Input port, ioctl flush failed %d\n", + errno); + } + } + m_is_event_done = 0; + pthread_mutex_unlock(&m_event_lock); +} + +/*============================================================================= +FUNCTION: + event_complete + +DESCRIPTION: + informs about the occurance of an event + +INPUT/OUTPUT PARAMETERS: + None + +RETURN VALUE: + None + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +void omx_qcelp13_aenc::event_complete() +{ + pthread_mutex_lock(&m_event_lock); + if (0 == m_is_event_done) + { + m_is_event_done = 1; + pthread_cond_signal(&cond); + } + pthread_mutex_unlock(&m_event_lock); +} + +// All this non-sense because of a single qcelp13 object +void omx_qcelp13_aenc::in_th_goto_sleep() +{ + pthread_mutex_lock(&m_in_th_lock); + while (0 == m_is_in_th_sleep) + { + pthread_cond_wait(&in_cond, &m_in_th_lock); + } + m_is_in_th_sleep = 0; + pthread_mutex_unlock(&m_in_th_lock); +} + +void omx_qcelp13_aenc::in_th_wakeup() +{ + pthread_mutex_lock(&m_in_th_lock); + if (0 == m_is_in_th_sleep) + { + m_is_in_th_sleep = 1; + pthread_cond_signal(&in_cond); + } + pthread_mutex_unlock(&m_in_th_lock); +} + +void omx_qcelp13_aenc::out_th_goto_sleep() +{ + + pthread_mutex_lock(&m_out_th_lock); + while (0 == m_is_out_th_sleep) + { + pthread_cond_wait(&out_cond, &m_out_th_lock); + } + m_is_out_th_sleep = 0; + pthread_mutex_unlock(&m_out_th_lock); +} + +void omx_qcelp13_aenc::out_th_wakeup() +{ + pthread_mutex_lock(&m_out_th_lock); + if (0 == m_is_out_th_sleep) + { + m_is_out_th_sleep = 1; + pthread_cond_signal(&out_cond); + } + pthread_mutex_unlock(&m_out_th_lock); +} +/* ====================================================================== +FUNCTION + omx_qcelp13_aenc::omx_qcelp13_aenc + +DESCRIPTION + Constructor + +PARAMETERS + None + +RETURN VALUE + None. +========================================================================== */ +omx_qcelp13_aenc::omx_qcelp13_aenc(): m_tmp_meta_buf(NULL), + m_tmp_out_meta_buf(NULL), + m_flush_cnt(255), + m_comp_deinit(0), + m_volume(25), + m_app_data(NULL), + nNumInputBuf(0), + nNumOutputBuf(0), + m_drv_fd(-1), + bFlushinprogress(0), + is_in_th_sleep(false), + is_out_th_sleep(false), + m_flags(0), + nTimestamp(0), + pcm_input(0), + m_inp_act_buf_count (OMX_CORE_NUM_INPUT_BUFFERS), + m_out_act_buf_count (OMX_CORE_NUM_OUTPUT_BUFFERS), + m_inp_current_buf_count(0), + m_out_current_buf_count(0), + output_buffer_size((OMX_U32)OMX_QCELP13_OUTPUT_BUFFER_SIZE), + input_buffer_size(OMX_CORE_INPUT_BUFFER_SIZE), + m_session_id(0), + m_inp_bEnabled(OMX_TRUE), + m_out_bEnabled(OMX_TRUE), + m_inp_bPopulated(OMX_FALSE), + m_out_bPopulated(OMX_FALSE), + m_is_event_done(0), + m_state(OMX_StateInvalid), + m_ipc_to_in_th(NULL), + m_ipc_to_out_th(NULL), + m_ipc_to_cmd_th(NULL), + m_ipc_to_event_th(NULL) +{ + int cond_ret = 0; + component_Role.nSize = 0; + memset(&m_cmp, 0, sizeof(m_cmp)); + memset(&m_cb, 0, sizeof(m_cb)); + memset(&m_qcelp13_pb_stats, 0, sizeof(m_qcelp13_pb_stats)); + memset(&m_qcelp13_param, 0, sizeof(m_qcelp13_param)); + memset(&m_pcm_param, 0, sizeof(m_pcm_param)); + memset(&m_buffer_supplier, 0, sizeof(m_buffer_supplier)); + memset(&m_priority_mgm, 0, sizeof(m_priority_mgm)); + + pthread_mutexattr_init(&m_lock_attr); + pthread_mutex_init(&m_lock, &m_lock_attr); + pthread_mutexattr_init(&m_commandlock_attr); + pthread_mutex_init(&m_commandlock, &m_commandlock_attr); + + pthread_mutexattr_init(&m_outputlock_attr); + pthread_mutex_init(&m_outputlock, &m_outputlock_attr); + + pthread_mutexattr_init(&m_state_attr); + pthread_mutex_init(&m_state_lock, &m_state_attr); + + pthread_mutexattr_init(&m_event_attr); + pthread_mutex_init(&m_event_lock, &m_event_attr); + + pthread_mutexattr_init(&m_flush_attr); + pthread_mutex_init(&m_flush_lock, &m_flush_attr); + + pthread_mutexattr_init(&m_event_attr); + pthread_mutex_init(&m_event_lock, &m_event_attr); + + pthread_mutexattr_init(&m_in_th_attr); + pthread_mutex_init(&m_in_th_lock, &m_in_th_attr); + + pthread_mutexattr_init(&m_out_th_attr); + pthread_mutex_init(&m_out_th_lock, &m_out_th_attr); + + pthread_mutexattr_init(&m_in_th_attr_1); + pthread_mutex_init(&m_in_th_lock_1, &m_in_th_attr_1); + + pthread_mutexattr_init(&m_out_th_attr_1); + pthread_mutex_init(&m_out_th_lock_1, &m_out_th_attr_1); + + pthread_mutexattr_init(&out_buf_count_lock_attr); + pthread_mutex_init(&out_buf_count_lock, &out_buf_count_lock_attr); + + pthread_mutexattr_init(&in_buf_count_lock_attr); + pthread_mutex_init(&in_buf_count_lock, &in_buf_count_lock_attr); + if ((cond_ret = pthread_cond_init (&cond, NULL)) != 0) + { + DEBUG_PRINT_ERROR("pthread_cond_init returns non zero for cond\n"); + if (cond_ret == EAGAIN) + DEBUG_PRINT_ERROR("The system lacked necessary \ + resources(other than mem)\n"); + else if (cond_ret == ENOMEM) + DEBUG_PRINT_ERROR("Insufficient memory to initialise \ + condition variable\n"); + } + if ((cond_ret = pthread_cond_init (&in_cond, NULL)) != 0) + { + DEBUG_PRINT_ERROR("pthread_cond_init returns non zero for in_cond\n"); + if (cond_ret == EAGAIN) + DEBUG_PRINT_ERROR("The system lacked necessary \ + resources(other than mem)\n"); + else if (cond_ret == ENOMEM) + DEBUG_PRINT_ERROR("Insufficient memory to initialise \ + condition variable\n"); + } + if ((cond_ret = pthread_cond_init (&out_cond, NULL)) != 0) + { + DEBUG_PRINT_ERROR("pthread_cond_init returns non zero for out_cond\n"); + if (cond_ret == EAGAIN) + DEBUG_PRINT_ERROR("The system lacked necessary \ + resources(other than mem)\n"); + else if (cond_ret == ENOMEM) + DEBUG_PRINT_ERROR("Insufficient memory to initialise \ + condition variable\n"); + } + + sem_init(&sem_read_msg,0, 0); + sem_init(&sem_write_msg,0, 0); + sem_init(&sem_States,0, 0); + return; +} + + +/* ====================================================================== +FUNCTION + omx_qcelp13_aenc::~omx_qcelp13_aenc + +DESCRIPTION + Destructor + +PARAMETERS + None + +RETURN VALUE + None. +========================================================================== */ +omx_qcelp13_aenc::~omx_qcelp13_aenc() +{ + DEBUG_PRINT_ERROR("QCELP13 Object getting destroyed comp-deinit=%d\n", + m_comp_deinit); + if ( !m_comp_deinit ) + { + deinit_encoder(); + } + pthread_mutexattr_destroy(&m_lock_attr); + pthread_mutex_destroy(&m_lock); + + pthread_mutexattr_destroy(&m_commandlock_attr); + pthread_mutex_destroy(&m_commandlock); + + pthread_mutexattr_destroy(&m_outputlock_attr); + pthread_mutex_destroy(&m_outputlock); + + pthread_mutexattr_destroy(&m_state_attr); + pthread_mutex_destroy(&m_state_lock); + + pthread_mutexattr_destroy(&m_event_attr); + pthread_mutex_destroy(&m_event_lock); + + pthread_mutexattr_destroy(&m_flush_attr); + pthread_mutex_destroy(&m_flush_lock); + + pthread_mutexattr_destroy(&m_in_th_attr); + pthread_mutex_destroy(&m_in_th_lock); + + pthread_mutexattr_destroy(&m_out_th_attr); + pthread_mutex_destroy(&m_out_th_lock); + + pthread_mutexattr_destroy(&out_buf_count_lock_attr); + pthread_mutex_destroy(&out_buf_count_lock); + + pthread_mutexattr_destroy(&in_buf_count_lock_attr); + pthread_mutex_destroy(&in_buf_count_lock); + + pthread_mutexattr_destroy(&m_in_th_attr_1); + pthread_mutex_destroy(&m_in_th_lock_1); + + pthread_mutexattr_destroy(&m_out_th_attr_1); + pthread_mutex_destroy(&m_out_th_lock_1); + pthread_cond_destroy(&cond); + pthread_cond_destroy(&in_cond); + pthread_cond_destroy(&out_cond); + sem_destroy (&sem_read_msg); + sem_destroy (&sem_write_msg); + sem_destroy (&sem_States); + DEBUG_PRINT_ERROR("OMX QCELP13 component destroyed\n"); + return; +} + +/** + @brief memory function for sending EmptyBufferDone event + back to IL client + + @param bufHdr OMX buffer header to be passed back to IL client + @return none + */ +void omx_qcelp13_aenc::buffer_done_cb(OMX_BUFFERHEADERTYPE *bufHdr) +{ + if (m_cb.EmptyBufferDone) + { + PrintFrameHdr(OMX_COMPONENT_GENERATE_BUFFER_DONE,bufHdr); + bufHdr->nFilledLen = 0; + + m_cb.EmptyBufferDone(&m_cmp, m_app_data, bufHdr); + pthread_mutex_lock(&in_buf_count_lock); + m_qcelp13_pb_stats.ebd_cnt++; + nNumInputBuf--; + DEBUG_DETAIL("EBD CB:: in_buf_len=%d nNumInputBuf=%d\n ebd_cnt=%d",\ + m_qcelp13_pb_stats.tot_in_buf_len, + nNumInputBuf, m_qcelp13_pb_stats.ebd_cnt); + pthread_mutex_unlock(&in_buf_count_lock); + } + + return; +} + +/*============================================================================= +FUNCTION: + flush_ack + +DESCRIPTION: + + +INPUT/OUTPUT PARAMETERS: + None + +RETURN VALUE: + None + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +void omx_qcelp13_aenc::flush_ack() +{ + // Decrement the FLUSH ACK count and notify the waiting recepients + pthread_mutex_lock(&m_flush_lock); + --m_flush_cnt; + if (0 == m_flush_cnt) + { + event_complete(); + } + DEBUG_PRINT("Rxed FLUSH ACK cnt=%d\n",m_flush_cnt); + pthread_mutex_unlock(&m_flush_lock); +} +void omx_qcelp13_aenc::frame_done_cb(OMX_BUFFERHEADERTYPE *bufHdr) +{ + if (m_cb.FillBufferDone) + { + PrintFrameHdr(OMX_COMPONENT_GENERATE_FRAME_DONE,bufHdr); + m_qcelp13_pb_stats.fbd_cnt++; + pthread_mutex_lock(&out_buf_count_lock); + nNumOutputBuf--; + DEBUG_PRINT("FBD CB:: nNumOutputBuf=%d out_buf_len=%u fbd_cnt=%u\n",\ + nNumOutputBuf, + m_qcelp13_pb_stats.tot_out_buf_len, + m_qcelp13_pb_stats.fbd_cnt); + m_qcelp13_pb_stats.tot_out_buf_len += bufHdr->nFilledLen; + m_qcelp13_pb_stats.tot_pb_time = bufHdr->nTimeStamp; + DEBUG_PRINT("FBD:in_buf_len=%u out_buf_len=%u\n", + m_qcelp13_pb_stats.tot_in_buf_len, + m_qcelp13_pb_stats.tot_out_buf_len); + + pthread_mutex_unlock(&out_buf_count_lock); + m_cb.FillBufferDone(&m_cmp, m_app_data, bufHdr); + } + return; +} + +/*============================================================================= +FUNCTION: + process_out_port_msg + +DESCRIPTION: + Function for handling all commands from IL client +IL client commands are processed and callbacks are generated through +this routine Audio Command Server provides the thread context for this routine + +INPUT/OUTPUT PARAMETERS: + [INOUT] client_data + [IN] id + +RETURN VALUE: + None + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +void omx_qcelp13_aenc::process_out_port_msg(void *client_data, unsigned char id) +{ + unsigned long p1 = 0; // Parameter - 1 + unsigned long p2 = 0; // Parameter - 2 + unsigned char ident = 0; + unsigned qsize = 0; // qsize + unsigned tot_qsize = 0; + omx_qcelp13_aenc *pThis = (omx_qcelp13_aenc *) client_data; + OMX_STATETYPE state; + +loopback_out: + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + if ( state == OMX_StateLoaded ) + { + DEBUG_PRINT(" OUT: IN LOADED STATE RETURN\n"); + return; + } + pthread_mutex_lock(&pThis->m_outputlock); + + qsize = pThis->m_output_ctrl_cmd_q.m_size; + tot_qsize = pThis->m_output_ctrl_cmd_q.m_size; + tot_qsize += pThis->m_output_ctrl_fbd_q.m_size; + tot_qsize += pThis->m_output_q.m_size; + + if ( 0 == tot_qsize ) + { + pthread_mutex_unlock(&pThis->m_outputlock); + DEBUG_DETAIL("OUT-->BREAK FROM LOOP...%d\n",tot_qsize); + return; + } + if ( (state != OMX_StateExecuting) && !qsize ) + { + pthread_mutex_unlock(&pThis->m_outputlock); + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + if ( state == OMX_StateLoaded ) + return; + + DEBUG_DETAIL("OUT:1.SLEEPING OUT THREAD\n"); + pthread_mutex_lock(&pThis->m_out_th_lock_1); + pThis->is_out_th_sleep = true; + pthread_mutex_unlock(&pThis->m_out_th_lock_1); + pThis->out_th_goto_sleep(); + + /* Get the updated state */ + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + } + + if ( ((!pThis->m_output_ctrl_cmd_q.m_size) && !pThis->m_out_bEnabled) ) + { + // case where no port reconfig and nothing in the flush q + DEBUG_DETAIL("No flush/port reconfig qsize=%d tot_qsize=%d",\ + qsize,tot_qsize); + pthread_mutex_unlock(&pThis->m_outputlock); + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + if ( state == OMX_StateLoaded ) + return; + + if(pThis->m_output_ctrl_cmd_q.m_size || !(pThis->bFlushinprogress)) + { + DEBUG_PRINT("OUT:2. SLEEPING OUT THREAD \n"); + pthread_mutex_lock(&pThis->m_out_th_lock_1); + pThis->is_out_th_sleep = true; + pthread_mutex_unlock(&pThis->m_out_th_lock_1); + pThis->out_th_goto_sleep(); + } + /* Get the updated state */ + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + } + qsize = pThis->m_output_ctrl_cmd_q.m_size; + tot_qsize = pThis->m_output_ctrl_cmd_q.m_size; + tot_qsize += pThis->m_output_ctrl_fbd_q.m_size; + tot_qsize += pThis->m_output_q.m_size; + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + DEBUG_DETAIL("OUT-->QSIZE-flush=%d,fbd=%d QSIZE=%d state=%d\n",\ + pThis->m_output_ctrl_cmd_q.m_size, + pThis->m_output_ctrl_fbd_q.m_size, + pThis->m_output_q.m_size,state); + + + if (qsize) + { + // process FLUSH message + pThis->m_output_ctrl_cmd_q.pop_entry(&p1,&p2,&ident); + } else if ( (qsize = pThis->m_output_ctrl_fbd_q.m_size) && + (pThis->m_out_bEnabled) && (state == OMX_StateExecuting) ) + { + // then process EBD's + pThis->m_output_ctrl_fbd_q.pop_entry(&p1,&p2,&ident); + } else if ( (qsize = pThis->m_output_q.m_size) && + (pThis->m_out_bEnabled) && (state == OMX_StateExecuting) ) + { + // if no FLUSH and FBD's then process FTB's + pThis->m_output_q.pop_entry(&p1,&p2,&ident); + } else if ( state == OMX_StateLoaded ) + { + pthread_mutex_unlock(&pThis->m_outputlock); + DEBUG_PRINT("IN: ***in OMX_StateLoaded so exiting\n"); + return ; + } else + { + qsize = 0; + DEBUG_PRINT("OUT--> Empty Queue state=%d %d %d %d\n",state, + pThis->m_output_ctrl_cmd_q.m_size, + pThis->m_output_ctrl_fbd_q.m_size, + pThis->m_output_q.m_size); + + if(state == OMX_StatePause) + { + DEBUG_DETAIL("OUT: SLEEPING AGAIN OUT THREAD\n"); + pthread_mutex_lock(&pThis->m_out_th_lock_1); + pThis->is_out_th_sleep = true; + pthread_mutex_unlock(&pThis->m_out_th_lock_1); + pthread_mutex_unlock(&pThis->m_outputlock); + pThis->out_th_goto_sleep(); + goto loopback_out; + } + } + pthread_mutex_unlock(&pThis->m_outputlock); + + if ( qsize > 0 ) + { + id = ident; + ident = 0; + DEBUG_DETAIL("OUT->state[%d]ident[%d]flushq[%d]fbd[%d]dataq[%d]\n",\ + pThis->m_state, + ident, + pThis->m_output_ctrl_cmd_q.m_size, + pThis->m_output_ctrl_fbd_q.m_size, + pThis->m_output_q.m_size); + + if ( OMX_COMPONENT_GENERATE_FRAME_DONE == id ) + { + pThis->frame_done_cb((OMX_BUFFERHEADERTYPE *)p2); + } else if ( OMX_COMPONENT_GENERATE_FTB == id ) + { + pThis->fill_this_buffer_proxy((OMX_HANDLETYPE)p1, + (OMX_BUFFERHEADERTYPE *)p2); + } else if ( OMX_COMPONENT_GENERATE_EOS == id ) + { + pThis->m_cb.EventHandler(&pThis->m_cmp, + pThis->m_app_data, + OMX_EventBufferFlag, + 1, 1, NULL ); + + } + else if(id == OMX_COMPONENT_RESUME) + { + DEBUG_PRINT("RESUMED...\n"); + } + else if(id == OMX_COMPONENT_GENERATE_COMMAND) + { + // Execute FLUSH command + if ( OMX_CommandFlush == p1 ) + { + DEBUG_DETAIL("Executing FLUSH command on Output port\n"); + pThis->execute_output_omx_flush(); + } else + { + DEBUG_DETAIL("Invalid command[%lu]\n",p1); + } + } else + { + DEBUG_PRINT_ERROR("ERROR:OUT-->Invalid Id[%d]\n",id); + } + } else + { + DEBUG_DETAIL("ERROR: OUT--> Empty OUTPUTQ\n"); + } + + return; +} + +/*============================================================================= +FUNCTION: + process_command_msg + +DESCRIPTION: + + +INPUT/OUTPUT PARAMETERS: + [INOUT] client_data + [IN] id + +RETURN VALUE: + None + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +void omx_qcelp13_aenc::process_command_msg(void *client_data, unsigned char id) +{ + unsigned long p1 = 0; // Parameter - 1 + unsigned long p2 = 0; // Parameter - 2 + unsigned char ident = 0; + unsigned qsize = 0; + omx_qcelp13_aenc *pThis = (omx_qcelp13_aenc*)client_data; + pthread_mutex_lock(&pThis->m_commandlock); + + qsize = pThis->m_command_q.m_size; + DEBUG_DETAIL("CMD-->QSIZE=%d state=%d\n",pThis->m_command_q.m_size, + pThis->m_state); + + if (!qsize) + { + DEBUG_DETAIL("CMD-->BREAKING FROM LOOP\n"); + pthread_mutex_unlock(&pThis->m_commandlock); + return; + } else + { + pThis->m_command_q.pop_entry(&p1,&p2,&ident); + } + pthread_mutex_unlock(&pThis->m_commandlock); + + id = ident; + DEBUG_DETAIL("CMD->state[%d]id[%d]cmdq[%d]n",\ + pThis->m_state,ident, \ + pThis->m_command_q.m_size); + + if (OMX_COMPONENT_GENERATE_EVENT == id) + { + if (pThis->m_cb.EventHandler) + { + if (OMX_CommandStateSet == p1) + { + pthread_mutex_lock(&pThis->m_state_lock); + pThis->m_state = (OMX_STATETYPE) p2; + pthread_mutex_unlock(&pThis->m_state_lock); + DEBUG_PRINT("CMD:Process->state set to %d \n", \ + pThis->m_state); + + if (pThis->m_state == OMX_StateExecuting || + pThis->m_state == OMX_StateLoaded) + { + + pthread_mutex_lock(&pThis->m_in_th_lock_1); + if (pThis->is_in_th_sleep) + { + pThis->is_in_th_sleep = false; + DEBUG_DETAIL("CMD:WAKING UP IN THREADS\n"); + pThis->in_th_wakeup(); + } + pthread_mutex_unlock(&pThis->m_in_th_lock_1); + + pthread_mutex_lock(&pThis->m_out_th_lock_1); + if (pThis->is_out_th_sleep) + { + DEBUG_DETAIL("CMD:WAKING UP OUT THREADS\n"); + pThis->is_out_th_sleep = false; + pThis->out_th_wakeup(); + } + pthread_mutex_unlock(&pThis->m_out_th_lock_1); + } + } + if (OMX_StateInvalid == pThis->m_state) + { + pThis->m_cb.EventHandler(&pThis->m_cmp, + pThis->m_app_data, + OMX_EventError, + OMX_ErrorInvalidState, + 0, NULL ); + } else if ((signed)p2 == OMX_ErrorPortUnpopulated) + { + pThis->m_cb.EventHandler(&pThis->m_cmp, + pThis->m_app_data, + OMX_EventError, + (OMX_U32)p2, + 0, + 0 ); + } else + { + pThis->m_cb.EventHandler(&pThis->m_cmp, + pThis->m_app_data, + OMX_EventCmdComplete, + (OMX_U32)p1, (OMX_U32)p2, NULL ); + } + } else + { + DEBUG_PRINT_ERROR("ERROR:CMD-->EventHandler NULL \n"); + } + } else if (OMX_COMPONENT_GENERATE_COMMAND == id) + { + pThis->send_command_proxy(&pThis->m_cmp, + (OMX_COMMANDTYPE)p1, + (OMX_U32)p2,(OMX_PTR)NULL); + } else if (OMX_COMPONENT_PORTSETTINGS_CHANGED == id) + { + DEBUG_DETAIL("CMD-->RXED PORTSETTINGS_CHANGED"); + pThis->m_cb.EventHandler(&pThis->m_cmp, + pThis->m_app_data, + OMX_EventPortSettingsChanged, + 1, 1, NULL ); + } + else + { + DEBUG_PRINT_ERROR("CMD->state[%d]id[%d]\n",pThis->m_state,ident); + } + return; +} + +/*============================================================================= +FUNCTION: + process_in_port_msg + +DESCRIPTION: + + +INPUT/OUTPUT PARAMETERS: + [INOUT] client_data + [IN] id + +RETURN VALUE: + None + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +void omx_qcelp13_aenc::process_in_port_msg(void *client_data, unsigned char id) +{ + unsigned long p1 = 0; // Parameter - 1 + unsigned long p2 = 0; // Parameter - 2 + unsigned char ident = 0; + unsigned qsize = 0; + unsigned tot_qsize = 0; + omx_qcelp13_aenc *pThis = (omx_qcelp13_aenc *) client_data; + OMX_STATETYPE state; + + if (!pThis) + { + DEBUG_PRINT_ERROR("ERROR:IN--> Invalid Obj \n"); + return; + } +loopback_in: + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + if ( state == OMX_StateLoaded ) + { + DEBUG_PRINT(" IN: IN LOADED STATE RETURN\n"); + return; + } + // Protect the shared queue data structure + pthread_mutex_lock(&pThis->m_lock); + + qsize = pThis->m_input_ctrl_cmd_q.m_size; + tot_qsize = qsize; + tot_qsize += pThis->m_input_ctrl_ebd_q.m_size; + tot_qsize += pThis->m_input_q.m_size; + + if ( 0 == tot_qsize ) + { + DEBUG_DETAIL("IN-->BREAKING FROM IN LOOP"); + pthread_mutex_unlock(&pThis->m_lock); + return; + } + + if ( (state != OMX_StateExecuting) && ! (pThis->m_input_ctrl_cmd_q.m_size)) + { + pthread_mutex_unlock(&pThis->m_lock); + DEBUG_DETAIL("SLEEPING IN THREAD\n"); + pthread_mutex_lock(&pThis->m_in_th_lock_1); + pThis->is_in_th_sleep = true; + pthread_mutex_unlock(&pThis->m_in_th_lock_1); + pThis->in_th_goto_sleep(); + + /* Get the updated state */ + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + } + else if ((state == OMX_StatePause)) + { + if(!(pThis->m_input_ctrl_cmd_q.m_size)) + { + pthread_mutex_unlock(&pThis->m_lock); + + DEBUG_DETAIL("IN: SLEEPING IN THREAD\n"); + pthread_mutex_lock(&pThis->m_in_th_lock_1); + pThis->is_in_th_sleep = true; + pthread_mutex_unlock(&pThis->m_in_th_lock_1); + pThis->in_th_goto_sleep(); + + pthread_mutex_lock(&pThis->m_state_lock); + pThis->get_state(&pThis->m_cmp, &state); + pthread_mutex_unlock(&pThis->m_state_lock); + } + } + + qsize = pThis->m_input_ctrl_cmd_q.m_size; + tot_qsize = qsize; + tot_qsize += pThis->m_input_ctrl_ebd_q.m_size; + tot_qsize += pThis->m_input_q.m_size; + + DEBUG_DETAIL("Input-->QSIZE-flush=%d,ebd=%d QSIZE=%d state=%d\n",\ + pThis->m_input_ctrl_cmd_q.m_size, + pThis->m_input_ctrl_ebd_q.m_size, + pThis->m_input_q.m_size, state); + + + if ( qsize ) + { + // process FLUSH message + pThis->m_input_ctrl_cmd_q.pop_entry(&p1,&p2,&ident); + } else if ( (qsize = pThis->m_input_ctrl_ebd_q.m_size) && + (state == OMX_StateExecuting) ) + { + // then process EBD's + pThis->m_input_ctrl_ebd_q.pop_entry(&p1,&p2,&ident); + } else if ((qsize = pThis->m_input_q.m_size) && + (state == OMX_StateExecuting)) + { + // if no FLUSH and EBD's then process ETB's + pThis->m_input_q.pop_entry(&p1, &p2, &ident); + } else if ( state == OMX_StateLoaded ) + { + pthread_mutex_unlock(&pThis->m_lock); + DEBUG_PRINT("IN: ***in OMX_StateLoaded so exiting\n"); + return ; + } else + { + qsize = 0; + DEBUG_PRINT("IN-->state[%d]cmdq[%d]ebdq[%d]in[%d]\n",\ + state,pThis->m_input_ctrl_cmd_q.m_size, + pThis->m_input_ctrl_ebd_q.m_size, + pThis->m_input_q.m_size); + + if(state == OMX_StatePause) + { + DEBUG_DETAIL("IN: SLEEPING AGAIN IN THREAD\n"); + pthread_mutex_lock(&pThis->m_in_th_lock_1); + pThis->is_in_th_sleep = true; + pthread_mutex_unlock(&pThis->m_in_th_lock_1); + pthread_mutex_unlock(&pThis->m_lock); + pThis->in_th_goto_sleep(); + goto loopback_in; + } + } + pthread_mutex_unlock(&pThis->m_lock); + + if ( qsize > 0 ) + { + id = ident; + DEBUG_DETAIL("Input->state[%d]id[%d]flushq[%d]ebdq[%d]dataq[%d]\n",\ + pThis->m_state, + ident, + pThis->m_input_ctrl_cmd_q.m_size, + pThis->m_input_ctrl_ebd_q.m_size, + pThis->m_input_q.m_size); + if ( OMX_COMPONENT_GENERATE_BUFFER_DONE == id ) + { + pThis->buffer_done_cb((OMX_BUFFERHEADERTYPE *)p2); + } + else if(id == OMX_COMPONENT_GENERATE_EOS) + { + pThis->m_cb.EventHandler(&pThis->m_cmp, pThis->m_app_data, + OMX_EventBufferFlag, 0, 1, NULL ); + } else if ( OMX_COMPONENT_GENERATE_ETB == id ) + { + pThis->empty_this_buffer_proxy((OMX_HANDLETYPE)p1, + (OMX_BUFFERHEADERTYPE *)p2); + } else if ( OMX_COMPONENT_GENERATE_COMMAND == id ) + { + // Execute FLUSH command + if ( OMX_CommandFlush == p1 ) + { + DEBUG_DETAIL(" Executing FLUSH command on Input port\n"); + pThis->execute_input_omx_flush(); + } else + { + DEBUG_DETAIL("Invalid command[%lu]\n",p1); + } + } + else + { + DEBUG_PRINT_ERROR("ERROR:IN-->Invalid Id[%d]\n",id); + } + } else + { + DEBUG_DETAIL("ERROR:IN-->Empty INPUT Q\n"); + } + return; +} + +/** + @brief member function for performing component initialization + + @param role C string mandating role of this component + @return Error status + */ +OMX_ERRORTYPE omx_qcelp13_aenc::component_init(OMX_STRING role) +{ + + OMX_ERRORTYPE eRet = OMX_ErrorNone; + m_state = OMX_StateLoaded; + + /* DSP does not give information about the bitstream + randomly assign the value right now. Query will result in + incorrect param */ + memset(&m_qcelp13_param, 0, sizeof(m_qcelp13_param)); + m_qcelp13_param.nSize = (OMX_U32)sizeof(m_qcelp13_param); + m_qcelp13_param.nChannels = OMX_QCELP13_DEFAULT_CH_CFG; + //Current DSP does not have config + m_qcelp13_param.eCDMARate = OMX_AUDIO_CDMARateFull; + m_qcelp13_param.nMinBitRate = OMX_QCELP13_DEFAULT_MINRATE; + m_qcelp13_param.nMaxBitRate = OMX_QCELP13_DEFAULT_MAXRATE; + m_volume = OMX_QCELP13_DEFAULT_VOL; /* Close to unity gain */ + memset(&m_qcelp13_pb_stats,0,sizeof(QCELP13_PB_STATS)); + memset(&m_pcm_param, 0, sizeof(m_pcm_param)); + m_pcm_param.nSize = (OMX_U32)sizeof(m_pcm_param); + m_pcm_param.nChannels = OMX_QCELP13_DEFAULT_CH_CFG; + m_pcm_param.nSamplingRate = OMX_QCELP13_DEFAULT_SF; + nTimestamp = 0; + + + nNumInputBuf = 0; + nNumOutputBuf = 0; + m_ipc_to_in_th = NULL; // Command server instance + m_ipc_to_out_th = NULL; // Client server instance + m_ipc_to_cmd_th = NULL; // command instance + m_is_out_th_sleep = 0; + m_is_in_th_sleep = 0; + is_out_th_sleep= false; + + is_in_th_sleep=false; + + memset(&m_priority_mgm, 0, sizeof(m_priority_mgm)); + m_priority_mgm.nGroupID =0; + m_priority_mgm.nGroupPriority=0; + + memset(&m_buffer_supplier, 0, sizeof(m_buffer_supplier)); + m_buffer_supplier.nPortIndex=OMX_BufferSupplyUnspecified; + + DEBUG_PRINT_ERROR(" component init: role = %s\n",role); + + DEBUG_PRINT(" component init: role = %s\n",role); + component_Role.nVersion.nVersion = OMX_SPEC_VERSION; + if (!strcmp(role,"OMX.qcom.audio.encoder.qcelp13")) + { + pcm_input = 1; + component_Role.nSize = (OMX_U32)sizeof(role); + strlcpy((char *)component_Role.cRole, (const char*)role, + sizeof(component_Role.cRole)); + DEBUG_PRINT("\ncomponent_init: Component %s LOADED \n", role); + } else if (!strcmp(role,"OMX.qcom.audio.encoder.tunneled.qcelp13")) + { + pcm_input = 0; + component_Role.nSize = (OMX_U32)sizeof(role); + strlcpy((char *)component_Role.cRole, (const char*)role, + sizeof(component_Role.cRole)); + DEBUG_PRINT("\ncomponent_init: Component %s LOADED \n", role); + } else + { + component_Role.nSize = (OMX_U32)sizeof("\0"); + strlcpy((char *)component_Role.cRole, (const char*)"\0", + sizeof(component_Role.cRole)); + DEBUG_PRINT("\ncomponent_init: Component %s LOADED is invalid\n", role); + } + if(pcm_input) + { + + + m_tmp_meta_buf = (OMX_U8*) malloc(sizeof(OMX_U8) * + (OMX_CORE_INPUT_BUFFER_SIZE + sizeof(META_IN))); + + if (m_tmp_meta_buf == NULL){ + DEBUG_PRINT_ERROR("Mem alloc failed for in meta buf\n"); + return OMX_ErrorInsufficientResources; + } + } + m_tmp_out_meta_buf = + (OMX_U8*)malloc(sizeof(OMX_U8)*OMX_QCELP13_OUTPUT_BUFFER_SIZE); + if ( m_tmp_out_meta_buf == NULL ) { + DEBUG_PRINT_ERROR("Mem alloc failed for out meta buf\n"); + return OMX_ErrorInsufficientResources; + } + + if(0 == pcm_input) + { + m_drv_fd = open("/dev/msm_qcelp_in",O_RDONLY); + DEBUG_PRINT("Driver in Tunnel mode open\n"); + } + else + { + m_drv_fd = open("/dev/msm_qcelp_in",O_RDWR); + DEBUG_PRINT("Driver in Non Tunnel mode open\n"); + } + if (m_drv_fd < 0) + { + DEBUG_PRINT_ERROR("Component_init Open Failed[%d] errno[%d]",\ + m_drv_fd,errno); + + return OMX_ErrorInsufficientResources; + } + if(ioctl(m_drv_fd, AUDIO_GET_SESSION_ID,&m_session_id) == -1) + { + DEBUG_PRINT_ERROR("AUDIO_GET_SESSION_ID FAILED\n"); + } + if(pcm_input) + { + if (!m_ipc_to_in_th) + { + m_ipc_to_in_th = omx_qcelp13_thread_create(process_in_port_msg, + this, (char *)"INPUT_THREAD"); + if (!m_ipc_to_in_th) + { + DEBUG_PRINT_ERROR("ERROR!!! Failed to start \ + Input port thread\n"); + return OMX_ErrorInsufficientResources; + } + } + } + + if (!m_ipc_to_cmd_th) + { + m_ipc_to_cmd_th = omx_qcelp13_thread_create(process_command_msg, + this, (char *)"CMD_THREAD"); + if (!m_ipc_to_cmd_th) + { + DEBUG_PRINT_ERROR("ERROR!!!Failed to start " + "command message thread\n"); + return OMX_ErrorInsufficientResources; + } + } + + if (!m_ipc_to_out_th) + { + m_ipc_to_out_th = omx_qcelp13_thread_create(process_out_port_msg, + this, (char *)"OUTPUT_THREAD"); + if (!m_ipc_to_out_th) + { + DEBUG_PRINT_ERROR("ERROR!!! Failed to start output " + "port thread\n"); + return OMX_ErrorInsufficientResources; + } + } + return eRet; +} + +/** + + @brief member function to retrieve version of component + + + + @param hComp handle to this component instance + @param componentName name of component + @param componentVersion pointer to memory space which stores the + version number + @param specVersion pointer to memory sapce which stores version of + openMax specification + @param componentUUID + @return Error status + */ +OMX_ERRORTYPE omx_qcelp13_aenc::get_component_version +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_OUT OMX_STRING componentName, + OMX_OUT OMX_VERSIONTYPE* componentVersion, + OMX_OUT OMX_VERSIONTYPE* specVersion, + OMX_OUT OMX_UUIDTYPE* componentUUID) +{ + if((hComp == NULL) || (componentName == NULL) || + (specVersion == NULL) || (componentUUID == NULL)) + { + componentVersion = NULL; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_state == OMX_StateInvalid) + { + DEBUG_PRINT_ERROR("Get Comp Version in Invalid State\n"); + return OMX_ErrorInvalidState; + } + componentVersion->nVersion = OMX_SPEC_VERSION; + specVersion->nVersion = OMX_SPEC_VERSION; + return OMX_ErrorNone; +} +/** + @brief member function handles command from IL client + + This function simply queue up commands from IL client. + Commands will be processed in command server thread context later + + @param hComp handle to component instance + @param cmd type of command + @param param1 parameters associated with the command type + @param cmdData + @return Error status +*/ +OMX_ERRORTYPE omx_qcelp13_aenc::send_command(OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_COMMANDTYPE cmd, + OMX_IN OMX_U32 param1, + OMX_IN OMX_PTR cmdData) +{ + int portIndex = (int)param1; + + if(hComp == NULL) + { + cmdData = cmdData; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (OMX_StateInvalid == m_state) + { + return OMX_ErrorInvalidState; + } + if ( (cmd == OMX_CommandFlush) && (portIndex > 1) ) + { + return OMX_ErrorBadPortIndex; + } + post_command((unsigned)cmd,(unsigned)param1,OMX_COMPONENT_GENERATE_COMMAND); + DEBUG_PRINT("Send Command : returns with OMX_ErrorNone \n"); + DEBUG_PRINT("send_command : recieved state before semwait= %u\n",param1); + sem_wait (&sem_States); + DEBUG_PRINT("send_command : recieved state after semwait\n"); + return OMX_ErrorNone; +} + +/** + @brief member function performs actual processing of commands excluding + empty buffer call + + @param hComp handle to component + @param cmd command type + @param param1 parameter associated with the command + @param cmdData + + @return error status +*/ +OMX_ERRORTYPE omx_qcelp13_aenc::send_command_proxy(OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_COMMANDTYPE cmd, + OMX_IN OMX_U32 param1, + OMX_IN OMX_PTR cmdData) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + // Handle only IDLE and executing + OMX_STATETYPE eState = (OMX_STATETYPE) param1; + int bFlag = 1; + nState = eState; + + if(hComp == NULL) + { + cmdData = cmdData; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (OMX_CommandStateSet == cmd) + { + /***************************/ + /* Current State is Loaded */ + /***************************/ + if (OMX_StateLoaded == m_state) + { + if (OMX_StateIdle == eState) + { + + if (allocate_done() || + (m_inp_bEnabled == OMX_FALSE + && m_out_bEnabled == OMX_FALSE)) + { + DEBUG_PRINT("SCP-->Allocate Done Complete\n"); + } + else + { + DEBUG_PRINT("SCP-->Loaded to Idle-Pending\n"); + BITMASK_SET(&m_flags, OMX_COMPONENT_IDLE_PENDING); + bFlag = 0; + } + + } else if (eState == OMX_StateLoaded) + { + DEBUG_PRINT("OMXCORE-SM: Loaded-->Loaded\n"); + m_cb.EventHandler(&this->m_cmp, + this->m_app_data, + OMX_EventError, + OMX_ErrorSameState, + 0, NULL ); + eRet = OMX_ErrorSameState; + } + + else if (eState == OMX_StateWaitForResources) + { + DEBUG_PRINT("OMXCORE-SM: Loaded-->WaitForResources\n"); + eRet = OMX_ErrorNone; + } + + else if (eState == OMX_StateExecuting) + { + DEBUG_PRINT("OMXCORE-SM: Loaded-->Executing\n"); + m_cb.EventHandler(&this->m_cmp, + this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } + + else if (eState == OMX_StatePause) + { + DEBUG_PRINT("OMXCORE-SM: Loaded-->Pause\n"); + m_cb.EventHandler(&this->m_cmp, + this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } + + else if (eState == OMX_StateInvalid) + { + DEBUG_PRINT("OMXCORE-SM: Loaded-->Invalid\n"); + m_cb.EventHandler(&this->m_cmp, + this->m_app_data, + OMX_EventError, + OMX_ErrorInvalidState, + 0, NULL ); + m_state = OMX_StateInvalid; + eRet = OMX_ErrorInvalidState; + } else + { + DEBUG_PRINT_ERROR("SCP-->Loaded to Invalid(%d))\n",eState); + eRet = OMX_ErrorBadParameter; + } + } + + /***************************/ + /* Current State is IDLE */ + /***************************/ + else if (OMX_StateIdle == m_state) + { + if (OMX_StateLoaded == eState) + { + if (release_done(-1)) + { + if (ioctl(m_drv_fd, AUDIO_STOP, 0) == -1) + { + DEBUG_PRINT_ERROR("SCP:Idle->Loaded,\ + ioctl stop failed %d\n", errno); + } + + nTimestamp=0; + + DEBUG_PRINT("SCP-->Idle to Loaded\n"); + } else + { + DEBUG_PRINT("SCP--> Idle to Loaded-Pending\n"); + BITMASK_SET(&m_flags, OMX_COMPONENT_LOADING_PENDING); + // Skip the event notification + bFlag = 0; + } + } + else if (OMX_StateExecuting == eState) + { + + struct msm_audio_qcelp_enc_config drv_qcelp13_enc_config; + struct msm_audio_stream_config drv_stream_config; + struct msm_audio_buf_cfg buf_cfg; + struct msm_audio_config pcm_cfg; + + if(ioctl(m_drv_fd, AUDIO_GET_STREAM_CONFIG, &drv_stream_config) + == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_GET_STREAM_CONFIG failed, \ + errno[%d]\n", errno); + } + if(ioctl(m_drv_fd, AUDIO_SET_STREAM_CONFIG, &drv_stream_config) + == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_SET_STREAM_CONFIG failed, \ + errno[%d]\n", errno); + } + + if(ioctl(m_drv_fd, AUDIO_GET_QCELP_ENC_CONFIG, + &drv_qcelp13_enc_config) == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_GET_QCELP_ENC_CONFIG failed,\ + errno[%d]\n", errno); + } + drv_qcelp13_enc_config.min_bit_rate = m_qcelp13_param.nMinBitRate; + drv_qcelp13_enc_config.max_bit_rate = m_qcelp13_param.nMaxBitRate; + if(ioctl(m_drv_fd, AUDIO_SET_QCELP_ENC_CONFIG, &drv_qcelp13_enc_config) + == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_SET_QCELP_ENC_CONFIG \ + failed, errno[%d]\n", errno); + } + if (ioctl(m_drv_fd, AUDIO_GET_BUF_CFG, &buf_cfg) == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_GET_BUF_CFG, errno[%d]\n", + errno); + } + buf_cfg.meta_info_enable = 1; + buf_cfg.frames_per_buf = NUMOFFRAMES; + if (ioctl(m_drv_fd, AUDIO_SET_BUF_CFG, &buf_cfg) == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_SET_BUF_CFG, errno[%d]\n", + errno); + } + if(pcm_input) + { + if (ioctl(m_drv_fd, AUDIO_GET_CONFIG, &pcm_cfg) == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_GET_CONFIG, errno[%d]\n", + errno); + } + pcm_cfg.channel_count = m_pcm_param.nChannels; + pcm_cfg.sample_rate = m_pcm_param.nSamplingRate; + DEBUG_PRINT("pcm config %u %u\n",m_pcm_param.nChannels, + m_pcm_param.nSamplingRate); + + if (ioctl(m_drv_fd, AUDIO_SET_CONFIG, &pcm_cfg) == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_SET_CONFIG, errno[%d]\n", + errno); + } + } + if(ioctl(m_drv_fd, AUDIO_START, 0) == -1) + { + DEBUG_PRINT_ERROR("ioctl AUDIO_START failed, errno[%d]\n", + errno); + } + DEBUG_PRINT("SCP-->Idle to Executing\n"); + nState = eState; + } else if (eState == OMX_StateIdle) + { + DEBUG_PRINT("OMXCORE-SM: Idle-->Idle\n"); + m_cb.EventHandler(&this->m_cmp, + this->m_app_data, + OMX_EventError, + OMX_ErrorSameState, + 0, NULL ); + eRet = OMX_ErrorSameState; + } else if (eState == OMX_StateWaitForResources) + { + DEBUG_PRINT("OMXCORE-SM: Idle-->WaitForResources\n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } + + else if (eState == OMX_StatePause) + { + DEBUG_PRINT("OMXCORE-SM: Idle-->Pause\n"); + } + + else if (eState == OMX_StateInvalid) + { + DEBUG_PRINT("OMXCORE-SM: Idle-->Invalid\n"); + m_state = OMX_StateInvalid; + this->m_cb.EventHandler(&this->m_cmp, + this->m_app_data, + OMX_EventError, + OMX_ErrorInvalidState, + 0, NULL ); + eRet = OMX_ErrorInvalidState; + } else + { + DEBUG_PRINT_ERROR("SCP--> Idle to %d Not Handled\n",eState); + eRet = OMX_ErrorBadParameter; + } + } + + /******************************/ + /* Current State is Executing */ + /******************************/ + else if (OMX_StateExecuting == m_state) + { + if (OMX_StateIdle == eState) + { + DEBUG_PRINT("SCP-->Executing to Idle \n"); + if(pcm_input) + execute_omx_flush(-1,false); + else + execute_omx_flush(1,false); + + + } else if (OMX_StatePause == eState) + { + DEBUG_DETAIL("*************************\n"); + DEBUG_PRINT("SCP-->RXED PAUSE STATE\n"); + DEBUG_DETAIL("*************************\n"); + //ioctl(m_drv_fd, AUDIO_PAUSE, 0); + } else if (eState == OMX_StateLoaded) + { + DEBUG_PRINT("\n OMXCORE-SM: Executing --> Loaded \n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } else if (eState == OMX_StateWaitForResources) + { + DEBUG_PRINT("\n OMXCORE-SM: Executing --> WaitForResources \n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } else if (eState == OMX_StateExecuting) + { + DEBUG_PRINT("\n OMXCORE-SM: Executing --> Executing \n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, OMX_ErrorSameState, + 0, NULL ); + eRet = OMX_ErrorSameState; + } else if (eState == OMX_StateInvalid) + { + DEBUG_PRINT("\n OMXCORE-SM: Executing --> Invalid \n"); + m_state = OMX_StateInvalid; + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, OMX_ErrorInvalidState, + 0, NULL ); + eRet = OMX_ErrorInvalidState; + } else + { + DEBUG_PRINT_ERROR("SCP--> Executing to %d Not Handled\n", + eState); + eRet = OMX_ErrorBadParameter; + } + } + /***************************/ + /* Current State is Pause */ + /***************************/ + else if (OMX_StatePause == m_state) + { + if( (eState == OMX_StateExecuting || eState == OMX_StateIdle) ) + { + pthread_mutex_lock(&m_out_th_lock_1); + if(is_out_th_sleep) + { + DEBUG_DETAIL("PE: WAKING UP OUT THREAD\n"); + is_out_th_sleep = false; + out_th_wakeup(); + } + pthread_mutex_unlock(&m_out_th_lock_1); + } + if ( OMX_StateExecuting == eState ) + { + nState = eState; + } else if ( OMX_StateIdle == eState ) + { + DEBUG_PRINT("SCP-->Paused to Idle \n"); + DEBUG_PRINT ("\n Internal flush issued"); + pthread_mutex_lock(&m_flush_lock); + m_flush_cnt = 2; + pthread_mutex_unlock(&m_flush_lock); + if(pcm_input) + execute_omx_flush(-1,false); + else + execute_omx_flush(1,false); + + } else if ( eState == OMX_StateLoaded ) + { + DEBUG_PRINT("\n Pause --> loaded \n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } else if (eState == OMX_StateWaitForResources) + { + DEBUG_PRINT("\n Pause --> WaitForResources \n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } else if (eState == OMX_StatePause) + { + DEBUG_PRINT("\n Pause --> Pause \n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, OMX_ErrorSameState, + 0, NULL ); + eRet = OMX_ErrorSameState; + } else if (eState == OMX_StateInvalid) + { + DEBUG_PRINT("\n Pause --> Invalid \n"); + m_state = OMX_StateInvalid; + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, OMX_ErrorInvalidState, + 0, NULL ); + eRet = OMX_ErrorInvalidState; + } else + { + DEBUG_PRINT("SCP-->Paused to %d Not Handled\n",eState); + eRet = OMX_ErrorBadParameter; + } + } + /**************************************/ + /* Current State is WaitForResources */ + /**************************************/ + else if (m_state == OMX_StateWaitForResources) + { + if (eState == OMX_StateLoaded) + { + DEBUG_PRINT("OMXCORE-SM: WaitForResources-->Loaded\n"); + } else if (eState == OMX_StateWaitForResources) + { + DEBUG_PRINT("OMXCORE-SM: \ + WaitForResources-->WaitForResources\n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, OMX_ErrorSameState, + 0, NULL ); + eRet = OMX_ErrorSameState; + } else if (eState == OMX_StateExecuting) + { + DEBUG_PRINT("OMXCORE-SM: WaitForResources-->Executing\n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } else if (eState == OMX_StatePause) + { + DEBUG_PRINT("OMXCORE-SM: WaitForResources-->Pause\n"); + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorIncorrectStateTransition, + 0, NULL ); + eRet = OMX_ErrorIncorrectStateTransition; + } else if (eState == OMX_StateInvalid) + { + DEBUG_PRINT("OMXCORE-SM: WaitForResources-->Invalid\n"); + m_state = OMX_StateInvalid; + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, + OMX_ErrorInvalidState, + 0, NULL ); + eRet = OMX_ErrorInvalidState; + } else + { + DEBUG_PRINT_ERROR("SCP--> %d to %d(Not Handled)\n", + m_state,eState); + eRet = OMX_ErrorBadParameter; + } + } + /****************************/ + /* Current State is Invalid */ + /****************************/ + else if (m_state == OMX_StateInvalid) + { + if (OMX_StateLoaded == eState || OMX_StateWaitForResources == eState + || OMX_StateIdle == eState || OMX_StateExecuting == eState + || OMX_StatePause == eState || OMX_StateInvalid == eState) + { + DEBUG_PRINT("OMXCORE-SM: Invalid-->Loaded/Idle/Executing" + "/Pause/Invalid/WaitForResources\n"); + m_state = OMX_StateInvalid; + this->m_cb.EventHandler(&this->m_cmp, this->m_app_data, + OMX_EventError, OMX_ErrorInvalidState, + 0, NULL ); + eRet = OMX_ErrorInvalidState; + } + } else + { + DEBUG_PRINT_ERROR("OMXCORE-SM: %d --> %d(Not Handled)\n",\ + m_state,eState); + eRet = OMX_ErrorBadParameter; + } + } else if (OMX_CommandFlush == cmd) + { + DEBUG_DETAIL("*************************\n"); + DEBUG_PRINT("SCP-->RXED FLUSH COMMAND port=%u\n",param1); + DEBUG_DETAIL("*************************\n"); + bFlag = 0; + if ( param1 == OMX_CORE_INPUT_PORT_INDEX || + param1 == OMX_CORE_OUTPUT_PORT_INDEX || + (signed)param1 == -1 ) + { + execute_omx_flush(param1); + } else + { + eRet = OMX_ErrorBadPortIndex; + m_cb.EventHandler(&m_cmp, m_app_data, OMX_EventError, + OMX_CommandFlush, OMX_ErrorBadPortIndex, NULL ); + } + } else if ( cmd == OMX_CommandPortDisable ) + { + bFlag = 0; + if ( param1 == OMX_CORE_INPUT_PORT_INDEX || param1 == OMX_ALL ) + { + DEBUG_PRINT("SCP: Disabling Input port Indx\n"); + m_inp_bEnabled = OMX_FALSE; + if ( (m_state == OMX_StateLoaded || m_state == OMX_StateIdle) + && release_done(0) ) + { + DEBUG_PRINT("send_command_proxy:OMX_CommandPortDisable:\ + OMX_CORE_INPUT_PORT_INDEX:release_done \n"); + DEBUG_PRINT("************* OMX_CommandPortDisable:\ + m_inp_bEnabled=%d********\n",m_inp_bEnabled); + + post_command(OMX_CommandPortDisable, + OMX_CORE_INPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + } + + else + { + if (m_state == OMX_StatePause ||m_state == OMX_StateExecuting) + { + DEBUG_PRINT("SCP: execute_omx_flush in Disable in "\ + " param1=%u m_state=%d \n",param1, m_state); + execute_omx_flush(param1); + } + DEBUG_PRINT("send_command_proxy:OMX_CommandPortDisable:\ + OMX_CORE_INPUT_PORT_INDEX \n"); + BITMASK_SET(&m_flags, OMX_COMPONENT_INPUT_DISABLE_PENDING); + // Skip the event notification + + } + + } + if (param1 == OMX_CORE_OUTPUT_PORT_INDEX || param1 == OMX_ALL) + { + + DEBUG_PRINT("SCP: Disabling Output port Indx\n"); + m_out_bEnabled = OMX_FALSE; + if ((m_state == OMX_StateLoaded || m_state == OMX_StateIdle) + && release_done(1)) + { + DEBUG_PRINT("send_command_proxy:OMX_CommandPortDisable:\ + OMX_CORE_OUTPUT_PORT_INDEX:release_done \n"); + DEBUG_PRINT("************* OMX_CommandPortDisable:\ + m_out_bEnabled=%d********\n",m_inp_bEnabled); + + post_command(OMX_CommandPortDisable, + OMX_CORE_OUTPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + } else + { + if (m_state == OMX_StatePause ||m_state == OMX_StateExecuting) + { + DEBUG_PRINT("SCP: execute_omx_flush in Disable out "\ + "param1=%u m_state=%d \n",param1, m_state); + execute_omx_flush(param1); + } + BITMASK_SET(&m_flags, OMX_COMPONENT_OUTPUT_DISABLE_PENDING); + // Skip the event notification + + } + } else + { + DEBUG_PRINT_ERROR("OMX_CommandPortDisable: disable wrong port ID"); + } + + } else if (cmd == OMX_CommandPortEnable) + { + bFlag = 0; + if (param1 == OMX_CORE_INPUT_PORT_INDEX || param1 == OMX_ALL) + { + m_inp_bEnabled = OMX_TRUE; + DEBUG_PRINT("SCP: Enabling Input port Indx\n"); + if ((m_state == OMX_StateLoaded + && !BITMASK_PRESENT(&m_flags,OMX_COMPONENT_IDLE_PENDING)) + || (m_state == OMX_StateWaitForResources) + || (m_inp_bPopulated == OMX_TRUE)) + { + post_command(OMX_CommandPortEnable, + OMX_CORE_INPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + + + } else + { + BITMASK_SET(&m_flags, OMX_COMPONENT_INPUT_ENABLE_PENDING); + // Skip the event notification + + } + } + + if (param1 == OMX_CORE_OUTPUT_PORT_INDEX || param1 == OMX_ALL) + { + DEBUG_PRINT("SCP: Enabling Output port Indx\n"); + m_out_bEnabled = OMX_TRUE; + if ((m_state == OMX_StateLoaded + && !BITMASK_PRESENT(&m_flags,OMX_COMPONENT_IDLE_PENDING)) + || (m_state == OMX_StateWaitForResources) + || (m_out_bPopulated == OMX_TRUE)) + { + post_command(OMX_CommandPortEnable, + OMX_CORE_OUTPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + } else + { + DEBUG_PRINT("send_command_proxy:OMX_CommandPortEnable:\ + OMX_CORE_OUTPUT_PORT_INDEX:release_done \n"); + BITMASK_SET(&m_flags, OMX_COMPONENT_OUTPUT_ENABLE_PENDING); + // Skip the event notification + + } + pthread_mutex_lock(&m_in_th_lock_1); + if(is_in_th_sleep) + { + is_in_th_sleep = false; + DEBUG_DETAIL("SCP:WAKING UP IN THREADS\n"); + in_th_wakeup(); + } + pthread_mutex_unlock(&m_in_th_lock_1); + pthread_mutex_lock(&m_out_th_lock_1); + if (is_out_th_sleep) + { + is_out_th_sleep = false; + DEBUG_PRINT("SCP:WAKING OUT THR, OMX_CommandPortEnable\n"); + out_th_wakeup(); + } + pthread_mutex_unlock(&m_out_th_lock_1); + } else + { + DEBUG_PRINT_ERROR("OMX_CommandPortEnable: disable wrong port ID"); + } + + } else + { + DEBUG_PRINT_ERROR("SCP-->ERROR: Invali Command [%d]\n",cmd); + eRet = OMX_ErrorNotImplemented; + } + DEBUG_PRINT("posting sem_States\n"); + sem_post (&sem_States); + if (eRet == OMX_ErrorNone && bFlag) + { + post_command(cmd,eState,OMX_COMPONENT_GENERATE_EVENT); + } + return eRet; +} + +/*============================================================================= +FUNCTION: + execute_omx_flush + +DESCRIPTION: + Function that flushes buffers that are pending to be written to driver + +INPUT/OUTPUT PARAMETERS: + [IN] param1 + [IN] cmd_cmpl + +RETURN VALUE: + true + false + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +bool omx_qcelp13_aenc::execute_omx_flush(OMX_IN OMX_U32 param1, bool cmd_cmpl) +{ + bool bRet = true; + + DEBUG_PRINT("Execute_omx_flush Port[%u]", param1); + struct timespec abs_timeout; + abs_timeout.tv_sec = 1; + abs_timeout.tv_nsec = 0; + + if ((signed)param1 == -1) + { + bFlushinprogress = true; + DEBUG_PRINT("Execute flush for both I/p O/p port\n"); + pthread_mutex_lock(&m_flush_lock); + m_flush_cnt = 2; + pthread_mutex_unlock(&m_flush_lock); + + // Send Flush commands to input and output threads + post_input(OMX_CommandFlush, + OMX_CORE_INPUT_PORT_INDEX,OMX_COMPONENT_GENERATE_COMMAND); + post_output(OMX_CommandFlush, + OMX_CORE_OUTPUT_PORT_INDEX,OMX_COMPONENT_GENERATE_COMMAND); + // Send Flush to the kernel so that the in and out buffers are released + if (ioctl( m_drv_fd, AUDIO_FLUSH, 0) == -1) + DEBUG_PRINT_ERROR("FLush:ioctl flush failed errno=%d\n",errno); + DEBUG_DETAIL("****************************************"); + DEBUG_DETAIL("is_in_th_sleep=%d is_out_th_sleep=%d\n",\ + is_in_th_sleep,is_out_th_sleep); + DEBUG_DETAIL("****************************************"); + + pthread_mutex_lock(&m_in_th_lock_1); + if (is_in_th_sleep) + { + is_in_th_sleep = false; + DEBUG_DETAIL("For FLUSH-->WAKING UP IN THREADS\n"); + in_th_wakeup(); + } + pthread_mutex_unlock(&m_in_th_lock_1); + + pthread_mutex_lock(&m_out_th_lock_1); + if (is_out_th_sleep) + { + is_out_th_sleep = false; + DEBUG_DETAIL("For FLUSH-->WAKING UP OUT THREADS\n"); + out_th_wakeup(); + } + pthread_mutex_unlock(&m_out_th_lock_1); + + + // sleep till the FLUSH ACK are done by both the input and + // output threads + DEBUG_DETAIL("WAITING FOR FLUSH ACK's param1=%d",param1); + wait_for_event(); + + DEBUG_PRINT("RECIEVED BOTH FLUSH ACK's param1=%u cmd_cmpl=%d",\ + param1,cmd_cmpl); + + // If not going to idle state, Send FLUSH complete message + // to the Client, now that FLUSH ACK's have been recieved. + if (cmd_cmpl) + { + m_cb.EventHandler(&m_cmp, m_app_data, OMX_EventCmdComplete, + OMX_CommandFlush, OMX_CORE_INPUT_PORT_INDEX, + NULL ); + m_cb.EventHandler(&m_cmp, m_app_data, OMX_EventCmdComplete, + OMX_CommandFlush, OMX_CORE_OUTPUT_PORT_INDEX, + NULL ); + DEBUG_PRINT("Inside FLUSH.. sending FLUSH CMPL\n"); + } + bFlushinprogress = false; + } + else if (param1 == OMX_CORE_INPUT_PORT_INDEX) + { + DEBUG_PRINT("Execute FLUSH for I/p port\n"); + pthread_mutex_lock(&m_flush_lock); + m_flush_cnt = 1; + pthread_mutex_unlock(&m_flush_lock); + post_input(OMX_CommandFlush, + OMX_CORE_INPUT_PORT_INDEX,OMX_COMPONENT_GENERATE_COMMAND); + if (ioctl( m_drv_fd, AUDIO_FLUSH, 0) == -1) + DEBUG_PRINT_ERROR("Flush:Input port, ioctl flush failed %d\n", + errno); + DEBUG_DETAIL("****************************************"); + DEBUG_DETAIL("is_in_th_sleep=%d is_out_th_sleep=%d\n",\ + is_in_th_sleep,is_out_th_sleep); + DEBUG_DETAIL("****************************************"); + + if (is_in_th_sleep) + { + pthread_mutex_lock(&m_in_th_lock_1); + is_in_th_sleep = false; + pthread_mutex_unlock(&m_in_th_lock_1); + DEBUG_DETAIL("For FLUSH-->WAKING UP IN THREADS\n"); + in_th_wakeup(); + } + + if (is_out_th_sleep) + { + pthread_mutex_lock(&m_out_th_lock_1); + is_out_th_sleep = false; + pthread_mutex_unlock(&m_out_th_lock_1); + DEBUG_DETAIL("For FLUSH-->WAKING UP OUT THREADS\n"); + out_th_wakeup(); + } + + //sleep till the FLUSH ACK are done by both the input and output threads + DEBUG_DETAIL("Executing FLUSH for I/p port\n"); + DEBUG_DETAIL("WAITING FOR FLUSH ACK's param1=%d",param1); + wait_for_event(); + DEBUG_DETAIL(" RECIEVED FLUSH ACK FOR I/P PORT param1=%d",param1); + + // Send FLUSH complete message to the Client, + // now that FLUSH ACK's have been recieved. + if (cmd_cmpl) + { + m_cb.EventHandler(&m_cmp, m_app_data, OMX_EventCmdComplete, + OMX_CommandFlush, OMX_CORE_INPUT_PORT_INDEX, + NULL ); + } + } else if (OMX_CORE_OUTPUT_PORT_INDEX == param1) + { + DEBUG_PRINT("Executing FLUSH for O/p port\n"); + pthread_mutex_lock(&m_flush_lock); + m_flush_cnt = 1; + pthread_mutex_unlock(&m_flush_lock); + DEBUG_DETAIL("Executing FLUSH for O/p port\n"); + DEBUG_DETAIL("WAITING FOR FLUSH ACK's param1=%d",param1); + post_output(OMX_CommandFlush, + OMX_CORE_OUTPUT_PORT_INDEX,OMX_COMPONENT_GENERATE_COMMAND); + if (ioctl( m_drv_fd, AUDIO_FLUSH, 0) ==-1) + DEBUG_PRINT_ERROR("Flush:Output port, ioctl flush failed %d\n", + errno); + DEBUG_DETAIL("****************************************"); + DEBUG_DETAIL("is_in_th_sleep=%d is_out_th_sleep=%d\n",\ + is_in_th_sleep,is_out_th_sleep); + DEBUG_DETAIL("****************************************"); + if (is_in_th_sleep) + { + pthread_mutex_lock(&m_in_th_lock_1); + is_in_th_sleep = false; + pthread_mutex_unlock(&m_in_th_lock_1); + DEBUG_DETAIL("For FLUSH-->WAKING UP IN THREADS\n"); + in_th_wakeup(); + } + + if (is_out_th_sleep) + { + pthread_mutex_lock(&m_out_th_lock_1); + is_out_th_sleep = false; + pthread_mutex_unlock(&m_out_th_lock_1); + DEBUG_DETAIL("For FLUSH-->WAKING UP OUT THREADS\n"); + out_th_wakeup(); + } + + // sleep till the FLUSH ACK are done by both the input and + // output threads + wait_for_event(); + // Send FLUSH complete message to the Client, + // now that FLUSH ACK's have been recieved. + if (cmd_cmpl) + { + m_cb.EventHandler(&m_cmp, m_app_data, OMX_EventCmdComplete, + OMX_CommandFlush, OMX_CORE_OUTPUT_PORT_INDEX, + NULL ); + } + DEBUG_DETAIL("RECIEVED FLUSH ACK FOR O/P PORT param1=%d",param1); + } else + { + DEBUG_PRINT("Invalid Port ID[%u]",param1); + } + return bRet; +} + +/*============================================================================= +FUNCTION: + execute_input_omx_flush + +DESCRIPTION: + Function that flushes buffers that are pending to be written to driver + +INPUT/OUTPUT PARAMETERS: + None + +RETURN VALUE: + true + false + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +bool omx_qcelp13_aenc::execute_input_omx_flush() +{ + OMX_BUFFERHEADERTYPE *omx_buf; + unsigned long p1 = 0; // Parameter - 1 + unsigned long p2 = 0; // Parameter - 2 + unsigned char ident = 0; + unsigned qsize=0; // qsize + unsigned tot_qsize=0; // qsize + + DEBUG_PRINT("Execute_omx_flush on input port"); + + pthread_mutex_lock(&m_lock); + do + { + qsize = m_input_q.m_size; + tot_qsize = qsize; + tot_qsize += m_input_ctrl_ebd_q.m_size; + + DEBUG_DETAIL("Input FLUSH-->flushq[%d] ebd[%d]dataq[%d]",\ + m_input_ctrl_cmd_q.m_size, + m_input_ctrl_ebd_q.m_size,qsize); + if (!tot_qsize) + { + DEBUG_DETAIL("Input-->BREAKING FROM execute_input_flush LOOP"); + pthread_mutex_unlock(&m_lock); + break; + } + if (qsize) + { + m_input_q.pop_entry(&p1, &p2, &ident); + if ((ident == OMX_COMPONENT_GENERATE_ETB) || + (ident == OMX_COMPONENT_GENERATE_BUFFER_DONE)) + { + omx_buf = (OMX_BUFFERHEADERTYPE *) p2; + DEBUG_DETAIL("Flush:Input dataq=%p \n", omx_buf); + omx_buf->nFilledLen = 0; + buffer_done_cb((OMX_BUFFERHEADERTYPE *)omx_buf); + } + } else if (m_input_ctrl_ebd_q.m_size) + { + m_input_ctrl_ebd_q.pop_entry(&p1, &p2, &ident); + if (ident == OMX_COMPONENT_GENERATE_BUFFER_DONE) + { + omx_buf = (OMX_BUFFERHEADERTYPE *) p2; + omx_buf->nFilledLen = 0; + DEBUG_DETAIL("Flush:ctrl dataq=%p \n", omx_buf); + buffer_done_cb((OMX_BUFFERHEADERTYPE *)omx_buf); + } + } else + { + } + }while (tot_qsize>0); + DEBUG_DETAIL("*************************\n"); + DEBUG_DETAIL("IN-->FLUSHING DONE\n"); + DEBUG_DETAIL("*************************\n"); + flush_ack(); + pthread_mutex_unlock(&m_lock); + return true; +} + +/*============================================================================= +FUNCTION: + execute_output_omx_flush + +DESCRIPTION: + Function that flushes buffers that are pending to be written to driver + +INPUT/OUTPUT PARAMETERS: + None + +RETURN VALUE: + true + false + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +bool omx_qcelp13_aenc::execute_output_omx_flush() +{ + OMX_BUFFERHEADERTYPE *omx_buf; + unsigned long p1 = 0; // Parameter - 1 + unsigned long p2 = 0; // Parameter - 2 + unsigned char ident = 0; + unsigned qsize=0; // qsize + unsigned tot_qsize=0; // qsize + + DEBUG_PRINT("Execute_omx_flush on output port"); + + pthread_mutex_lock(&m_outputlock); + do + { + qsize = m_output_q.m_size; + DEBUG_DETAIL("OUT FLUSH-->flushq[%d] fbd[%d]dataq[%d]",\ + m_output_ctrl_cmd_q.m_size, + m_output_ctrl_fbd_q.m_size,qsize); + tot_qsize = qsize; + tot_qsize += m_output_ctrl_fbd_q.m_size; + if (!tot_qsize) + { + DEBUG_DETAIL("OUT-->BREAKING FROM execute_input_flush LOOP"); + pthread_mutex_unlock(&m_outputlock); + break; + } + if (qsize) + { + m_output_q.pop_entry(&p1,&p2,&ident); + if ( (OMX_COMPONENT_GENERATE_FTB == ident) || + (OMX_COMPONENT_GENERATE_FRAME_DONE == ident)) + { + omx_buf = (OMX_BUFFERHEADERTYPE *) p2; + DEBUG_DETAIL("Ouput Buf_Addr=%p TS[0x%x] \n",\ + omx_buf,nTimestamp); + omx_buf->nTimeStamp = nTimestamp; + omx_buf->nFilledLen = 0; + frame_done_cb((OMX_BUFFERHEADERTYPE *)omx_buf); + DEBUG_DETAIL("CALLING FBD FROM FLUSH"); + } + } else if ((qsize = m_output_ctrl_fbd_q.m_size)) + { + m_output_ctrl_fbd_q.pop_entry(&p1, &p2, &ident); + if (OMX_COMPONENT_GENERATE_FRAME_DONE == ident) + { + omx_buf = (OMX_BUFFERHEADERTYPE *) p2; + DEBUG_DETAIL("Ouput Buf_Addr=%p TS[0x%x] \n", \ + omx_buf,nTimestamp); + omx_buf->nTimeStamp = nTimestamp; + omx_buf->nFilledLen = 0; + frame_done_cb((OMX_BUFFERHEADERTYPE *)omx_buf); + DEBUG_DETAIL("CALLING FROM CTRL-FBDQ FROM FLUSH"); + } + } + }while (qsize>0); + DEBUG_DETAIL("*************************\n"); + DEBUG_DETAIL("OUT-->FLUSHING DONE\n"); + DEBUG_DETAIL("*************************\n"); + flush_ack(); + pthread_mutex_unlock(&m_outputlock); + return true; +} + +/*============================================================================= +FUNCTION: + post_input + +DESCRIPTION: + Function that posts command in the command queue + +INPUT/OUTPUT PARAMETERS: + [IN] p1 + [IN] p2 + [IN] id - command ID + [IN] lock - self-locking mode + +RETURN VALUE: + true + false + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +bool omx_qcelp13_aenc::post_input(unsigned long p1, + unsigned long p2, + unsigned char id) +{ + bool bRet = false; + pthread_mutex_lock(&m_lock); + + if((OMX_COMPONENT_GENERATE_COMMAND == id) || (id == OMX_COMPONENT_SUSPEND)) + { + // insert flush message and ebd + m_input_ctrl_cmd_q.insert_entry(p1,p2,id); + } else if ((OMX_COMPONENT_GENERATE_BUFFER_DONE == id)) + { + // insert ebd + m_input_ctrl_ebd_q.insert_entry(p1,p2,id); + } else + { + // ETBS in this queue + m_input_q.insert_entry(p1,p2,id); + } + + if (m_ipc_to_in_th) + { + bRet = true; + omx_qcelp13_post_msg(m_ipc_to_in_th, id); + } + + DEBUG_DETAIL("PostInput-->state[%d]id[%d]flushq[%d]ebdq[%d]dataq[%d] \n",\ + m_state, + id, + m_input_ctrl_cmd_q.m_size, + m_input_ctrl_ebd_q.m_size, + m_input_q.m_size); + + pthread_mutex_unlock(&m_lock); + return bRet; +} + +/*============================================================================= +FUNCTION: + post_command + +DESCRIPTION: + Function that posts command in the command queue + +INPUT/OUTPUT PARAMETERS: + [IN] p1 + [IN] p2 + [IN] id - command ID + [IN] lock - self-locking mode + +RETURN VALUE: + true + false + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +bool omx_qcelp13_aenc::post_command(unsigned int p1, + unsigned int p2, + unsigned char id) +{ + bool bRet = false; + + pthread_mutex_lock(&m_commandlock); + + m_command_q.insert_entry(p1,p2,id); + + if (m_ipc_to_cmd_th) + { + bRet = true; + omx_qcelp13_post_msg(m_ipc_to_cmd_th, id); + } + + DEBUG_DETAIL("PostCmd-->state[%d]id[%d]cmdq[%d]flags[%x]\n",\ + m_state, + id, + m_command_q.m_size, + m_flags >> 3); + + pthread_mutex_unlock(&m_commandlock); + return bRet; +} + +/*============================================================================= +FUNCTION: + post_output + +DESCRIPTION: + Function that posts command in the command queue + +INPUT/OUTPUT PARAMETERS: + [IN] p1 + [IN] p2 + [IN] id - command ID + [IN] lock - self-locking mode + +RETURN VALUE: + true + false + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +bool omx_qcelp13_aenc::post_output(unsigned long p1, + unsigned long p2, + unsigned char id) +{ + bool bRet = false; + + pthread_mutex_lock(&m_outputlock); + if((OMX_COMPONENT_GENERATE_COMMAND == id) || (id == OMX_COMPONENT_SUSPEND) + || (id == OMX_COMPONENT_RESUME)) + { + // insert flush message and fbd + m_output_ctrl_cmd_q.insert_entry(p1,p2,id); + } else if ( (OMX_COMPONENT_GENERATE_FRAME_DONE == id) ) + { + // insert flush message and fbd + m_output_ctrl_fbd_q.insert_entry(p1,p2,id); + } else + { + m_output_q.insert_entry(p1,p2,id); + } + if ( m_ipc_to_out_th ) + { + bRet = true; + omx_qcelp13_post_msg(m_ipc_to_out_th, id); + } + DEBUG_DETAIL("PostOutput-->state[%d]id[%d]flushq[%d]ebdq[%d]dataq[%d]\n",\ + m_state, + id, + m_output_ctrl_cmd_q.m_size, + m_output_ctrl_fbd_q.m_size, + m_output_q.m_size); + + pthread_mutex_unlock(&m_outputlock); + return bRet; +} +/** + @brief member function that return parameters to IL client + + @param hComp handle to component instance + @param paramIndex Parameter type + @param paramData pointer to memory space which would hold the + paramter + @return error status +*/ +OMX_ERRORTYPE omx_qcelp13_aenc::get_parameter(OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_INDEXTYPE paramIndex, + OMX_INOUT OMX_PTR paramData) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_state == OMX_StateInvalid) + { + DEBUG_PRINT_ERROR("Get Param in Invalid State\n"); + return OMX_ErrorInvalidState; + } + if (paramData == NULL) + { + DEBUG_PRINT("get_parameter: paramData is NULL\n"); + return OMX_ErrorBadParameter; + } + + switch ((int)paramIndex) + { + case OMX_IndexParamPortDefinition: + { + OMX_PARAM_PORTDEFINITIONTYPE *portDefn; + portDefn = (OMX_PARAM_PORTDEFINITIONTYPE *) paramData; + + DEBUG_PRINT("OMX_IndexParamPortDefinition " \ + "portDefn->nPortIndex = %u\n", + portDefn->nPortIndex); + + portDefn->nVersion.nVersion = OMX_SPEC_VERSION; + portDefn->nSize = (OMX_U32)sizeof(portDefn); + portDefn->eDomain = OMX_PortDomainAudio; + + if (0 == portDefn->nPortIndex) + { + portDefn->eDir = OMX_DirInput; + portDefn->bEnabled = m_inp_bEnabled; + portDefn->bPopulated = m_inp_bPopulated; + portDefn->nBufferCountActual = m_inp_act_buf_count; + portDefn->nBufferCountMin = OMX_CORE_NUM_INPUT_BUFFERS; + portDefn->nBufferSize = input_buffer_size; + portDefn->format.audio.bFlagErrorConcealment = OMX_TRUE; + portDefn->format.audio.eEncoding = OMX_AUDIO_CodingPCM; + portDefn->format.audio.pNativeRender = 0; + } else if (1 == portDefn->nPortIndex) + { + portDefn->eDir = OMX_DirOutput; + portDefn->bEnabled = m_out_bEnabled; + portDefn->bPopulated = m_out_bPopulated; + portDefn->nBufferCountActual = m_out_act_buf_count; + portDefn->nBufferCountMin = OMX_CORE_NUM_OUTPUT_BUFFERS; + portDefn->nBufferSize = output_buffer_size; + portDefn->format.audio.bFlagErrorConcealment = OMX_TRUE; + portDefn->format.audio.eEncoding = OMX_AUDIO_CodingQCELP13; + portDefn->format.audio.pNativeRender = 0; + } else + { + portDefn->eDir = OMX_DirMax; + DEBUG_PRINT_ERROR("Bad Port idx %d\n",\ + (int)portDefn->nPortIndex); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + + case OMX_IndexParamAudioInit: + { + OMX_PORT_PARAM_TYPE *portParamType = + (OMX_PORT_PARAM_TYPE *) paramData; + DEBUG_PRINT("OMX_IndexParamAudioInit\n"); + + portParamType->nVersion.nVersion = OMX_SPEC_VERSION; + portParamType->nSize = (OMX_U32)sizeof(portParamType); + portParamType->nPorts = 2; + portParamType->nStartPortNumber = 0; + break; + } + + case OMX_IndexParamAudioPortFormat: + { + OMX_AUDIO_PARAM_PORTFORMATTYPE *portFormatType = + (OMX_AUDIO_PARAM_PORTFORMATTYPE *) paramData; + DEBUG_PRINT("OMX_IndexParamAudioPortFormat\n"); + portFormatType->nVersion.nVersion = OMX_SPEC_VERSION; + portFormatType->nSize = (OMX_U32)sizeof(portFormatType); + + if (OMX_CORE_INPUT_PORT_INDEX == portFormatType->nPortIndex) + { + + portFormatType->eEncoding = OMX_AUDIO_CodingPCM; + } else if (OMX_CORE_OUTPUT_PORT_INDEX == + portFormatType->nPortIndex) + { + DEBUG_PRINT("get_parameter: OMX_IndexParamAudioFormat: "\ + "%u\n", portFormatType->nIndex); + + portFormatType->eEncoding = OMX_AUDIO_CodingQCELP13; + } else + { + DEBUG_PRINT_ERROR("get_parameter: Bad port index %d\n", + (int)portFormatType->nPortIndex); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + + case OMX_IndexParamAudioQcelp13: + { + OMX_AUDIO_PARAM_QCELP13TYPE *qcelp13Param = + (OMX_AUDIO_PARAM_QCELP13TYPE *) paramData; + DEBUG_PRINT("OMX_IndexParamAudioQcelp13\n"); + if (OMX_CORE_OUTPUT_PORT_INDEX== qcelp13Param->nPortIndex) + { + memcpy(qcelp13Param,&m_qcelp13_param, + sizeof(OMX_AUDIO_PARAM_QCELP13TYPE)); + } else + { + DEBUG_PRINT_ERROR("get_parameter:\ + OMX_IndexParamAudioQcelp13 \ + OMX_ErrorBadPortIndex %d\n", \ + (int)qcelp13Param->nPortIndex); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + case QOMX_IndexParamAudioSessionId: + { + QOMX_AUDIO_STREAM_INFO_DATA *streaminfoparam = + (QOMX_AUDIO_STREAM_INFO_DATA *) paramData; + streaminfoparam->sessionId = (OMX_U8)m_session_id; + break; + } + + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmparam = + (OMX_AUDIO_PARAM_PCMMODETYPE *) paramData; + + if (OMX_CORE_INPUT_PORT_INDEX== pcmparam->nPortIndex) + { + memcpy(pcmparam,&m_pcm_param,\ + sizeof(OMX_AUDIO_PARAM_PCMMODETYPE)); + DEBUG_PRINT("get_parameter: Sampling rate %u",\ + pcmparam->nSamplingRate); + DEBUG_PRINT("get_parameter: Number of channels %u",\ + pcmparam->nChannels); + } else + { + DEBUG_PRINT_ERROR("get_parameter:OMX_IndexParamAudioPcm "\ + "OMX_ErrorBadPortIndex %d\n", \ + (int)pcmparam->nPortIndex); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + case OMX_IndexParamComponentSuspended: + { + OMX_PARAM_SUSPENSIONTYPE *suspend = + (OMX_PARAM_SUSPENSIONTYPE *) paramData; + DEBUG_PRINT("get_parameter: OMX_IndexParamComponentSuspended %p\n", + suspend); + break; + } + case OMX_IndexParamVideoInit: + { + OMX_PORT_PARAM_TYPE *portParamType = + (OMX_PORT_PARAM_TYPE *) paramData; + DEBUG_PRINT("get_parameter: OMX_IndexParamVideoInit\n"); + portParamType->nVersion.nVersion = OMX_SPEC_VERSION; + portParamType->nSize = (OMX_U32)sizeof(portParamType); + portParamType->nPorts = 0; + portParamType->nStartPortNumber = 0; + break; + } + case OMX_IndexParamPriorityMgmt: + { + OMX_PRIORITYMGMTTYPE *priorityMgmtType = + (OMX_PRIORITYMGMTTYPE*)paramData; + DEBUG_PRINT("get_parameter: OMX_IndexParamPriorityMgmt\n"); + priorityMgmtType->nSize = (OMX_U32)sizeof(priorityMgmtType); + priorityMgmtType->nVersion.nVersion = OMX_SPEC_VERSION; + priorityMgmtType->nGroupID = m_priority_mgm.nGroupID; + priorityMgmtType->nGroupPriority = + m_priority_mgm.nGroupPriority; + break; + } + case OMX_IndexParamImageInit: + { + OMX_PORT_PARAM_TYPE *portParamType = + (OMX_PORT_PARAM_TYPE *) paramData; + DEBUG_PRINT("get_parameter: OMX_IndexParamImageInit\n"); + portParamType->nVersion.nVersion = OMX_SPEC_VERSION; + portParamType->nSize = (OMX_U32)sizeof(portParamType); + portParamType->nPorts = 0; + portParamType->nStartPortNumber = 0; + break; + } + + case OMX_IndexParamCompBufferSupplier: + { + DEBUG_PRINT("get_parameter: \ + OMX_IndexParamCompBufferSupplier\n"); + OMX_PARAM_BUFFERSUPPLIERTYPE *bufferSupplierType + = (OMX_PARAM_BUFFERSUPPLIERTYPE*) paramData; + DEBUG_PRINT("get_parameter: \ + OMX_IndexParamCompBufferSupplier\n"); + + bufferSupplierType->nSize = (OMX_U32)sizeof(bufferSupplierType); + bufferSupplierType->nVersion.nVersion = OMX_SPEC_VERSION; + if (OMX_CORE_INPUT_PORT_INDEX == + bufferSupplierType->nPortIndex) + { + bufferSupplierType->nPortIndex = + OMX_BufferSupplyUnspecified; + } else if (OMX_CORE_OUTPUT_PORT_INDEX == + bufferSupplierType->nPortIndex) + { + bufferSupplierType->nPortIndex = + OMX_BufferSupplyUnspecified; + } else + { + DEBUG_PRINT_ERROR("get_parameter:"\ + "OMX_IndexParamCompBufferSupplier eRet"\ + "%08x\n", eRet); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + + /*Component should support this port definition*/ + case OMX_IndexParamOtherInit: + { + OMX_PORT_PARAM_TYPE *portParamType = + (OMX_PORT_PARAM_TYPE *) paramData; + DEBUG_PRINT("get_parameter: OMX_IndexParamOtherInit\n"); + portParamType->nVersion.nVersion = OMX_SPEC_VERSION; + portParamType->nSize = (OMX_U32)sizeof(portParamType); + portParamType->nPorts = 0; + portParamType->nStartPortNumber = 0; + break; + } + case OMX_IndexParamStandardComponentRole: + { + OMX_PARAM_COMPONENTROLETYPE *componentRole; + componentRole = (OMX_PARAM_COMPONENTROLETYPE*)paramData; + componentRole->nSize = component_Role.nSize; + componentRole->nVersion = component_Role.nVersion; + strlcpy((char *)componentRole->cRole, + (const char*)component_Role.cRole, + sizeof(componentRole->cRole)); + DEBUG_PRINT_ERROR("nSize = %d , nVersion = %d, cRole = %s\n", + component_Role.nSize, + component_Role.nVersion, + component_Role.cRole); + break; + + } + default: + { + DEBUG_PRINT_ERROR("unknown param %08x\n", paramIndex); + eRet = OMX_ErrorUnsupportedIndex; + } + } + return eRet; + +} + +/** + @brief member function that set paramter from IL client + + @param hComp handle to component instance + @param paramIndex parameter type + @param paramData pointer to memory space which holds the paramter + @return error status + */ +OMX_ERRORTYPE omx_qcelp13_aenc::set_parameter(OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_INDEXTYPE paramIndex, + OMX_IN OMX_PTR paramData) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_state != OMX_StateLoaded) + { + DEBUG_PRINT_ERROR("set_parameter is not in proper state\n"); + return OMX_ErrorIncorrectStateOperation; + } + if (paramData == NULL) + { + DEBUG_PRINT("param data is NULL"); + return OMX_ErrorBadParameter; + } + + switch (paramIndex) + { + case OMX_IndexParamAudioQcelp13: + { + DEBUG_PRINT("OMX_IndexParamAudioQcelp13"); + OMX_AUDIO_PARAM_QCELP13TYPE *qcelp13param + = (OMX_AUDIO_PARAM_QCELP13TYPE *) paramData; + memcpy(&m_qcelp13_param,qcelp13param, + sizeof(OMX_AUDIO_PARAM_QCELP13TYPE)); + break; + } + case OMX_IndexParamPortDefinition: + { + OMX_PARAM_PORTDEFINITIONTYPE *portDefn; + portDefn = (OMX_PARAM_PORTDEFINITIONTYPE *) paramData; + + if (((m_state == OMX_StateLoaded)&& + !BITMASK_PRESENT(&m_flags,OMX_COMPONENT_IDLE_PENDING)) + || (m_state == OMX_StateWaitForResources && + ((OMX_DirInput == portDefn->eDir && + m_inp_bEnabled == true)|| + (OMX_DirInput == portDefn->eDir && + m_out_bEnabled == true))) + ||(((OMX_DirInput == portDefn->eDir && + m_inp_bEnabled == false)|| + (OMX_DirInput == portDefn->eDir && + m_out_bEnabled == false)) && + (m_state != OMX_StateWaitForResources))) + { + DEBUG_PRINT("Set Parameter called in valid state\n"); + } else + { + DEBUG_PRINT_ERROR("Set Parameter called in \ + Invalid State\n"); + return OMX_ErrorIncorrectStateOperation; + } + DEBUG_PRINT("OMX_IndexParamPortDefinition portDefn->nPortIndex " + "= %u\n",portDefn->nPortIndex); + if (OMX_CORE_INPUT_PORT_INDEX == portDefn->nPortIndex) + { + if ( portDefn->nBufferCountActual > + OMX_CORE_NUM_INPUT_BUFFERS ) + { + m_inp_act_buf_count = portDefn->nBufferCountActual; + } else + { + m_inp_act_buf_count =OMX_CORE_NUM_INPUT_BUFFERS; + } + input_buffer_size = portDefn->nBufferSize; + + } else if (OMX_CORE_OUTPUT_PORT_INDEX == portDefn->nPortIndex) + { + if ( portDefn->nBufferCountActual > + OMX_CORE_NUM_OUTPUT_BUFFERS ) + { + m_out_act_buf_count = portDefn->nBufferCountActual; + } else + { + m_out_act_buf_count =OMX_CORE_NUM_OUTPUT_BUFFERS; + } + output_buffer_size = portDefn->nBufferSize; + } else + { + DEBUG_PRINT(" set_parameter: Bad Port idx %d",\ + (int)portDefn->nPortIndex); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + case OMX_IndexParamPriorityMgmt: + { + DEBUG_PRINT("set_parameter: OMX_IndexParamPriorityMgmt\n"); + + if (m_state != OMX_StateLoaded) + { + DEBUG_PRINT_ERROR("Set Parameter called in \ + Invalid State\n"); + return OMX_ErrorIncorrectStateOperation; + } + OMX_PRIORITYMGMTTYPE *priorityMgmtype + = (OMX_PRIORITYMGMTTYPE*) paramData; + DEBUG_PRINT("set_parameter: OMX_IndexParamPriorityMgmt %u\n", + priorityMgmtype->nGroupID); + + DEBUG_PRINT("set_parameter: priorityMgmtype %u\n", + priorityMgmtype->nGroupPriority); + + m_priority_mgm.nGroupID = priorityMgmtype->nGroupID; + m_priority_mgm.nGroupPriority = priorityMgmtype->nGroupPriority; + + break; + } + case OMX_IndexParamAudioPortFormat: + { + + OMX_AUDIO_PARAM_PORTFORMATTYPE *portFormatType = + (OMX_AUDIO_PARAM_PORTFORMATTYPE *) paramData; + DEBUG_PRINT("set_parameter: OMX_IndexParamAudioPortFormat\n"); + + if (OMX_CORE_INPUT_PORT_INDEX== portFormatType->nPortIndex) + { + portFormatType->eEncoding = OMX_AUDIO_CodingPCM; + } else if (OMX_CORE_OUTPUT_PORT_INDEX == + portFormatType->nPortIndex) + { + DEBUG_PRINT("set_parameter: OMX_IndexParamAudioFormat:"\ + " %u\n", portFormatType->nIndex); + portFormatType->eEncoding = OMX_AUDIO_CodingQCELP13; + } else + { + DEBUG_PRINT_ERROR("set_parameter: Bad port index %d\n", \ + (int)portFormatType->nPortIndex); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + + + case OMX_IndexParamCompBufferSupplier: + { + DEBUG_PRINT("set_parameter: \ + OMX_IndexParamCompBufferSupplier\n"); + OMX_PARAM_BUFFERSUPPLIERTYPE *bufferSupplierType + = (OMX_PARAM_BUFFERSUPPLIERTYPE*) paramData; + DEBUG_PRINT("set_param: OMX_IndexParamCompBufferSupplier %d",\ + bufferSupplierType->eBufferSupplier); + + if (bufferSupplierType->nPortIndex == OMX_CORE_INPUT_PORT_INDEX + || bufferSupplierType->nPortIndex == + OMX_CORE_OUTPUT_PORT_INDEX) + { + DEBUG_PRINT("set_parameter:\ + OMX_IndexParamCompBufferSupplier\n"); + m_buffer_supplier.eBufferSupplier = + bufferSupplierType->eBufferSupplier; + } else + { + DEBUG_PRINT_ERROR("set_param:\ + IndexParamCompBufferSup %08x\n", eRet); + eRet = OMX_ErrorBadPortIndex; + } + + break; } + + case OMX_IndexParamAudioPcm: + { + DEBUG_PRINT("set_parameter: OMX_IndexParamAudioPcm\n"); + OMX_AUDIO_PARAM_PCMMODETYPE *pcmparam + = (OMX_AUDIO_PARAM_PCMMODETYPE *) paramData; + + if (OMX_CORE_INPUT_PORT_INDEX== pcmparam->nPortIndex) + { + memcpy(&m_pcm_param,pcmparam,\ + sizeof(OMX_AUDIO_PARAM_PCMMODETYPE)); + DEBUG_PRINT("set_pcm_parameter: %u %u",\ + m_pcm_param.nChannels, + m_pcm_param.nSamplingRate); + } else + { + DEBUG_PRINT_ERROR("Set_parameter:OMX_IndexParamAudioPcm " + "OMX_ErrorBadPortIndex %d\n", + (int)pcmparam->nPortIndex); + eRet = OMX_ErrorBadPortIndex; + } + break; + } + case OMX_IndexParamSuspensionPolicy: + { + eRet = OMX_ErrorNotImplemented; + break; + } + case OMX_IndexParamStandardComponentRole: + { + OMX_PARAM_COMPONENTROLETYPE *componentRole; + componentRole = (OMX_PARAM_COMPONENTROLETYPE*)paramData; + component_Role.nSize = componentRole->nSize; + component_Role.nVersion = componentRole->nVersion; + strlcpy((char *)component_Role.cRole, + (const char*)componentRole->cRole, + sizeof(component_Role.cRole)); + break; + } + + default: + { + DEBUG_PRINT_ERROR("unknown param %d\n", paramIndex); + eRet = OMX_ErrorUnsupportedIndex; + } + } + return eRet; +} + +/* ====================================================================== +FUNCTION + omx_qcelp13_aenc::GetConfig + +DESCRIPTION + OMX Get Config Method implementation. + +PARAMETERS + . + +RETURN VALUE + OMX Error None if successful. + +========================================================================== */ +OMX_ERRORTYPE omx_qcelp13_aenc::get_config(OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_INDEXTYPE configIndex, + OMX_INOUT OMX_PTR configData) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_state == OMX_StateInvalid) + { + DEBUG_PRINT_ERROR("Get Config in Invalid State\n"); + return OMX_ErrorInvalidState; + } + + switch (configIndex) + { + case OMX_IndexConfigAudioVolume: + { + OMX_AUDIO_CONFIG_VOLUMETYPE *volume = + (OMX_AUDIO_CONFIG_VOLUMETYPE*) configData; + + if (OMX_CORE_INPUT_PORT_INDEX == volume->nPortIndex) + { + volume->nSize = (OMX_U32)sizeof(volume); + volume->nVersion.nVersion = OMX_SPEC_VERSION; + volume->bLinear = OMX_TRUE; + volume->sVolume.nValue = m_volume; + volume->sVolume.nMax = OMX_AENC_MAX; + volume->sVolume.nMin = OMX_AENC_MIN; + } else + { + eRet = OMX_ErrorBadPortIndex; + } + } + break; + + case OMX_IndexConfigAudioMute: + { + OMX_AUDIO_CONFIG_MUTETYPE *mute = + (OMX_AUDIO_CONFIG_MUTETYPE*) configData; + + if (OMX_CORE_INPUT_PORT_INDEX == mute->nPortIndex) + { + mute->nSize = (OMX_U32)sizeof(mute); + mute->nVersion.nVersion = OMX_SPEC_VERSION; + mute->bMute = (BITMASK_PRESENT(&m_flags, + OMX_COMPONENT_MUTED)?OMX_TRUE:OMX_FALSE); + } else + { + eRet = OMX_ErrorBadPortIndex; + } + } + break; + + default: + eRet = OMX_ErrorUnsupportedIndex; + break; + } + return eRet; +} + +/* ====================================================================== +FUNCTION + omx_qcelp13_aenc::SetConfig + +DESCRIPTION + OMX Set Config method implementation + +PARAMETERS + . + +RETURN VALUE + OMX Error None if successful. +========================================================================== */ +OMX_ERRORTYPE omx_qcelp13_aenc::set_config(OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_INDEXTYPE configIndex, + OMX_IN OMX_PTR configData) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_state == OMX_StateInvalid) + { + DEBUG_PRINT_ERROR("Set Config in Invalid State\n"); + return OMX_ErrorInvalidState; + } + if ( m_state == OMX_StateExecuting) + { + DEBUG_PRINT_ERROR("set_config:Ignore in Exe state\n"); + return OMX_ErrorInvalidState; + } + + switch (configIndex) + { + case OMX_IndexConfigAudioVolume: + { + OMX_AUDIO_CONFIG_VOLUMETYPE *vol = + (OMX_AUDIO_CONFIG_VOLUMETYPE*)configData; + if (vol->nPortIndex == OMX_CORE_INPUT_PORT_INDEX) + { + if ((vol->sVolume.nValue <= OMX_AENC_MAX) && + (vol->sVolume.nValue >= OMX_AENC_MIN)) + { + m_volume = vol->sVolume.nValue; + if (BITMASK_ABSENT(&m_flags, OMX_COMPONENT_MUTED)) + { + /* ioctl(m_drv_fd, AUDIO_VOLUME, + m_volume * OMX_AENC_VOLUME_STEP); */ + } + + } else + { + eRet = OMX_ErrorBadParameter; + } + } else + { + eRet = OMX_ErrorBadPortIndex; + } + } + break; + + case OMX_IndexConfigAudioMute: + { + OMX_AUDIO_CONFIG_MUTETYPE *mute = (OMX_AUDIO_CONFIG_MUTETYPE*) + configData; + if (mute->nPortIndex == OMX_CORE_INPUT_PORT_INDEX) + { + if (mute->bMute == OMX_TRUE) + { + BITMASK_SET(&m_flags, OMX_COMPONENT_MUTED); + /* ioctl(m_drv_fd, AUDIO_VOLUME, 0); */ + } else + { + BITMASK_CLEAR(&m_flags, OMX_COMPONENT_MUTED); + /* ioctl(m_drv_fd, AUDIO_VOLUME, + m_volume * OMX_AENC_VOLUME_STEP); */ + } + } else + { + eRet = OMX_ErrorBadPortIndex; + } + } + break; + + default: + eRet = OMX_ErrorUnsupportedIndex; + break; + } + return eRet; +} + +/* ====================================================================== +FUNCTION + omx_qcelp13_aenc::GetExtensionIndex + +DESCRIPTION + OMX GetExtensionIndex method implementaion. + +PARAMETERS + . + +RETURN VALUE + OMX Error None if everything successful. + +========================================================================== */ +OMX_ERRORTYPE omx_qcelp13_aenc::get_extension_index( + OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_STRING paramName, + OMX_OUT OMX_INDEXTYPE* indexType) +{ + if((hComp == NULL) || (paramName == NULL) || (indexType == NULL)) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_state == OMX_StateInvalid) + { + DEBUG_PRINT_ERROR("Get Extension Index in Invalid State\n"); + return OMX_ErrorInvalidState; + } + if(strncmp(paramName,"OMX.Qualcomm.index.audio.sessionId", + strlen("OMX.Qualcomm.index.audio.sessionId")) == 0) + { + *indexType =(OMX_INDEXTYPE)QOMX_IndexParamAudioSessionId; + DEBUG_PRINT("Extension index type - %d\n", *indexType); + + } + else + { + return OMX_ErrorBadParameter; + + } + return OMX_ErrorNone; +} + +/* ====================================================================== +FUNCTION + omx_qcelp13_aenc::GetState + +DESCRIPTION + Returns the state information back to the caller. + +PARAMETERS + . + +RETURN VALUE + Error None if everything is successful. +========================================================================== */ +OMX_ERRORTYPE omx_qcelp13_aenc::get_state(OMX_IN OMX_HANDLETYPE hComp, + OMX_OUT OMX_STATETYPE* state) +{ + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + *state = m_state; + DEBUG_PRINT("Returning the state %d\n",*state); + return OMX_ErrorNone; +} + +/* ====================================================================== +FUNCTION + omx_qcelp13_aenc::ComponentTunnelRequest + +DESCRIPTION + OMX Component Tunnel Request method implementation. + +PARAMETERS + None. + +RETURN VALUE + OMX Error None if everything successful. + +========================================================================== */ +OMX_ERRORTYPE omx_qcelp13_aenc::component_tunnel_request +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_U32 port, + OMX_IN OMX_HANDLETYPE peerComponent, + OMX_IN OMX_U32 peerPort, + OMX_INOUT OMX_TUNNELSETUPTYPE* tunnelSetup) +{ + DEBUG_PRINT_ERROR("Error: component_tunnel_request Not Implemented\n"); + + if((hComp == NULL) || (peerComponent == NULL) || (tunnelSetup == NULL)) + { + port = port; + peerPort = peerPort; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + return OMX_ErrorNotImplemented; +} + +/* ====================================================================== +FUNCTION + omx_qcelp13_aenc::AllocateInputBuffer + +DESCRIPTION + Helper function for allocate buffer in the input pin + +PARAMETERS + None. + +RETURN VALUE + true/false + +========================================================================== */ +OMX_ERRORTYPE omx_qcelp13_aenc::allocate_input_buffer +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE** bufferHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + OMX_BUFFERHEADERTYPE *bufHdr; + unsigned nBufSize = MAX(bytes, input_buffer_size); + char *buf_ptr; + if(m_inp_current_buf_count < m_inp_act_buf_count) + { + buf_ptr = (char *) calloc((nBufSize + \ + sizeof(OMX_BUFFERHEADERTYPE)+sizeof(META_IN)) , 1); + + if(hComp == NULL) + { + port = port; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + free(buf_ptr); + return OMX_ErrorBadParameter; + } + if (buf_ptr != NULL) + { + bufHdr = (OMX_BUFFERHEADERTYPE *) buf_ptr; + *bufferHdr = bufHdr; + memset(bufHdr,0,sizeof(OMX_BUFFERHEADERTYPE)); + + bufHdr->pBuffer = (OMX_U8 *)((buf_ptr) + sizeof(META_IN)+ + sizeof(OMX_BUFFERHEADERTYPE)); + bufHdr->nSize = (OMX_U32)sizeof(OMX_BUFFERHEADERTYPE); + bufHdr->nVersion.nVersion = OMX_SPEC_VERSION; + bufHdr->nAllocLen = nBufSize; + bufHdr->pAppPrivate = appData; + bufHdr->nInputPortIndex = OMX_CORE_INPUT_PORT_INDEX; + m_input_buf_hdrs.insert(bufHdr, NULL); + + m_inp_current_buf_count++; + DEBUG_PRINT("AIB:bufHdr %p bufHdr->pBuffer %p m_inp_buf_cnt=%d \ + bytes=%u", bufHdr, bufHdr->pBuffer,m_inp_current_buf_count, + bytes); + + } else + { + DEBUG_PRINT("Input buffer memory allocation failed 1 \n"); + eRet = OMX_ErrorInsufficientResources; + } + } + else + { + DEBUG_PRINT("Input buffer memory allocation failed 2\n"); + eRet = OMX_ErrorInsufficientResources; + } + return eRet; +} + +OMX_ERRORTYPE omx_qcelp13_aenc::allocate_output_buffer +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE** bufferHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + OMX_BUFFERHEADERTYPE *bufHdr; + unsigned nBufSize = MAX(bytes,output_buffer_size); + char *buf_ptr; + + if(hComp == NULL) + { + port = port; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_out_current_buf_count < m_out_act_buf_count) + { + buf_ptr = (char *) calloc( (nBufSize + sizeof(OMX_BUFFERHEADERTYPE)),1); + + if (buf_ptr != NULL) + { + bufHdr = (OMX_BUFFERHEADERTYPE *) buf_ptr; + *bufferHdr = bufHdr; + memset(bufHdr,0,sizeof(OMX_BUFFERHEADERTYPE)); + + bufHdr->pBuffer = (OMX_U8 *)((buf_ptr) + + sizeof(OMX_BUFFERHEADERTYPE)); + bufHdr->nSize = (OMX_U32)sizeof(OMX_BUFFERHEADERTYPE); + bufHdr->nVersion.nVersion = OMX_SPEC_VERSION; + bufHdr->nAllocLen = nBufSize; + bufHdr->pAppPrivate = appData; + bufHdr->nOutputPortIndex = OMX_CORE_OUTPUT_PORT_INDEX; + m_output_buf_hdrs.insert(bufHdr, NULL); + m_out_current_buf_count++; + DEBUG_PRINT("AOB::bufHdr %p bufHdr->pBuffer %p m_out_buf_cnt=%d "\ + "bytes=%u",bufHdr, bufHdr->pBuffer,\ + m_out_current_buf_count, bytes); + } else + { + DEBUG_PRINT("Output buffer memory allocation failed 1 \n"); + eRet = OMX_ErrorInsufficientResources; + } + } else + { + DEBUG_PRINT("Output buffer memory allocation failed\n"); + eRet = OMX_ErrorInsufficientResources; + } + return eRet; +} + + +// AllocateBuffer -- API Call +/* ====================================================================== +FUNCTION + omx_qcelp13_aenc::AllocateBuffer + +DESCRIPTION + Returns zero if all the buffers released.. + +PARAMETERS + None. + +RETURN VALUE + true/false + +========================================================================== */ +OMX_ERRORTYPE omx_qcelp13_aenc::allocate_buffer +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE** bufferHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes) +{ + + OMX_ERRORTYPE eRet = OMX_ErrorNone; // OMX return type + + if (m_state == OMX_StateInvalid) + { + DEBUG_PRINT_ERROR("Allocate Buf in Invalid State\n"); + return OMX_ErrorInvalidState; + } + // What if the client calls again. + if (OMX_CORE_INPUT_PORT_INDEX == port) + { + eRet = allocate_input_buffer(hComp,bufferHdr,port,appData,bytes); + } else if (OMX_CORE_OUTPUT_PORT_INDEX == port) + { + eRet = allocate_output_buffer(hComp,bufferHdr,port,appData,bytes); + } else + { + DEBUG_PRINT_ERROR("Error: Invalid Port Index received %d\n", + (int)port); + eRet = OMX_ErrorBadPortIndex; + } + + if (eRet == OMX_ErrorNone) + { + DEBUG_PRINT("allocate_buffer: before allocate_done \n"); + if (allocate_done()) + { + DEBUG_PRINT("allocate_buffer: after allocate_done \n"); + if (BITMASK_PRESENT(&m_flags,OMX_COMPONENT_IDLE_PENDING)) + { + BITMASK_CLEAR(&m_flags, OMX_COMPONENT_IDLE_PENDING); + post_command(OMX_CommandStateSet,OMX_StateIdle, + OMX_COMPONENT_GENERATE_EVENT); + DEBUG_PRINT("allocate_buffer: post idle transition event \n"); + } + DEBUG_PRINT("allocate_buffer: complete \n"); + } + if (port == OMX_CORE_INPUT_PORT_INDEX && m_inp_bPopulated) + { + if (BITMASK_PRESENT(&m_flags,OMX_COMPONENT_INPUT_ENABLE_PENDING)) + { + BITMASK_CLEAR((&m_flags),OMX_COMPONENT_INPUT_ENABLE_PENDING); + post_command(OMX_CommandPortEnable, OMX_CORE_INPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + } + } + if (port == OMX_CORE_OUTPUT_PORT_INDEX && m_out_bPopulated) + { + if (BITMASK_PRESENT(&m_flags,OMX_COMPONENT_OUTPUT_ENABLE_PENDING)) + { + BITMASK_CLEAR((&m_flags),OMX_COMPONENT_OUTPUT_ENABLE_PENDING); + m_out_bEnabled = OMX_TRUE; + + DEBUG_PRINT("AllocBuf-->is_out_th_sleep=%d\n",is_out_th_sleep); + pthread_mutex_lock(&m_out_th_lock_1); + if (is_out_th_sleep) + { + is_out_th_sleep = false; + DEBUG_DETAIL("AllocBuf:WAKING UP OUT THREADS\n"); + out_th_wakeup(); + } + pthread_mutex_unlock(&m_out_th_lock_1); + pthread_mutex_lock(&m_in_th_lock_1); + if(is_in_th_sleep) + { + is_in_th_sleep = false; + DEBUG_DETAIL("AB:WAKING UP IN THREADS\n"); + in_th_wakeup(); + } + pthread_mutex_unlock(&m_in_th_lock_1); + post_command(OMX_CommandPortEnable, OMX_CORE_OUTPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + } + } + } + DEBUG_PRINT("Allocate Buffer exit with ret Code %d\n", eRet); + return eRet; +} + +/*============================================================================= +FUNCTION: + use_buffer + +DESCRIPTION: + OMX Use Buffer method implementation. + +INPUT/OUTPUT PARAMETERS: + [INOUT] bufferHdr + [IN] hComp + [IN] port + [IN] appData + [IN] bytes + [IN] buffer + +RETURN VALUE: + OMX_ERRORTYPE + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +OMX_ERRORTYPE omx_qcelp13_aenc::use_buffer +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE** bufferHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes, + OMX_IN OMX_U8* buffer) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + + if (OMX_CORE_INPUT_PORT_INDEX == port) + { + eRet = use_input_buffer(hComp,bufferHdr,port,appData,bytes,buffer); + + } else if (OMX_CORE_OUTPUT_PORT_INDEX == port) + { + eRet = use_output_buffer(hComp,bufferHdr,port,appData,bytes,buffer); + } else + { + DEBUG_PRINT_ERROR("Error: Invalid Port Index received %d\n",(int)port); + eRet = OMX_ErrorBadPortIndex; + } + + if (eRet == OMX_ErrorNone) + { + DEBUG_PRINT("Checking for Output Allocate buffer Done"); + if (allocate_done()) + { + if (BITMASK_PRESENT(&m_flags,OMX_COMPONENT_IDLE_PENDING)) + { + BITMASK_CLEAR(&m_flags, OMX_COMPONENT_IDLE_PENDING); + post_command(OMX_CommandStateSet,OMX_StateIdle, + OMX_COMPONENT_GENERATE_EVENT); + } + } + if (port == OMX_CORE_INPUT_PORT_INDEX && m_inp_bPopulated) + { + if (BITMASK_PRESENT(&m_flags,OMX_COMPONENT_INPUT_ENABLE_PENDING)) + { + BITMASK_CLEAR((&m_flags),OMX_COMPONENT_INPUT_ENABLE_PENDING); + post_command(OMX_CommandPortEnable, OMX_CORE_INPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + + } + } + if (port == OMX_CORE_OUTPUT_PORT_INDEX && m_out_bPopulated) + { + if (BITMASK_PRESENT(&m_flags,OMX_COMPONENT_OUTPUT_ENABLE_PENDING)) + { + BITMASK_CLEAR((&m_flags),OMX_COMPONENT_OUTPUT_ENABLE_PENDING); + post_command(OMX_CommandPortEnable, OMX_CORE_OUTPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + pthread_mutex_lock(&m_out_th_lock_1); + if (is_out_th_sleep) + { + is_out_th_sleep = false; + DEBUG_DETAIL("UseBuf:WAKING UP OUT THREADS\n"); + out_th_wakeup(); + } + pthread_mutex_unlock(&m_out_th_lock_1); + pthread_mutex_lock(&m_in_th_lock_1); + if(is_in_th_sleep) + { + is_in_th_sleep = false; + DEBUG_DETAIL("UB:WAKING UP IN THREADS\n"); + in_th_wakeup(); + } + pthread_mutex_unlock(&m_in_th_lock_1); + } + } + } + DEBUG_PRINT("Use Buffer for port[%u] eRet[%d]\n", port,eRet); + return eRet; +} +/*============================================================================= +FUNCTION: + use_input_buffer + +DESCRIPTION: + Helper function for Use buffer in the input pin + +INPUT/OUTPUT PARAMETERS: + [INOUT] bufferHdr + [IN] hComp + [IN] port + [IN] appData + [IN] bytes + [IN] buffer + +RETURN VALUE: + OMX_ERRORTYPE + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +OMX_ERRORTYPE omx_qcelp13_aenc::use_input_buffer +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE** bufferHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes, + OMX_IN OMX_U8* buffer) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + OMX_BUFFERHEADERTYPE *bufHdr; + unsigned nBufSize = MAX(bytes, input_buffer_size); + char *buf_ptr; + + if(hComp == NULL) + { + port = port; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if(bytes < input_buffer_size) + { + /* return if i\p buffer size provided by client + is less than min i\p buffer size supported by omx component*/ + return OMX_ErrorInsufficientResources; + } + if (m_inp_current_buf_count < m_inp_act_buf_count) + { + buf_ptr = (char *) calloc(sizeof(OMX_BUFFERHEADERTYPE), 1); + + if (buf_ptr != NULL) + { + bufHdr = (OMX_BUFFERHEADERTYPE *) buf_ptr; + *bufferHdr = bufHdr; + memset(bufHdr,0,sizeof(OMX_BUFFERHEADERTYPE)); + + bufHdr->pBuffer = (OMX_U8 *)(buffer); + DEBUG_PRINT("use_input_buffer:bufHdr %p bufHdr->pBuffer %p \ + bytes=%u", bufHdr, bufHdr->pBuffer,bytes); + bufHdr->nSize = (OMX_U32)sizeof(OMX_BUFFERHEADERTYPE); + bufHdr->nVersion.nVersion = OMX_SPEC_VERSION; + bufHdr->nAllocLen = nBufSize; + input_buffer_size = nBufSize; + bufHdr->pAppPrivate = appData; + bufHdr->nInputPortIndex = OMX_CORE_INPUT_PORT_INDEX; + bufHdr->nOffset = 0; + m_input_buf_hdrs.insert(bufHdr, NULL); + m_inp_current_buf_count++; + } else + { + DEBUG_PRINT("Input buffer memory allocation failed 1 \n"); + eRet = OMX_ErrorInsufficientResources; + } + } else + { + DEBUG_PRINT("Input buffer memory allocation failed\n"); + eRet = OMX_ErrorInsufficientResources; + } + return eRet; +} + +/*============================================================================= +FUNCTION: + use_output_buffer + +DESCRIPTION: + Helper function for Use buffer in the output pin + +INPUT/OUTPUT PARAMETERS: + [INOUT] bufferHdr + [IN] hComp + [IN] port + [IN] appData + [IN] bytes + [IN] buffer + +RETURN VALUE: + OMX_ERRORTYPE + +Dependency: + None + +SIDE EFFECTS: + None +=============================================================================*/ +OMX_ERRORTYPE omx_qcelp13_aenc::use_output_buffer +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE** bufferHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN OMX_U32 bytes, + OMX_IN OMX_U8* buffer) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + OMX_BUFFERHEADERTYPE *bufHdr; + unsigned nBufSize = MAX(bytes,output_buffer_size); + char *buf_ptr; + + if(hComp == NULL) + { + port = port; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (bytes < output_buffer_size) + { + /* return if o\p buffer size provided by client + is less than min o\p buffer size supported by omx component*/ + return OMX_ErrorInsufficientResources; + } + + DEBUG_PRINT("Inside omx_qcelp13_aenc::use_output_buffer"); + if (m_out_current_buf_count < m_out_act_buf_count) + { + + buf_ptr = (char *) calloc(sizeof(OMX_BUFFERHEADERTYPE), 1); + + if (buf_ptr != NULL) + { + bufHdr = (OMX_BUFFERHEADERTYPE *) buf_ptr; + DEBUG_PRINT("BufHdr=%p buffer=%p\n",bufHdr,buffer); + *bufferHdr = bufHdr; + memset(bufHdr,0,sizeof(OMX_BUFFERHEADERTYPE)); + + bufHdr->pBuffer = (OMX_U8 *)(buffer); + DEBUG_PRINT("use_output_buffer:bufHdr %p bufHdr->pBuffer %p \ + len=%u\n", bufHdr, bufHdr->pBuffer,bytes); + bufHdr->nSize = (OMX_U32)sizeof(OMX_BUFFERHEADERTYPE); + bufHdr->nVersion.nVersion = OMX_SPEC_VERSION; + bufHdr->nAllocLen = nBufSize; + output_buffer_size = nBufSize; + bufHdr->pAppPrivate = appData; + bufHdr->nOutputPortIndex = OMX_CORE_OUTPUT_PORT_INDEX; + bufHdr->nOffset = 0; + m_output_buf_hdrs.insert(bufHdr, NULL); + m_out_current_buf_count++; + + } else + { + DEBUG_PRINT("Output buffer memory allocation failed\n"); + eRet = OMX_ErrorInsufficientResources; + } + } else + { + DEBUG_PRINT("Output buffer memory allocation failed 2\n"); + eRet = OMX_ErrorInsufficientResources; + } + return eRet; +} +/** + @brief member function that searches for caller buffer + + @param buffer pointer to buffer header + @return bool value indicating whether buffer is found + */ +bool omx_qcelp13_aenc::search_input_bufhdr(OMX_BUFFERHEADERTYPE *buffer) +{ + + bool eRet = false; + OMX_BUFFERHEADERTYPE *temp = NULL; + + //access only in IL client context + temp = m_input_buf_hdrs.find_ele(buffer); + if (buffer && temp) + { + DEBUG_DETAIL("search_input_bufhdr %p \n", buffer); + eRet = true; + } + return eRet; +} + +/** + @brief member function that searches for caller buffer + + @param buffer pointer to buffer header + @return bool value indicating whether buffer is found + */ +bool omx_qcelp13_aenc::search_output_bufhdr(OMX_BUFFERHEADERTYPE *buffer) +{ + + bool eRet = false; + OMX_BUFFERHEADERTYPE *temp = NULL; + + //access only in IL client context + temp = m_output_buf_hdrs.find_ele(buffer); + if (buffer && temp) + { + DEBUG_DETAIL("search_output_bufhdr %p \n", buffer); + eRet = true; + } + return eRet; +} + +// Free Buffer - API call +/** + @brief member function that handles free buffer command from IL client + + This function is a block-call function that handles IL client request to + freeing the buffer + + @param hComp handle to component instance + @param port id of port which holds the buffer + @param buffer buffer header + @return Error status +*/ +OMX_ERRORTYPE omx_qcelp13_aenc::free_buffer( + OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_U32 port, + OMX_IN OMX_BUFFERHEADERTYPE* buffer) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + + DEBUG_PRINT("Free_Buffer buf %p\n", buffer); + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (m_state == OMX_StateIdle && + (BITMASK_PRESENT(&m_flags ,OMX_COMPONENT_LOADING_PENDING))) + { + DEBUG_PRINT(" free buffer while Component in Loading pending\n"); + } else if ((m_inp_bEnabled == OMX_FALSE && + port == OMX_CORE_INPUT_PORT_INDEX)|| + (m_out_bEnabled == OMX_FALSE && + port == OMX_CORE_OUTPUT_PORT_INDEX)) + { + DEBUG_PRINT("Free Buffer while port %u disabled\n", port); + } else if (m_state == OMX_StateExecuting || m_state == OMX_StatePause) + { + DEBUG_PRINT("Invalid state to free buffer,ports need to be disabled:\ + OMX_ErrorPortUnpopulated\n"); + post_command(OMX_EventError, + OMX_ErrorPortUnpopulated, + OMX_COMPONENT_GENERATE_EVENT); + + return eRet; + } else + { + DEBUG_PRINT("free_buffer: Invalid state to free buffer,ports need to be\ + disabled:OMX_ErrorPortUnpopulated\n"); + post_command(OMX_EventError, + OMX_ErrorPortUnpopulated, + OMX_COMPONENT_GENERATE_EVENT); + } + if (OMX_CORE_INPUT_PORT_INDEX == port) + { + if (m_inp_current_buf_count != 0) + { + m_inp_bPopulated = OMX_FALSE; + if (true == search_input_bufhdr(buffer)) + { + /* Buffer exist */ + //access only in IL client context + DEBUG_PRINT("Free_Buf:in_buffer[%p]\n",buffer); + m_input_buf_hdrs.erase(buffer); + free(buffer); + m_inp_current_buf_count--; + } else + { + DEBUG_PRINT_ERROR("Free_Buf:Error-->free_buffer, \ + Invalid Input buffer header\n"); + eRet = OMX_ErrorBadParameter; + } + } else + { + DEBUG_PRINT_ERROR("Error: free_buffer,Port Index calculation \ + came out Invalid\n"); + eRet = OMX_ErrorBadPortIndex; + } + if (BITMASK_PRESENT((&m_flags),OMX_COMPONENT_INPUT_DISABLE_PENDING) + && release_done(0)) + { + DEBUG_PRINT("INPUT PORT MOVING TO DISABLED STATE \n"); + BITMASK_CLEAR((&m_flags),OMX_COMPONENT_INPUT_DISABLE_PENDING); + post_command(OMX_CommandPortDisable, + OMX_CORE_INPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + } + } else if (OMX_CORE_OUTPUT_PORT_INDEX == port) + { + if (m_out_current_buf_count != 0) + { + m_out_bPopulated = OMX_FALSE; + if (true == search_output_bufhdr(buffer)) + { + /* Buffer exist */ + //access only in IL client context + DEBUG_PRINT("Free_Buf:out_buffer[%p]\n",buffer); + m_output_buf_hdrs.erase(buffer); + free(buffer); + m_out_current_buf_count--; + } else + { + DEBUG_PRINT("Free_Buf:Error-->free_buffer , \ + Invalid Output buffer header\n"); + eRet = OMX_ErrorBadParameter; + } + } else + { + eRet = OMX_ErrorBadPortIndex; + } + + if (BITMASK_PRESENT((&m_flags),OMX_COMPONENT_OUTPUT_DISABLE_PENDING) + && release_done(1)) + { + DEBUG_PRINT("OUTPUT PORT MOVING TO DISABLED STATE \n"); + BITMASK_CLEAR((&m_flags),OMX_COMPONENT_OUTPUT_DISABLE_PENDING); + post_command(OMX_CommandPortDisable, + OMX_CORE_OUTPUT_PORT_INDEX, + OMX_COMPONENT_GENERATE_EVENT); + + } + } else + { + eRet = OMX_ErrorBadPortIndex; + } + if ((OMX_ErrorNone == eRet) && + (BITMASK_PRESENT(&m_flags ,OMX_COMPONENT_LOADING_PENDING))) + { + if (release_done(-1)) + { + if(ioctl(m_drv_fd, AUDIO_STOP, 0) < 0) + DEBUG_PRINT_ERROR("AUDIO STOP in free buffer failed\n"); + else + DEBUG_PRINT("AUDIO STOP in free buffer passed\n"); + + + DEBUG_PRINT("Free_Buf: Free buffer\n"); + + + // Send the callback now + BITMASK_CLEAR((&m_flags),OMX_COMPONENT_LOADING_PENDING); + DEBUG_PRINT("Before OMX_StateLoaded \ + OMX_COMPONENT_GENERATE_EVENT\n"); + post_command(OMX_CommandStateSet, + OMX_StateLoaded,OMX_COMPONENT_GENERATE_EVENT); + DEBUG_PRINT("After OMX_StateLoaded OMX_COMPONENT_GENERATE_EVENT\n"); + + } + } + return eRet; +} + + +/** + @brief member function that that handles empty this buffer command + + This function meremly queue up the command and data would be consumed + in command server thread context + + @param hComp handle to component instance + @param buffer pointer to buffer header + @return error status + */ +OMX_ERRORTYPE omx_qcelp13_aenc::empty_this_buffer( + OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_BUFFERHEADERTYPE* buffer) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + + DEBUG_PRINT("ETB:Buf:%p Len %u TS %lld numInBuf=%d\n", \ + buffer, buffer->nFilledLen, buffer->nTimeStamp, (nNumInputBuf)); + if (m_state == OMX_StateInvalid) + { + DEBUG_PRINT("Empty this buffer in Invalid State\n"); + return OMX_ErrorInvalidState; + } + if (!m_inp_bEnabled) + { + DEBUG_PRINT("empty_this_buffer OMX_ErrorIncorrectStateOperation "\ + "Port Status %d \n", m_inp_bEnabled); + return OMX_ErrorIncorrectStateOperation; + } + if (buffer->nSize != sizeof(OMX_BUFFERHEADERTYPE)) + { + DEBUG_PRINT("omx_qcelp13_aenc::etb--> Buffer Size Invalid\n"); + return OMX_ErrorBadParameter; + } + if (buffer->nVersion.nVersion != OMX_SPEC_VERSION) + { + DEBUG_PRINT("omx_qcelp13_aenc::etb--> OMX Version Invalid\n"); + return OMX_ErrorVersionMismatch; + } + + if (buffer->nInputPortIndex != OMX_CORE_INPUT_PORT_INDEX) + { + return OMX_ErrorBadPortIndex; + } + if ((m_state != OMX_StateExecuting) && + (m_state != OMX_StatePause)) + { + DEBUG_PRINT_ERROR("Invalid state\n"); + eRet = OMX_ErrorInvalidState; + } + if (OMX_ErrorNone == eRet) + { + if (search_input_bufhdr(buffer) == true) + { + post_input((unsigned long)hComp, + (unsigned long) buffer,OMX_COMPONENT_GENERATE_ETB); + } else + { + DEBUG_PRINT_ERROR("Bad header %p \n", buffer); + eRet = OMX_ErrorBadParameter; + } + } + pthread_mutex_lock(&in_buf_count_lock); + nNumInputBuf++; + m_qcelp13_pb_stats.etb_cnt++; + pthread_mutex_unlock(&in_buf_count_lock); + return eRet; +} +/** + @brief member function that writes data to kernel driver + + @param hComp handle to component instance + @param buffer pointer to buffer header + @return error status + */ +OMX_ERRORTYPE omx_qcelp13_aenc::empty_this_buffer_proxy +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE* buffer) +{ + OMX_STATETYPE state; + META_IN meta_in; + //Pointer to the starting location of the data to be transcoded + OMX_U8 *srcStart; + //The total length of the data to be transcoded + srcStart = buffer->pBuffer; + OMX_U8 *data = NULL; + PrintFrameHdr(OMX_COMPONENT_GENERATE_ETB,buffer); + memset(&meta_in,0,sizeof(meta_in)); + if ( search_input_bufhdr(buffer) == false ) + { + DEBUG_PRINT("ETBP: INVALID BUF HDR\n"); + buffer_done_cb((OMX_BUFFERHEADERTYPE *)buffer); + return OMX_ErrorBadParameter; + } + if (m_tmp_meta_buf) + { + data = m_tmp_meta_buf; + + // copy the metadata info from the BufHdr and insert to payload + meta_in.offsetVal = (OMX_U16)sizeof(META_IN); + meta_in.nTimeStamp.LowPart = + (unsigned int)((((OMX_BUFFERHEADERTYPE*)buffer)->nTimeStamp) & 0xFFFFFFFF); + meta_in.nTimeStamp.HighPart = + (unsigned int)(((((OMX_BUFFERHEADERTYPE*)buffer)->nTimeStamp) >> 32) & 0xFFFFFFFF); + meta_in.nFlags &= ~OMX_BUFFERFLAG_EOS; + if(buffer->nFlags & OMX_BUFFERFLAG_EOS) + { + DEBUG_PRINT("EOS OCCURED \n"); + meta_in.nFlags |= OMX_BUFFERFLAG_EOS; + } + memcpy(data,&meta_in, meta_in.offsetVal); + DEBUG_PRINT("meta_in.nFlags = 0x%8x\n",meta_in.nFlags); + } + + memcpy(&data[sizeof(META_IN)],buffer->pBuffer,buffer->nFilledLen); + write(m_drv_fd, data, buffer->nFilledLen+sizeof(META_IN)); + + pthread_mutex_lock(&m_state_lock); + get_state(&m_cmp, &state); + pthread_mutex_unlock(&m_state_lock); + + if (OMX_StateExecuting == state) + { + DEBUG_DETAIL("In Exe state, EBD CB"); + buffer_done_cb((OMX_BUFFERHEADERTYPE *)buffer); + } else + { + /* Assume empty this buffer function has already checked + validity of buffer */ + DEBUG_PRINT("Empty buffer %p to kernel driver\n", buffer); + post_input((unsigned long) & hComp,(unsigned long) buffer, + OMX_COMPONENT_GENERATE_BUFFER_DONE); + } + return OMX_ErrorNone; +} + +OMX_ERRORTYPE omx_qcelp13_aenc::fill_this_buffer_proxy +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_BUFFERHEADERTYPE* buffer) +{ + OMX_STATETYPE state; + ENC_META_OUT *meta_out = NULL; + ssize_t nReadbytes = 0; + + pthread_mutex_lock(&m_state_lock); + get_state(&m_cmp, &state); + pthread_mutex_unlock(&m_state_lock); + + if (true == search_output_bufhdr(buffer)) + { + DEBUG_PRINT("\nBefore Read..m_drv_fd = %d,\n",m_drv_fd); + nReadbytes = read(m_drv_fd,buffer->pBuffer,output_buffer_size ); + DEBUG_DETAIL("FTBP->Al_len[%lu]buf[%p]size[%d]numOutBuf[%d]\n",\ + buffer->nAllocLen,buffer->pBuffer, + nReadbytes,nNumOutputBuf); + if (nReadbytes <= 0) { + buffer->nFilledLen = 0; + buffer->nOffset = 0; + buffer->nTimeStamp = nTimestamp; + frame_done_cb((OMX_BUFFERHEADERTYPE *)buffer); + return OMX_ErrorNone; + } else + DEBUG_PRINT("Read bytes %d\n",nReadbytes); + + // Buffer from Driver will have + // 1 byte => Nr of frame field + // (sizeof(ENC_META_OUT) * Nr of frame) bytes => meta_out->offset_to_frame + // Frame Size * Nr of frame => + + meta_out = (ENC_META_OUT *)(buffer->pBuffer + sizeof(unsigned char)); + buffer->nTimeStamp = (((OMX_TICKS)meta_out->msw_ts << 32)+ + meta_out->lsw_ts); + buffer->nFlags |= meta_out->nflags; + buffer->nOffset = (OMX_U32) (meta_out->offset_to_frame + + sizeof(unsigned char)); + buffer->nFilledLen = (OMX_U32)(nReadbytes - buffer->nOffset); + nTimestamp = buffer->nTimeStamp; + DEBUG_PRINT("nflags %d frame_size %d offset_to_frame %d \ + timestamp %lld\n", meta_out->nflags, + meta_out->frame_size, meta_out->offset_to_frame, + buffer->nTimeStamp); + + if ((buffer->nFlags & OMX_BUFFERFLAG_EOS) == OMX_BUFFERFLAG_EOS ) + { + buffer->nFilledLen = 0; + buffer->nOffset = 0; + buffer->nTimeStamp = nTimestamp; + frame_done_cb((OMX_BUFFERHEADERTYPE *)buffer); + if ((buffer->nFlags & OMX_BUFFERFLAG_EOS) == OMX_BUFFERFLAG_EOS ) + { + DEBUG_PRINT("FTBP: Now, Send EOS flag to Client \n"); + m_cb.EventHandler(&m_cmp, + m_app_data, + OMX_EventBufferFlag, + 1, 1, NULL ); + } + + return OMX_ErrorNone; + } + DEBUG_PRINT("nState %d \n",nState ); + + pthread_mutex_lock(&m_state_lock); + get_state(&m_cmp, &state); + pthread_mutex_unlock(&m_state_lock); + + if (state == OMX_StatePause) + { + DEBUG_PRINT("FTBP:Post the FBD to event thread currstate=%d\n",\ + state); + post_output((unsigned long) & hComp,(unsigned long) buffer, + OMX_COMPONENT_GENERATE_FRAME_DONE); + } + else + { + frame_done_cb((OMX_BUFFERHEADERTYPE *)buffer); + + } + + } + else + DEBUG_PRINT("\n FTBP-->Invalid buffer in FTB \n"); + + + return OMX_ErrorNone; +} + +/* ====================================================================== +FUNCTION + omx_qcelp13_aenc::FillThisBuffer + +DESCRIPTION + IL client uses this method to release the frame buffer + after displaying them. + + + +PARAMETERS + + None. + +RETURN VALUE + true/false + +========================================================================== */ +OMX_ERRORTYPE omx_qcelp13_aenc::fill_this_buffer +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_BUFFERHEADERTYPE* buffer) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + if (buffer->nSize != sizeof(OMX_BUFFERHEADERTYPE)) + { + DEBUG_PRINT("omx_qcelp13_aenc::ftb--> Buffer Size Invalid\n"); + return OMX_ErrorBadParameter; + } + if (m_out_bEnabled == OMX_FALSE) + { + return OMX_ErrorIncorrectStateOperation; + } + + if (buffer->nVersion.nVersion != OMX_SPEC_VERSION) + { + DEBUG_PRINT("omx_qcelp13_aenc::ftb--> OMX Version Invalid\n"); + return OMX_ErrorVersionMismatch; + } + if (buffer->nOutputPortIndex != OMX_CORE_OUTPUT_PORT_INDEX) + { + return OMX_ErrorBadPortIndex; + } + pthread_mutex_lock(&out_buf_count_lock); + nNumOutputBuf++; + m_qcelp13_pb_stats.ftb_cnt++; + DEBUG_DETAIL("FTB:nNumOutputBuf is %d", nNumOutputBuf); + pthread_mutex_unlock(&out_buf_count_lock); + post_output((unsigned long)hComp, + (unsigned long) buffer,OMX_COMPONENT_GENERATE_FTB); + return eRet; +} + +/* ====================================================================== +FUNCTION + omx_qcelp13_aenc::SetCallbacks + +DESCRIPTION + Set the callbacks. + +PARAMETERS + None. + +RETURN VALUE + OMX Error None if everything successful. + +========================================================================== */ +OMX_ERRORTYPE omx_qcelp13_aenc::set_callbacks( + OMX_IN OMX_HANDLETYPE hComp, + OMX_IN OMX_CALLBACKTYPE* callbacks, + OMX_IN OMX_PTR appData) +{ + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + m_cb = *callbacks; + m_app_data = appData; + + return OMX_ErrorNone; +} + +/* ====================================================================== +FUNCTION + omx_qcelp13_aenc::ComponentDeInit + +DESCRIPTION + Destroys the component and release memory allocated to the heap. + +PARAMETERS + . + +RETURN VALUE + OMX Error None if everything successful. + +========================================================================== */ +OMX_ERRORTYPE omx_qcelp13_aenc::component_deinit(OMX_IN OMX_HANDLETYPE hComp) +{ + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (OMX_StateLoaded != m_state && OMX_StateInvalid != m_state) + { + DEBUG_PRINT_ERROR("Warning: Rxed DeInit when not in LOADED state %d\n", + m_state); + } + deinit_encoder(); + +DEBUG_PRINT_ERROR("%s:COMPONENT DEINIT...\n", __FUNCTION__); + return OMX_ErrorNone; +} + +/* ====================================================================== +FUNCTION + omx_qcelp13_aenc::deinit_encoder + +DESCRIPTION + Closes all the threads and release memory allocated to the heap. + +PARAMETERS + None. + +RETURN VALUE + None. + +========================================================================== */ +void omx_qcelp13_aenc::deinit_encoder() +{ + DEBUG_PRINT("Component-deinit being processed\n"); + DEBUG_PRINT("********************************\n"); + DEBUG_PRINT("STATS: in-buf-len[%u]out-buf-len[%u] tot-pb-time[%lld]",\ + m_qcelp13_pb_stats.tot_in_buf_len, + m_qcelp13_pb_stats.tot_out_buf_len, + m_qcelp13_pb_stats.tot_pb_time); + DEBUG_PRINT("STATS: fbd-cnt[%u]ftb-cnt[%u]etb-cnt[%u]ebd-cnt[%u]",\ + m_qcelp13_pb_stats.fbd_cnt,m_qcelp13_pb_stats.ftb_cnt, + m_qcelp13_pb_stats.etb_cnt, + m_qcelp13_pb_stats.ebd_cnt); + memset(&m_qcelp13_pb_stats,0,sizeof(QCELP13_PB_STATS)); + + if((OMX_StateLoaded != m_state) && (OMX_StateInvalid != m_state)) + { + DEBUG_PRINT_ERROR("%s,Deinit called in state[%d]\n",__FUNCTION__,\ + m_state); + // Get back any buffers from driver + if(pcm_input) + execute_omx_flush(-1,false); + else + execute_omx_flush(1,false); + // force state change to loaded so that all threads can be exited + pthread_mutex_lock(&m_state_lock); + m_state = OMX_StateLoaded; + pthread_mutex_unlock(&m_state_lock); + DEBUG_PRINT_ERROR("Freeing Buf:inp_current_buf_count[%d][%d]\n",\ + m_inp_current_buf_count, + m_input_buf_hdrs.size()); + m_input_buf_hdrs.eraseall(); + DEBUG_PRINT_ERROR("Freeing Buf:out_current_buf_count[%d][%d]\n",\ + m_out_current_buf_count, + m_output_buf_hdrs.size()); + m_output_buf_hdrs.eraseall(); + + } + if(pcm_input) + { + pthread_mutex_lock(&m_in_th_lock_1); + if (is_in_th_sleep) + { + is_in_th_sleep = false; + DEBUG_DETAIL("Deinit:WAKING UP IN THREADS\n"); + in_th_wakeup(); + } + pthread_mutex_unlock(&m_in_th_lock_1); + } + pthread_mutex_lock(&m_out_th_lock_1); + if (is_out_th_sleep) + { + is_out_th_sleep = false; + DEBUG_DETAIL("SCP:WAKING UP OUT THREADS\n"); + out_th_wakeup(); + } + pthread_mutex_unlock(&m_out_th_lock_1); + if(pcm_input) + { + if (m_ipc_to_in_th != NULL) + { + omx_qcelp13_thread_stop(m_ipc_to_in_th); + m_ipc_to_in_th = NULL; + } + } + + if (m_ipc_to_cmd_th != NULL) + { + omx_qcelp13_thread_stop(m_ipc_to_cmd_th); + m_ipc_to_cmd_th = NULL; + } + if (m_ipc_to_out_th != NULL) + { + DEBUG_DETAIL("Inside omx_qcelp13_thread_stop\n"); + omx_qcelp13_thread_stop(m_ipc_to_out_th); + m_ipc_to_out_th = NULL; + } + + + if(ioctl(m_drv_fd, AUDIO_STOP, 0) <0) + DEBUG_PRINT_ERROR("De-init: AUDIO_STOP FAILED\n"); + + if(pcm_input && m_tmp_meta_buf ) + { + free(m_tmp_meta_buf); + } + + if(m_tmp_out_meta_buf) + { + free(m_tmp_out_meta_buf); + } + nNumInputBuf = 0; + nNumOutputBuf = 0; + bFlushinprogress = 0; + + m_inp_current_buf_count=0; + m_out_current_buf_count=0; + m_out_act_buf_count = 0; + m_inp_act_buf_count = 0; + m_inp_bEnabled = OMX_FALSE; + m_out_bEnabled = OMX_FALSE; + m_inp_bPopulated = OMX_FALSE; + m_out_bPopulated = OMX_FALSE; + + if ( m_drv_fd >= 0 ) + { + if(close(m_drv_fd) < 0) + DEBUG_PRINT("De-init: Driver Close Failed \n"); + m_drv_fd = -1; + } + else + { + DEBUG_PRINT_ERROR(" QCELP13 device already closed\n"); + } + m_comp_deinit=1; + m_is_out_th_sleep = 1; + m_is_in_th_sleep = 1; + DEBUG_PRINT("************************************\n"); + DEBUG_PRINT(" DEINIT COMPLETED"); + DEBUG_PRINT("************************************\n"); + +} + +/* ====================================================================== +FUNCTION + omx_qcelp13_aenc::UseEGLImage + +DESCRIPTION + OMX Use EGL Image method implementation . + +PARAMETERS + . + +RETURN VALUE + Not Implemented error. + +========================================================================== */ +OMX_ERRORTYPE omx_qcelp13_aenc::use_EGL_image +( + OMX_IN OMX_HANDLETYPE hComp, + OMX_INOUT OMX_BUFFERHEADERTYPE** bufferHdr, + OMX_IN OMX_U32 port, + OMX_IN OMX_PTR appData, + OMX_IN void* eglImage) +{ + DEBUG_PRINT_ERROR("Error : use_EGL_image: Not Implemented \n"); + + if((hComp == NULL) || (appData == NULL) || (eglImage == NULL)) + { + bufferHdr = bufferHdr; + port = port; + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + return OMX_ErrorNotImplemented; +} + +/* ====================================================================== +FUNCTION + omx_qcelp13_aenc::ComponentRoleEnum + +DESCRIPTION + OMX Component Role Enum method implementation. + +PARAMETERS + . + +RETURN VALUE + OMX Error None if everything is successful. +========================================================================== */ +OMX_ERRORTYPE omx_qcelp13_aenc::component_role_enum( + OMX_IN OMX_HANDLETYPE hComp, + OMX_OUT OMX_U8* role, + OMX_IN OMX_U32 index) +{ + OMX_ERRORTYPE eRet = OMX_ErrorNone; + const char *cmp_role = "audio_encoder.qcelp13"; + + if(hComp == NULL) + { + DEBUG_PRINT_ERROR("Returning OMX_ErrorBadParameter\n"); + return OMX_ErrorBadParameter; + } + if (index == 0 && role) + { + memcpy(role, cmp_role, strlen(cmp_role)); + *(((char *) role) + strlen(cmp_role) + 1) = '\0'; + } else + { + eRet = OMX_ErrorNoMore; + } + return eRet; +} + + + + +/* ====================================================================== +FUNCTION + omx_qcelp13_aenc::AllocateDone + +DESCRIPTION + Checks if entire buffer pool is allocated by IL Client or not. + Need this to move to IDLE state. + +PARAMETERS + None. + +RETURN VALUE + true/false. + +========================================================================== */ +bool omx_qcelp13_aenc::allocate_done(void) +{ + OMX_BOOL bRet = OMX_FALSE; + if (pcm_input==1) + { + if ((m_inp_act_buf_count == m_inp_current_buf_count) + &&(m_out_act_buf_count == m_out_current_buf_count)) + { + bRet=OMX_TRUE; + + } + if ((m_inp_act_buf_count == m_inp_current_buf_count) && m_inp_bEnabled ) + { + m_inp_bPopulated = OMX_TRUE; + } + + if ((m_out_act_buf_count == m_out_current_buf_count) && m_out_bEnabled ) + { + m_out_bPopulated = OMX_TRUE; + } + } else if (pcm_input==0) + { + if (m_out_act_buf_count == m_out_current_buf_count) + { + bRet=OMX_TRUE; + + } + if ((m_out_act_buf_count == m_out_current_buf_count) && m_out_bEnabled ) + { + m_out_bPopulated = OMX_TRUE; + } + + } + return bRet; +} + + +/* ====================================================================== +FUNCTION + omx_qcelp13_aenc::ReleaseDone + +DESCRIPTION + Checks if IL client has released all the buffers. + +PARAMETERS + None. + +RETURN VALUE + true/false + +========================================================================== */ +bool omx_qcelp13_aenc::release_done(OMX_U32 param1) +{ + DEBUG_PRINT("Inside omx_qcelp13_aenc::release_done"); + OMX_BOOL bRet = OMX_FALSE; + + if (param1 == OMX_ALL) + { + if ((0 == m_inp_current_buf_count)&&(0 == m_out_current_buf_count)) + { + bRet=OMX_TRUE; + } + } else if (param1 == OMX_CORE_INPUT_PORT_INDEX ) + { + if ((0 == m_inp_current_buf_count)) + { + bRet=OMX_TRUE; + } + } else if (param1 == OMX_CORE_OUTPUT_PORT_INDEX) + { + if ((0 == m_out_current_buf_count)) + { + bRet=OMX_TRUE; + } + } + return bRet; +} diff --git a/audio/mm-audio/aenc-qcelp13/qdsp6/test/omx_qcelp13_enc_test.c b/audio/mm-audio/aenc-qcelp13/qdsp6/test/omx_qcelp13_enc_test.c new file mode 100644 index 0000000..432c07a --- /dev/null +++ b/audio/mm-audio/aenc-qcelp13/qdsp6/test/omx_qcelp13_enc_test.c @@ -0,0 +1,1101 @@ + +/*-------------------------------------------------------------------------- +Copyright (c) 2010-2014, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of The Linux Foundation nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------*/ + + +/* + An Open max test application .... +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "OMX_Core.h" +#include "OMX_Component.h" +#include "pthread.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "QOMX_AudioExtensions.h" +#include "QOMX_AudioIndexExtensions.h" +#ifdef AUDIOV2 +#include "control.h" +#endif + + +#include + +typedef unsigned char uint8; +typedef unsigned char byte; +typedef unsigned int uint32; +typedef unsigned int uint16; +QOMX_AUDIO_STREAM_INFO_DATA streaminfoparam; +/* maximum ADTS frame header length */ +void Release_Encoder(); + +#ifdef AUDIOV2 +unsigned short session_id; +int device_id; +int control = 0; +const char *device="handset_tx"; +#define DIR_TX 2 +#endif + +uint32_t samplerate = 8000; +uint32_t channels = 1; +uint32_t min_bitrate = 0; +uint32_t max_bitrate = 0; +uint32_t cdmarate = 0; +uint32_t rectime = 0; +uint32_t recpath = 0; +uint32_t pcmplayback = 0; +uint32_t tunnel = 0; +uint32_t format = 1; +#define DEBUG_PRINT printf +unsigned to_idle_transition = 0; +unsigned long total_pcm_bytes; + +/************************************************************************/ +/* GLOBAL INIT */ +/************************************************************************/ + +/************************************************************************/ +/* #DEFINES */ +/************************************************************************/ +#define false 0 +#define true 1 + +#define CONFIG_VERSION_SIZE(param) \ + param.nVersion.nVersion = CURRENT_OMX_SPEC_VERSION;\ + param.nSize = sizeof(param); + +#define QCP_HEADER_SIZE sizeof(struct qcp_header) +#define MIN_BITRATE 4 /* Bit rate 1 - 13.6 , 2 - 6.2 , 3 - 2.7 , 4 - 1.0 kbps*/ +#define MAX_BITRATE 4 + +#define FAILED(result) (result != OMX_ErrorNone) + +#define SUCCEEDED(result) (result == OMX_ErrorNone) + +/************************************************************************/ +/* GLOBAL DECLARATIONS */ +/************************************************************************/ + +pthread_mutex_t lock; +pthread_cond_t cond; +pthread_mutex_t elock; +pthread_cond_t econd; +pthread_cond_t fcond; +pthread_mutex_t etb_lock; +pthread_mutex_t etb_lock1; +pthread_cond_t etb_cond; +FILE * inputBufferFile; +FILE * outputBufferFile; +OMX_PARAM_PORTDEFINITIONTYPE inputportFmt; +OMX_PARAM_PORTDEFINITIONTYPE outputportFmt; +OMX_AUDIO_PARAM_QCELP13TYPE qcelp13param; +OMX_AUDIO_PARAM_PCMMODETYPE pcmparam; +OMX_PORT_PARAM_TYPE portParam; +OMX_PORT_PARAM_TYPE portFmt; +OMX_ERRORTYPE error; + + + + +#define ID_RIFF 0x46464952 +#define ID_WAVE 0x45564157 +#define ID_FMT 0x20746d66 +#define ID_DATA 0x61746164 + +#define FORMAT_PCM 1 + +struct wav_header { + uint32_t riff_id; + uint32_t riff_sz; + uint32_t riff_fmt; + uint32_t fmt_id; + uint32_t fmt_sz; + uint16_t audio_format; + uint16_t num_channels; + uint32_t sample_rate; + uint32_t byte_rate; /* sample_rate * num_channels * bps / 8 */ + uint16_t block_align; /* num_channels * bps / 8 */ + uint16_t bits_per_sample; + uint32_t data_id; + uint32_t data_sz; +}; +struct enc_meta_out{ + unsigned int offset_to_frame; + unsigned int frame_size; + unsigned int encoded_pcm_samples; + unsigned int msw_ts; + unsigned int lsw_ts; + unsigned int nflags; +} __attribute__ ((packed)); + +struct qcp_header { + /* RIFF Section */ + char riff[4]; + unsigned int s_riff; + char qlcm[4]; + + /* Format chunk */ + char fmt[4]; + unsigned int s_fmt; + char mjr; + char mnr; + unsigned int data1; /* UNIQUE ID of the codec */ + unsigned short data2; + unsigned short data3; + char data4[8]; + unsigned short ver; /* Codec Info */ + char name[80]; + unsigned short abps; /* average bits per sec of the codec */ + unsigned short bytes_per_pkt; + unsigned short samp_per_block; + unsigned short samp_per_sec; + unsigned short bits_per_samp; + unsigned char vr_num_of_rates; /* Rate Header fmt info */ + unsigned char rvd1[3]; + unsigned short vr_bytes_per_pkt[8]; + unsigned int rvd2[5]; + + /* Vrat chunk */ + unsigned char vrat[4]; + unsigned int s_vrat; + unsigned int v_rate; + unsigned int size_in_pkts; + + /* Data chunk */ + unsigned char data[4]; + unsigned int s_data; +} __attribute__ ((packed)); + + /* Common part */ + static struct qcp_header append_header = { + {'R', 'I', 'F', 'F'}, 0, {'Q', 'L', 'C', 'M'}, + {'f', 'm', 't', ' '}, 150, 1, 0, 0, 0, 0,{0}, 0, {0},0,0,160,8000,16,0,{0},{0},{0}, + {'v','r','a','t'},0, 0, 0,{'d','a','t','a'},0 + }; + +static int totaldatalen = 0; +static int framecnt = 0; +/************************************************************************/ +/* GLOBAL INIT */ +/************************************************************************/ + +unsigned int input_buf_cnt = 0; +unsigned int output_buf_cnt = 0; +int used_ip_buf_cnt = 0; +volatile int event_is_done = 0; +volatile int ebd_event_is_done = 0; +volatile int fbd_event_is_done = 0; +volatile int etb_event_is_done = 0; +int ebd_cnt; +int bInputEosReached = 0; +int bOutputEosReached = 0; +int bInputEosReached_tunnel = 0; +static int etb_done = 0; +int bFlushing = false; +int bPause = false; +const char *in_filename; +const char *out_filename; + +int timeStampLfile = 0; +int timestampInterval = 100; + +//* OMX Spec Version supported by the wrappers. Version = 1.1 */ +const OMX_U32 CURRENT_OMX_SPEC_VERSION = 0x00000101; +OMX_COMPONENTTYPE* qcelp13_enc_handle = 0; + +OMX_BUFFERHEADERTYPE **pInputBufHdrs = NULL; +OMX_BUFFERHEADERTYPE **pOutputBufHdrs = NULL; + +/************************************************************************/ +/* GLOBAL FUNC DECL */ +/************************************************************************/ +int Init_Encoder(char*); +int Play_Encoder(); +OMX_STRING aud_comp; +/**************************************************************************/ +/* STATIC DECLARATIONS */ +/**************************************************************************/ + +static int open_audio_file (); +static int Read_Buffer(OMX_BUFFERHEADERTYPE *pBufHdr ); +static OMX_ERRORTYPE Allocate_Buffer ( OMX_COMPONENTTYPE *qcelp13_enc_handle, + OMX_BUFFERHEADERTYPE ***pBufHdrs, + OMX_U32 nPortIndex, + unsigned int bufCntMin, unsigned int bufSize); + + +static OMX_ERRORTYPE EventHandler(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_EVENTTYPE eEvent, + OMX_IN OMX_U32 nData1, OMX_IN OMX_U32 nData2, + OMX_IN OMX_PTR pEventData); +static OMX_ERRORTYPE EmptyBufferDone(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer); + +static OMX_ERRORTYPE FillBufferDone(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer); +static OMX_ERRORTYPE parse_pcm_header(); +void wait_for_event(void) +{ + pthread_mutex_lock(&lock); + DEBUG_PRINT("%s: event_is_done=%d", __FUNCTION__, event_is_done); + while (event_is_done == 0) { + pthread_cond_wait(&cond, &lock); + } + event_is_done = 0; + pthread_mutex_unlock(&lock); +} + +void event_complete(void ) +{ + pthread_mutex_lock(&lock); + if (event_is_done == 0) { + event_is_done = 1; + pthread_cond_broadcast(&cond); + } + pthread_mutex_unlock(&lock); +} + +void etb_wait_for_event(void) +{ + pthread_mutex_lock(&etb_lock1); + DEBUG_PRINT("%s: etb_event_is_done=%d", __FUNCTION__, etb_event_is_done); + while (etb_event_is_done == 0) { + pthread_cond_wait(&etb_cond, &etb_lock1); + } + etb_event_is_done = 0; + pthread_mutex_unlock(&etb_lock1); +} + +void etb_event_complete(void ) +{ + pthread_mutex_lock(&etb_lock1); + if (etb_event_is_done == 0) { + etb_event_is_done = 1; + pthread_cond_broadcast(&etb_cond); + } + pthread_mutex_unlock(&etb_lock1); +} + +static void create_qcp_header(int Datasize, int Frames) +{ + append_header.s_riff = (unsigned)(Datasize + (int)QCP_HEADER_SIZE - 8); + /* exclude riff id and size field */ + append_header.data1 = 0x5E7F6D41; + append_header.data2 = 0xB115; + append_header.data3 = 0x11D0; + append_header.data4[0] = 0xBA; + append_header.data4[1] = 0x91; + append_header.data4[2] = 0x00; + append_header.data4[3] = 0x80; + append_header.data4[4] = 0x5F; + append_header.data4[5] = 0xB4; + append_header.data4[6] = 0xB9; + append_header.data4[7] = 0x7E; + append_header.ver = 0x0002; + memcpy(append_header.name, "Qcelp 13K", 9); + append_header.abps = 13000; + append_header.bytes_per_pkt = 35; + append_header.vr_num_of_rates = 5; + append_header.vr_bytes_per_pkt[0] = 0x0422; + append_header.vr_bytes_per_pkt[1] = 0x0310; + append_header.vr_bytes_per_pkt[2] = 0x0207; + append_header.vr_bytes_per_pkt[3] = 0x0103; + append_header.s_vrat = 0x00000008; + append_header.v_rate = 0x00000001; + append_header.size_in_pkts = (unsigned)Frames; + append_header.s_data = (unsigned)Datasize; + return; +} + +OMX_ERRORTYPE EventHandler(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_EVENTTYPE eEvent, + OMX_IN OMX_U32 nData1, OMX_IN OMX_U32 nData2, + OMX_IN OMX_PTR pEventData) +{ + DEBUG_PRINT("Function %s \n", __FUNCTION__); + + /* To remove warning for unused variable to keep prototype same */ + (void)hComponent; + (void)pAppData; + (void)pEventData; + + switch(eEvent) { + case OMX_EventCmdComplete: + DEBUG_PRINT("\n OMX_EventCmdComplete event=%d data1=%u data2=%u\n",(OMX_EVENTTYPE)eEvent, + nData1,nData2); + event_complete(); + break; + case OMX_EventError: + DEBUG_PRINT("\n OMX_EventError \n"); + break; + case OMX_EventBufferFlag: + DEBUG_PRINT("\n OMX_EventBufferFlag \n"); + bOutputEosReached = true; + event_complete(); + break; + case OMX_EventPortSettingsChanged: + DEBUG_PRINT("\n OMX_EventPortSettingsChanged \n"); + break; + default: + DEBUG_PRINT("\n Unknown Event \n"); + break; + } + return OMX_ErrorNone; +} + +OMX_ERRORTYPE FillBufferDone(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) +{ + size_t bytes_writen = 0; + size_t total_bytes_writen = 0; + size_t len = 0; + struct enc_meta_out *meta = NULL; + OMX_U8 *src = pBuffer->pBuffer; + unsigned int num_of_frames = 1; + + /* To remove warning for unused variable to keep prototype same */ + (void)pAppData; + + if(((pBuffer->nFlags & OMX_BUFFERFLAG_EOS) == OMX_BUFFERFLAG_EOS)) { + DEBUG_PRINT("FBD::EOS on output port\n "); + bOutputEosReached = true; + return OMX_ErrorNone; + } + if(bInputEosReached_tunnel || bOutputEosReached) + { + DEBUG_PRINT("EOS REACHED NO MORE PROCESSING OF BUFFERS\n"); + return OMX_ErrorNone; + } + if(num_of_frames != src[0]){ + + printf("Data corrupt\n"); + return OMX_ErrorNone; + } + /* Skip the first bytes */ + + + + src += sizeof(unsigned char); + meta = (struct enc_meta_out *)src; + while (num_of_frames > 0) { + meta = (struct enc_meta_out *)src; + /*printf("offset=%d framesize=%d encoded_pcm[%d] msw_ts[%d]lsw_ts[%d] nflags[%d]\n", + meta->offset_to_frame, + meta->frame_size, + meta->encoded_pcm_samples, meta->msw_ts, meta->lsw_ts, meta->nflags);*/ + len = meta->frame_size; + + bytes_writen = fwrite(pBuffer->pBuffer + sizeof(unsigned char) + meta->offset_to_frame,1,len,outputBufferFile); + if(bytes_writen < len) + { + DEBUG_PRINT("error: invalid QCELP13 encoded data \n"); + return OMX_ErrorNone; + } + src += sizeof(struct enc_meta_out); + num_of_frames--; + total_bytes_writen += len; + } + DEBUG_PRINT(" FillBufferDone size writen to file %zu count %d\n",total_bytes_writen, framecnt); + totaldatalen = totaldatalen + (int)total_bytes_writen; + framecnt++; + + DEBUG_PRINT(" FBD calling FTB\n"); + OMX_FillThisBuffer(hComponent,pBuffer); + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE EmptyBufferDone(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) +{ + int readBytes =0; + + /* To remove warning for unused variable to keep prototype same */ + (void)pAppData; + + ebd_cnt++; + used_ip_buf_cnt--; + pthread_mutex_lock(&etb_lock); + if(!etb_done) + { + DEBUG_PRINT("\n*********************************************\n"); + DEBUG_PRINT("Wait till first set of buffers are given to component\n"); + DEBUG_PRINT("\n*********************************************\n"); + etb_done++; + pthread_mutex_unlock(&etb_lock); + etb_wait_for_event(); + } + else + { + pthread_mutex_unlock(&etb_lock); + } + + + if(bInputEosReached) + { + DEBUG_PRINT("\n*********************************************\n"); + DEBUG_PRINT(" EBD::EOS on input port\n "); + DEBUG_PRINT("*********************************************\n"); + return OMX_ErrorNone; + }else if (bFlushing == true) { + DEBUG_PRINT("omx_qcelp13_adec_test: bFlushing is set to TRUE used_ip_buf_cnt=%d\n",used_ip_buf_cnt); + if (used_ip_buf_cnt == 0) { + bFlushing = false; + } else { + DEBUG_PRINT("omx_qcelp13_adec_test: more buffer to come back used_ip_buf_cnt=%d\n",used_ip_buf_cnt); + return OMX_ErrorNone; + } + } + + if((readBytes = Read_Buffer(pBuffer)) > 0) { + pBuffer->nFilledLen = (OMX_U32)readBytes; + used_ip_buf_cnt++; + OMX_EmptyThisBuffer(hComponent,pBuffer); + } + else{ + pBuffer->nFlags |= OMX_BUFFERFLAG_EOS; + used_ip_buf_cnt++; + bInputEosReached = true; + pBuffer->nFilledLen = 0; + OMX_EmptyThisBuffer(hComponent,pBuffer); + DEBUG_PRINT("EBD..Either EOS or Some Error while reading file\n"); + } + return OMX_ErrorNone; +} + +void signal_handler(int sig_id) { + + /* Flush */ + if (sig_id == SIGUSR1) { + DEBUG_PRINT("%s Initiate flushing\n", __FUNCTION__); + bFlushing = true; + OMX_SendCommand(qcelp13_enc_handle, OMX_CommandFlush, OMX_ALL, NULL); + } else if (sig_id == SIGUSR2) { + if (bPause == true) { + DEBUG_PRINT("%s resume record\n", __FUNCTION__); + bPause = false; + OMX_SendCommand(qcelp13_enc_handle, OMX_CommandStateSet, OMX_StateExecuting, NULL); + } else { + DEBUG_PRINT("%s pause record\n", __FUNCTION__); + bPause = true; + OMX_SendCommand(qcelp13_enc_handle, OMX_CommandStateSet, OMX_StatePause, NULL); + } + } +} + +int main(int argc, char **argv) +{ + unsigned int bufCnt=0; + OMX_ERRORTYPE result; + + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = &signal_handler; + sigaction(SIGABRT, &sa, NULL); + sigaction(SIGUSR1, &sa, NULL); + sigaction(SIGUSR2, &sa, NULL); + + (void) signal(SIGINT, Release_Encoder); + + pthread_cond_init(&cond, 0); + pthread_mutex_init(&lock, 0); + pthread_cond_init(&etb_cond, 0); + pthread_mutex_init(&etb_lock, 0); + pthread_mutex_init(&etb_lock1, 0); + + if (argc >= 9) { + in_filename = argv[1]; + out_filename = argv[2]; + tunnel = (uint32_t)atoi(argv[3]); + min_bitrate = (uint32_t)atoi(argv[4]); + max_bitrate = (uint32_t)atoi(argv[5]); + cdmarate = (uint32_t)atoi(argv[6]); + recpath = (uint32_t)atoi(argv[7]); // No configuration support yet.. + rectime = (uint32_t)atoi(argv[8]); + + } else { + DEBUG_PRINT(" invalid format: \n"); + DEBUG_PRINT("ex: ./mm-aenc-omxqcelp13-test INPUTFILE OUTPUTFILE Tunnel MINRATE MAXRATE CDMARATE RECORDPATH RECORDTIME\n"); + DEBUG_PRINT("MINRATE, MAXRATE and CDMARATE 1 to 4\n"); + DEBUG_PRINT("RECORDPATH 0(TX),1(RX),2(BOTH),3(MIC)\n"); + DEBUG_PRINT("RECORDTIME in seconds for AST Automation\n"); + return 0; + } + if(recpath != 3) { + DEBUG_PRINT("For RECORDPATH Only MIC supported\n"); + return 0; + } + + if(tunnel == 0) + aud_comp = "OMX.qcom.audio.encoder.qcelp13"; + else + aud_comp = "OMX.qcom.audio.encoder.tunneled.qcelp13"; + if(Init_Encoder(aud_comp)!= 0x00) + { + DEBUG_PRINT("Decoder Init failed\n"); + return -1; + } + + fcntl(0, F_SETFL, O_NONBLOCK); + + if(Play_Encoder() != 0x00) + { + DEBUG_PRINT("Play_Decoder failed\n"); + return -1; + } + + // Wait till EOS is reached... + if(rectime && tunnel) + { + sleep(rectime); + rectime = 0; + bInputEosReached_tunnel = 1; + DEBUG_PRINT("\EOS ON INPUT PORT\n"); + } + else + { + wait_for_event(); + } + + if((bInputEosReached_tunnel) || ((bOutputEosReached) && !tunnel)) + { + + DEBUG_PRINT("\nMoving the decoder to idle state \n"); + OMX_SendCommand(qcelp13_enc_handle, OMX_CommandStateSet, OMX_StateIdle,0); + wait_for_event(); + + DEBUG_PRINT("\nMoving the encoder to loaded state \n"); + OMX_SendCommand(qcelp13_enc_handle, OMX_CommandStateSet, OMX_StateLoaded,0); + sleep(1); + if (!tunnel) + { + DEBUG_PRINT("\nFillBufferDone: Deallocating i/p buffers \n"); + for(bufCnt=0; bufCnt < input_buf_cnt; ++bufCnt) { + OMX_FreeBuffer(qcelp13_enc_handle, 0, pInputBufHdrs[bufCnt]); + } + } + + DEBUG_PRINT ("\nFillBufferDone: Deallocating o/p buffers \n"); + for(bufCnt=0; bufCnt < output_buf_cnt; ++bufCnt) { + OMX_FreeBuffer(qcelp13_enc_handle, 1, pOutputBufHdrs[bufCnt]); + } + wait_for_event(); + create_qcp_header(totaldatalen, framecnt); + fseek(outputBufferFile, 0,SEEK_SET); + fwrite(&append_header,1,QCP_HEADER_SIZE,outputBufferFile); + + + result = OMX_FreeHandle(qcelp13_enc_handle); + if (result != OMX_ErrorNone) { + DEBUG_PRINT ("\nOMX_FreeHandle error. Error code: %d\n", result); + } + + /* Deinit OpenMAX */ + if(tunnel) + { + #ifdef AUDIOV2 + if (msm_route_stream(DIR_TX,session_id,device_id, 0)) + { + DEBUG_PRINT("\ncould not set stream routing\n"); + return -1; + } + if (msm_en_device(device_id, 0)) + { + DEBUG_PRINT("\ncould not enable device\n"); + return -1; + } + msm_mixer_close(); + #endif + } + OMX_Deinit(); + ebd_cnt=0; + bOutputEosReached = false; + bInputEosReached_tunnel = false; + bInputEosReached = 0; + qcelp13_enc_handle = NULL; + pthread_cond_destroy(&cond); + pthread_mutex_destroy(&lock); + fclose(outputBufferFile); + DEBUG_PRINT("*****************************************\n"); + DEBUG_PRINT("******...QCELP13 ENC TEST COMPLETED...***************\n"); + DEBUG_PRINT("*****************************************\n"); + } + return 0; +} + +void Release_Encoder() +{ + static int cnt=0; + OMX_ERRORTYPE result; + + DEBUG_PRINT("END OF QCELP13 ENCODING: EXITING PLEASE WAIT\n"); + bInputEosReached_tunnel = 1; + event_complete(); + cnt++; + if(cnt > 1) + { + /* FORCE RESET */ + qcelp13_enc_handle = NULL; + ebd_cnt=0; + bInputEosReached_tunnel = false; + + result = OMX_FreeHandle(qcelp13_enc_handle); + if (result != OMX_ErrorNone) { + DEBUG_PRINT ("\nOMX_FreeHandle error. Error code: %d\n", result); + } + + /* Deinit OpenMAX */ + + OMX_Deinit(); + + pthread_cond_destroy(&cond); + pthread_mutex_destroy(&lock); + DEBUG_PRINT("*****************************************\n"); + DEBUG_PRINT("******...QCELP13 ENC TEST COMPLETED...***************\n"); + DEBUG_PRINT("*****************************************\n"); + exit(0); + } +} + +int Init_Encoder(OMX_STRING audio_component) +{ + DEBUG_PRINT("Inside %s \n", __FUNCTION__); + OMX_ERRORTYPE omxresult; + OMX_U32 total = 0; + typedef OMX_U8* OMX_U8_PTR; + char *role ="audio_encoder"; + + static OMX_CALLBACKTYPE call_back = { + &EventHandler,&EmptyBufferDone,&FillBufferDone + }; + + /* Init. the OpenMAX Core */ + DEBUG_PRINT("\nInitializing OpenMAX Core....\n"); + omxresult = OMX_Init(); + + if(OMX_ErrorNone != omxresult) { + DEBUG_PRINT("\n Failed to Init OpenMAX core"); + return -1; + } + else { + DEBUG_PRINT("\nOpenMAX Core Init Done\n"); + } + + /* Query for audio decoders*/ + DEBUG_PRINT("Qcelp13_test: Before entering OMX_GetComponentOfRole"); + OMX_GetComponentsOfRole(role, &total, 0); + DEBUG_PRINT ("\nTotal components of role=%s :%u", role, total); + + + omxresult = OMX_GetHandle((OMX_HANDLETYPE*)(&qcelp13_enc_handle), + (OMX_STRING)audio_component, NULL, &call_back); + if (FAILED(omxresult)) { + DEBUG_PRINT("\nFailed to Load the component:%s\n", audio_component); + return -1; + } + else + { + DEBUG_PRINT("\nComponent %s is in LOADED state\n", audio_component); + } + + /* Get the port information */ + CONFIG_VERSION_SIZE(portParam); + omxresult = OMX_GetParameter(qcelp13_enc_handle, OMX_IndexParamAudioInit, + (OMX_PTR)&portParam); + + if(FAILED(omxresult)) { + DEBUG_PRINT("\nFailed to get Port Param\n"); + return -1; + } + else + { + DEBUG_PRINT("\nportParam.nPorts:%u\n", portParam.nPorts); + DEBUG_PRINT("\nportParam.nStartPortNumber:%u\n", + portParam.nStartPortNumber); + } + + if(OMX_ErrorNone != omxresult) + { + DEBUG_PRINT("Set parameter failed"); + } + + return 0; +} + +int Play_Encoder() +{ + unsigned int i; + int Size=0; + DEBUG_PRINT("Inside %s \n", __FUNCTION__); + OMX_ERRORTYPE ret; + OMX_INDEXTYPE index; +#ifdef __LP64__ + DEBUG_PRINT("sizeof[%ld]\n", sizeof(OMX_BUFFERHEADERTYPE)); +#else + DEBUG_PRINT("sizeof[%d]\n", sizeof(OMX_BUFFERHEADERTYPE)); +#endif + + /* open the i/p and o/p files based on the video file format passed */ + if(open_audio_file()) { + DEBUG_PRINT("\n Returning -1"); + return -1; + } + + /* Query the encoder input min buf requirements */ + CONFIG_VERSION_SIZE(inputportFmt); + + /* Port for which the Client needs to obtain info */ + inputportFmt.nPortIndex = portParam.nStartPortNumber; + + OMX_GetParameter(qcelp13_enc_handle,OMX_IndexParamPortDefinition,&inputportFmt); + DEBUG_PRINT ("\nEnc Input Buffer Count %u\n", inputportFmt.nBufferCountMin); + DEBUG_PRINT ("\nEnc: Input Buffer Size %u\n", inputportFmt.nBufferSize); + + if(OMX_DirInput != inputportFmt.eDir) { + DEBUG_PRINT ("\nEnc: Expect Input Port\n"); + return -1; + } + + pcmparam.nPortIndex = 0; + pcmparam.nChannels = channels; + pcmparam.nSamplingRate = samplerate; + OMX_SetParameter(qcelp13_enc_handle,OMX_IndexParamAudioPcm,&pcmparam); + + + /* Query the encoder outport's min buf requirements */ + CONFIG_VERSION_SIZE(outputportFmt); + /* Port for which the Client needs to obtain info */ + outputportFmt.nPortIndex = portParam.nStartPortNumber + 1; + + OMX_GetParameter(qcelp13_enc_handle,OMX_IndexParamPortDefinition,&outputportFmt); + DEBUG_PRINT ("\nEnc: Output Buffer Count %u\n", outputportFmt.nBufferCountMin); + DEBUG_PRINT ("\nEnc: Output Buffer Size %u\n", outputportFmt.nBufferSize); + + if(OMX_DirOutput != outputportFmt.eDir) { + DEBUG_PRINT ("\nEnc: Expect Output Port\n"); + return -1; + } + + + CONFIG_VERSION_SIZE(qcelp13param); + + qcelp13param.nPortIndex = 1; + qcelp13param.nChannels = channels; //2 ; /* 1-> mono 2-> stereo*/ + qcelp13param.nMinBitRate = min_bitrate; + qcelp13param.nMaxBitRate = max_bitrate; + OMX_SetParameter(qcelp13_enc_handle,OMX_IndexParamAudioQcelp13,&qcelp13param); + OMX_GetExtensionIndex(qcelp13_enc_handle,"OMX.Qualcomm.index.audio.sessionId",&index); + OMX_GetParameter(qcelp13_enc_handle,index,&streaminfoparam); + if(tunnel) { + #ifdef AUDIOV2 + session_id = streaminfoparam.sessionId; + control = msm_mixer_open("/dev/snd/controlC0", 0); + if(control < 0) + printf("ERROR opening the device\n"); + device_id = msm_get_device(device); + DEBUG_PRINT ("\ndevice_id = %d\n",device_id); + DEBUG_PRINT("\nsession_id = %d\n",session_id); + if (msm_en_device(device_id, 1)) + { + perror("could not enable device\n"); + return -1; + } + if (msm_route_stream(DIR_TX,session_id,device_id, 1)) + { + perror("could not set stream routing\n"); + return -1; + } + #endif + } + + DEBUG_PRINT ("\nOMX_SendCommand Encoder -> IDLE\n"); + OMX_SendCommand(qcelp13_enc_handle, OMX_CommandStateSet, OMX_StateIdle,0); + /* wait_for_event(); should not wait here event complete status will + not come until enough buffer are allocated */ + if (tunnel == 0) + { + input_buf_cnt = inputportFmt.nBufferCountActual; // inputportFmt.nBufferCountMin + 5; + DEBUG_PRINT("Transition to Idle State succesful...\n"); + /* Allocate buffer on decoder's i/p port */ + error = Allocate_Buffer(qcelp13_enc_handle, &pInputBufHdrs, inputportFmt.nPortIndex, + input_buf_cnt, inputportFmt.nBufferSize); + if (error != OMX_ErrorNone || pInputBufHdrs == NULL) { + DEBUG_PRINT ("\nOMX_AllocateBuffer Input buffer error\n"); + return -1; + } + else { + DEBUG_PRINT ("\nOMX_AllocateBuffer Input buffer success\n"); + } + } + output_buf_cnt = outputportFmt.nBufferCountMin ; + + /* Allocate buffer on encoder's O/Pp port */ + error = Allocate_Buffer(qcelp13_enc_handle, &pOutputBufHdrs, outputportFmt.nPortIndex, + output_buf_cnt, outputportFmt.nBufferSize); + if (error != OMX_ErrorNone || pOutputBufHdrs == NULL ) { + DEBUG_PRINT ("\nOMX_AllocateBuffer Output buffer error\n"); + return -1; + } + else { + DEBUG_PRINT ("\nOMX_AllocateBuffer Output buffer success\n"); + } + + wait_for_event(); + + + if (tunnel == 1) + { + DEBUG_PRINT ("\nOMX_SendCommand to enable TUNNEL MODE during IDLE\n"); + OMX_SendCommand(qcelp13_enc_handle, OMX_CommandPortDisable,0,0); // disable input port + wait_for_event(); + } + + DEBUG_PRINT ("\nOMX_SendCommand encoder -> Executing\n"); + OMX_SendCommand(qcelp13_enc_handle, OMX_CommandStateSet, OMX_StateExecuting,0); + wait_for_event(); + + DEBUG_PRINT(" Start sending OMX_FILLthisbuffer\n"); + + for(i=0; i < output_buf_cnt; i++) { + DEBUG_PRINT ("\nOMX_FillThisBuffer on output buf no.%d\n",i); + pOutputBufHdrs[i]->nOutputPortIndex = 1; + pOutputBufHdrs[i]->nFlags = pOutputBufHdrs[i]->nFlags & (unsigned)~OMX_BUFFERFLAG_EOS; + ret = OMX_FillThisBuffer(qcelp13_enc_handle, pOutputBufHdrs[i]); + if (OMX_ErrorNone != ret) { + DEBUG_PRINT("OMX_FillThisBuffer failed with result %d\n", ret); + } + else { + DEBUG_PRINT("OMX_FillThisBuffer success!\n"); + } + } + +if(tunnel == 0) +{ + DEBUG_PRINT(" Start sending OMX_emptythisbuffer\n"); + for (i = 0;i < input_buf_cnt;i++) { + DEBUG_PRINT ("\nOMX_EmptyThisBuffer on Input buf no.%d\n",i); + pInputBufHdrs[i]->nInputPortIndex = 0; + Size = Read_Buffer(pInputBufHdrs[i]); + if(Size <=0 ){ + DEBUG_PRINT("NO DATA READ\n"); + bInputEosReached = true; + pInputBufHdrs[i]->nFlags= OMX_BUFFERFLAG_EOS; + } + pInputBufHdrs[i]->nFilledLen = (OMX_U32)Size; + pInputBufHdrs[i]->nInputPortIndex = 0; + used_ip_buf_cnt++; + ret = OMX_EmptyThisBuffer(qcelp13_enc_handle, pInputBufHdrs[i]); + if (OMX_ErrorNone != ret) { + DEBUG_PRINT("OMX_EmptyThisBuffer failed with result %d\n", ret); + } + else { + DEBUG_PRINT("OMX_EmptyThisBuffer success!\n"); + } + if(Size <=0 ){ + break;//eos reached + } + } + pthread_mutex_lock(&etb_lock); + if(etb_done) +{ + DEBUG_PRINT("Component is waiting for EBD to be released.\n"); + etb_event_complete(); + } + else + { + DEBUG_PRINT("\n****************************\n"); + DEBUG_PRINT("EBD not yet happened ...\n"); + DEBUG_PRINT("\n****************************\n"); + etb_done++; + } + pthread_mutex_unlock(&etb_lock); +} + + return 0; +} + + + +static OMX_ERRORTYPE Allocate_Buffer ( OMX_COMPONENTTYPE *avc_enc_handle, + OMX_BUFFERHEADERTYPE ***pBufHdrs, + OMX_U32 nPortIndex, + unsigned int bufCntMin, unsigned int bufSize) +{ + DEBUG_PRINT("Inside %s \n", __FUNCTION__); + OMX_ERRORTYPE error=OMX_ErrorNone; + unsigned int bufCnt=0; + + /* To remove warning for unused variable to keep prototype same */ + (void)avc_enc_handle; + + *pBufHdrs= (OMX_BUFFERHEADERTYPE **) + malloc(sizeof(OMX_BUFFERHEADERTYPE*)*bufCntMin); + + for(bufCnt=0; bufCnt < bufCntMin; ++bufCnt) { + DEBUG_PRINT("\n OMX_AllocateBuffer No %d \n", bufCnt); + error = OMX_AllocateBuffer(qcelp13_enc_handle, &((*pBufHdrs)[bufCnt]), + nPortIndex, NULL, bufSize); + } + + return error; +} + + + + +static int Read_Buffer (OMX_BUFFERHEADERTYPE *pBufHdr ) +{ + + size_t bytes_read=0; + + + pBufHdr->nFilledLen = 0; + pBufHdr->nFlags |= OMX_BUFFERFLAG_EOS; + + bytes_read = fread(pBufHdr->pBuffer, 1, pBufHdr->nAllocLen , inputBufferFile); + + pBufHdr->nFilledLen = (OMX_U32)bytes_read; + // Time stamp logic + ((OMX_BUFFERHEADERTYPE *)pBufHdr)->nTimeStamp = \ + + (OMX_TICKS) ((total_pcm_bytes * 1000)/(samplerate * channels *2)); + + DEBUG_PRINT ("\n--time stamp -- %ld\n", (unsigned long)((OMX_BUFFERHEADERTYPE *)pBufHdr)->nTimeStamp); + if(bytes_read == 0) + { + pBufHdr->nFlags |= OMX_BUFFERFLAG_EOS; + DEBUG_PRINT ("\nBytes read zero\n"); + } + else + { + pBufHdr->nFlags = pBufHdr->nFlags & (unsigned)~OMX_BUFFERFLAG_EOS; + + total_pcm_bytes = (unsigned)(total_pcm_bytes + bytes_read); + } + + return (int)bytes_read;; +} + + + +//In Encoder this Should Open a PCM or WAV file for input. + +static int open_audio_file () +{ + int error_code = 0; + + if (!tunnel) + { + DEBUG_PRINT("Inside %s filename=%s\n", __FUNCTION__, in_filename); + inputBufferFile = fopen (in_filename, "rb"); + if (inputBufferFile == NULL) { + DEBUG_PRINT("\ni/p file %s could NOT be opened\n", + in_filename); + error_code = -1; + } + if(parse_pcm_header() != 0x00) + { + DEBUG_PRINT("PCM parser failed \n"); + return -1; + } + } + + DEBUG_PRINT("Inside %s filename=%s\n", __FUNCTION__, out_filename); + outputBufferFile = fopen (out_filename, "wb"); + if (outputBufferFile == NULL) { + DEBUG_PRINT("\ni/p file %s could NOT be opened\n", + out_filename); + error_code = -1; + return error_code; + } + fseek(outputBufferFile, QCP_HEADER_SIZE, SEEK_SET); + return error_code; +} + +static OMX_ERRORTYPE parse_pcm_header() +{ + struct wav_header hdr; + + DEBUG_PRINT("\n***************************************************************\n"); + if(fread(&hdr, 1, sizeof(hdr),inputBufferFile)!=sizeof(hdr)) + { + DEBUG_PRINT("Wav file cannot read header\n"); + return -1; + } + + if ((hdr.riff_id != ID_RIFF) || + (hdr.riff_fmt != ID_WAVE)|| + (hdr.fmt_id != ID_FMT)) + { + DEBUG_PRINT("Wav file is not a riff/wave file\n"); + return -1; + } + + if (hdr.audio_format != FORMAT_PCM) + { + DEBUG_PRINT("Wav file is not adpcm format %d and fmt size is %d\n", + hdr.audio_format, hdr.fmt_sz); + return -1; + } + + DEBUG_PRINT("Samplerate is %d\n", hdr.sample_rate); + DEBUG_PRINT("Channel Count is %d\n", hdr.num_channels); + DEBUG_PRINT("\n***************************************************************\n"); + + samplerate = hdr.sample_rate; + channels = hdr.num_channels; + total_pcm_bytes = 0; + + return OMX_ErrorNone; +} diff --git a/audio/mm-audio/autogen.sh b/audio/mm-audio/autogen.sh new file mode 100644 index 0000000..de72aa1 --- /dev/null +++ b/audio/mm-audio/autogen.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +# autogen.sh -- Autotools bootstrapping + +libtoolize --copy --force +aclocal &&\ +autoheader &&\ +autoconf &&\ +automake --add-missing --copy + diff --git a/audio/policy_hal/Android.mk b/audio/policy_hal/Android.mk new file mode 100644 index 0000000..26ee63c --- /dev/null +++ b/audio/policy_hal/Android.mk @@ -0,0 +1,87 @@ +ifneq ($(USE_LEGACY_AUDIO_POLICY), 1) +ifeq ($(USE_CUSTOM_AUDIO_POLICY), 1) +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := AudioPolicyManager.cpp + +LOCAL_C_INCLUDES := $(TOPDIR)frameworks/av/services + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + liblog \ + libsoundtrigger \ + libaudiopolicymanagerdefault + +LOCAL_STATIC_LIBRARIES := \ + libmedia_helper \ + libserviceutility + +LOCAL_MODULE := libaudiopolicymanager + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_COMPRESS_VOIP)),true) +LOCAL_CFLAGS += -DAUDIO_EXTN_COMPRESS_VOIP_ENABLED +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_EXTN_FORMATS)),true) +LOCAL_CFLAGS += -DAUDIO_EXTN_FORMATS_ENABLED +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_FM)),true) +LOCAL_CFLAGS += -DAUDIO_EXTN_FM_ENABLED +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_HDMI_SPK)),true) +LOCAL_CFLAGS += -DAUDIO_EXTN_HDMI_SPK_ENABLED +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_INCALL_MUSIC)),true) +LOCAL_CFLAGS += -DAUDIO_EXTN_INCALL_MUSIC_ENABLED +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_MULTIPLE_TUNNEL)), true) +LOCAL_CFLAGS += -DMULTIPLE_OFFLOAD_ENABLED +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_PCM_OFFLOAD)),true) + LOCAL_CFLAGS += -DPCM_OFFLOAD_ENABLED +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_PROXY_DEVICE)),true) +LOCAL_CFLAGS += -DAUDIO_EXTN_AFE_PROXY_ENABLED +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_SSR)),true) +LOCAL_CFLAGS += -DAUDIO_EXTN_SSR_ENABLED +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_VOICE_CONCURRENCY)),true) +LOCAL_CFLAGS += -DVOICE_CONCURRENCY +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_WFD_CONCURRENCY)),true) +LOCAL_CFLAGS += -DWFD_CONCURRENCY +endif + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_RECORD_PLAY_CONCURRENCY)),true) +LOCAL_CFLAGS += -DRECORD_PLAY_CONCURRENCY +endif + +ifeq ($(strip $(DOLBY_UDC)),true) + LOCAL_CFLAGS += -DDOLBY_UDC +endif #DOLBY_UDC +ifeq ($(strip $(DOLBY_DDP)),true) + LOCAL_CFLAGS += -DDOLBY_DDP +endif #DOLBY_DDP +ifeq ($(strip $(DOLBY_DAP)),true) + ifdef DOLBY_DAP_OPENSLES + LOCAL_CFLAGS += -DDOLBY_DAP_OPENSLES + endif +endif #DOLBY_END + + +include $(BUILD_SHARED_LIBRARY) + +endif +endif diff --git a/audio/policy_hal/AudioPolicyManager.cpp b/audio/policy_hal/AudioPolicyManager.cpp new file mode 100644 index 0000000..ec48a71 --- /dev/null +++ b/audio/policy_hal/AudioPolicyManager.cpp @@ -0,0 +1,2351 @@ +/* + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * Not a contribution. + * + * Copyright (C) 2009 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 "AudioPolicyManager" +//#define LOG_NDEBUG 0 + +//#define VERY_VERBOSE_LOGGING +#ifdef VERY_VERBOSE_LOGGING +#define ALOGVV ALOGV +#else +#define ALOGVV(a...) do { } while(0) +#endif + +// A device mask for all audio input devices that are considered "virtual" when evaluating +// active inputs in getActiveInput() +#ifdef AUDIO_EXTN_FM_ENABLED +#define APM_AUDIO_IN_DEVICE_VIRTUAL_ALL (AUDIO_DEVICE_IN_REMOTE_SUBMIX | AUDIO_DEVICE_IN_FM_RX_A2DP) +#else +#define APM_AUDIO_IN_DEVICE_VIRTUAL_ALL AUDIO_DEVICE_IN_REMOTE_SUBMIX +#endif +// A device mask for all audio output devices that are considered "remote" when evaluating +// active output devices in isStreamActiveRemotely() +#define APM_AUDIO_OUT_DEVICE_REMOTE_ALL AUDIO_DEVICE_OUT_REMOTE_SUBMIX +// A device mask for all audio input and output devices where matching inputs/outputs on device +// type alone is not enough: the address must match too +#define APM_AUDIO_DEVICE_MATCH_ADDRESS_ALL (AUDIO_DEVICE_IN_REMOTE_SUBMIX | \ + AUDIO_DEVICE_OUT_REMOTE_SUBMIX) + +#include +#include + +#include +#include +#include +#include +#include +#include +#include "AudioPolicyManager.h" + +namespace android { + +// ---------------------------------------------------------------------------- +// AudioPolicyInterface implementation +// ---------------------------------------------------------------------------- + +status_t AudioPolicyManagerCustom::setDeviceConnectionState(audio_devices_t device, + audio_policy_dev_state_t state, + const char *device_address) +{ + String8 address = (device_address == NULL) ? String8("") : String8(device_address); + + ALOGV("setDeviceConnectionState() device: %x, state %d, address %s", + device, state, address.string()); + + // connect/disconnect only 1 device at a time + if (!audio_is_output_device(device) && !audio_is_input_device(device)) return BAD_VALUE; + + // handle output devices + if (audio_is_output_device(device)) { + SortedVector outputs; + + sp devDesc = new DeviceDescriptor(String8(""), device); + devDesc->mAddress = address; + ssize_t index = mAvailableOutputDevices.indexOf(devDesc); + + // save a copy of the opened output descriptors before any output is opened or closed + // by checkOutputsForDevice(). This will be needed by checkOutputForAllStrategies() + mPreviousOutputs = mOutputs; + switch (state) + { + // handle output device connection + case AUDIO_POLICY_DEVICE_STATE_AVAILABLE: + if (index >= 0) { +#ifdef AUDIO_EXTN_HDMI_SPK_ENABLED + if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_AUX_DIGITAL)) { + if (!strncmp(device_address, "hdmi_spkr", 9)) { + mHdmiAudioDisabled = false; + } else { + mHdmiAudioEvent = true; + } + } +#endif + ALOGW("setDeviceConnectionState() device already connected: %x", device); + return INVALID_OPERATION; + } + ALOGV("setDeviceConnectionState() connecting device %x", device); + + // register new device as available + index = mAvailableOutputDevices.add(devDesc); + +#ifdef AUDIO_EXTN_HDMI_SPK_ENABLED + if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_AUX_DIGITAL)) { + if (!strncmp(device_address, "hdmi_spkr", 9)) { + mHdmiAudioDisabled = false; + } else { + mHdmiAudioEvent = true; + } + if (mHdmiAudioDisabled || !mHdmiAudioEvent) { + mAvailableOutputDevices.remove(devDesc); + } + } +#endif + if (index >= 0) { + sp module = getModuleForDevice(device); + if (module == 0) { + ALOGD("setDeviceConnectionState() could not find HW module for device %08x", + device); + mAvailableOutputDevices.remove(devDesc); + return INVALID_OPERATION; + } + mAvailableOutputDevices[index]->mId = nextUniqueId(); + mAvailableOutputDevices[index]->mModule = module; + } else { + return NO_MEMORY; + } + + if (checkOutputsForDevice(devDesc, state, outputs, address) != NO_ERROR) { + mAvailableOutputDevices.remove(devDesc); + return INVALID_OPERATION; + } + // outputs should never be empty here + ALOG_ASSERT(outputs.size() != 0, "setDeviceConnectionState():" + "checkOutputsForDevice() returned no outputs but status OK"); + ALOGV("setDeviceConnectionState() checkOutputsForDevice() returned %zu outputs", + outputs.size()); + break; + // handle output device disconnection + case AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE: { + if (index < 0) { +#ifdef AUDIO_EXTN_HDMI_SPK_ENABLED + if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_AUX_DIGITAL)) { + if (!strncmp(device_address, "hdmi_spkr", 9)) { + mHdmiAudioDisabled = true; + } else { + mHdmiAudioEvent = false; + } + } +#endif + ALOGW("setDeviceConnectionState() device not connected: %x", device); + return INVALID_OPERATION; + } + + ALOGV("setDeviceConnectionState() disconnecting output device %x", device); + + // Set Disconnect to HALs + AudioParameter param = AudioParameter(address); + param.addInt(String8(AUDIO_PARAMETER_DEVICE_DISCONNECT), device); + mpClientInterface->setParameters(AUDIO_IO_HANDLE_NONE, param.toString()); + + // remove device from available output devices + mAvailableOutputDevices.remove(devDesc); + +#ifdef AUDIO_EXTN_HDMI_SPK_ENABLED + if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_AUX_DIGITAL)) { + if (!strncmp(device_address, "hdmi_spkr", 9)) { + mHdmiAudioDisabled = true; + } else { + mHdmiAudioEvent = false; + } + } +#endif + checkOutputsForDevice(devDesc, state, outputs, address); + } break; + + default: + ALOGE("setDeviceConnectionState() invalid state: %x", state); + return BAD_VALUE; + } + + // checkA2dpSuspend must run before checkOutputForAllStrategies so that A2DP + // output is suspended before any tracks are moved to it + checkA2dpSuspend(); + checkOutputForAllStrategies(); + // outputs must be closed after checkOutputForAllStrategies() is executed + if (!outputs.isEmpty()) { + for (size_t i = 0; i < outputs.size(); i++) { + sp desc = mOutputs.valueFor(outputs[i]); + // close unused outputs after device disconnection or direct outputs that have been + // opened by checkOutputsForDevice() to query dynamic parameters + if ((state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) || + (((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) != 0) && + (desc->mDirectOpenCount == 0))) { + closeOutput(outputs[i]); + } + } + // check again after closing A2DP output to reset mA2dpSuspended if needed + checkA2dpSuspend(); + } + + updateDevicesAndOutputs(); + audio_devices_t newDevice = getNewOutputDevice(mPrimaryOutput, false /*fromCache*/); + if (mPhoneState == AUDIO_MODE_IN_CALL) { + updateCallRouting(newDevice); + } + +#ifdef AUDIO_EXTN_FM_ENABLED + if(device == AUDIO_DEVICE_OUT_FM) { + if (state == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) { + mOutputs.valueFor(mPrimaryOutput)->changeRefCount(AUDIO_STREAM_MUSIC, 1); + newDevice = (audio_devices_t)(getNewOutputDevice(mPrimaryOutput, false) | AUDIO_DEVICE_OUT_FM); + } else { + mOutputs.valueFor(mPrimaryOutput)->changeRefCount(AUDIO_STREAM_MUSIC, -1); + } + + AudioParameter param = AudioParameter(); + param.addInt(String8("handle_fm"), (int)newDevice); + ALOGV("setDeviceConnectionState() setParameters handle_fm"); + mpClientInterface->setParameters(mPrimaryOutput, param.toString()); + } +#endif + for (size_t i = 0; i < mOutputs.size(); i++) { + audio_io_handle_t output = mOutputs.keyAt(i); + if ((mPhoneState != AUDIO_MODE_IN_CALL) || (output != mPrimaryOutput)) { + audio_devices_t newDevice = getNewOutputDevice(mOutputs.keyAt(i), + true /*fromCache*/); + // do not force device change on duplicated output because if device is 0, it will + // also force a device 0 for the two outputs it is duplicated to which may override + // a valid device selection on those outputs. + bool force = !mOutputs.valueAt(i)->isDuplicated() + && (!deviceDistinguishesOnAddress(device) + // always force when disconnecting (a non-duplicated device) + || (state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE)); + setOutputDevice(output, newDevice, force, 0); + } + } + + mpClientInterface->onAudioPortListUpdate(); + return NO_ERROR; + } // end if is output device + + // handle input devices + if (audio_is_input_device(device)) { + SortedVector inputs; + + sp devDesc = new DeviceDescriptor(String8(""), device); + devDesc->mAddress = address; + ssize_t index = mAvailableInputDevices.indexOf(devDesc); + switch (state) + { + // handle input device connection + case AUDIO_POLICY_DEVICE_STATE_AVAILABLE: { + if (index >= 0) { + ALOGW("setDeviceConnectionState() device already connected: %d", device); + return INVALID_OPERATION; + } + sp module = getModuleForDevice(device); + if (module == NULL) { + ALOGW("setDeviceConnectionState(): could not find HW module for device %08x", + device); + return INVALID_OPERATION; + } + if (checkInputsForDevice(device, state, inputs, address) != NO_ERROR) { + return INVALID_OPERATION; + } + + index = mAvailableInputDevices.add(devDesc); + if (index >= 0) { + mAvailableInputDevices[index]->mId = nextUniqueId(); + mAvailableInputDevices[index]->mModule = module; + } else { + return NO_MEMORY; + } + } break; + + // handle input device disconnection + case AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE: { + if (index < 0) { + ALOGW("setDeviceConnectionState() device not connected: %d", device); + return INVALID_OPERATION; + } + + ALOGV("setDeviceConnectionState() disconnecting input device %x", device); + + // Set Disconnect to HALs + AudioParameter param = AudioParameter(address); + param.addInt(String8(AUDIO_PARAMETER_DEVICE_DISCONNECT), device); + mpClientInterface->setParameters(AUDIO_IO_HANDLE_NONE, param.toString()); + + checkInputsForDevice(device, state, inputs, address); + mAvailableInputDevices.remove(devDesc); + + } break; + + default: + ALOGE("setDeviceConnectionState() invalid state: %x", state); + return BAD_VALUE; + } + + closeAllInputs(); + + if (mPhoneState == AUDIO_MODE_IN_CALL) { + audio_devices_t newDevice = getNewOutputDevice(mPrimaryOutput, false /*fromCache*/); + updateCallRouting(newDevice); + } + + mpClientInterface->onAudioPortListUpdate(); + return NO_ERROR; + } // end if is input device + + ALOGW("setDeviceConnectionState() invalid device: %x", device); + return BAD_VALUE; +} + +audio_policy_dev_state_t AudioPolicyManagerCustom::getDeviceConnectionState(audio_devices_t device, + const char *device_address) +{ + audio_policy_dev_state_t state = AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE; + sp devDesc = new DeviceDescriptor(String8(""), device); + devDesc->mAddress = (device_address == NULL) ? String8("") : String8(device_address); + ssize_t index; + DeviceVector *deviceVector; + + if (audio_is_output_device(device)) { + deviceVector = &mAvailableOutputDevices; + } else if (audio_is_input_device(device)) { + deviceVector = &mAvailableInputDevices; + } else { + ALOGW("getDeviceConnectionState() invalid device type %08x", device); + return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE; + } + + index = deviceVector->indexOf(devDesc); + if (index >= 0) { + return AUDIO_POLICY_DEVICE_STATE_AVAILABLE; + } else { + return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE; + } +} + +void AudioPolicyManagerCustom::setPhoneState(audio_mode_t state) +{ + ALOGD("setPhoneState() state %d", state); + audio_devices_t newDevice = AUDIO_DEVICE_NONE; + + if (state < 0 || state >= AUDIO_MODE_CNT) { + ALOGW("setPhoneState() invalid state %d", state); + return; + } + + if (state == mPhoneState ) { + ALOGW("setPhoneState() setting same state %d", state); + return; + } + + // if leaving call state, handle special case of active streams + // pertaining to sonification strategy see handleIncallSonification() + if (isInCall()) { + ALOGV("setPhoneState() in call state management: new state is %d", state); + for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) { + handleIncallSonification((audio_stream_type_t)stream, false, true); + } + } + + // store previous phone state for management of sonification strategy below + int oldState = mPhoneState; + mPhoneState = state; + bool force = false; + + // are we entering or starting a call + if (!isStateInCall(oldState) && isStateInCall(state)) { + ALOGV(" Entering call in setPhoneState()"); + // force routing command to audio hardware when starting a call + // even if no device change is needed + force = true; + for (int j = 0; j < DEVICE_CATEGORY_CNT; j++) { + mStreams[AUDIO_STREAM_DTMF].mVolumeCurve[j] = + sVolumeProfiles[AUDIO_STREAM_VOICE_CALL][j]; + } + } else if (isStateInCall(oldState) && !isStateInCall(state)) { + ALOGV(" Exiting call in setPhoneState()"); + // force routing command to audio hardware when exiting a call + // even if no device change is needed + force = true; + for (int j = 0; j < DEVICE_CATEGORY_CNT; j++) { + mStreams[AUDIO_STREAM_DTMF].mVolumeCurve[j] = + sVolumeProfiles[AUDIO_STREAM_DTMF][j]; + } + } else if (isStateInCall(state) && (state != oldState)) { + ALOGV(" Switching between telephony and VoIP in setPhoneState()"); + // force routing command to audio hardware when switching between telephony and VoIP + // even if no device change is needed + force = true; + } + + // check for device and output changes triggered by new phone state + checkA2dpSuspend(); + checkOutputForAllStrategies(); + updateDevicesAndOutputs(); + + sp hwOutputDesc = mOutputs.valueFor(mPrimaryOutput); + +#ifdef VOICE_CONCURRENCY + int voice_call_state = 0; + char propValue[PROPERTY_VALUE_MAX]; + bool prop_playback_enabled = false, prop_rec_enabled=false, prop_voip_enabled = false; + + if(property_get("voice.playback.conc.disabled", propValue, NULL)) { + prop_playback_enabled = atoi(propValue) || !strncmp("true", propValue, 4); + } + + if(property_get("voice.record.conc.disabled", propValue, NULL)) { + prop_rec_enabled = atoi(propValue) || !strncmp("true", propValue, 4); + } + + if(property_get("voice.voip.conc.disabled", propValue, NULL)) { + prop_voip_enabled = atoi(propValue) || !strncmp("true", propValue, 4); + } + + bool mode_in_call = (AUDIO_MODE_IN_CALL != oldState) && (AUDIO_MODE_IN_CALL == state); + //query if it is a actual voice call initiated by telephony + if (mode_in_call) { + String8 valueStr = mpClientInterface->getParameters((audio_io_handle_t)0, String8("in_call")); + AudioParameter result = AudioParameter(valueStr); + if (result.getInt(String8("in_call"), voice_call_state) == NO_ERROR) + ALOGD("SetPhoneState: Voice call state = %d", voice_call_state); + } + + if (mode_in_call && voice_call_state) { + ALOGD("Entering to call mode oldState :: %d state::%d ",oldState, state); + mvoice_call_state = voice_call_state; + if (prop_playback_enabled) { + //Call invalidate to reset all opened non ULL audio tracks + // Move tracks associated to this strategy from previous output to new output + for (int i = AUDIO_STREAM_SYSTEM; i < (int)AUDIO_STREAM_CNT; i++) { + ALOGV(" Invalidate on call mode for stream :: %d ", i); + //FIXME see fixme on name change + mpClientInterface->invalidateStream((audio_stream_type_t)i); + } + } + + if (prop_rec_enabled) { + //Close all active inputs + audio_io_handle_t activeInput = getActiveInput(); + if (activeInput != 0) { + sp activeDesc = mInputs.valueFor(activeInput); + switch(activeDesc->mInputSource) { + case AUDIO_SOURCE_VOICE_UPLINK: + case AUDIO_SOURCE_VOICE_DOWNLINK: + case AUDIO_SOURCE_VOICE_CALL: + ALOGD("FOUND active input during call active: %d",activeDesc->mInputSource); + break; + + case AUDIO_SOURCE_VOICE_COMMUNICATION: + if(prop_voip_enabled) { + ALOGD("CLOSING VoIP input source on call setup :%d ",activeDesc->mInputSource); + stopInput(activeInput, activeDesc->mSessions.itemAt(0)); + releaseInput(activeInput, activeDesc->mSessions.itemAt(0)); + } + break; + + default: + ALOGD("CLOSING input on call setup for inputSource: %d",activeDesc->mInputSource); + stopInput(activeInput, activeDesc->mSessions.itemAt(0)); + releaseInput(activeInput, activeDesc->mSessions.itemAt(0)); + break; + } + } + } else if (prop_voip_enabled) { + audio_io_handle_t activeInput = getActiveInput(); + if (activeInput != 0) { + sp activeDesc = mInputs.valueFor(activeInput); + if (AUDIO_SOURCE_VOICE_COMMUNICATION == activeDesc->mInputSource) { + ALOGD("CLOSING VoIP on call setup : %d",activeDesc->mInputSource); + stopInput(activeInput, activeDesc->mSessions.itemAt(0)); + releaseInput(activeInput, activeDesc->mSessions.itemAt(0)); + } + } + } + + //suspend PCM (deep-buffer) output & close compress & direct tracks + for (size_t i = 0; i < mOutputs.size(); i++) { + sp outputDesc = mOutputs.valueAt(i); + if ( (outputDesc == NULL) || (outputDesc->mProfile == NULL)) { + ALOGD("ouput desc / profile is NULL"); + continue; + } + if (((!outputDesc->isDuplicated() &&outputDesc->mProfile->mFlags & AUDIO_OUTPUT_FLAG_PRIMARY)) + && prop_playback_enabled) { + ALOGD(" calling suspendOutput on call mode for primary output"); + mpClientInterface->suspendOutput(mOutputs.keyAt(i)); + } //Close compress all sessions + else if ((outputDesc->mProfile->mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) + && prop_playback_enabled) { + ALOGD(" calling closeOutput on call mode for COMPRESS output"); + closeOutput(mOutputs.keyAt(i)); + } + else if ((outputDesc->mProfile->mFlags & AUDIO_OUTPUT_FLAG_VOIP_RX) + && prop_voip_enabled) { + ALOGD(" calling closeOutput on call mode for DIRECT output"); + closeOutput(mOutputs.keyAt(i)); + } + } + } + + if ((AUDIO_MODE_IN_CALL == oldState || AUDIO_MODE_IN_COMMUNICATION == oldState) && + (AUDIO_MODE_NORMAL == state) && prop_playback_enabled && mvoice_call_state) { + ALOGD("EXITING from call mode oldState :: %d state::%d \n",oldState, state); + mvoice_call_state = 0; + //restore PCM (deep-buffer) output after call termination + for (size_t i = 0; i < mOutputs.size(); i++) { + sp outputDesc = mOutputs.valueAt(i); + if ( (outputDesc == NULL) || (outputDesc->mProfile == NULL)) { + ALOGD("ouput desc / profile is NULL"); + continue; + } + if (!outputDesc->isDuplicated() && outputDesc->mProfile->mFlags & AUDIO_OUTPUT_FLAG_PRIMARY) { + ALOGD("calling restoreOutput after call mode for primary output"); + mpClientInterface->restoreOutput(mOutputs.keyAt(i)); + } + } + //call invalidate tracks so that any open streams can fall back to deep buffer/compress path from ULL + for (int i = AUDIO_STREAM_SYSTEM; i < (int)AUDIO_STREAM_CNT; i++) { + ALOGD("Invalidate after call ends for stream :: %d ", i); + //FIXME see fixme on name change + mpClientInterface->invalidateStream((audio_stream_type_t)i); + } + } +#endif +#ifdef RECORD_PLAY_CONCURRENCY + char recConcPropValue[PROPERTY_VALUE_MAX]; + bool prop_rec_play_enabled = false; + + if (property_get("rec.playback.conc.disabled", recConcPropValue, NULL)) { + prop_rec_play_enabled = atoi(recConcPropValue) || !strncmp("true", recConcPropValue, 4); + } + if (prop_rec_play_enabled) { + if (AUDIO_MODE_IN_COMMUNICATION == mPhoneState) { + ALOGD("phone state changed to MODE_IN_COMM invlaidating music and voice streams"); + // call invalidate for voice streams, so that it can use deepbuffer with VoIP out device from HAL + mpClientInterface->invalidateStream(AUDIO_STREAM_VOICE_CALL); + // call invalidate for music, so that compress will fallback to deep-buffer with VoIP out device + mpClientInterface->invalidateStream(AUDIO_STREAM_MUSIC); + + // close compress output to make sure session will be closed before timeout(60sec) + for (size_t i = 0; i < mOutputs.size(); i++) { + + sp outputDesc = mOutputs.valueAt(i); + if ((outputDesc == NULL) || (outputDesc->mProfile == NULL)) { + ALOGD("ouput desc / profile is NULL"); + continue; + } + + if (outputDesc->mProfile->mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) { + ALOGD("calling closeOutput on call mode for COMPRESS output"); + closeOutput(mOutputs.keyAt(i)); + } + } + } else if ((oldState == AUDIO_MODE_IN_COMMUNICATION) && + (mPhoneState == AUDIO_MODE_NORMAL)) { + // call invalidate for music so that music can fallback to compress + mpClientInterface->invalidateStream(AUDIO_STREAM_MUSIC); + } + } +#endif + + mPrevPhoneState = oldState; + + int delayMs = 0; + if (isStateInCall(state)) { + nsecs_t sysTime = systemTime(); + for (size_t i = 0; i < mOutputs.size(); i++) { + sp desc = mOutputs.valueAt(i); + // mute media and sonification strategies and delay device switch by the largest + // latency of any output where either strategy is active. + // This avoid sending the ring tone or music tail into the earpiece or headset. + if ((desc->isStrategyActive(STRATEGY_MEDIA, + SONIFICATION_HEADSET_MUSIC_DELAY, + sysTime) || + desc->isStrategyActive(STRATEGY_SONIFICATION, + SONIFICATION_HEADSET_MUSIC_DELAY, + sysTime)) && + (delayMs < (int)desc->mLatency*2)) { + delayMs = desc->mLatency*2; + } + setStrategyMute(STRATEGY_MEDIA, true, mOutputs.keyAt(i)); + setStrategyMute(STRATEGY_MEDIA, false, mOutputs.keyAt(i), MUTE_TIME_MS, + getDeviceForStrategy(STRATEGY_MEDIA, true /*fromCache*/)); + setStrategyMute(STRATEGY_SONIFICATION, true, mOutputs.keyAt(i)); + setStrategyMute(STRATEGY_SONIFICATION, false, mOutputs.keyAt(i), MUTE_TIME_MS, + getDeviceForStrategy(STRATEGY_SONIFICATION, true /*fromCache*/)); + } + } + + // Note that despite the fact that getNewOutputDevice() is called on the primary output, + // the device returned is not necessarily reachable via this output + audio_devices_t rxDevice = getNewOutputDevice(mPrimaryOutput, false /*fromCache*/); + // force routing command to audio hardware when ending call + // even if no device change is needed + if (isStateInCall(oldState) && rxDevice == AUDIO_DEVICE_NONE) { + rxDevice = hwOutputDesc->device(); + } + + if (state == AUDIO_MODE_IN_CALL) { + updateCallRouting(rxDevice, delayMs); + } else if (oldState == AUDIO_MODE_IN_CALL) { + if (mCallRxPatch != 0) { + mpClientInterface->releaseAudioPatch(mCallRxPatch->mAfPatchHandle, 0); + mCallRxPatch.clear(); + } + if (mCallTxPatch != 0) { + mpClientInterface->releaseAudioPatch(mCallTxPatch->mAfPatchHandle, 0); + mCallTxPatch.clear(); + } + setOutputDevice(mPrimaryOutput, rxDevice, force, 0); + } else { + setOutputDevice(mPrimaryOutput, rxDevice, force, 0); + } + + //update device for all non-primary outputs + for (size_t i = 0; i < mOutputs.size(); i++) { + audio_io_handle_t output = mOutputs.keyAt(i); + if (output != mPrimaryOutput) { + newDevice = getNewOutputDevice(output, false /*fromCache*/); + setOutputDevice(output, newDevice, (newDevice != AUDIO_DEVICE_NONE)); + } + } + + // if entering in call state, handle special case of active streams + // pertaining to sonification strategy see handleIncallSonification() + if (isStateInCall(state)) { + ALOGV("setPhoneState() in call state management: new state is %d", state); + for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) { + handleIncallSonification((audio_stream_type_t)stream, true, true); + } + } + + // Flag that ringtone volume must be limited to music volume until we exit MODE_RINGTONE + if (state == AUDIO_MODE_RINGTONE && + isStreamActive(AUDIO_STREAM_MUSIC, SONIFICATION_HEADSET_MUSIC_DELAY)) { + mLimitRingtoneVolume = true; + } else { + mLimitRingtoneVolume = false; + } +} + +void AudioPolicyManagerCustom::setForceUse(audio_policy_force_use_t usage, + audio_policy_forced_cfg_t config) +{ + ALOGV("setForceUse() usage %d, config %d, mPhoneState %d", usage, config, mPhoneState); + + bool forceVolumeReeval = false; + switch(usage) { + case AUDIO_POLICY_FORCE_FOR_COMMUNICATION: + if (config != AUDIO_POLICY_FORCE_SPEAKER && config != AUDIO_POLICY_FORCE_BT_SCO && + config != AUDIO_POLICY_FORCE_NONE) { + ALOGW("setForceUse() invalid config %d for FOR_COMMUNICATION", config); + return; + } + forceVolumeReeval = true; + mForceUse[usage] = config; + break; + case AUDIO_POLICY_FORCE_FOR_MEDIA: + if (config != AUDIO_POLICY_FORCE_HEADPHONES && config != AUDIO_POLICY_FORCE_BT_A2DP && +#ifdef AUDIO_EXTN_FM_ENABLED + config != AUDIO_POLICY_FORCE_SPEAKER && +#endif + config != AUDIO_POLICY_FORCE_WIRED_ACCESSORY && + config != AUDIO_POLICY_FORCE_ANALOG_DOCK && + config != AUDIO_POLICY_FORCE_DIGITAL_DOCK && config != AUDIO_POLICY_FORCE_NONE && + config != AUDIO_POLICY_FORCE_NO_BT_A2DP) { + ALOGW("setForceUse() invalid config %d for FOR_MEDIA", config); + return; + } + mForceUse[usage] = config; + break; + case AUDIO_POLICY_FORCE_FOR_RECORD: + if (config != AUDIO_POLICY_FORCE_BT_SCO && config != AUDIO_POLICY_FORCE_WIRED_ACCESSORY && + config != AUDIO_POLICY_FORCE_NONE) { + ALOGW("setForceUse() invalid config %d for FOR_RECORD", config); + return; + } + mForceUse[usage] = config; + break; + case AUDIO_POLICY_FORCE_FOR_DOCK: + if (config != AUDIO_POLICY_FORCE_NONE && config != AUDIO_POLICY_FORCE_BT_CAR_DOCK && + config != AUDIO_POLICY_FORCE_BT_DESK_DOCK && + config != AUDIO_POLICY_FORCE_WIRED_ACCESSORY && + config != AUDIO_POLICY_FORCE_ANALOG_DOCK && + config != AUDIO_POLICY_FORCE_DIGITAL_DOCK) { + ALOGW("setForceUse() invalid config %d for FOR_DOCK", config); + } + forceVolumeReeval = true; + mForceUse[usage] = config; + break; + case AUDIO_POLICY_FORCE_FOR_SYSTEM: + if (config != AUDIO_POLICY_FORCE_NONE && + config != AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) { + ALOGW("setForceUse() invalid config %d for FOR_SYSTEM", config); + } + forceVolumeReeval = true; + mForceUse[usage] = config; + break; + case AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO: + if (config != AUDIO_POLICY_FORCE_NONE && + config != AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED) { + ALOGW("setForceUse() invalid config %d forHDMI_SYSTEM_AUDIO", config); + } + mForceUse[usage] = config; + break; + default: + ALOGW("setForceUse() invalid usage %d", usage); + break; + } + + // check for device and output changes triggered by new force usage + checkA2dpSuspend(); + checkOutputForAllStrategies(); + updateDevicesAndOutputs(); + if (mPhoneState == AUDIO_MODE_IN_CALL) { + audio_devices_t newDevice = getNewOutputDevice(mPrimaryOutput, true /*fromCache*/); + updateCallRouting(newDevice); + } + for (size_t i = 0; i < mOutputs.size(); i++) { + audio_io_handle_t output = mOutputs.keyAt(i); + audio_devices_t newDevice = getNewOutputDevice(output, true /*fromCache*/); + if ((mPhoneState != AUDIO_MODE_IN_CALL) || (output != mPrimaryOutput)) { + setOutputDevice(output, newDevice, (newDevice != AUDIO_DEVICE_NONE)); + } + if (forceVolumeReeval && (newDevice != AUDIO_DEVICE_NONE)) { + applyStreamVolumes(output, newDevice, 0, true); + } + } + + audio_io_handle_t activeInput = getActiveInput(); + if (activeInput != 0) { + setInputDevice(activeInput, getNewInputDevice(activeInput)); + } + +} + +audio_io_handle_t AudioPolicyManagerCustom::getOutputForDevice( + audio_devices_t device, + audio_stream_type_t stream, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) +{ + audio_io_handle_t output = AUDIO_IO_HANDLE_NONE; + uint32_t latency = 0; + status_t status; + +#ifdef AUDIO_POLICY_TEST + if (mCurOutput != 0) { + ALOGV("getOutput() test output mCurOutput %d, samplingRate %d, format %d, channelMask %x, mDirectOutput %d", + mCurOutput, mTestSamplingRate, mTestFormat, mTestChannels, mDirectOutput); + + if (mTestOutputs[mCurOutput] == 0) { + ALOGV("getOutput() opening test output"); + sp outputDesc = new AudioOutputDescriptor(NULL); + outputDesc->mDevice = mTestDevice; + outputDesc->mLatency = mTestLatencyMs; + outputDesc->mFlags = + (audio_output_flags_t)(mDirectOutput ? AUDIO_OUTPUT_FLAG_DIRECT : 0); + outputDesc->mRefCount[stream] = 0; + audio_config_t config = AUDIO_CONFIG_INITIALIZER; + config.sample_rate = mTestSamplingRate; + config.channel_mask = mTestChannels; + config.format = mTestFormat; + if (offloadInfo != NULL) { + config.offload_info = *offloadInfo; + } + status = mpClientInterface->openOutput(0, + &mTestOutputs[mCurOutput], + &config, + &outputDesc->mDevice, + String8(""), + &outputDesc->mLatency, + outputDesc->mFlags); + if (status == NO_ERROR) { + outputDesc->mSamplingRate = config.sample_rate; + outputDesc->mFormat = config.format; + outputDesc->mChannelMask = config.channel_mask; + AudioParameter outputCmd = AudioParameter(); + outputCmd.addInt(String8("set_id"),mCurOutput); + mpClientInterface->setParameters(mTestOutputs[mCurOutput],outputCmd.toString()); + addOutput(mTestOutputs[mCurOutput], outputDesc); + } + } + return mTestOutputs[mCurOutput]; + } +#endif //AUDIO_POLICY_TEST + +#ifdef VOICE_CONCURRENCY + char propValue[PROPERTY_VALUE_MAX]; + bool prop_play_enabled=false, prop_voip_enabled = false; + + if(property_get("voice.playback.conc.disabled", propValue, NULL)) { + prop_play_enabled = atoi(propValue) || !strncmp("true", propValue, 4); + } + + if(property_get("voice.voip.conc.disabled", propValue, NULL)) { + prop_voip_enabled = atoi(propValue) || !strncmp("true", propValue, 4); + } + + if (prop_play_enabled && mvoice_call_state) { + //check if voice call is active / running in background + if((AUDIO_MODE_IN_CALL == mPhoneState) || + ((AUDIO_MODE_IN_CALL == mPrevPhoneState) + && (AUDIO_MODE_IN_COMMUNICATION == mPhoneState))) + { + if(AUDIO_OUTPUT_FLAG_VOIP_RX & flags) { + if(prop_voip_enabled) { + ALOGD(" IN call mode returing no output .. for VoIP usecase flags: %x ", flags ); + // flags = (AudioSystem::output_flags)AUDIO_OUTPUT_FLAG_FAST; + return 0; + } + } + else { + ALOGD(" IN call mode adding ULL flags .. flags: %x ", flags ); + flags = AUDIO_OUTPUT_FLAG_FAST; + } + } + } else if (prop_voip_enabled && mvoice_call_state) { + //check if voice call is active / running in background + //some of VoIP apps(like SIP2SIP call) supports resume of VoIP call when call in progress + //return only ULL ouput + if((AUDIO_MODE_IN_CALL == mPhoneState) || + ((AUDIO_MODE_IN_CALL == mPrevPhoneState) + && (AUDIO_MODE_IN_COMMUNICATION == mPhoneState))) + { + if(AUDIO_OUTPUT_FLAG_VOIP_RX & flags) { + ALOGD(" IN call mode returing no output .. for VoIP usecase flags: %x ", flags ); + // flags = (AudioSystem::output_flags)AUDIO_OUTPUT_FLAG_FAST; + return 0; + } + } + } +#endif + +#ifdef WFD_CONCURRENCY + audio_devices_t availableOutputDeviceTypes = mAvailableOutputDevices.types(); + if ((availableOutputDeviceTypes & AUDIO_DEVICE_OUT_PROXY) + && (stream != AUDIO_STREAM_MUSIC)) { + ALOGD(" WFD mode adding ULL flags for non music stream.. flags: %x ", flags ); + //For voip paths + if(flags & AUDIO_OUTPUT_FLAG_DIRECT) + flags = AUDIO_OUTPUT_FLAG_DIRECT; + else //route every thing else to ULL path + flags = AUDIO_OUTPUT_FLAG_FAST; + } +#endif + +#ifdef RECORD_PLAY_CONCURRENCY + char recConcPropValue[PROPERTY_VALUE_MAX]; + bool prop_rec_play_enabled = false; + + if (property_get("rec.playback.conc.disabled", recConcPropValue, NULL)) { + prop_rec_play_enabled = atoi(recConcPropValue) || !strncmp("true", recConcPropValue, 4); + } + if ((prop_rec_play_enabled) && + ((true == mIsInputRequestOnProgress) || (activeInputsCount() > 0))) { + if (AUDIO_MODE_IN_COMMUNICATION == mPhoneState) { + if (AUDIO_OUTPUT_FLAG_VOIP_RX & flags) { + // allow VoIP using voice path + // Do nothing + } else if((flags & AUDIO_OUTPUT_FLAG_FAST) != 0) { + ALOGD(" MODE_IN_COMM is setforcing deep buffer output for non ULL... flags: %x", flags); + // use deep buffer path for all non ULL outputs + flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER; + } + } else if ((flags & AUDIO_OUTPUT_FLAG_FAST) != 0) { + ALOGD(" Record mode is on forcing deep buffer output for non ULL... flags: %x ", flags); + // use deep buffer path for all non ULL outputs + flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER; + } + } + if (prop_rec_play_enabled && + (stream == AUDIO_STREAM_ENFORCED_AUDIBLE)) { + ALOGD("Record conc is on forcing ULL output for ENFORCED_AUDIBLE"); + flags = AUDIO_OUTPUT_FLAG_FAST; + } +#endif + // open a direct output if required by specified parameters + //force direct flag if offload flag is set: offloading implies a direct output stream + // and all common behaviors are driven by checking only the direct flag + // this should normally be set appropriately in the policy configuration file + if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) { + flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_DIRECT); + } + if ((flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC) != 0) { + flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_DIRECT); + } + + if ((format == AUDIO_FORMAT_PCM_16_BIT) &&(popcount(channelMask) > 2)) { + ALOGV("owerwrite flag(%x) for PCM16 multi-channel(CM:%x) playback", flags ,channelMask); + flags = AUDIO_OUTPUT_FLAG_DIRECT; + } + + sp profile; + + // skip direct output selection if the request can obviously be attached to a mixed output + // and not explicitly requested + if (((flags & AUDIO_OUTPUT_FLAG_DIRECT) == 0) && + audio_is_linear_pcm(format) && samplingRate <= MAX_MIXER_SAMPLING_RATE && + audio_channel_count_from_out_mask(channelMask) <= 2) { + goto non_direct_output; + } + + // Do not allow offloading if one non offloadable effect is enabled. This prevents from + // creating an offloaded track and tearing it down immediately after start when audioflinger + // detects there is an active non offloadable effect. + // FIXME: We should check the audio session here but we do not have it in this context. + // This may prevent offloading in rare situations where effects are left active by apps + // in the background. + + if ((((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0) || + !isNonOffloadableEffectEnabled()) && + flags & AUDIO_OUTPUT_FLAG_DIRECT) { + profile = getProfileForDirectOutput(device, + samplingRate, + format, + channelMask, + (audio_output_flags_t)flags); + } + + if (profile != 0) { + sp outputDesc = NULL; + + for (size_t i = 0; i < mOutputs.size(); i++) { + sp desc = mOutputs.valueAt(i); + if (!desc->isDuplicated() && (profile == desc->mProfile)) { + outputDesc = desc; + // reuse direct output if currently open and configured with same parameters + if ((samplingRate == outputDesc->mSamplingRate) && + (format == outputDesc->mFormat) && + (channelMask == outputDesc->mChannelMask)) { + outputDesc->mDirectOpenCount++; + ALOGV("getOutput() reusing direct output %d", mOutputs.keyAt(i)); + return mOutputs.keyAt(i); + } + } + } + // close direct output if currently open and configured with different parameters + if (outputDesc != NULL) { + closeOutput(outputDesc->mIoHandle); + } + outputDesc = new AudioOutputDescriptor(profile); + outputDesc->mDevice = device; + outputDesc->mLatency = 0; + outputDesc->mFlags =(audio_output_flags_t) (outputDesc->mFlags | flags); + audio_config_t config = AUDIO_CONFIG_INITIALIZER; + config.sample_rate = samplingRate; + config.channel_mask = channelMask; + config.format = format; + if (offloadInfo != NULL) { + config.offload_info = *offloadInfo; + } + status = mpClientInterface->openOutput(profile->mModule->mHandle, + &output, + &config, + &outputDesc->mDevice, + String8(""), + &outputDesc->mLatency, + outputDesc->mFlags); + + // only accept an output with the requested parameters + if (status != NO_ERROR || + (samplingRate != 0 && samplingRate != config.sample_rate) || + (format != AUDIO_FORMAT_DEFAULT && format != config.format) || + (channelMask != 0 && channelMask != config.channel_mask)) { + ALOGV("getOutput() failed opening direct output: output %d samplingRate %d %d," + "format %d %d, channelMask %04x %04x", output, samplingRate, + outputDesc->mSamplingRate, format, outputDesc->mFormat, channelMask, + outputDesc->mChannelMask); + if (output != AUDIO_IO_HANDLE_NONE) { + mpClientInterface->closeOutput(output); + } + return AUDIO_IO_HANDLE_NONE; + } + outputDesc->mSamplingRate = config.sample_rate; + outputDesc->mChannelMask = config.channel_mask; + outputDesc->mFormat = config.format; + outputDesc->mRefCount[stream] = 0; + outputDesc->mStopTime[stream] = 0; + outputDesc->mDirectOpenCount = 1; + + audio_io_handle_t srcOutput = getOutputForEffect(); + addOutput(output, outputDesc); + audio_io_handle_t dstOutput = getOutputForEffect(); + if (dstOutput == output) { + mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, srcOutput, dstOutput); + } + mPreviousOutputs = mOutputs; + ALOGV("getOutput() returns new direct output %d", output); + mpClientInterface->onAudioPortListUpdate(); + return output; + } + +non_direct_output: + + // ignoring channel mask due to downmix capability in mixer + + // open a non direct output + + // for non direct outputs, only PCM is supported + if (audio_is_linear_pcm(format)) { + // get which output is suitable for the specified stream. The actual + // routing change will happen when startOutput() will be called + SortedVector outputs = getOutputsForDevice(device, mOutputs); + + // at this stage we should ignore the DIRECT flag as no direct output could be found earlier + flags = (audio_output_flags_t)(flags & ~AUDIO_OUTPUT_FLAG_DIRECT); + output = selectOutput(outputs, flags, format); + } + ALOGW_IF((output == 0), "getOutput() could not find output for stream %d, samplingRate %d," + "format %d, channels %x, flags %x", stream, samplingRate, format, channelMask, flags); + + ALOGV("getOutput() returns output %d", output); + + return output; +} + + +status_t AudioPolicyManagerCustom::stopOutput(audio_io_handle_t output, + audio_stream_type_t stream, + int session) +{ + ALOGV("stopOutput() output %d, stream %d, session %d", output, stream, session); + ssize_t index = mOutputs.indexOfKey(output); + if (index < 0) { + ALOGW("stopOutput() unknown output %d", output); + return BAD_VALUE; + } + + sp outputDesc = mOutputs.valueAt(index); + + // handle special case for sonification while in call + if ((isInCall()) && (outputDesc->mRefCount[stream] == 1)) { + handleIncallSonification(stream, false, false); + } + + if (outputDesc->mRefCount[stream] > 0) { + // decrement usage count of this stream on the output + outputDesc->changeRefCount(stream, -1); + // store time at which the stream was stopped - see isStreamActive() + if (outputDesc->mRefCount[stream] == 0) { + outputDesc->mStopTime[stream] = systemTime(); + audio_devices_t newDevice = getNewOutputDevice(output, false /*fromCache*/); + // delay the device switch by twice the latency because stopOutput() is executed when + // the track stop() command is received and at that time the audio track buffer can + // still contain data that needs to be drained. The latency only covers the audio HAL + // and kernel buffers. Also the latency does not always include additional delay in the + // audio path (audio DSP, CODEC ...) + setOutputDevice(output, newDevice, false, outputDesc->mLatency*2); + + // force restoring the device selection on other active outputs if it differs from the + // one being selected for this output + for (size_t i = 0; i < mOutputs.size(); i++) { + audio_io_handle_t curOutput = mOutputs.keyAt(i); + sp desc = mOutputs.valueAt(i); + if (curOutput != output && + desc->isActive() && + outputDesc->sharesHwModuleWith(desc) && + (newDevice != desc->device())) { + setOutputDevice(curOutput, + getNewOutputDevice(curOutput, false /*fromCache*/), + true, + outputDesc->mLatency*2); + } + } + // update the outputs if stopping one with a stream that can affect notification routing + handleNotificationRoutingForStream(stream); + } + return NO_ERROR; + } else { + ALOGW("stopOutput() refcount is already 0 for output %d", output); + return INVALID_OPERATION; + } +} + +audio_io_handle_t AudioPolicyManagerCustom::getInput(audio_source_t inputSource, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_session_t session, + audio_input_flags_t flags) +{ + ALOGV("getInput() inputSource %d, samplingRate %d, format %d, channelMask %x, session %d, " + "flags %#x", + inputSource, samplingRate, format, channelMask, session, flags); + + audio_devices_t device = getDeviceForInputSource(inputSource); + + if (device == AUDIO_DEVICE_NONE) { + ALOGW("getInput() could not find device for inputSource %d", inputSource); + return AUDIO_IO_HANDLE_NONE; + } + + /*The below code is intentionally not ported. + It's not needed to update the channel mask based on source because + the source is sent to audio HAL through set_parameters(). + For example, if source = VOICE_CALL, does not mean we need to capture two channels. + If the sound recorder app selects AMR as encoding format but source as RX+TX, + we need both in ONE channel. So we use the channels set by the app and use source + to tell the driver what needs to captured (RX only, TX only, or RX+TX ).*/ + // adapt channel selection to input source + /*switch (inputSource) { + case AUDIO_SOURCE_VOICE_UPLINK: + channelMask = AUDIO_CHANNEL_IN_VOICE_UPLINK; + break; + case AUDIO_SOURCE_VOICE_DOWNLINK: + channelMask = AUDIO_CHANNEL_IN_VOICE_DNLINK; + break; + case AUDIO_SOURCE_VOICE_CALL: + channelMask = AUDIO_CHANNEL_IN_VOICE_UPLINK | AUDIO_CHANNEL_IN_VOICE_DNLINK; + break; + default: + break; + }*/ + +#ifdef VOICE_CONCURRENCY + + char propValue[PROPERTY_VALUE_MAX]; + bool prop_rec_enabled=false, prop_voip_enabled = false; + + if(property_get("voice.record.conc.disabled", propValue, NULL)) { + prop_rec_enabled = atoi(propValue) || !strncmp("true", propValue, 4); + } + + if(property_get("voice.voip.conc.disabled", propValue, NULL)) { + prop_voip_enabled = atoi(propValue) || !strncmp("true", propValue, 4); + } + + if (prop_rec_enabled && mvoice_call_state) { + //check if voice call is active / running in background + //some of VoIP apps(like SIP2SIP call) supports resume of VoIP call when call in progress + //Need to block input request + if((AUDIO_MODE_IN_CALL == mPhoneState) || + ((AUDIO_MODE_IN_CALL == mPrevPhoneState) && + (AUDIO_MODE_IN_COMMUNICATION == mPhoneState))) + { + switch(inputSource) { + case AUDIO_SOURCE_VOICE_UPLINK: + case AUDIO_SOURCE_VOICE_DOWNLINK: + case AUDIO_SOURCE_VOICE_CALL: + ALOGD("Creating input during incall mode for inputSource: %d ",inputSource); + break; + + case AUDIO_SOURCE_VOICE_COMMUNICATION: + if(prop_voip_enabled) { + ALOGD("BLOCKING VoIP request during incall mode for inputSource: %d ",inputSource); + return 0; + } + break; + default: + ALOGD("BLOCKING input during incall mode for inputSource: %d ",inputSource); + return 0; + } + } + }//check for VoIP flag + else if(prop_voip_enabled && mvoice_call_state) { + //check if voice call is active / running in background + //some of VoIP apps(like SIP2SIP call) supports resume of VoIP call when call in progress + //Need to block input request + if((AUDIO_MODE_IN_CALL == mPhoneState) || + ((AUDIO_MODE_IN_CALL == mPrevPhoneState) && + (AUDIO_MODE_IN_COMMUNICATION == mPhoneState))) + { + if(inputSource == AUDIO_SOURCE_VOICE_COMMUNICATION) { + ALOGD("BLOCKING VoIP request during incall mode for inputSource: %d ",inputSource); + return 0; + } + } + } + +#endif + + audio_io_handle_t input = AUDIO_IO_HANDLE_NONE; + bool isSoundTrigger = false; + audio_source_t halInputSource = inputSource; + if (inputSource == AUDIO_SOURCE_HOTWORD) { + ssize_t index = mSoundTriggerSessions.indexOfKey(session); + if (index >= 0) { + input = mSoundTriggerSessions.valueFor(session); + isSoundTrigger = true; + flags = (audio_input_flags_t)(flags | AUDIO_INPUT_FLAG_HW_HOTWORD); + ALOGV("SoundTrigger capture on session %d input %d", session, input); + } else { + halInputSource = AUDIO_SOURCE_VOICE_RECOGNITION; + } + } + + sp profile = getInputProfile(device, + samplingRate, + format, + channelMask, + flags); + if (profile == 0) { + //retry without flags + audio_input_flags_t log_flags = flags; + flags = AUDIO_INPUT_FLAG_NONE; + profile = getInputProfile(device, + samplingRate, + format, + channelMask, + flags); + if (profile == 0) { + ALOGW("getInput() could not find profile for device 0x%X, samplingRate %u, format %#x, " + "channelMask 0x%X, flags %#x", + device, samplingRate, format, channelMask, log_flags); + return AUDIO_IO_HANDLE_NONE; + } + } + + if (profile->mModule->mHandle == 0) { + ALOGE("getInput(): HW module %s not opened", profile->mModule->mName); + return AUDIO_IO_HANDLE_NONE; + } + + audio_config_t config = AUDIO_CONFIG_INITIALIZER; + config.sample_rate = samplingRate; + config.channel_mask = channelMask; + config.format = format; + + status_t status = mpClientInterface->openInput(profile->mModule->mHandle, + &input, + &config, + &device, + String8(""), + halInputSource, + flags); + + // only accept input with the exact requested set of parameters + if (status != NO_ERROR || + (samplingRate != config.sample_rate) || + (format != config.format) || + (channelMask != config.channel_mask)) { + ALOGW("getInput() failed opening input: samplingRate %d, format %d, channelMask %x", + samplingRate, format, channelMask); + if (input != AUDIO_IO_HANDLE_NONE) { + mpClientInterface->closeInput(input); + } + return AUDIO_IO_HANDLE_NONE; + } + + sp inputDesc = new AudioInputDescriptor(profile); + inputDesc->mInputSource = inputSource; + inputDesc->mRefCount = 0; + inputDesc->mOpenRefCount = 1; + inputDesc->mSamplingRate = samplingRate; + inputDesc->mFormat = format; + inputDesc->mChannelMask = channelMask; + inputDesc->mDevice = device; + inputDesc->mSessions.add(session); + inputDesc->mIsSoundTrigger = isSoundTrigger; + + addInput(input, inputDesc); + mpClientInterface->onAudioPortListUpdate(); + return input; +} + +status_t AudioPolicyManagerCustom::startInput(audio_io_handle_t input, + audio_session_t session) +{ + ALOGV("startInput() input %d", input); + ssize_t index = mInputs.indexOfKey(input); + if (index < 0) { + ALOGW("startInput() unknown input %d", input); + return BAD_VALUE; + } + sp inputDesc = mInputs.valueAt(index); + + index = inputDesc->mSessions.indexOf(session); + if (index < 0) { + ALOGW("startInput() unknown session %d on input %d", session, input); + return BAD_VALUE; + } + + // virtual input devices are compatible with other input devices + if (!isVirtualInputDevice(inputDesc->mDevice)) { + + // for a non-virtual input device, check if there is another (non-virtual) active input + audio_io_handle_t activeInput = getActiveInput(); + if (activeInput != 0 && activeInput != input) { + + // If the already active input uses AUDIO_SOURCE_HOTWORD then it is closed, + // otherwise the active input continues and the new input cannot be started. + sp activeDesc = mInputs.valueFor(activeInput); + if (activeDesc->mInputSource == AUDIO_SOURCE_HOTWORD) { + ALOGW("startInput(%d) preempting low-priority input %d", input, activeInput); + stopInput(activeInput, activeDesc->mSessions.itemAt(0)); + releaseInput(activeInput, activeDesc->mSessions.itemAt(0)); + } else { + ALOGE("startInput(%d) failed: other input %d already started", input, activeInput); + return INVALID_OPERATION; + } + } + } + +#ifdef RECORD_PLAY_CONCURRENCY + mIsInputRequestOnProgress = true; + + char getPropValue[PROPERTY_VALUE_MAX]; + bool prop_rec_play_enabled = false; + + if (property_get("rec.playback.conc.disabled", getPropValue, NULL)) { + prop_rec_play_enabled = atoi(getPropValue) || !strncmp("true", getPropValue, 4); + } + + if ((prop_rec_play_enabled) &&(activeInputsCount() == 0)){ + // send update to HAL on record playback concurrency + AudioParameter param = AudioParameter(); + param.add(String8("rec_play_conc_on"), String8("true")); + ALOGD("startInput() setParameters rec_play_conc is setting to ON "); + mpClientInterface->setParameters(0, param.toString()); + + // Call invalidate to reset all opened non ULL audio tracks + // Move tracks associated to this strategy from previous output to new output + for (int i = AUDIO_STREAM_SYSTEM; i < (int)AUDIO_STREAM_CNT; i++) { + // Do not call invalidate for ENFORCED_AUDIBLE (otherwise pops are seen for camcorder) + if (i != AUDIO_STREAM_ENFORCED_AUDIBLE) { + ALOGD("Invalidate on releaseInput for stream :: %d ", i); + //FIXME see fixme on name change + mpClientInterface->invalidateStream((audio_stream_type_t)i); + } + } + // close compress tracks + for (size_t i = 0; i < mOutputs.size(); i++) { + sp outputDesc = mOutputs.valueAt(i); + if ((outputDesc == NULL) || (outputDesc->mProfile == NULL)) { + ALOGD("ouput desc / profile is NULL"); + continue; + } + if (outputDesc->mProfile->mFlags + & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) { + // close compress sessions + ALOGD("calling closeOutput on record conc for COMPRESS output"); + closeOutput(mOutputs.keyAt(i)); + } + } + } +#endif + + if (inputDesc->mRefCount == 0) { + if (activeInputsCount() == 0) { + SoundTrigger::setCaptureState(true); + } + setInputDevice(input, getNewInputDevice(input), true /* force */); + + // Automatically enable the remote submix output when input is started. + // For remote submix (a virtual device), we open only one input per capture request. + if (audio_is_remote_submix_device(inputDesc->mDevice)) { + setDeviceConnectionState(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, + AUDIO_POLICY_DEVICE_STATE_AVAILABLE, AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS); + } + } + + ALOGV("AudioPolicyManagerCustom::startInput() input source = %d", inputDesc->mInputSource); + + inputDesc->mRefCount++; +#ifdef RECORD_PLAY_CONCURRENCY + mIsInputRequestOnProgress = false; +#endif + return NO_ERROR; +} + +status_t AudioPolicyManagerCustom::stopInput(audio_io_handle_t input, + audio_session_t session) +{ + ALOGV("stopInput() input %d", input); + ssize_t index = mInputs.indexOfKey(input); + if (index < 0) { + ALOGW("stopInput() unknown input %d", input); + return BAD_VALUE; + } + sp inputDesc = mInputs.valueAt(index); + + index = inputDesc->mSessions.indexOf(session); + if (index < 0) { + ALOGW("stopInput() unknown session %d on input %d", session, input); + return BAD_VALUE; + } + + if (inputDesc->mRefCount == 0) { + ALOGW("stopInput() input %d already stopped", input); + return INVALID_OPERATION; + } + + inputDesc->mRefCount--; + if (inputDesc->mRefCount == 0) { + + // automatically disable the remote submix output when input is stopped + if (audio_is_remote_submix_device(inputDesc->mDevice)) { + setDeviceConnectionState(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, + AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS); + } + + resetInputDevice(input); + + if (activeInputsCount() == 0) { + SoundTrigger::setCaptureState(false); + } + } + +#ifdef RECORD_PLAY_CONCURRENCY + char propValue[PROPERTY_VALUE_MAX]; + bool prop_rec_play_enabled = false; + + if (property_get("rec.playback.conc.disabled", propValue, NULL)) { + prop_rec_play_enabled = atoi(propValue) || !strncmp("true", propValue, 4); + } + + if ((prop_rec_play_enabled) && (activeInputsCount() == 0)) { + + //send update to HAL on record playback concurrency + AudioParameter param = AudioParameter(); + param.add(String8("rec_play_conc_on"), String8("false")); + ALOGD("stopInput() setParameters rec_play_conc is setting to OFF "); + mpClientInterface->setParameters(0, param.toString()); + + //call invalidate tracks so that any open streams can fall back to deep buffer/compress path from ULL + for (int i = AUDIO_STREAM_SYSTEM; i < (int)AUDIO_STREAM_CNT; i++) { + //Do not call invalidate for ENFORCED_AUDIBLE (otherwise pops are seen for camcorder stop tone) + if (i != AUDIO_STREAM_ENFORCED_AUDIBLE) { + ALOGD(" Invalidate on stopInput for stream :: %d ", i); + //FIXME see fixme on name change + mpClientInterface->invalidateStream((audio_stream_type_t)i); + } + } + } +#endif + return NO_ERROR; +} + +status_t AudioPolicyManagerCustom::setStreamVolumeIndex(audio_stream_type_t stream, + int index, + audio_devices_t device) +{ + + if ((index < mStreams[stream].mIndexMin) || (index > mStreams[stream].mIndexMax)) { + return BAD_VALUE; + } + if (!audio_is_output_device(device)) { + return BAD_VALUE; + } + + // Force max volume if stream cannot be muted + if (!mStreams[stream].mCanBeMuted) index = mStreams[stream].mIndexMax; + + ALOGV("setStreamVolumeIndex() stream %d, device %04x, index %d", + stream, device, index); + + // if device is AUDIO_DEVICE_OUT_DEFAULT set default value and + // clear all device specific values + if (device == AUDIO_DEVICE_OUT_DEFAULT) { + mStreams[stream].mIndexCur.clear(); + } + mStreams[stream].mIndexCur.add(device, index); + + // compute and apply stream volume on all outputs according to connected device + status_t status = NO_ERROR; + for (size_t i = 0; i < mOutputs.size(); i++) { + audio_devices_t curDevice = + getDeviceForVolume(mOutputs.valueAt(i)->device()); +#ifdef AUDIO_EXTN_FM_ENABLED + audio_devices_t availableOutputDeviceTypes = mAvailableOutputDevices.types(); + if (((device == AUDIO_DEVICE_OUT_DEFAULT) && + ((availableOutputDeviceTypes & AUDIO_DEVICE_OUT_FM) != AUDIO_DEVICE_OUT_FM)) || + (device == curDevice)) { +#else + if ((device == AUDIO_DEVICE_OUT_DEFAULT) || (device == curDevice)) { +#endif + status_t volStatus = checkAndSetVolume(stream, index, mOutputs.keyAt(i), curDevice); + if (volStatus != NO_ERROR) { + status = volStatus; + } + } + } + return status; +} + +// This function checks for the parameters which can be offloaded. +// This can be enhanced depending on the capability of the DSP and policy +// of the system. +bool AudioPolicyManagerCustom::isOffloadSupported(const audio_offload_info_t& offloadInfo) +{ + ALOGD("copl: isOffloadSupported: SR=%u, CM=0x%x, Format=0x%x, StreamType=%d," + " BitRate=%u, duration=%lld us, has_video=%d", + offloadInfo.sample_rate, offloadInfo.channel_mask, + offloadInfo.format, + offloadInfo.stream_type, offloadInfo.bit_rate, offloadInfo.duration_us, + offloadInfo.has_video); + +#ifdef VOICE_CONCURRENCY + char concpropValue[PROPERTY_VALUE_MAX]; + if (property_get("voice.playback.conc.disabled", concpropValue, NULL)) { + bool propenabled = atoi(concpropValue) || !strncmp("true", concpropValue, 4); + if (propenabled) { + if (isInCall()) + { + ALOGD("\n copl: blocking compress offload on call mode\n"); + return false; + } + } + } +#endif +#ifdef RECORD_PLAY_CONCURRENCY + char recConcPropValue[PROPERTY_VALUE_MAX]; + bool prop_rec_play_enabled = false; + + if (property_get("rec.playback.conc.disabled", recConcPropValue, NULL)) { + prop_rec_play_enabled = atoi(recConcPropValue) || !strncmp("true", recConcPropValue, 4); + } + + if ((prop_rec_play_enabled) && + ((true == mIsInputRequestOnProgress) || (activeInputsCount() > 0))) { + ALOGD("copl: blocking compress offload for record concurrency"); + return false; + } +#endif + // Check if stream type is music, then only allow offload as of now. + if (offloadInfo.stream_type != AUDIO_STREAM_MUSIC) + { + ALOGD("isOffloadSupported: stream_type != MUSIC, returning false"); + return false; + } + + char propValue[PROPERTY_VALUE_MAX]; + bool pcmOffload = false; +#ifdef PCM_OFFLOAD_ENABLED + if (audio_is_offload_pcm(offloadInfo.format)) { + if(property_get("audio.offload.pcm.enable", propValue, NULL)) { + bool prop_enabled = atoi(propValue) || !strncmp("true", propValue, 4); + if (prop_enabled) { + ALOGW("PCM offload property is enabled"); + pcmOffload = true; + } + } + if (!pcmOffload) { + ALOGD("PCM offload disabled by property audio.offload.pcm.enable"); + return false; + } + } +#endif + + if (!pcmOffload) { + // Check if offload has been disabled + if (property_get("audio.offload.disable", propValue, "0")) { + if (atoi(propValue) != 0) { + ALOGD("offload disabled by audio.offload.disable=%s", propValue ); + return false; + } + } + + //check if it's multi-channel AAC (includes sub formats) and FLAC format + if ((popcount(offloadInfo.channel_mask) > 2) && + (((offloadInfo.format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_AAC) || + ((offloadInfo.format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_FLAC))) { + ALOGD("offload disabled for multi-channel AAC and FLAC format"); + return false; + } + + if (offloadInfo.has_video) + { + if(property_get("av.offload.enable", propValue, NULL)) { + bool prop_enabled = atoi(propValue) || !strncmp("true", propValue, 4); + if (!prop_enabled) { + ALOGW("offload disabled by av.offload.enable = %s ", propValue ); + return false; + } + } else { + return false; + } + + if(offloadInfo.is_streaming) { + if (property_get("av.streaming.offload.enable", propValue, NULL)) { + bool prop_enabled = atoi(propValue) || !strncmp("true", propValue, 4); + if (!prop_enabled) { + ALOGW("offload disabled by av.streaming.offload.enable = %s ", propValue ); + return false; + } + } else { + //Do not offload AV streamnig if the property is not defined + return false; + } + } + ALOGD("copl: isOffloadSupported: has_video == true, property\ + set to enable offload"); + } + } + + //If duration is less than minimum value defined in property, return false + if (property_get("audio.offload.min.duration.secs", propValue, NULL)) { + if (offloadInfo.duration_us < (atoi(propValue) * 1000000 )) { + ALOGD("copl: Offload denied by duration < audio.offload.min.duration.secs(=%s)", propValue); + return false; + } + } else if (offloadInfo.duration_us < OFFLOAD_DEFAULT_MIN_DURATION_SECS * 1000000) { + ALOGD("copl: Offload denied by duration < default min(=%u)", OFFLOAD_DEFAULT_MIN_DURATION_SECS); + //duration checks only valid for MP3/AAC formats, + //do not check duration for other audio formats, e.g. dolby AAC/AC3 and amrwb+ formats + if ((offloadInfo.format == AUDIO_FORMAT_MP3) || + ((offloadInfo.format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_AAC) || + ((offloadInfo.format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_FLAC) || + pcmOffload) + return false; + } + + // Do not allow offloading if one non offloadable effect is enabled. This prevents from + // creating an offloaded track and tearing it down immediately after start when audioflinger + // detects there is an active non offloadable effect. + // FIXME: We should check the audio session here but we do not have it in this context. + // This may prevent offloading in rare situations where effects are left active by apps + // in the background. + if (isNonOffloadableEffectEnabled()) { + return false; + } + + // See if there is a profile to support this. + // AUDIO_DEVICE_NONE + sp profile = getProfileForDirectOutput(AUDIO_DEVICE_NONE /*ignore device */, + offloadInfo.sample_rate, + offloadInfo.format, + offloadInfo.channel_mask, + AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD); + ALOGD("copl: isOffloadSupported() profile %sfound", profile != 0 ? "" : "NOT "); + return (profile != 0); +} + +uint32_t AudioPolicyManagerCustom::nextUniqueId() +{ + return android_atomic_inc(&mNextUniqueId); +} + +AudioPolicyManagerCustom::routing_strategy AudioPolicyManagerCustom::getStrategy( + audio_stream_type_t stream) { + // stream to strategy mapping + switch (stream) { + case AUDIO_STREAM_VOICE_CALL: + case AUDIO_STREAM_BLUETOOTH_SCO: + return STRATEGY_PHONE; + case AUDIO_STREAM_RING: + case AUDIO_STREAM_ALARM: + return STRATEGY_SONIFICATION; + case AUDIO_STREAM_NOTIFICATION: + return STRATEGY_SONIFICATION_RESPECTFUL; + case AUDIO_STREAM_DTMF: + return STRATEGY_DTMF; + default: + ALOGE("unknown stream type"); + case AUDIO_STREAM_SYSTEM: + // NOTE: SYSTEM stream uses MEDIA strategy because muting music and switching outputs + // while key clicks are played produces a poor result + case AUDIO_STREAM_TTS: + case AUDIO_STREAM_MUSIC: +#ifdef AUDIO_EXTN_INCALL_MUSIC_ENABLED + case AUDIO_STREAM_INCALL_MUSIC: +#endif + return STRATEGY_MEDIA; + case AUDIO_STREAM_ENFORCED_AUDIBLE: + return STRATEGY_ENFORCED_AUDIBLE; + } +} + +void AudioPolicyManagerCustom::handleNotificationRoutingForStream(audio_stream_type_t stream) { + switch(stream) { + case AUDIO_STREAM_MUSIC: + checkOutputForStrategy(STRATEGY_SONIFICATION_RESPECTFUL); + updateDevicesAndOutputs(); + break; + default: + break; + } +} + +audio_devices_t AudioPolicyManagerCustom::getDeviceForStrategy(routing_strategy strategy, + bool fromCache) +{ + uint32_t device = AUDIO_DEVICE_NONE; + + if (fromCache) { + ALOGVV("getDeviceForStrategy() from cache strategy %d, device %x", + strategy, mDeviceForStrategy[strategy]); + return mDeviceForStrategy[strategy]; + } + audio_devices_t availableOutputDeviceTypes = mAvailableOutputDevices.types(); + switch (strategy) { + + case STRATEGY_SONIFICATION_RESPECTFUL: + if (isInCall()) { + device = getDeviceForStrategy(STRATEGY_SONIFICATION, false /*fromCache*/); + } else if (isStreamActiveRemotely(AUDIO_STREAM_MUSIC, + SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) { + // while media is playing on a remote device, use the the sonification behavior. + // Note that we test this usecase before testing if media is playing because + // the isStreamActive() method only informs about the activity of a stream, not + // if it's for local playback. Note also that we use the same delay between both tests + device = getDeviceForStrategy(STRATEGY_SONIFICATION, false /*fromCache*/); + //user "safe" speaker if available instead of normal speaker to avoid triggering + //other acoustic safety mechanisms for notification + if (device == AUDIO_DEVICE_OUT_SPEAKER && (availableOutputDeviceTypes & AUDIO_DEVICE_OUT_SPEAKER_SAFE)) + device = AUDIO_DEVICE_OUT_SPEAKER_SAFE; + } else if (isStreamActive(AUDIO_STREAM_MUSIC, SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) { + // while media is playing (or has recently played), use the same device + device = getDeviceForStrategy(STRATEGY_MEDIA, false /*fromCache*/); + } else { + // when media is not playing anymore, fall back on the sonification behavior + device = getDeviceForStrategy(STRATEGY_SONIFICATION, false /*fromCache*/); + //user "safe" speaker if available instead of normal speaker to avoid triggering + //other acoustic safety mechanisms for notification + if (device == AUDIO_DEVICE_OUT_SPEAKER && (availableOutputDeviceTypes & AUDIO_DEVICE_OUT_SPEAKER_SAFE)) + device = AUDIO_DEVICE_OUT_SPEAKER_SAFE; + } + + break; + + case STRATEGY_DTMF: + if (!isInCall()) { + // when off call, DTMF strategy follows the same rules as MEDIA strategy + device = getDeviceForStrategy(STRATEGY_MEDIA, false /*fromCache*/); + break; + } + // when in call, DTMF and PHONE strategies follow the same rules + // FALL THROUGH + + case STRATEGY_PHONE: + // Force use of only devices on primary output if: + // - in call AND + // - cannot route from voice call RX OR + // - audio HAL version is < 3.0 and TX device is on the primary HW module + if (mPhoneState == AUDIO_MODE_IN_CALL) { + audio_devices_t txDevice = getDeviceForInputSource(AUDIO_SOURCE_VOICE_COMMUNICATION); + sp hwOutputDesc = mOutputs.valueFor(mPrimaryOutput); + if (((mAvailableInputDevices.types() & + AUDIO_DEVICE_IN_TELEPHONY_RX & ~AUDIO_DEVICE_BIT_IN) == 0) || + (((txDevice & availablePrimaryInputDevices() & ~AUDIO_DEVICE_BIT_IN) != 0) && + (hwOutputDesc->getAudioPort()->mModule->mHalVersion < + AUDIO_DEVICE_API_VERSION_3_0))) { + availableOutputDeviceTypes = availablePrimaryOutputDevices(); + } + } + // for phone strategy, we first consider the forced use and then the available devices by order + // of priority + switch (mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION]) { + case AUDIO_POLICY_FORCE_BT_SCO: + if (!isInCall() || strategy != STRATEGY_DTMF) { + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT; + if (device) break; + } + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_SCO; + if (device) break; + // if SCO device is requested but no SCO device is available, fall back to default case + // FALL THROUGH + + default: // FORCE_NONE + // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP + if (!isInCall() && + (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] != AUDIO_POLICY_FORCE_NO_BT_A2DP) && + (getA2dpOutput() != 0) && !mA2dpSuspended) { + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES; + if (device) break; + } + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_WIRED_HEADPHONE; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_WIRED_HEADSET; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_USB_DEVICE; + if (device) break; + if (mPhoneState != AUDIO_MODE_IN_CALL) { + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_USB_ACCESSORY; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_AUX_DIGITAL; + if (device) break; + } + + // Allow voice call on USB ANLG DOCK headset + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; + if (device) break; + + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_EARPIECE; + if (device) break; + device = mDefaultOutputDevice->mDeviceType; + if (device == AUDIO_DEVICE_NONE) { + ALOGE("getDeviceForStrategy() no device found for STRATEGY_PHONE"); + } + break; + + case AUDIO_POLICY_FORCE_SPEAKER: + // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to + // A2DP speaker when forcing to speaker output + if (!isInCall() && + (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] != AUDIO_POLICY_FORCE_NO_BT_A2DP) && + (getA2dpOutput() != 0) && !mA2dpSuspended) { + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER; + if (device) break; + } + if (mPhoneState != AUDIO_MODE_IN_CALL) { + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_USB_ACCESSORY; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_USB_DEVICE; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_AUX_DIGITAL; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; + if (device) break; + } + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_LINE; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_SPEAKER; + if (device) break; + device = mDefaultOutputDevice->mDeviceType; + if (device == AUDIO_DEVICE_NONE) { + ALOGE("getDeviceForStrategy() no device found for STRATEGY_PHONE, FORCE_SPEAKER"); + } + break; + } + + if (isInCall() && (device == AUDIO_DEVICE_NONE)) { + // when in call, get the device for Phone strategy + device = getDeviceForStrategy(STRATEGY_PHONE, false /*fromCache*/); + break; + } + +#ifdef AUDIO_EXTN_FM_ENABLED + if (availableOutputDeviceTypes & AUDIO_DEVICE_OUT_FM) { + if (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] == AUDIO_POLICY_FORCE_SPEAKER) { + device = AUDIO_DEVICE_OUT_SPEAKER; + } + } +#endif + break; + + case STRATEGY_SONIFICATION: + + // If incall, just select the STRATEGY_PHONE device: The rest of the behavior is handled by + // handleIncallSonification(). + if (isInCall()) { + device = getDeviceForStrategy(STRATEGY_PHONE, false /*fromCache*/); + break; + } + // FALL THROUGH + + case STRATEGY_ENFORCED_AUDIBLE: + // strategy STRATEGY_ENFORCED_AUDIBLE uses same routing policy as STRATEGY_SONIFICATION + // except: + // - when in call where it doesn't default to STRATEGY_PHONE behavior + // - in countries where not enforced in which case it follows STRATEGY_MEDIA + + if ((strategy == STRATEGY_SONIFICATION) || + (mForceUse[AUDIO_POLICY_FORCE_FOR_SYSTEM] == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED)) { + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_SPEAKER; + if (device == AUDIO_DEVICE_NONE) { + ALOGE("getDeviceForStrategy() speaker device not found for STRATEGY_SONIFICATION"); + } + } + // The second device used for sonification is the same as the device used by media strategy + // FALL THROUGH + + case STRATEGY_MEDIA: { + uint32_t device2 = AUDIO_DEVICE_NONE; + + if (isInCall() && (device == AUDIO_DEVICE_NONE)) { + // when in call, get the device for Phone strategy + device = getDeviceForStrategy(STRATEGY_PHONE, false /*fromCache*/); + break; + } +#ifdef AUDIO_EXTN_FM_ENABLED + if (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] == AUDIO_POLICY_FORCE_SPEAKER) { + device = AUDIO_DEVICE_OUT_SPEAKER; + break; + } +#endif + + if (strategy != STRATEGY_SONIFICATION) { + // no sonification on remote submix (e.g. WFD) + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_REMOTE_SUBMIX; + } + if ((device2 == AUDIO_DEVICE_NONE) && + (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] != AUDIO_POLICY_FORCE_NO_BT_A2DP) && + (getA2dpOutput() != 0) && !mA2dpSuspended) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP; + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES; + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER; + } + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_WIRED_HEADPHONE; + } + if ((device2 == AUDIO_DEVICE_NONE)) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_LINE; + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_WIRED_HEADSET; + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_USB_ACCESSORY; + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_USB_DEVICE; + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET; + } + if ((strategy != STRATEGY_SONIFICATION) && (device == AUDIO_DEVICE_NONE) + && (device2 == AUDIO_DEVICE_NONE)) { + // no sonification on aux digital (e.g. HDMI) + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_AUX_DIGITAL; + } + if ((device2 == AUDIO_DEVICE_NONE) && + (mForceUse[AUDIO_POLICY_FORCE_FOR_DOCK] == AUDIO_POLICY_FORCE_ANALOG_DOCK) + && (strategy != STRATEGY_SONIFICATION)) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; + } +#ifdef AUDIO_EXTN_FM_ENABLED + if ((strategy != STRATEGY_SONIFICATION) && (device == AUDIO_DEVICE_NONE) + && (device2 == AUDIO_DEVICE_NONE)) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_FM_TX; + } +#endif +#ifdef AUDIO_EXTN_AFE_PROXY_ENABLED + if ((strategy != STRATEGY_SONIFICATION) && (device == AUDIO_DEVICE_NONE) + && (device2 == AUDIO_DEVICE_NONE)) { + // no sonification on WFD sink + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_PROXY; + } +#endif + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_SPEAKER; + } + int device3 = AUDIO_DEVICE_NONE; + if (strategy == STRATEGY_MEDIA) { + // ARC, SPDIF and AUX_LINE can co-exist with others. + device3 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_HDMI_ARC; + device3 |= (availableOutputDeviceTypes & AUDIO_DEVICE_OUT_SPDIF); + device3 |= (availableOutputDeviceTypes & AUDIO_DEVICE_OUT_AUX_LINE); + } + + device2 |= device3; + // device is DEVICE_OUT_SPEAKER if we come from case STRATEGY_SONIFICATION or + // STRATEGY_ENFORCED_AUDIBLE, AUDIO_DEVICE_NONE otherwise + device |= device2; + + // If hdmi system audio mode is on, remove speaker out of output list. + if ((strategy == STRATEGY_MEDIA) && + (mForceUse[AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO] == + AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED)) { + device &= ~AUDIO_DEVICE_OUT_SPEAKER; + } + + if (device) break; + device = mDefaultOutputDevice->mDeviceType; + if (device == AUDIO_DEVICE_NONE) { + ALOGE("getDeviceForStrategy() no device found for STRATEGY_MEDIA"); + } + } break; + + default: + ALOGW("getDeviceForStrategy() unknown strategy: %d", strategy); + break; + } + + ALOGVV("getDeviceForStrategy() strategy %d, device %x", strategy, device); + return device; +} + +audio_devices_t AudioPolicyManagerCustom::getDeviceForInputSource(audio_source_t inputSource) +{ + uint32_t device = AUDIO_DEVICE_NONE; + audio_devices_t availableDeviceTypes = mAvailableInputDevices.types() & + ~AUDIO_DEVICE_BIT_IN; + switch (inputSource) { + case AUDIO_SOURCE_VOICE_UPLINK: + if (availableDeviceTypes & AUDIO_DEVICE_IN_VOICE_CALL) { + device = AUDIO_DEVICE_IN_VOICE_CALL; + break; + } + break; + + case AUDIO_SOURCE_DEFAULT: + case AUDIO_SOURCE_MIC: + if (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_A2DP) { + device = AUDIO_DEVICE_IN_BLUETOOTH_A2DP; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) { + device = AUDIO_DEVICE_IN_WIRED_HEADSET; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_DEVICE) { + device = AUDIO_DEVICE_IN_USB_DEVICE; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) { + device = AUDIO_DEVICE_IN_BUILTIN_MIC; + } + break; + + case AUDIO_SOURCE_VOICE_COMMUNICATION: + // Allow only use of devices on primary input if in call and HAL does not support routing + // to voice call path. + if ((mPhoneState == AUDIO_MODE_IN_CALL) && + (mAvailableOutputDevices.types() & AUDIO_DEVICE_OUT_TELEPHONY_TX) == 0) { + availableDeviceTypes = availablePrimaryInputDevices() & ~AUDIO_DEVICE_BIT_IN; + } + + switch (mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION]) { + case AUDIO_POLICY_FORCE_BT_SCO: + // if SCO device is requested but no SCO device is available, fall back to default case + if (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) { + device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET; + break; + } + // FALL THROUGH + + default: // FORCE_NONE + if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) { + device = AUDIO_DEVICE_IN_WIRED_HEADSET; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_DEVICE) { + device = AUDIO_DEVICE_IN_USB_DEVICE; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) { + device = AUDIO_DEVICE_IN_BUILTIN_MIC; + } + break; + + case AUDIO_POLICY_FORCE_SPEAKER: + if (availableDeviceTypes & AUDIO_DEVICE_IN_BACK_MIC) { + device = AUDIO_DEVICE_IN_BACK_MIC; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) { + device = AUDIO_DEVICE_IN_BUILTIN_MIC; + } + break; + } + break; + + case AUDIO_SOURCE_VOICE_RECOGNITION: + case AUDIO_SOURCE_HOTWORD: + if (mForceUse[AUDIO_POLICY_FORCE_FOR_RECORD] == AUDIO_POLICY_FORCE_BT_SCO && + availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) { + device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) { + device = AUDIO_DEVICE_IN_WIRED_HEADSET; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_DEVICE) { + device = AUDIO_DEVICE_IN_USB_DEVICE; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET) { + device = AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) { + device = AUDIO_DEVICE_IN_BUILTIN_MIC; + } + break; + case AUDIO_SOURCE_CAMCORDER: + if (availableDeviceTypes & AUDIO_DEVICE_IN_BACK_MIC) { + device = AUDIO_DEVICE_IN_BACK_MIC; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) { + device = AUDIO_DEVICE_IN_BUILTIN_MIC; + } + break; + case AUDIO_SOURCE_VOICE_DOWNLINK: + case AUDIO_SOURCE_VOICE_CALL: + if (availableDeviceTypes & AUDIO_DEVICE_IN_VOICE_CALL) { + device = AUDIO_DEVICE_IN_VOICE_CALL; + } + break; + case AUDIO_SOURCE_REMOTE_SUBMIX: + if (availableDeviceTypes & AUDIO_DEVICE_IN_REMOTE_SUBMIX) { + device = AUDIO_DEVICE_IN_REMOTE_SUBMIX; + } + break; +#ifdef AUDIO_EXTN_FM_ENABLED + case AUDIO_SOURCE_FM_RX: + device = AUDIO_DEVICE_IN_FM_RX; + break; + case AUDIO_SOURCE_FM_RX_A2DP: + device = AUDIO_DEVICE_IN_FM_RX_A2DP; + break; +#endif + default: + ALOGW("getDeviceForInputSource() invalid input source %d", inputSource); + break; + } + ALOGV("getDeviceForInputSource()input source %d, device %08x", inputSource, device); + return device; +} + +bool AudioPolicyManagerCustom::isVirtualInputDevice(audio_devices_t device) +{ + if ((device & AUDIO_DEVICE_BIT_IN) != 0) { + device &= ~AUDIO_DEVICE_BIT_IN; + if ((popcount(device) == 1) && ((device & ~APM_AUDIO_IN_DEVICE_VIRTUAL_ALL) == 0)) + return true; + } + return false; +} + +bool AudioPolicyManagerCustom::deviceDistinguishesOnAddress(audio_devices_t device) { + return ((device & APM_AUDIO_DEVICE_MATCH_ADDRESS_ALL) != 0); +} + +AudioPolicyManagerCustom::device_category AudioPolicyManagerCustom::getDeviceCategory(audio_devices_t device) +{ + switch(getDeviceForVolume(device)) { + case AUDIO_DEVICE_OUT_EARPIECE: + return DEVICE_CATEGORY_EARPIECE; + case AUDIO_DEVICE_OUT_WIRED_HEADSET: + case AUDIO_DEVICE_OUT_WIRED_HEADPHONE: + case AUDIO_DEVICE_OUT_BLUETOOTH_SCO: + case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET: + case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP: + case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES: +#ifdef AUDIO_EXTN_FM_ENABLED + case AUDIO_DEVICE_OUT_FM: +#endif + return DEVICE_CATEGORY_HEADSET; + case AUDIO_DEVICE_OUT_LINE: + case AUDIO_DEVICE_OUT_AUX_DIGITAL: + /*USB? Remote submix?*/ + return DEVICE_CATEGORY_EXT_MEDIA; + case AUDIO_DEVICE_OUT_SPEAKER: + case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT: + case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER: + case AUDIO_DEVICE_OUT_USB_ACCESSORY: + case AUDIO_DEVICE_OUT_USB_DEVICE: + case AUDIO_DEVICE_OUT_REMOTE_SUBMIX: +#ifdef AUDIO_EXTN_AFE_PROXY_ENABLED + case AUDIO_DEVICE_OUT_PROXY: +#endif + default: + return DEVICE_CATEGORY_SPEAKER; + } +} + +float AudioPolicyManagerCustom::volIndexToAmpl(audio_devices_t device, const StreamDescriptor& streamDesc, + int indexInUi) +{ + device_category deviceCategory = getDeviceCategory(device); + const VolumeCurvePoint *curve = streamDesc.mVolumeCurve[deviceCategory]; + + // the volume index in the UI is relative to the min and max volume indices for this stream type + int nbSteps = 1 + curve[VOLMAX].mIndex - + curve[VOLMIN].mIndex; + int volIdx = (nbSteps * (indexInUi - streamDesc.mIndexMin)) / + (streamDesc.mIndexMax - streamDesc.mIndexMin); + + // find what part of the curve this index volume belongs to, or if it's out of bounds + int segment = 0; + if (volIdx < curve[VOLMIN].mIndex) { // out of bounds + return 0.0f; + } else if (volIdx < curve[VOLKNEE1].mIndex) { + segment = 0; + } else if (volIdx < curve[VOLKNEE2].mIndex) { + segment = 1; + } else if (volIdx <= curve[VOLMAX].mIndex) { + segment = 2; + } else { // out of bounds + return 1.0f; + } + + // linear interpolation in the attenuation table in dB + float decibels = curve[segment].mDBAttenuation + + ((float)(volIdx - curve[segment].mIndex)) * + ( (curve[segment+1].mDBAttenuation - + curve[segment].mDBAttenuation) / + ((float)(curve[segment+1].mIndex - + curve[segment].mIndex)) ); + + float amplification = exp( decibels * 0.115129f); // exp( dB * ln(10) / 20 ) + + ALOGVV("VOLUME vol index=[%d %d %d], dB=[%.1f %.1f %.1f] ampl=%.5f", + curve[segment].mIndex, volIdx, + curve[segment+1].mIndex, + curve[segment].mDBAttenuation, + decibels, + curve[segment+1].mDBAttenuation, + amplification); + + return amplification; +} + +float AudioPolicyManagerCustom::computeVolume(audio_stream_type_t stream, + int index, + audio_io_handle_t output, + audio_devices_t device) +{ + float volume = 1.0; + sp outputDesc = mOutputs.valueFor(output); + StreamDescriptor &streamDesc = mStreams[stream]; + + if (device == AUDIO_DEVICE_NONE) { + device = outputDesc->device(); + } + + // if volume is not 0 (not muted), force media volume to max on digital output + if (stream == AUDIO_STREAM_MUSIC && + index != mStreams[stream].mIndexMin && + (device == AUDIO_DEVICE_OUT_AUX_DIGITAL || +#ifdef AUDIO_EXTN_AFE_PROXY_ENABLED + device == AUDIO_DEVICE_OUT_PROXY || +#endif + device == AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET)) { + return 1.0; + } + +#ifdef AUDIO_EXTN_INCALL_MUSIC_ENABLED + if (stream == AUDIO_STREAM_INCALL_MUSIC) { + return 1.0; + } +#endif + + volume = volIndexToAmpl(device, streamDesc, index); + + // if a headset is connected, apply the following rules to ring tones and notifications + // to avoid sound level bursts in user's ears: + // - always attenuate ring tones and notifications volume by 6dB + // - if music is playing, always limit the volume to current music volume, + // with a minimum threshold at -36dB so that notification is always perceived. + const routing_strategy stream_strategy = getStrategy(stream); + if ((device & (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | + AUDIO_DEVICE_OUT_WIRED_HEADSET | + AUDIO_DEVICE_OUT_WIRED_HEADPHONE)) && + ((stream_strategy == STRATEGY_SONIFICATION) + || (stream_strategy == STRATEGY_SONIFICATION_RESPECTFUL) + || (stream == AUDIO_STREAM_SYSTEM) + || ((stream_strategy == STRATEGY_ENFORCED_AUDIBLE) && + (mForceUse[AUDIO_POLICY_FORCE_FOR_SYSTEM] == AUDIO_POLICY_FORCE_NONE))) && + streamDesc.mCanBeMuted) { + volume *= SONIFICATION_HEADSET_VOLUME_FACTOR; + // when the phone is ringing we must consider that music could have been paused just before + // by the music application and behave as if music was active if the last music track was + // just stopped + if (isStreamActive(AUDIO_STREAM_MUSIC, SONIFICATION_HEADSET_MUSIC_DELAY) || + mLimitRingtoneVolume) { + audio_devices_t musicDevice = getDeviceForStrategy(STRATEGY_MEDIA, true /*fromCache*/); + float musicVol = computeVolume(AUDIO_STREAM_MUSIC, + mStreams[AUDIO_STREAM_MUSIC].getVolumeIndex(musicDevice), + output, + musicDevice); + float minVol = (musicVol > SONIFICATION_HEADSET_VOLUME_MIN) ? + musicVol : SONIFICATION_HEADSET_VOLUME_MIN; + if (volume > minVol) { + volume = minVol; + ALOGV("computeVolume limiting volume to %f musicVol %f", minVol, musicVol); + } + } + } + + return volume; +} + +status_t AudioPolicyManagerCustom::checkAndSetVolume(audio_stream_type_t stream, + int index, + audio_io_handle_t output, + audio_devices_t device, + int delayMs, + bool force) +{ + + // do not change actual stream volume if the stream is muted + if (mOutputs.valueFor(output)->mMuteCount[stream] != 0) { + ALOGVV("checkAndSetVolume() stream %d muted count %d", + stream, mOutputs.valueFor(output)->mMuteCount[stream]); + return NO_ERROR; + } + + // do not change in call volume if bluetooth is connected and vice versa + if ((stream == AUDIO_STREAM_VOICE_CALL && + mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION] == AUDIO_POLICY_FORCE_BT_SCO) || + (stream == AUDIO_STREAM_BLUETOOTH_SCO && + mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION] != AUDIO_POLICY_FORCE_BT_SCO)) { + ALOGV("checkAndSetVolume() cannot set stream %d volume with force use = %d for comm", + stream, mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION]); + return INVALID_OPERATION; + } + + float volume = computeVolume(stream, index, output, device); + // We actually change the volume if: + // - the float value returned by computeVolume() changed + // - the force flag is set + if (volume != mOutputs.valueFor(output)->mCurVolume[stream] || + force) { + mOutputs.valueFor(output)->mCurVolume[stream] = volume; + ALOGVV("checkAndSetVolume() for output %d stream %d, volume %f, delay %d", output, stream, volume, delayMs); + // Force VOICE_CALL to track BLUETOOTH_SCO stream volume when bluetooth audio is + // enabled + if (stream == AUDIO_STREAM_BLUETOOTH_SCO) { + mpClientInterface->setStreamVolume(AUDIO_STREAM_VOICE_CALL, volume, output, delayMs); +#ifdef AUDIO_EXTN_FM_ENABLED + } else if (stream == AUDIO_STREAM_MUSIC && + output == mPrimaryOutput) { + if (volume >= 0) { + AudioParameter param = AudioParameter(); + param.addFloat(String8("fm_volume"), volume); + ALOGV("checkAndSetVolume setParameters volume, volume=:%f delay=:%d",volume,delayMs*2); + //Double delayMs to avoid sound burst while device switch. + mpClientInterface->setParameters(mPrimaryOutput, param.toString(), delayMs*2); + } +#endif + } + mpClientInterface->setStreamVolume(stream, volume, output, delayMs); + } + + if (stream == AUDIO_STREAM_VOICE_CALL || + stream == AUDIO_STREAM_BLUETOOTH_SCO) { + float voiceVolume; + // Force voice volume to max for bluetooth SCO as volume is managed by the headset + if (stream == AUDIO_STREAM_VOICE_CALL) { + voiceVolume = (float)index/(float)mStreams[stream].mIndexMax; + } else { + voiceVolume = 1.0; + } + + if (voiceVolume != mLastVoiceVolume && ((output == mPrimaryOutput) || + isDirectOutput(output))) { + mpClientInterface->setVoiceVolume(voiceVolume, delayMs); + mLastVoiceVolume = voiceVolume; + } + } + + return NO_ERROR; +} + +bool AudioPolicyManagerCustom::isStateInCall(int state) { + return ((state == AUDIO_MODE_IN_CALL) || (state == AUDIO_MODE_IN_COMMUNICATION) || + ((state == AUDIO_MODE_RINGTONE) && (mPrevPhoneState == AUDIO_MODE_IN_CALL))); +} + + +extern "C" AudioPolicyInterface* createAudioPolicyManager( + AudioPolicyClientInterface *clientInterface) +{ + return new AudioPolicyManager(clientInterface); +} + +extern "C" void destroyAudioPolicyManager(AudioPolicyInterface *interface) +{ + delete interface; +} + +}; // namespace android diff --git a/audio/policy_hal/AudioPolicyManager.h b/audio/policy_hal/AudioPolicyManager.h new file mode 100644 index 0000000..e37f83f --- /dev/null +++ b/audio/policy_hal/AudioPolicyManager.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * Not a contribution. + * + * Copyright (C) 2009 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 +#include + + + +namespace android { + +// ---------------------------------------------------------------------------- + +class AudioPolicyManagerCustom: public AudioPolicyManager +{ + +public: + AudioPolicyManagerCustom(AudioPolicyClientInterface *clientInterface) + : AudioPolicyManager(clientInterface) { + mHdmiAudioDisabled = false; + mHdmiAudioEvent = false; } + + virtual ~AudioPolicyManagerCustom() {} + + virtual status_t setDeviceConnectionState(audio_devices_t device, + audio_policy_dev_state_t state, + const char *device_address); + virtual audio_policy_dev_state_t getDeviceConnectionState(audio_devices_t device, + const char *device_address); + virtual void setPhoneState(audio_mode_t state); + virtual void setForceUse(audio_policy_force_use_t usage, + audio_policy_forced_cfg_t config); + virtual status_t stopOutput(audio_io_handle_t output, + audio_stream_type_t stream, + int session = 0); + virtual audio_io_handle_t getInput(audio_source_t inputSource, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_session_t session, + audio_input_flags_t flags); + + // indicates to the audio policy manager that the input starts being used. + virtual status_t startInput(audio_io_handle_t input, + audio_session_t session); + + // indicates to the audio policy manager that the input stops being used. + virtual status_t stopInput(audio_io_handle_t input, + audio_session_t session); + virtual status_t setStreamVolumeIndex(audio_stream_type_t stream, + int index, + audio_devices_t device); + virtual bool isOffloadSupported(const audio_offload_info_t& offloadInfo); + + // true if given state represents a device in a telephony or VoIP call + virtual bool isStateInCall(int state); +protected: + // return the strategy corresponding to a given stream type + static routing_strategy getStrategy(audio_stream_type_t stream); + + // return appropriate device for streams handled by the specified strategy according to current + // phone state, connected devices... + // if fromCache is true, the device is returned from mDeviceForStrategy[], + // otherwise it is determine by current state + // (device connected,phone state, force use, a2dp output...) + // This allows to: + // 1 speed up process when the state is stable (when starting or stopping an output) + // 2 access to either current device selection (fromCache == true) or + // "future" device selection (fromCache == false) when called from a context + // where conditions are changing (setDeviceConnectionState(), setPhoneState()...) AND + // before updateDevicesAndOutputs() is called. + virtual audio_devices_t getDeviceForStrategy(routing_strategy strategy, + bool fromCache); + // select input device corresponding to requested audio source + virtual audio_devices_t getDeviceForInputSource(audio_source_t inputSource); + + // compute the actual volume for a given stream according to the requested index and a particular + // device + virtual float computeVolume(audio_stream_type_t stream, int index, + audio_io_handle_t output, audio_devices_t device); + + // check that volume change is permitted, compute and send new volume to audio hardware + status_t checkAndSetVolume(audio_stream_type_t stream, int index, audio_io_handle_t output, + audio_devices_t device, int delayMs = 0, bool force = false); + + // returns the category the device belongs to with regard to volume curve management + static device_category getDeviceCategory(audio_devices_t device); + + + //parameter indicates of HDMI speakers disabled + bool mHdmiAudioDisabled; + //parameter indicates if HDMI plug in/out detected + bool mHdmiAudioEvent; +private: + static float volIndexToAmpl(audio_devices_t device, const StreamDescriptor& streamDesc, + int indexInUi); + // updates device caching and output for streams that can influence the + // routing of notifications + void handleNotificationRoutingForStream(audio_stream_type_t stream); + static bool isVirtualInputDevice(audio_devices_t device); + static bool deviceDistinguishesOnAddress(audio_devices_t device); + uint32_t nextUniqueId(); + // internal method to return the output handle for the given device and format + audio_io_handle_t getOutputForDevice( + audio_devices_t device, + audio_stream_type_t stream, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo); + + // Used for voip + voice concurrency usecase + int mPrevPhoneState; + int mvoice_call_state; +#ifdef RECORD_PLAY_CONCURRENCY + // Used for record + playback concurrency + bool mIsInputRequestOnProgress; +#endif + +}; + +}; diff --git a/audio/post_proc/Android.mk b/audio/post_proc/Android.mk new file mode 100644 index 0000000..9d14c49 --- /dev/null +++ b/audio/post_proc/Android.mk @@ -0,0 +1,34 @@ + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +ifeq ($(strip $(AUDIO_FEATURE_ENABLED_PROXY_DEVICE)),true) + LOCAL_CFLAGS += -DAFE_PROXY_ENABLED +endif + +LOCAL_SRC_FILES:= \ + bundle.c \ + equalizer.c \ + bass_boost.c \ + virtualizer.c \ + reverb.c \ + effect_api.c + +LOCAL_CFLAGS+= -O2 -fvisibility=hidden + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + liblog \ + libtinyalsa + +LOCAL_MODULE_TAGS := optional + +LOCAL_MODULE_RELATIVE_PATH := soundfx +LOCAL_MODULE:= libqcompostprocbundle + +LOCAL_C_INCLUDES := \ + external/tinyalsa/include \ + $(call include-path-for, audio-effects) + +include $(BUILD_SHARED_LIBRARY) diff --git a/audio/post_proc/bass_boost.c b/audio/post_proc/bass_boost.c new file mode 100644 index 0000000..7cd170b --- /dev/null +++ b/audio/post_proc/bass_boost.c @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2013 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 "offload_effect_bass_boost" +#define LOG_NDEBUG 0 + +#include +#include +#include +#include +#include + +#include "effect_api.h" +#include "bass_boost.h" + +/* Offload bassboost UUID: 2c4a8c24-1581-487f-94f6-0002a5d5c51b */ +const effect_descriptor_t bassboost_descriptor = { + {0x0634f220, 0xddd4, 0x11db, 0xa0fc, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b }}, + {0x2c4a8c24, 0x1581, 0x487f, 0x94f6, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // uuid + EFFECT_CONTROL_API_VERSION, + (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_DEVICE_IND | EFFECT_FLAG_HW_ACC_TUNNEL), + 0, /* TODO */ + 1, + "MSM offload bassboost", + "The Android Open Source Project", +}; + +/* + * Bassboost operations + */ + +int bassboost_get_strength(bassboost_context_t *context) +{ + ALOGV("%s: ctxt %p, strength: %d", __func__, + context, context->strength); + return context->strength; +} + +int bassboost_set_strength(bassboost_context_t *context, uint32_t strength) +{ + ALOGV("%s: ctxt %p, strength: %d", __func__, context, strength); + context->strength = strength; + + offload_bassboost_set_strength(&(context->offload_bass), strength); + if (context->ctl) + offload_bassboost_send_params(context->ctl, context->offload_bass, + OFFLOAD_SEND_BASSBOOST_ENABLE_FLAG | + OFFLOAD_SEND_BASSBOOST_STRENGTH); + return 0; +} + +int bassboost_get_parameter(effect_context_t *context, effect_param_t *p, + uint32_t *size) +{ + bassboost_context_t *bass_ctxt = (bassboost_context_t *)context; + int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t); + int32_t *param_tmp = (int32_t *)p->data; + int32_t param = *param_tmp++; + void *value = p->data + voffset; + int i; + + ALOGV("%s: ctxt %p, param %d", __func__, bass_ctxt, param); + + p->status = 0; + + switch (param) { + case BASSBOOST_PARAM_STRENGTH_SUPPORTED: + if (p->vsize < sizeof(uint32_t)) + p->status = -EINVAL; + p->vsize = sizeof(uint32_t); + break; + case BASSBOOST_PARAM_STRENGTH: + if (p->vsize < sizeof(int16_t)) + p->status = -EINVAL; + p->vsize = sizeof(int16_t); + break; + default: + p->status = -EINVAL; + } + + *size = sizeof(effect_param_t) + voffset + p->vsize; + + if (p->status != 0) + return 0; + + switch (param) { + case BASSBOOST_PARAM_STRENGTH_SUPPORTED: + *(uint32_t *)value = 1; + break; + + case BASSBOOST_PARAM_STRENGTH: + *(int16_t *)value = bassboost_get_strength(bass_ctxt); + break; + + default: + p->status = -EINVAL; + break; + } + + return 0; +} + +int bassboost_set_parameter(effect_context_t *context, effect_param_t *p, + uint32_t size __unused) +{ + bassboost_context_t *bass_ctxt = (bassboost_context_t *)context; + int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t); + void *value = p->data + voffset; + int32_t *param_tmp = (int32_t *)p->data; + int32_t param = *param_tmp++; + uint32_t strength; + + ALOGV("%s: ctxt %p, param %d", __func__, bass_ctxt, param); + + p->status = 0; + + switch (param) { + case BASSBOOST_PARAM_STRENGTH: + strength = (uint32_t)(*(int16_t *)value); + bassboost_set_strength(bass_ctxt, strength); + break; + default: + p->status = -EINVAL; + break; + } + + return 0; +} + +int bassboost_set_device(effect_context_t *context, uint32_t device) +{ + bassboost_context_t *bass_ctxt = (bassboost_context_t *)context; + + ALOGV("%s: ctxt %p, device 0x%x", __func__, bass_ctxt, device); + bass_ctxt->device = device; + if((device == AUDIO_DEVICE_OUT_SPEAKER) || + (device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT) || + (device == AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER) || +#ifdef AFE_PROXY_ENABLED + (device == AUDIO_DEVICE_OUT_PROXY) || +#endif + (device == AUDIO_DEVICE_OUT_AUX_DIGITAL) || + (device == AUDIO_DEVICE_OUT_USB_ACCESSORY) || + (device == AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET)) { + if (!bass_ctxt->temp_disabled) { + if (effect_is_active(&bass_ctxt->common)) { + offload_bassboost_set_enable_flag(&(bass_ctxt->offload_bass), false); + if (bass_ctxt->ctl) + offload_bassboost_send_params(bass_ctxt->ctl, + bass_ctxt->offload_bass, + OFFLOAD_SEND_BASSBOOST_ENABLE_FLAG); + } + bass_ctxt->temp_disabled = true; + } + ALOGI("%s: ctxt %p, disabled based on device", __func__, bass_ctxt); + } else { + if (bass_ctxt->temp_disabled) { + if (effect_is_active(&bass_ctxt->common)) { + offload_bassboost_set_enable_flag(&(bass_ctxt->offload_bass), true); + if (bass_ctxt->ctl) + offload_bassboost_send_params(bass_ctxt->ctl, + bass_ctxt->offload_bass, + OFFLOAD_SEND_BASSBOOST_ENABLE_FLAG); + } + bass_ctxt->temp_disabled = false; + } + } + offload_bassboost_set_device(&(bass_ctxt->offload_bass), device); + return 0; +} + +int bassboost_reset(effect_context_t *context) +{ + bassboost_context_t *bass_ctxt = (bassboost_context_t *)context; + + return 0; +} + +int bassboost_init(effect_context_t *context) +{ + bassboost_context_t *bass_ctxt = (bassboost_context_t *)context; + + ALOGV("%s: ctxt %p", __func__, bass_ctxt); + context->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ; + context->config.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; + context->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT; + context->config.inputCfg.samplingRate = 44100; + context->config.inputCfg.bufferProvider.getBuffer = NULL; + context->config.inputCfg.bufferProvider.releaseBuffer = NULL; + context->config.inputCfg.bufferProvider.cookie = NULL; + context->config.inputCfg.mask = EFFECT_CONFIG_ALL; + context->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE; + context->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; + context->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT; + context->config.outputCfg.samplingRate = 44100; + context->config.outputCfg.bufferProvider.getBuffer = NULL; + context->config.outputCfg.bufferProvider.releaseBuffer = NULL; + context->config.outputCfg.bufferProvider.cookie = NULL; + context->config.outputCfg.mask = EFFECT_CONFIG_ALL; + + set_config(context, &context->config); + + bass_ctxt->temp_disabled = false; + memset(&(bass_ctxt->offload_bass), 0, sizeof(struct bass_boost_params)); + + return 0; +} + +int bassboost_enable(effect_context_t *context) +{ + bassboost_context_t *bass_ctxt = (bassboost_context_t *)context; + + ALOGV("%s: ctxt %p, strength %d", __func__, bass_ctxt, bass_ctxt->strength); + + if (!offload_bassboost_get_enable_flag(&(bass_ctxt->offload_bass)) && + !(bass_ctxt->temp_disabled)) { + offload_bassboost_set_enable_flag(&(bass_ctxt->offload_bass), true); + if (bass_ctxt->ctl && bass_ctxt->strength) + offload_bassboost_send_params(bass_ctxt->ctl, + bass_ctxt->offload_bass, + OFFLOAD_SEND_BASSBOOST_ENABLE_FLAG | + OFFLOAD_SEND_BASSBOOST_STRENGTH); + } + return 0; +} + +int bassboost_disable(effect_context_t *context) +{ + bassboost_context_t *bass_ctxt = (bassboost_context_t *)context; + + ALOGV("%s: ctxt %p", __func__, bass_ctxt); + if (offload_bassboost_get_enable_flag(&(bass_ctxt->offload_bass))) { + offload_bassboost_set_enable_flag(&(bass_ctxt->offload_bass), false); + if (bass_ctxt->ctl) + offload_bassboost_send_params(bass_ctxt->ctl, + bass_ctxt->offload_bass, + OFFLOAD_SEND_BASSBOOST_ENABLE_FLAG); + } + return 0; +} + +int bassboost_start(effect_context_t *context, output_context_t *output) +{ + bassboost_context_t *bass_ctxt = (bassboost_context_t *)context; + + ALOGV("%s: ctxt %p, ctl %p, strength %d", __func__, bass_ctxt, + output->ctl, bass_ctxt->strength); + bass_ctxt->ctl = output->ctl; + if (offload_bassboost_get_enable_flag(&(bass_ctxt->offload_bass))) + if (bass_ctxt->ctl) + offload_bassboost_send_params(bass_ctxt->ctl, bass_ctxt->offload_bass, + OFFLOAD_SEND_BASSBOOST_ENABLE_FLAG | + OFFLOAD_SEND_BASSBOOST_STRENGTH); + return 0; +} + +int bassboost_stop(effect_context_t *context, output_context_t *output __unused) +{ + bassboost_context_t *bass_ctxt = (bassboost_context_t *)context; + + ALOGV("%s: ctxt %p", __func__, bass_ctxt); + bass_ctxt->ctl = NULL; + return 0; +} diff --git a/audio/post_proc/bass_boost.h b/audio/post_proc/bass_boost.h new file mode 100644 index 0000000..430a07d --- /dev/null +++ b/audio/post_proc/bass_boost.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2013 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. + */ + +#ifndef OFFLOAD_EFFECT_BASS_BOOST_H_ +#define OFFLOAD_EFFECT_BASS_BOOST_H_ + +#include "bundle.h" + +extern const effect_descriptor_t bassboost_descriptor; + +typedef struct bassboost_context_s { + effect_context_t common; + + int strength; + + // Offload vars + struct mixer_ctl *ctl; + bool temp_disabled; + uint32_t device; + struct bass_boost_params offload_bass; +} bassboost_context_t; + +int bassboost_get_parameter(effect_context_t *context, effect_param_t *p, + uint32_t *size); + +int bassboost_set_parameter(effect_context_t *context, effect_param_t *p, + uint32_t size); + +int bassboost_set_device(effect_context_t *context, uint32_t device); + +int bassboost_reset(effect_context_t *context); + +int bassboost_init(effect_context_t *context); + +int bassboost_enable(effect_context_t *context); + +int bassboost_disable(effect_context_t *context); + +int bassboost_start(effect_context_t *context, output_context_t *output); + +int bassboost_stop(effect_context_t *context, output_context_t *output); + +#endif /* OFFLOAD_EFFECT_BASS_BOOST_H_ */ diff --git a/audio/post_proc/bundle.c b/audio/post_proc/bundle.c new file mode 100644 index 0000000..96c80d0 --- /dev/null +++ b/audio/post_proc/bundle.c @@ -0,0 +1,779 @@ +/* + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2013 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 "offload_effect_bundle" +#define LOG_NDEBUG 0 + +#include +#include +#include +#include +#include + +#include "bundle.h" +#include "equalizer.h" +#include "bass_boost.h" +#include "virtualizer.h" +#include "reverb.h" + +enum { + EFFECT_STATE_UNINITIALIZED, + EFFECT_STATE_INITIALIZED, + EFFECT_STATE_ACTIVE, +}; + +const effect_descriptor_t *descriptors[] = { + &equalizer_descriptor, + &bassboost_descriptor, + &virtualizer_descriptor, + &aux_env_reverb_descriptor, + &ins_env_reverb_descriptor, + &aux_preset_reverb_descriptor, + &ins_preset_reverb_descriptor, + NULL, +}; + +pthread_once_t once = PTHREAD_ONCE_INIT; +int init_status; +/* + * list of created effects. + * Updated by offload_effects_bundle_hal_start_output() + * and offload_effects_bundle_hal_stop_output() + */ +struct listnode created_effects_list; +/* + * list of active output streams. + * Updated by offload_effects_bundle_hal_start_output() + * and offload_effects_bundle_hal_stop_output() + */ +struct listnode active_outputs_list; +/* + * lock must be held when modifying or accessing + * created_effects_list or active_outputs_list + */ +pthread_mutex_t lock; + + +/* + * Local functions + */ +static void init_once() { + list_init(&created_effects_list); + list_init(&active_outputs_list); + + pthread_mutex_init(&lock, NULL); + + init_status = 0; +} + +int lib_init() +{ + pthread_once(&once, init_once); + return init_status; +} + +bool effect_exists(effect_context_t *context) +{ + struct listnode *node; + + list_for_each(node, &created_effects_list) { + effect_context_t *fx_ctxt = node_to_item(node, + effect_context_t, + effects_list_node); + if (fx_ctxt == context) { + return true; + } + } + return false; +} + +output_context_t *get_output(audio_io_handle_t output) +{ + struct listnode *node; + + list_for_each(node, &active_outputs_list) { + output_context_t *out_ctxt = node_to_item(node, + output_context_t, + outputs_list_node); + if (out_ctxt->handle == output) + return out_ctxt; + } + return NULL; +} + +void add_effect_to_output(output_context_t * output, effect_context_t *context) +{ + struct listnode *fx_node; + + ALOGV("%s: e_ctxt %p, o_ctxt %p", __func__, context, output); + list_for_each(fx_node, &output->effects_list) { + effect_context_t *fx_ctxt = node_to_item(fx_node, + effect_context_t, + output_node); + if (fx_ctxt == context) + return; + } + list_add_tail(&output->effects_list, &context->output_node); + if (context->ops.start) + context->ops.start(context, output); + +} + +void remove_effect_from_output(output_context_t * output, + effect_context_t *context) +{ + struct listnode *fx_node; + + ALOGV("%s: e_ctxt %p, o_ctxt %p", __func__, context, output); + list_for_each(fx_node, &output->effects_list) { + effect_context_t *fx_ctxt = node_to_item(fx_node, + effect_context_t, + output_node); + if (fx_ctxt == context) { + if (context->ops.stop) + context->ops.stop(context, output); + list_remove(&context->output_node); + return; + } + } +} + +bool effects_enabled() +{ + struct listnode *out_node; + + list_for_each(out_node, &active_outputs_list) { + struct listnode *fx_node; + output_context_t *out_ctxt = node_to_item(out_node, + output_context_t, + outputs_list_node); + + list_for_each(fx_node, &out_ctxt->effects_list) { + effect_context_t *fx_ctxt = node_to_item(fx_node, + effect_context_t, + output_node); + if ((fx_ctxt->state == EFFECT_STATE_ACTIVE) && + (fx_ctxt->ops.process != NULL)) + return true; + } + } + return false; +} + + +/* + * Interface from audio HAL + */ +__attribute__ ((visibility ("default"))) +int offload_effects_bundle_hal_start_output(audio_io_handle_t output, int pcm_id) +{ + int ret = 0; + struct listnode *node; + char mixer_string[128]; + output_context_t * out_ctxt = NULL; + + ALOGV("%s output %d pcm_id %d", __func__, output, pcm_id); + + if (lib_init() != 0) + return init_status; + + pthread_mutex_lock(&lock); + if (get_output(output) != NULL) { + ALOGW("%s output already started", __func__); + ret = -ENOSYS; + goto exit; + } + + out_ctxt = (output_context_t *) + malloc(sizeof(output_context_t)); + if (!out_ctxt) { + ALOGE("%s fail to allocate for output context", __func__); + ret = -ENOMEM; + goto exit; + } + out_ctxt->handle = output; + out_ctxt->pcm_device_id = pcm_id; + + /* populate the mixer control to send offload parameters */ + snprintf(mixer_string, sizeof(mixer_string), + "%s %d", "Audio Effects Config", out_ctxt->pcm_device_id); + out_ctxt->mixer = mixer_open(MIXER_CARD); + if (!out_ctxt->mixer) { + ALOGE("Failed to open mixer"); + out_ctxt->ctl = NULL; + ret = -EINVAL; + free(out_ctxt); + goto exit; + } else { + out_ctxt->ctl = mixer_get_ctl_by_name(out_ctxt->mixer, mixer_string); + if (!out_ctxt->ctl) { + ALOGE("mixer_get_ctl_by_name failed"); + mixer_close(out_ctxt->mixer); + out_ctxt->mixer = NULL; + ret = -EINVAL; + free(out_ctxt); + goto exit; + } + } + + list_init(&out_ctxt->effects_list); + + list_for_each(node, &created_effects_list) { + effect_context_t *fx_ctxt = node_to_item(node, + effect_context_t, + effects_list_node); + if (fx_ctxt->out_handle == output) { + if (fx_ctxt->ops.start) + fx_ctxt->ops.start(fx_ctxt, out_ctxt); + list_add_tail(&out_ctxt->effects_list, &fx_ctxt->output_node); + } + } + list_add_tail(&active_outputs_list, &out_ctxt->outputs_list_node); +exit: + pthread_mutex_unlock(&lock); + return ret; +} + +__attribute__ ((visibility ("default"))) +int offload_effects_bundle_hal_stop_output(audio_io_handle_t output, int pcm_id) +{ + int ret; + struct listnode *node; + struct listnode *fx_node; + output_context_t *out_ctxt; + + ALOGV("%s output %d pcm_id %d", __func__, output, pcm_id); + + if (lib_init() != 0) + return init_status; + + pthread_mutex_lock(&lock); + + out_ctxt = get_output(output); + if (out_ctxt == NULL) { + ALOGW("%s output not started", __func__); + ret = -ENOSYS; + goto exit; + } + + if (out_ctxt->mixer) + mixer_close(out_ctxt->mixer); + + list_for_each(fx_node, &out_ctxt->effects_list) { + effect_context_t *fx_ctxt = node_to_item(fx_node, + effect_context_t, + output_node); + if (fx_ctxt->ops.stop) + fx_ctxt->ops.stop(fx_ctxt, out_ctxt); + } + + list_remove(&out_ctxt->outputs_list_node); + + free(out_ctxt); + +exit: + pthread_mutex_unlock(&lock); + return ret; +} + + +/* + * Effect operations + */ +int set_config(effect_context_t *context, effect_config_t *config) +{ + context->config = *config; + + if (context->ops.reset) + context->ops.reset(context); + + return 0; +} + +void get_config(effect_context_t *context, effect_config_t *config) +{ + *config = context->config; +} + + +/* + * Effect Library Interface Implementation + */ +int effect_lib_create(const effect_uuid_t *uuid, + int32_t sessionId, + int32_t ioId, + effect_handle_t *pHandle) { + int ret; + int i; + + ALOGV("%s: sessionId: %d, ioId: %d", __func__, sessionId, ioId); + if (lib_init() != 0) + return init_status; + + if (pHandle == NULL || uuid == NULL) + return -EINVAL; + + for (i = 0; descriptors[i] != NULL; i++) { + if (memcmp(uuid, &descriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) + break; + } + + if (descriptors[i] == NULL) + return -EINVAL; + + effect_context_t *context; + if (memcmp(uuid, &equalizer_descriptor.uuid, + sizeof(effect_uuid_t)) == 0) { + equalizer_context_t *eq_ctxt = (equalizer_context_t *) + calloc(1, sizeof(equalizer_context_t)); + if (eq_ctxt == NULL) { + return -ENOMEM; + } + context = (effect_context_t *)eq_ctxt; + context->ops.init = equalizer_init; + context->ops.reset = equalizer_reset; + context->ops.set_parameter = equalizer_set_parameter; + context->ops.get_parameter = equalizer_get_parameter; + context->ops.set_device = equalizer_set_device; + context->ops.enable = equalizer_enable; + context->ops.disable = equalizer_disable; + context->ops.start = equalizer_start; + context->ops.stop = equalizer_stop; + + context->desc = &equalizer_descriptor; + eq_ctxt->ctl = NULL; + } else if (memcmp(uuid, &bassboost_descriptor.uuid, + sizeof(effect_uuid_t)) == 0) { + bassboost_context_t *bass_ctxt = (bassboost_context_t *) + calloc(1, sizeof(bassboost_context_t)); + if (bass_ctxt == NULL) { + return -ENOMEM; + } + context = (effect_context_t *)bass_ctxt; + context->ops.init = bassboost_init; + context->ops.reset = bassboost_reset; + context->ops.set_parameter = bassboost_set_parameter; + context->ops.get_parameter = bassboost_get_parameter; + context->ops.set_device = bassboost_set_device; + context->ops.enable = bassboost_enable; + context->ops.disable = bassboost_disable; + context->ops.start = bassboost_start; + context->ops.stop = bassboost_stop; + + context->desc = &bassboost_descriptor; + bass_ctxt->ctl = NULL; + } else if (memcmp(uuid, &virtualizer_descriptor.uuid, + sizeof(effect_uuid_t)) == 0) { + virtualizer_context_t *virt_ctxt = (virtualizer_context_t *) + calloc(1, sizeof(virtualizer_context_t)); + if (virt_ctxt == NULL) { + return -ENOMEM; + } + context = (effect_context_t *)virt_ctxt; + context->ops.init = virtualizer_init; + context->ops.reset = virtualizer_reset; + context->ops.set_parameter = virtualizer_set_parameter; + context->ops.get_parameter = virtualizer_get_parameter; + context->ops.set_device = virtualizer_set_device; + context->ops.enable = virtualizer_enable; + context->ops.disable = virtualizer_disable; + context->ops.start = virtualizer_start; + context->ops.stop = virtualizer_stop; + + context->desc = &virtualizer_descriptor; + virt_ctxt->ctl = NULL; + } else if ((memcmp(uuid, &aux_env_reverb_descriptor.uuid, + sizeof(effect_uuid_t)) == 0) || + (memcmp(uuid, &ins_env_reverb_descriptor.uuid, + sizeof(effect_uuid_t)) == 0) || + (memcmp(uuid, &aux_preset_reverb_descriptor.uuid, + sizeof(effect_uuid_t)) == 0) || + (memcmp(uuid, &ins_preset_reverb_descriptor.uuid, + sizeof(effect_uuid_t)) == 0)) { + reverb_context_t *reverb_ctxt = (reverb_context_t *) + calloc(1, sizeof(reverb_context_t)); + if (reverb_ctxt == NULL) { + return -ENOMEM; + } + context = (effect_context_t *)reverb_ctxt; + context->ops.init = reverb_init; + context->ops.reset = reverb_reset; + context->ops.set_parameter = reverb_set_parameter; + context->ops.get_parameter = reverb_get_parameter; + context->ops.set_device = reverb_set_device; + context->ops.enable = reverb_enable; + context->ops.disable = reverb_disable; + context->ops.start = reverb_start; + context->ops.stop = reverb_stop; + + if (memcmp(uuid, &aux_env_reverb_descriptor.uuid, + sizeof(effect_uuid_t)) == 0) { + context->desc = &aux_env_reverb_descriptor; + reverb_auxiliary_init(reverb_ctxt); + } else if (memcmp(uuid, &ins_env_reverb_descriptor.uuid, + sizeof(effect_uuid_t)) == 0) { + context->desc = &ins_env_reverb_descriptor; + reverb_preset_init(reverb_ctxt); + } else if (memcmp(uuid, &aux_preset_reverb_descriptor.uuid, + sizeof(effect_uuid_t)) == 0) { + context->desc = &aux_preset_reverb_descriptor; + reverb_auxiliary_init(reverb_ctxt); + } else if (memcmp(uuid, &ins_preset_reverb_descriptor.uuid, + sizeof(effect_uuid_t)) == 0) { + context->desc = &ins_preset_reverb_descriptor; + reverb_preset_init(reverb_ctxt); + } + reverb_ctxt->ctl = NULL; + } else { + return -EINVAL; + } + + context->itfe = &effect_interface; + context->state = EFFECT_STATE_UNINITIALIZED; + context->out_handle = (audio_io_handle_t)ioId; + + ret = context->ops.init(context); + if (ret < 0) { + ALOGW("%s init failed", __func__); + free(context); + return ret; + } + + context->state = EFFECT_STATE_INITIALIZED; + + pthread_mutex_lock(&lock); + list_add_tail(&created_effects_list, &context->effects_list_node); + output_context_t *out_ctxt = get_output(ioId); + if (out_ctxt != NULL) + add_effect_to_output(out_ctxt, context); + pthread_mutex_unlock(&lock); + + *pHandle = (effect_handle_t)context; + + ALOGV("%s created context %p", __func__, context); + + return 0; + +} + +int effect_lib_release(effect_handle_t handle) +{ + effect_context_t *context = (effect_context_t *)handle; + int status; + + if (lib_init() != 0) + return init_status; + + ALOGV("%s context %p", __func__, handle); + pthread_mutex_lock(&lock); + status = -EINVAL; + if (effect_exists(context)) { + output_context_t *out_ctxt = get_output(context->out_handle); + if (out_ctxt != NULL) + remove_effect_from_output(out_ctxt, context); + list_remove(&context->effects_list_node); + if (context->ops.release) + context->ops.release(context); + free(context); + status = 0; + } + pthread_mutex_unlock(&lock); + + return status; +} + +int effect_lib_get_descriptor(const effect_uuid_t *uuid, + effect_descriptor_t *descriptor) +{ + int i; + + if (lib_init() != 0) + return init_status; + + if (descriptor == NULL || uuid == NULL) { + ALOGV("%s called with NULL pointer", __func__); + return -EINVAL; + } + + for (i = 0; descriptors[i] != NULL; i++) { + if (memcmp(uuid, &descriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) { + *descriptor = *descriptors[i]; + return 0; + } + } + + return -EINVAL; +} + + +/* + * Effect Control Interface Implementation + */ + +/* Stub function for effect interface: never called for offloaded effects */ +int effect_process(effect_handle_t self, + audio_buffer_t *inBuffer __unused, + audio_buffer_t *outBuffer __unused) +{ + effect_context_t * context = (effect_context_t *)self; + int status = 0; + + ALOGW("%s: ctxt %p, Called ?????", __func__, context); + + pthread_mutex_lock(&lock); + if (!effect_exists(context)) { + status = -ENOSYS; + goto exit; + } + + if (context->state != EFFECT_STATE_ACTIVE) { + status = -ENODATA; + goto exit; + } + +exit: + pthread_mutex_unlock(&lock); + return status; +} + +int effect_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize, + void *pCmdData, uint32_t *replySize, void *pReplyData) +{ + + effect_context_t * context = (effect_context_t *)self; + int retsize; + int status = 0; + + pthread_mutex_lock(&lock); + + if (!effect_exists(context)) { + status = -ENOSYS; + goto exit; + } + + ALOGV("%s: ctxt %p, cmd %d", __func__, context, cmdCode); + if (context == NULL || context->state == EFFECT_STATE_UNINITIALIZED) { + status = -ENOSYS; + goto exit; + } + + switch (cmdCode) { + case EFFECT_CMD_INIT: + if (pReplyData == NULL || *replySize != sizeof(int)) { + status = -EINVAL; + goto exit; + } + if (context->ops.init) + *(int *) pReplyData = context->ops.init(context); + else + *(int *) pReplyData = 0; + break; + case EFFECT_CMD_SET_CONFIG: + if (pCmdData == NULL || cmdSize != sizeof(effect_config_t) + || pReplyData == NULL || *replySize != sizeof(int)) { + status = -EINVAL; + goto exit; + } + *(int *) pReplyData = set_config(context, (effect_config_t *) pCmdData); + break; + case EFFECT_CMD_GET_CONFIG: + if (pReplyData == NULL || + *replySize != sizeof(effect_config_t)) { + status = -EINVAL; + goto exit; + } + if (!context->offload_enabled) { + status = -EINVAL; + goto exit; + } + + get_config(context, (effect_config_t *)pReplyData); + break; + case EFFECT_CMD_RESET: + if (context->ops.reset) + context->ops.reset(context); + break; + case EFFECT_CMD_ENABLE: + if (pReplyData == NULL || *replySize != sizeof(int)) { + status = -EINVAL; + goto exit; + } + if (context->state != EFFECT_STATE_INITIALIZED) { + status = -ENOSYS; + goto exit; + } + context->state = EFFECT_STATE_ACTIVE; + if (context->ops.enable) + context->ops.enable(context); + *(int *)pReplyData = 0; + break; + case EFFECT_CMD_DISABLE: + if (pReplyData == NULL || *replySize != sizeof(int)) { + status = -EINVAL; + goto exit; + } + if (context->state != EFFECT_STATE_ACTIVE) { + status = -ENOSYS; + goto exit; + } + context->state = EFFECT_STATE_INITIALIZED; + if (context->ops.disable) + context->ops.disable(context); + *(int *)pReplyData = 0; + break; + case EFFECT_CMD_GET_PARAM: { + if (pCmdData == NULL || + cmdSize < (int)(sizeof(effect_param_t) + sizeof(uint32_t)) || + pReplyData == NULL || + *replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + + sizeof(uint16_t))) { + status = -EINVAL; + ALOGW("EFFECT_CMD_GET_PARAM invalid command cmdSize %d *replySize %d", + cmdSize, *replySize); + goto exit; + } + if (!context->offload_enabled) { + status = -EINVAL; + goto exit; + } + effect_param_t *q = (effect_param_t *)pCmdData; + memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + q->psize); + effect_param_t *p = (effect_param_t *)pReplyData; + if (context->ops.get_parameter) + context->ops.get_parameter(context, p, replySize); + } break; + case EFFECT_CMD_SET_PARAM: { + if (pCmdData == NULL || + cmdSize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + + sizeof(uint16_t)) || + pReplyData == NULL || *replySize != sizeof(int32_t)) { + status = -EINVAL; + ALOGW("EFFECT_CMD_SET_PARAM invalid command cmdSize %d *replySize %d", + cmdSize, *replySize); + goto exit; + } + *(int32_t *)pReplyData = 0; + effect_param_t *p = (effect_param_t *)pCmdData; + if (context->ops.set_parameter) + *(int32_t *)pReplyData = context->ops.set_parameter(context, p, + *replySize); + + } break; + case EFFECT_CMD_SET_DEVICE: { + uint32_t device; + ALOGV("\t EFFECT_CMD_SET_DEVICE start"); + if (pCmdData == NULL || cmdSize < sizeof(uint32_t)) { + status = -EINVAL; + ALOGW("EFFECT_CMD_SET_DEVICE invalid command cmdSize %d", cmdSize); + goto exit; + } + device = *(uint32_t *)pCmdData; + if (context->ops.set_device) + context->ops.set_device(context, device); + } break; + case EFFECT_CMD_SET_VOLUME: + case EFFECT_CMD_SET_AUDIO_MODE: + break; + + case EFFECT_CMD_OFFLOAD: { + output_context_t *out_ctxt; + + if (cmdSize != sizeof(effect_offload_param_t) || pCmdData == NULL + || pReplyData == NULL || *replySize != sizeof(int)) { + ALOGW("%s EFFECT_CMD_OFFLOAD bad format", __func__); + status = -EINVAL; + break; + } + + effect_offload_param_t* offload_param = (effect_offload_param_t*)pCmdData; + + ALOGV("%s EFFECT_CMD_OFFLOAD offload %d output %d", __func__, + offload_param->isOffload, offload_param->ioHandle); + + *(int *)pReplyData = 0; + + context->offload_enabled = offload_param->isOffload; + if (context->out_handle == offload_param->ioHandle) + break; + + out_ctxt = get_output(context->out_handle); + if (out_ctxt != NULL) + remove_effect_from_output(out_ctxt, context); + + context->out_handle = offload_param->ioHandle; + out_ctxt = get_output(context->out_handle); + if (out_ctxt != NULL) + add_effect_to_output(out_ctxt, context); + + } break; + + + default: + if (cmdCode >= EFFECT_CMD_FIRST_PROPRIETARY && context->ops.command) + status = context->ops.command(context, cmdCode, cmdSize, + pCmdData, replySize, pReplyData); + else { + ALOGW("%s invalid command %d", __func__, cmdCode); + status = -EINVAL; + } + break; + } + +exit: + pthread_mutex_unlock(&lock); + + return status; +} + +/* Effect Control Interface Implementation: get_descriptor */ +int effect_get_descriptor(effect_handle_t self, + effect_descriptor_t *descriptor) +{ + effect_context_t *context = (effect_context_t *)self; + + if (!effect_exists(context) || (descriptor == NULL)) + return -EINVAL; + + *descriptor = *context->desc; + + return 0; +} + +bool effect_is_active(effect_context_t * ctxt) { + return ctxt->state == EFFECT_STATE_ACTIVE; +} + +/* effect_handle_t interface implementation for offload effects */ +const struct effect_interface_s effect_interface = { + effect_process, + effect_command, + effect_get_descriptor, + NULL, +}; + +__attribute__ ((visibility ("default"))) +audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { + tag : AUDIO_EFFECT_LIBRARY_TAG, + version : EFFECT_LIBRARY_API_VERSION, + name : "Offload Effects Bundle Library", + implementor : "The Linux Foundation", + create_effect : effect_lib_create, + release_effect : effect_lib_release, + get_descriptor : effect_lib_get_descriptor, +}; diff --git a/audio/post_proc/bundle.h b/audio/post_proc/bundle.h new file mode 100644 index 0000000..cbe7dba --- /dev/null +++ b/audio/post_proc/bundle.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Not a contribution. + * + * Copyright (C) 2013 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. + */ + +#ifndef OFFLOAD_EFFECT_BUNDLE_H +#define OFFLOAD_EFFECT_BUNDLE_H + +#include +#include +#include "effect_api.h" + +/* Retry for delay for mixer open */ +#define RETRY_NUMBER 10 +#define RETRY_US 500000 + +#define MIXER_CARD 0 +#define SOUND_CARD 0 + +extern const struct effect_interface_s effect_interface; + +typedef struct output_context_s output_context_t; +typedef struct effect_ops_s effect_ops_t; +typedef struct effect_context_s effect_context_t; + +struct output_context_s { + /* node in active_outputs_list */ + struct listnode outputs_list_node; + /* io handle */ + audio_io_handle_t handle; + /* list of effects attached to this output */ + struct listnode effects_list; + /* pcm device id */ + int pcm_device_id; + struct mixer *mixer; + struct mixer_ctl *ctl; +}; + +/* effect specific operations. + * Only the init() and process() operations must be defined. + * Others are optional. + */ +struct effect_ops_s { + int (*init)(effect_context_t *context); + int (*release)(effect_context_t *context); + int (*reset)(effect_context_t *context); + int (*enable)(effect_context_t *context); + int (*start)(effect_context_t *context, output_context_t *output); + int (*stop)(effect_context_t *context, output_context_t *output); + int (*disable)(effect_context_t *context); + int (*process)(effect_context_t *context, audio_buffer_t *in, audio_buffer_t *out); + int (*set_parameter)(effect_context_t *context, effect_param_t *param, uint32_t size); + int (*get_parameter)(effect_context_t *context, effect_param_t *param, uint32_t *size); + int (*set_device)(effect_context_t *context, uint32_t device); + int (*command)(effect_context_t *context, uint32_t cmdCode, uint32_t cmdSize, + void *pCmdData, uint32_t *replySize, void *pReplyData); +}; + +struct effect_context_s { + const struct effect_interface_s *itfe; + /* node in created_effects_list */ + struct listnode effects_list_node; + /* node in output_context_t.effects_list */ + struct listnode output_node; + effect_config_t config; + const effect_descriptor_t *desc; + /* io handle of the output the effect is attached to */ + audio_io_handle_t out_handle; + uint32_t state; + bool offload_enabled; + effect_ops_t ops; +}; + +int set_config(effect_context_t *context, effect_config_t *config); + +bool effect_is_active(effect_context_t *context); + +#endif /* OFFLOAD_EFFECT_BUNDLE_H */ diff --git a/audio/post_proc/effect_api.c b/audio/post_proc/effect_api.c new file mode 100644 index 0000000..7474a01 --- /dev/null +++ b/audio/post_proc/effect_api.c @@ -0,0 +1,620 @@ +/* + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "offload_effect_api" +#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 + +#include +#include +#include +#include +#include + +#include "effect_api.h" + +#define ARRAY_SIZE(array) (sizeof array / sizeof array[0]) + +#define OFFLOAD_PRESET_START_OFFSET_FOR_OPENSL 19 +const int map_eq_opensl_preset_2_offload_preset[] = { + OFFLOAD_PRESET_START_OFFSET_FOR_OPENSL, /* Normal Preset */ + OFFLOAD_PRESET_START_OFFSET_FOR_OPENSL+1, /* Classical Preset */ + OFFLOAD_PRESET_START_OFFSET_FOR_OPENSL+2, /* Dance Preset */ + OFFLOAD_PRESET_START_OFFSET_FOR_OPENSL+3, /* Flat Preset */ + OFFLOAD_PRESET_START_OFFSET_FOR_OPENSL+4, /* Folk Preset */ + OFFLOAD_PRESET_START_OFFSET_FOR_OPENSL+5, /* Heavy Metal Preset */ + OFFLOAD_PRESET_START_OFFSET_FOR_OPENSL+6, /* Hip Hop Preset */ + OFFLOAD_PRESET_START_OFFSET_FOR_OPENSL+7, /* Jazz Preset */ + OFFLOAD_PRESET_START_OFFSET_FOR_OPENSL+8, /* Pop Preset */ + OFFLOAD_PRESET_START_OFFSET_FOR_OPENSL+9, /* Rock Preset */ + OFFLOAD_PRESET_START_OFFSET_FOR_OPENSL+10 /* FX Booster */ +}; + +const int map_reverb_opensl_preset_2_offload_preset + [NUM_OSL_REVERB_PRESETS_SUPPORTED][2] = { + {1, 15}, + {2, 16}, + {3, 17}, + {4, 18}, + {5, 3}, + {6, 20} +}; + +int offload_update_mixer_and_effects_ctl(int card, int device_id, + struct mixer *mixer, + struct mixer_ctl *ctl) +{ + char mixer_string[128]; + + snprintf(mixer_string, sizeof(mixer_string), + "%s %d", "Audio Effects Config", device_id); + ALOGV("%s: mixer_string: %s", __func__, mixer_string); + mixer = mixer_open(card); + if (!mixer) { + ALOGE("Failed to open mixer"); + ctl = NULL; + return -EINVAL; + } else { + ctl = mixer_get_ctl_by_name(mixer, mixer_string); + if (!ctl) { + ALOGE("mixer_get_ctl_by_name failed"); + mixer_close(mixer); + mixer = NULL; + return -EINVAL; + } + } + ALOGV("mixer: %p, ctl: %p", mixer, ctl); + return 0; +} + +void offload_close_mixer(struct mixer *mixer) +{ + mixer_close(mixer); +} + +void offload_bassboost_set_device(struct bass_boost_params *bassboost, + uint32_t device) +{ + ALOGVV("%s: device 0x%x", __func__, device); + bassboost->device = device; +} + +void offload_bassboost_set_enable_flag(struct bass_boost_params *bassboost, + bool enable) +{ + ALOGVV("%s: enable=%d", __func__, (int)enable); + bassboost->enable_flag = enable; +} + +int offload_bassboost_get_enable_flag(struct bass_boost_params *bassboost) +{ + ALOGVV("%s: enable=%d", __func__, (int)bassboost->enable_flag); + return bassboost->enable_flag; +} + +void offload_bassboost_set_strength(struct bass_boost_params *bassboost, + int strength) +{ + ALOGVV("%s: strength %d", __func__, strength); + bassboost->strength = strength; +} + +void offload_bassboost_set_mode(struct bass_boost_params *bassboost, + int mode) +{ + ALOGVV("%s: mode %d", __func__, mode); + bassboost->mode = mode; +} + +int offload_bassboost_send_params(struct mixer_ctl *ctl, + struct bass_boost_params bassboost, + unsigned param_send_flags) +{ + int param_values[128] = {0}; + int *p_param_values = param_values; + + ALOGV("%s: flags 0x%x", __func__, param_send_flags); + *p_param_values++ = BASS_BOOST_MODULE; + *p_param_values++ = bassboost.device; + *p_param_values++ = 0; /* num of commands*/ + if (param_send_flags & OFFLOAD_SEND_BASSBOOST_ENABLE_FLAG) { + *p_param_values++ = BASS_BOOST_ENABLE; + *p_param_values++ = CONFIG_SET; + *p_param_values++ = 0; /* start offset if param size if greater than 128 */ + *p_param_values++ = BASS_BOOST_ENABLE_PARAM_LEN; + *p_param_values++ = bassboost.enable_flag; + param_values[2] += 1; + } + if (param_send_flags & OFFLOAD_SEND_BASSBOOST_STRENGTH) { + *p_param_values++ = BASS_BOOST_STRENGTH; + *p_param_values++ = CONFIG_SET; + *p_param_values++ = 0; /* start offset if param size if greater than 128 */ + *p_param_values++ = BASS_BOOST_STRENGTH_PARAM_LEN; + *p_param_values++ = bassboost.strength; + param_values[2] += 1; + } + if (param_send_flags & OFFLOAD_SEND_BASSBOOST_MODE) { + *p_param_values++ = BASS_BOOST_MODE; + *p_param_values++ = CONFIG_SET; + *p_param_values++ = 0; /* start offset if param size if greater than 128 */ + *p_param_values++ = BASS_BOOST_MODE_PARAM_LEN; + *p_param_values++ = bassboost.mode; + param_values[2] += 1; + } + + if (param_values[2] && ctl) + mixer_ctl_set_array(ctl, param_values, ARRAY_SIZE(param_values)); + + return 0; +} + +void offload_virtualizer_set_device(struct virtualizer_params *virtualizer, + uint32_t device) +{ + ALOGVV("%s: device=0x%x", __func__, device); + virtualizer->device = device; +} + +void offload_virtualizer_set_enable_flag(struct virtualizer_params *virtualizer, + bool enable) +{ + ALOGVV("%s: enable=%d", __func__, (int)enable); + virtualizer->enable_flag = enable; +} + +int offload_virtualizer_get_enable_flag(struct virtualizer_params *virtualizer) +{ + ALOGVV("%s: enabled %d", __func__, (int)virtualizer->enable_flag); + return virtualizer->enable_flag; +} + +void offload_virtualizer_set_strength(struct virtualizer_params *virtualizer, + int strength) +{ + ALOGVV("%s: strength %d", __func__, strength); + virtualizer->strength = strength; +} + +void offload_virtualizer_set_out_type(struct virtualizer_params *virtualizer, + int out_type) +{ + ALOGVV("%s: out_type %d", __func__, out_type); + virtualizer->out_type = out_type; +} + +void offload_virtualizer_set_gain_adjust(struct virtualizer_params *virtualizer, + int gain_adjust) +{ + ALOGVV("%s: gain %d", __func__, gain_adjust); + virtualizer->gain_adjust = gain_adjust; +} + +int offload_virtualizer_send_params(struct mixer_ctl *ctl, + struct virtualizer_params virtualizer, + unsigned param_send_flags) +{ + int param_values[128] = {0}; + int *p_param_values = param_values; + + ALOGV("%s: flags 0x%x", __func__, param_send_flags); + *p_param_values++ = VIRTUALIZER_MODULE; + *p_param_values++ = virtualizer.device; + *p_param_values++ = 0; /* num of commands*/ + if (param_send_flags & OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG) { + *p_param_values++ = VIRTUALIZER_ENABLE; + *p_param_values++ = CONFIG_SET; + *p_param_values++ = 0; /* start offset if param size if greater than 128 */ + *p_param_values++ = VIRTUALIZER_ENABLE_PARAM_LEN; + *p_param_values++ = virtualizer.enable_flag; + param_values[2] += 1; + } + if (param_send_flags & OFFLOAD_SEND_VIRTUALIZER_STRENGTH) { + *p_param_values++ = VIRTUALIZER_STRENGTH; + *p_param_values++ = CONFIG_SET; + *p_param_values++ = 0; /* start offset if param size if greater than 128 */ + *p_param_values++ = VIRTUALIZER_STRENGTH_PARAM_LEN; + *p_param_values++ = virtualizer.strength; + param_values[2] += 1; + } + if (param_send_flags & OFFLOAD_SEND_VIRTUALIZER_OUT_TYPE) { + *p_param_values++ = VIRTUALIZER_OUT_TYPE; + *p_param_values++ = CONFIG_SET; + *p_param_values++ = 0; /* start offset if param size if greater than 128 */ + *p_param_values++ = VIRTUALIZER_OUT_TYPE_PARAM_LEN; + *p_param_values++ = virtualizer.out_type; + param_values[2] += 1; + } + if (param_send_flags & OFFLOAD_SEND_VIRTUALIZER_GAIN_ADJUST) { + *p_param_values++ = VIRTUALIZER_GAIN_ADJUST; + *p_param_values++ = CONFIG_SET; + *p_param_values++ = 0; /* start offset if param size if greater than 128 */ + *p_param_values++ = VIRTUALIZER_GAIN_ADJUST_PARAM_LEN; + *p_param_values++ = virtualizer.gain_adjust; + param_values[2] += 1; + } + + if (param_values[2] && ctl) + mixer_ctl_set_array(ctl, param_values, ARRAY_SIZE(param_values)); + + return 0; +} + +void offload_eq_set_device(struct eq_params *eq, uint32_t device) +{ + ALOGVV("%s: device 0x%x", __func__, device); + eq->device = device; +} + +void offload_eq_set_enable_flag(struct eq_params *eq, bool enable) +{ + ALOGVV("%s: enable=%d", __func__, (int)enable); + eq->enable_flag = enable; +} + +int offload_eq_get_enable_flag(struct eq_params *eq) +{ + ALOGVV("%s: enabled=%d", __func__, (int)eq->enable_flag); + return eq->enable_flag; +} + +void offload_eq_set_preset(struct eq_params *eq, int preset) +{ + ALOGVV("%s: preset %d", __func__, preset); + eq->config.preset_id = preset; + eq->config.eq_pregain = Q27_UNITY; +} + +void offload_eq_set_bands_level(struct eq_params *eq, int num_bands, + const uint16_t *band_freq_list, + int *band_gain_list) +{ + int i; + ALOGVV("%s", __func__); + eq->config.num_bands = num_bands; + for (i=0; iper_band_cfg[i].band_idx = i; + eq->per_band_cfg[i].filter_type = EQ_BAND_BOOST; + eq->per_band_cfg[i].freq_millihertz = band_freq_list[i] * 1000; + eq->per_band_cfg[i].gain_millibels = band_gain_list[i] * 100; + eq->per_band_cfg[i].quality_factor = Q8_UNITY; + } +} + +int offload_eq_send_params(struct mixer_ctl *ctl, struct eq_params eq, + unsigned param_send_flags) +{ + int param_values[128] = {0}; + int *p_param_values = param_values; + uint32_t i; + + ALOGV("%s: flags 0x%x", __func__, param_send_flags); + if ((eq.config.preset_id < -1) || + ((param_send_flags & OFFLOAD_SEND_EQ_PRESET) && (eq.config.preset_id == -1))) { + ALOGV("No Valid preset to set"); + return 0; + } + *p_param_values++ = EQ_MODULE; + *p_param_values++ = eq.device; + *p_param_values++ = 0; /* num of commands*/ + if (param_send_flags & OFFLOAD_SEND_EQ_ENABLE_FLAG) { + *p_param_values++ = EQ_ENABLE; + *p_param_values++ = CONFIG_SET; + *p_param_values++ = 0; /* start offset if param size if greater than 128 */ + *p_param_values++ = EQ_ENABLE_PARAM_LEN; + *p_param_values++ = eq.enable_flag; + param_values[2] += 1; + } + if (param_send_flags & OFFLOAD_SEND_EQ_PRESET) { + *p_param_values++ = EQ_CONFIG; + *p_param_values++ = CONFIG_SET; + *p_param_values++ = 0; /* start offset if param size if greater than 128 */ + *p_param_values++ = EQ_CONFIG_PARAM_LEN; + *p_param_values++ = eq.config.eq_pregain; + *p_param_values++ = + map_eq_opensl_preset_2_offload_preset[eq.config.preset_id]; + *p_param_values++ = 0; + param_values[2] += 1; + } + if (param_send_flags & OFFLOAD_SEND_EQ_BANDS_LEVEL) { + *p_param_values++ = EQ_CONFIG; + *p_param_values++ = CONFIG_SET; + *p_param_values++ = 0; /* start offset if param size if greater than 128 */ + *p_param_values++ = EQ_CONFIG_PARAM_LEN + + eq.config.num_bands * EQ_CONFIG_PER_BAND_PARAM_LEN; + *p_param_values++ = eq.config.eq_pregain; + *p_param_values++ = CUSTOM_OPENSL_PRESET; + *p_param_values++ = eq.config.num_bands; + for (i=0; idevice = device; +} + +void offload_reverb_set_enable_flag(struct reverb_params *reverb, bool enable) +{ + ALOGVV("%s: enable=%d", __func__, (int)enable); + reverb->enable_flag = enable; +} + +int offload_reverb_get_enable_flag(struct reverb_params *reverb) +{ + ALOGVV("%s: enabled=%d", __func__, reverb->enable_flag); + return reverb->enable_flag; +} + +void offload_reverb_set_mode(struct reverb_params *reverb, int mode) +{ + ALOGVV("%s", __func__); + reverb->mode = mode; +} + +void offload_reverb_set_preset(struct reverb_params *reverb, int preset) +{ + ALOGVV("%s: preset %d", __func__, preset); + if (preset && (preset <= NUM_OSL_REVERB_PRESETS_SUPPORTED)) + reverb->preset = map_reverb_opensl_preset_2_offload_preset[preset-1][1]; +} + +void offload_reverb_set_wet_mix(struct reverb_params *reverb, int wet_mix) +{ + ALOGVV("%s: wet_mix %d", __func__, wet_mix); + reverb->wet_mix = wet_mix; +} + +void offload_reverb_set_gain_adjust(struct reverb_params *reverb, + int gain_adjust) +{ + ALOGVV("%s: gain %d", __func__, gain_adjust); + reverb->gain_adjust = gain_adjust; +} + +void offload_reverb_set_room_level(struct reverb_params *reverb, int room_level) +{ + ALOGVV("%s: level %d", __func__, room_level); + reverb->room_level = room_level; +} + +void offload_reverb_set_room_hf_level(struct reverb_params *reverb, + int room_hf_level) +{ + ALOGVV("%s: level %d", __func__, room_hf_level); + reverb->room_hf_level = room_hf_level; +} + +void offload_reverb_set_decay_time(struct reverb_params *reverb, int decay_time) +{ + ALOGVV("%s: decay time %d", __func__, decay_time); + reverb->decay_time = decay_time; +} + +void offload_reverb_set_decay_hf_ratio(struct reverb_params *reverb, + int decay_hf_ratio) +{ + ALOGVV("%s: decay_hf_ratio %d", __func__, decay_hf_ratio); + reverb->decay_hf_ratio = decay_hf_ratio; +} + +void offload_reverb_set_reflections_level(struct reverb_params *reverb, + int reflections_level) +{ + ALOGVV("%s: ref level %d", __func__, reflections_level); + reverb->reflections_level = reflections_level; +} + +void offload_reverb_set_reflections_delay(struct reverb_params *reverb, + int reflections_delay) +{ + ALOGVV("%s: ref delay", __func__, reflections_delay); + reverb->reflections_delay = reflections_delay; +} + +void offload_reverb_set_reverb_level(struct reverb_params *reverb, + int reverb_level) +{ + ALOGD("%s: reverb level %d", __func__, reverb_level); + reverb->level = reverb_level; +} + +void offload_reverb_set_delay(struct reverb_params *reverb, int delay) +{ + ALOGVV("%s: delay %d", __func__, delay); + reverb->delay = delay; +} + +void offload_reverb_set_diffusion(struct reverb_params *reverb, int diffusion) +{ + ALOGVV("%s: diffusion %d", __func__, diffusion); + reverb->diffusion = diffusion; +} + +void offload_reverb_set_density(struct reverb_params *reverb, int density) +{ + ALOGVV("%s: density %d", __func__, density); + reverb->density = density; +} + +int offload_reverb_send_params(struct mixer_ctl *ctl, + struct reverb_params reverb, + unsigned param_send_flags) +{ + int param_values[128] = {0}; + int *p_param_values = param_values; + + ALOGV("%s: flags 0x%x", __func__, param_send_flags); + *p_param_values++ = REVERB_MODULE; + *p_param_values++ = reverb.device; + *p_param_values++ = 0; /* num of commands*/ + + if (param_send_flags & OFFLOAD_SEND_REVERB_ENABLE_FLAG) { + *p_param_values++ = REVERB_ENABLE; + *p_param_values++ = CONFIG_SET; + *p_param_values++ = 0; /* start offset if param size if greater than 128 */ + *p_param_values++ = REVERB_ENABLE_PARAM_LEN; + *p_param_values++ = reverb.enable_flag; + param_values[2] += 1; + } + if (param_send_flags & OFFLOAD_SEND_REVERB_MODE) { + *p_param_values++ = REVERB_MODE; + *p_param_values++ = CONFIG_SET; + *p_param_values++ = 0; /* start offset if param size if greater than 128 */ + *p_param_values++ = REVERB_MODE_PARAM_LEN; + *p_param_values++ = reverb.mode; + param_values[2] += 1; + } + if (param_send_flags & OFFLOAD_SEND_REVERB_PRESET) { + *p_param_values++ = REVERB_PRESET; + *p_param_values++ = CONFIG_SET; + *p_param_values++ = 0; /* start offset if param size if greater than 128 */ + *p_param_values++ = REVERB_PRESET_PARAM_LEN; + *p_param_values++ = reverb.preset; + param_values[2] += 1; + } + if (param_send_flags & OFFLOAD_SEND_REVERB_WET_MIX) { + *p_param_values++ = REVERB_WET_MIX; + *p_param_values++ = CONFIG_SET; + *p_param_values++ = 0; /* start offset if param size if greater than 128 */ + *p_param_values++ = REVERB_WET_MIX_PARAM_LEN; + *p_param_values++ = reverb.wet_mix; + param_values[2] += 1; + } + if (param_send_flags & OFFLOAD_SEND_REVERB_GAIN_ADJUST) { + *p_param_values++ = REVERB_GAIN_ADJUST; + *p_param_values++ = CONFIG_SET; + *p_param_values++ = 0; /* start offset if param size if greater than 128 */ + *p_param_values++ = REVERB_GAIN_ADJUST_PARAM_LEN; + *p_param_values++ = reverb.gain_adjust; + param_values[2] += 1; + } + if (param_send_flags & OFFLOAD_SEND_REVERB_ROOM_LEVEL) { + *p_param_values++ = REVERB_ROOM_LEVEL; + *p_param_values++ = CONFIG_SET; + *p_param_values++ = 0; /* start offset if param size if greater than 128 */ + *p_param_values++ = REVERB_ROOM_LEVEL_PARAM_LEN; + *p_param_values++ = reverb.room_level; + param_values[2] += 1; + } + if (param_send_flags & OFFLOAD_SEND_REVERB_ROOM_HF_LEVEL) { + *p_param_values++ = REVERB_ROOM_HF_LEVEL; + *p_param_values++ = CONFIG_SET; + *p_param_values++ = 0; /* start offset if param size if greater than 128 */ + *p_param_values++ = REVERB_ROOM_HF_LEVEL_PARAM_LEN; + *p_param_values++ = reverb.room_hf_level; + param_values[2] += 1; + } + if (param_send_flags & OFFLOAD_SEND_REVERB_DECAY_TIME) { + *p_param_values++ = REVERB_DECAY_TIME; + *p_param_values++ = CONFIG_SET; + *p_param_values++ = 0; /* start offset if param size if greater than 128 */ + *p_param_values++ = REVERB_DECAY_TIME_PARAM_LEN; + *p_param_values++ = reverb.decay_time; + param_values[2] += 1; + } + if (param_send_flags & OFFLOAD_SEND_REVERB_DECAY_HF_RATIO) { + *p_param_values++ = REVERB_DECAY_HF_RATIO; + *p_param_values++ = CONFIG_SET; + *p_param_values++ = 0; /* start offset if param size if greater than 128 */ + *p_param_values++ = REVERB_DECAY_HF_RATIO_PARAM_LEN; + *p_param_values++ = reverb.decay_hf_ratio; + param_values[2] += 1; + } + if (param_send_flags & OFFLOAD_SEND_REVERB_REFLECTIONS_LEVEL) { + *p_param_values++ = REVERB_REFLECTIONS_LEVEL; + *p_param_values++ = CONFIG_SET; + *p_param_values++ = 0; /* start offset if param size if greater than 128 */ + *p_param_values++ = REVERB_REFLECTIONS_LEVEL_PARAM_LEN; + *p_param_values++ = reverb.reflections_level; + param_values[2] += 1; + } + if (param_send_flags & OFFLOAD_SEND_REVERB_REFLECTIONS_DELAY) { + *p_param_values++ = REVERB_REFLECTIONS_DELAY; + *p_param_values++ = CONFIG_SET; + *p_param_values++ = 0; /* start offset if param size if greater than 128 */ + *p_param_values++ = REVERB_REFLECTIONS_DELAY_PARAM_LEN; + *p_param_values++ = reverb.reflections_delay; + param_values[2] += 1; + } + if (param_send_flags & OFFLOAD_SEND_REVERB_LEVEL) { + *p_param_values++ = REVERB_LEVEL; + *p_param_values++ = CONFIG_SET; + *p_param_values++ = 0; /* start offset if param size if greater than 128 */ + *p_param_values++ = REVERB_LEVEL_PARAM_LEN; + *p_param_values++ = reverb.level; + param_values[2] += 1; + } + if (param_send_flags & OFFLOAD_SEND_REVERB_DELAY) { + *p_param_values++ = REVERB_DELAY; + *p_param_values++ = CONFIG_SET; + *p_param_values++ = 0; /* start offset if param size if greater than 128 */ + *p_param_values++ = REVERB_DELAY_PARAM_LEN; + *p_param_values++ = reverb.delay; + param_values[2] += 1; + } + if (param_send_flags & OFFLOAD_SEND_REVERB_DIFFUSION) { + *p_param_values++ = REVERB_DIFFUSION; + *p_param_values++ = CONFIG_SET; + *p_param_values++ = 0; /* start offset if param size if greater than 128 */ + *p_param_values++ = REVERB_DIFFUSION_PARAM_LEN; + *p_param_values++ = reverb.diffusion; + param_values[2] += 1; + } + if (param_send_flags & OFFLOAD_SEND_REVERB_DENSITY) { + *p_param_values++ = REVERB_DENSITY; + *p_param_values++ = CONFIG_SET; + *p_param_values++ = 0; /* start offset if param size if greater than 128 */ + *p_param_values++ = REVERB_DENSITY_PARAM_LEN; + *p_param_values++ = reverb.density; + param_values[2] += 1; + } + + if (param_values[2] && ctl) + mixer_ctl_set_array(ctl, param_values, ARRAY_SIZE(param_values)); + + return 0; +} diff --git a/audio/post_proc/effect_api.h b/audio/post_proc/effect_api.h new file mode 100644 index 0000000..342c606 --- /dev/null +++ b/audio/post_proc/effect_api.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef OFFLOAD_EFFECT_API_H_ +#define OFFLOAD_EFFECT_API_H_ + +int offload_update_mixer_and_effects_ctl(int card, int device_id, + struct mixer *mixer, + struct mixer_ctl *ctl); +void offload_close_mixer(struct mixer *mixer); + +#define OFFLOAD_SEND_BASSBOOST_ENABLE_FLAG (1 << 0) +#define OFFLOAD_SEND_BASSBOOST_STRENGTH \ + (OFFLOAD_SEND_BASSBOOST_ENABLE_FLAG << 1) +#define OFFLOAD_SEND_BASSBOOST_MODE \ + (OFFLOAD_SEND_BASSBOOST_STRENGTH << 1) +void offload_bassboost_set_device(struct bass_boost_params *bassboost, + uint32_t device); +void offload_bassboost_set_enable_flag(struct bass_boost_params *bassboost, + bool enable); +int offload_bassboost_get_enable_flag(struct bass_boost_params *bassboost); +void offload_bassboost_set_strength(struct bass_boost_params *bassboost, + int strength); +void offload_bassboost_set_mode(struct bass_boost_params *bassboost, + int mode); +int offload_bassboost_send_params(struct mixer_ctl *ctl, + struct bass_boost_params bassboost, + unsigned param_send_flags); + +#define OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG (1 << 0) +#define OFFLOAD_SEND_VIRTUALIZER_STRENGTH \ + (OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG << 1) +#define OFFLOAD_SEND_VIRTUALIZER_OUT_TYPE \ + (OFFLOAD_SEND_VIRTUALIZER_STRENGTH << 1) +#define OFFLOAD_SEND_VIRTUALIZER_GAIN_ADJUST \ + (OFFLOAD_SEND_VIRTUALIZER_OUT_TYPE << 1) +void offload_virtualizer_set_device(struct virtualizer_params *virtualizer, + uint32_t device); +void offload_virtualizer_set_enable_flag(struct virtualizer_params *virtualizer, + bool enable); +int offload_virtualizer_get_enable_flag(struct virtualizer_params *virtualizer); +void offload_virtualizer_set_strength(struct virtualizer_params *virtualizer, + int strength); +void offload_virtualizer_set_out_type(struct virtualizer_params *virtualizer, + int out_type); +void offload_virtualizer_set_gain_adjust(struct virtualizer_params *virtualizer, + int gain_adjust); +int offload_virtualizer_send_params(struct mixer_ctl *ctl, + struct virtualizer_params virtualizer, + unsigned param_send_flags); + +#define OFFLOAD_SEND_EQ_ENABLE_FLAG (1 << 0) +#define OFFLOAD_SEND_EQ_PRESET \ + (OFFLOAD_SEND_EQ_ENABLE_FLAG << 1) +#define OFFLOAD_SEND_EQ_BANDS_LEVEL \ + (OFFLOAD_SEND_EQ_PRESET << 1) +void offload_eq_set_device(struct eq_params *eq, uint32_t device); +void offload_eq_set_enable_flag(struct eq_params *eq, bool enable); +int offload_eq_get_enable_flag(struct eq_params *eq); +void offload_eq_set_preset(struct eq_params *eq, int preset); +void offload_eq_set_bands_level(struct eq_params *eq, int num_bands, + const uint16_t *band_freq_list, + int *band_gain_list); +int offload_eq_send_params(struct mixer_ctl *ctl, struct eq_params eq, + unsigned param_send_flags); + +#define OFFLOAD_SEND_REVERB_ENABLE_FLAG (1 << 0) +#define OFFLOAD_SEND_REVERB_MODE \ + (OFFLOAD_SEND_REVERB_ENABLE_FLAG << 1) +#define OFFLOAD_SEND_REVERB_PRESET \ + (OFFLOAD_SEND_REVERB_MODE << 1) +#define OFFLOAD_SEND_REVERB_WET_MIX \ + (OFFLOAD_SEND_REVERB_PRESET << 1) +#define OFFLOAD_SEND_REVERB_GAIN_ADJUST \ + (OFFLOAD_SEND_REVERB_WET_MIX << 1) +#define OFFLOAD_SEND_REVERB_ROOM_LEVEL \ + (OFFLOAD_SEND_REVERB_GAIN_ADJUST << 1) +#define OFFLOAD_SEND_REVERB_ROOM_HF_LEVEL \ + (OFFLOAD_SEND_REVERB_ROOM_LEVEL << 1) +#define OFFLOAD_SEND_REVERB_DECAY_TIME \ + (OFFLOAD_SEND_REVERB_ROOM_HF_LEVEL << 1) +#define OFFLOAD_SEND_REVERB_DECAY_HF_RATIO \ + (OFFLOAD_SEND_REVERB_DECAY_TIME << 1) +#define OFFLOAD_SEND_REVERB_REFLECTIONS_LEVEL \ + (OFFLOAD_SEND_REVERB_DECAY_HF_RATIO << 1) +#define OFFLOAD_SEND_REVERB_REFLECTIONS_DELAY \ + (OFFLOAD_SEND_REVERB_REFLECTIONS_LEVEL << 1) +#define OFFLOAD_SEND_REVERB_LEVEL \ + (OFFLOAD_SEND_REVERB_REFLECTIONS_DELAY << 1) +#define OFFLOAD_SEND_REVERB_DELAY \ + (OFFLOAD_SEND_REVERB_LEVEL << 1) +#define OFFLOAD_SEND_REVERB_DIFFUSION \ + (OFFLOAD_SEND_REVERB_DELAY << 1) +#define OFFLOAD_SEND_REVERB_DENSITY \ + (OFFLOAD_SEND_REVERB_DIFFUSION << 1) +void offload_reverb_set_device(struct reverb_params *reverb, uint32_t device); +void offload_reverb_set_enable_flag(struct reverb_params *reverb, bool enable); +int offload_reverb_get_enable_flag(struct reverb_params *reverb); +void offload_reverb_set_mode(struct reverb_params *reverb, int mode); +void offload_reverb_set_preset(struct reverb_params *reverb, int preset); +void offload_reverb_set_wet_mix(struct reverb_params *reverb, int wet_mix); +void offload_reverb_set_gain_adjust(struct reverb_params *reverb, + int gain_adjust); +void offload_reverb_set_room_level(struct reverb_params *reverb, + int room_level); +void offload_reverb_set_room_hf_level(struct reverb_params *reverb, + int room_hf_level); +void offload_reverb_set_decay_time(struct reverb_params *reverb, + int decay_time); +void offload_reverb_set_decay_hf_ratio(struct reverb_params *reverb, + int decay_hf_ratio); +void offload_reverb_set_reflections_level(struct reverb_params *reverb, + int reflections_level); +void offload_reverb_set_reflections_delay(struct reverb_params *reverb, + int reflections_delay); +void offload_reverb_set_reverb_level(struct reverb_params *reverb, + int reverb_level); +void offload_reverb_set_delay(struct reverb_params *reverb, int delay); +void offload_reverb_set_diffusion(struct reverb_params *reverb, int diffusion); +void offload_reverb_set_density(struct reverb_params *reverb, int density); +int offload_reverb_send_params(struct mixer_ctl *ctl, + struct reverb_params reverb, + unsigned param_send_flags); + +#endif /*OFFLOAD_EFFECT_API_H_*/ diff --git a/audio/post_proc/equalizer.c b/audio/post_proc/equalizer.c new file mode 100644 index 0000000..ce1b462 --- /dev/null +++ b/audio/post_proc/equalizer.c @@ -0,0 +1,497 @@ +/* + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2013 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 "offload_effect_equalizer" +#define LOG_NDEBUG 0 + +#include +#include +#include +#include +#include + +#include "effect_api.h" +#include "equalizer.h" + +/* Offload equalizer UUID: a0dac280-401c-11e3-9379-0002a5d5c51b */ +const effect_descriptor_t equalizer_descriptor = { + {0x0bed4300, 0xddd6, 0x11db, 0x8f34, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // type + {0xa0dac280, 0x401c, 0x11e3, 0x9379, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // uuid + EFFECT_CONTROL_API_VERSION, + (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_HW_ACC_TUNNEL), + 0, /* TODO */ + 1, + "MSM offload equalizer", + "The Android Open Source Project", +}; + +static const char *equalizer_preset_names[] = { + "Normal", + "Classical", + "Dance", + "Flat", + "Folk", + "Heavy Metal", + "Hip Hop", + "Jazz", + "Pop", + "Rock" +}; + +static const uint32_t equalizer_band_freq_range[NUM_EQ_BANDS][2] = { + {30000, 120000}, + {120001, 460000}, + {460001, 1800000}, + {1800001, 7000000}, + {7000001, 20000000}}; + +static const int16_t equalizer_band_presets_level[] = { + 3, 0, 0, 0, 3, /* Normal Preset */ + 5, 3, -2, 4, 4, /* Classical Preset */ + 6, 0, 2, 4, 1, /* Dance Preset */ + 0, 0, 0, 0, 0, /* Flat Preset */ + 3, 0, 0, 2, -1, /* Folk Preset */ + 4, 1, 9, 3, 0, /* Heavy Metal Preset */ + 5, 3, 0, 1, 3, /* Hip Hop Preset */ + 4, 2, -2, 2, 5, /* Jazz Preset */ + -1, 2, 5, 1, -2, /* Pop Preset */ + 5, 3, -1, 3, 5}; /* Rock Preset */ + +const uint16_t equalizer_band_presets_freq[NUM_EQ_BANDS] = { + 60, /* Frequencies in Hz */ + 230, + 910, + 3600, + 14000 +}; + +/* + * Equalizer operations + */ + +int equalizer_get_band_level(equalizer_context_t *context, int32_t band) +{ + ALOGV("%s: ctxt %p, band: %d level: %d", __func__, context, band, + context->band_levels[band] * 100); + return context->band_levels[band] * 100; +} + +int equalizer_set_band_level(equalizer_context_t *context, int32_t band, + int32_t level) +{ + ALOGV("%s: ctxt %p, band: %d, level: %d", __func__, context, band, level); + if (level > 0) { + level = (int)((level+50)/100); + } else { + level = (int)((level-50)/100); + } + context->band_levels[band] = level; + context->preset = PRESET_CUSTOM; + + offload_eq_set_preset(&(context->offload_eq), PRESET_CUSTOM); + offload_eq_set_bands_level(&(context->offload_eq), + NUM_EQ_BANDS, + equalizer_band_presets_freq, + context->band_levels); + if (context->ctl) + offload_eq_send_params(context->ctl, context->offload_eq, + OFFLOAD_SEND_EQ_ENABLE_FLAG | + OFFLOAD_SEND_EQ_BANDS_LEVEL); + return 0; +} + +int equalizer_get_center_frequency(equalizer_context_t *context, int32_t band) +{ + ALOGV("%s: ctxt %p, band: %d", __func__, context, band); + return (equalizer_band_freq_range[band][0] + + equalizer_band_freq_range[band][1]) / 2; +} + +int equalizer_get_band_freq_range(equalizer_context_t *context, int32_t band, + uint32_t *low, uint32_t *high) +{ + ALOGV("%s: ctxt %p, band: %d", __func__, context, band); + *low = equalizer_band_freq_range[band][0]; + *high = equalizer_band_freq_range[band][1]; + return 0; +} + +int equalizer_get_band(equalizer_context_t *context, uint32_t freq) +{ + int i; + + ALOGV("%s: ctxt %p, freq: %d", __func__, context, freq); + for(i = 0; i < NUM_EQ_BANDS; i++) { + if (freq <= equalizer_band_freq_range[i][1]) { + return i; + } + } + return NUM_EQ_BANDS - 1; +} + +int equalizer_get_preset(equalizer_context_t *context) +{ + ALOGV("%s: ctxt %p, preset: %d", __func__, context, context->preset); + return context->preset; +} + +int equalizer_set_preset(equalizer_context_t *context, int preset) +{ + int i; + + ALOGV("%s: ctxt %p, preset: %d", __func__, context, preset); + context->preset = preset; + for (i=0; iband_levels[i] = + equalizer_band_presets_level[i + preset * NUM_EQ_BANDS]; + + offload_eq_set_preset(&(context->offload_eq), preset); + offload_eq_set_bands_level(&(context->offload_eq), + NUM_EQ_BANDS, + equalizer_band_presets_freq, + context->band_levels); + if(context->ctl) + offload_eq_send_params(context->ctl, context->offload_eq, + OFFLOAD_SEND_EQ_ENABLE_FLAG | + OFFLOAD_SEND_EQ_PRESET); + return 0; +} + +const char * equalizer_get_preset_name(equalizer_context_t *context, + int32_t preset) +{ + ALOGV("%s: ctxt %p, preset: %s", __func__, context, + equalizer_preset_names[preset]); + if (preset == PRESET_CUSTOM) { + return "Custom"; + } else { + return equalizer_preset_names[preset]; + } +} + +int equalizer_get_num_presets(equalizer_context_t *context) +{ + ALOGV("%s: ctxt %p, presets_num: %d", __func__, context, + sizeof(equalizer_preset_names)/sizeof(char *)); + return sizeof(equalizer_preset_names)/sizeof(char *); +} + +int equalizer_get_parameter(effect_context_t *context, effect_param_t *p, + uint32_t *size) +{ + equalizer_context_t *eq_ctxt = (equalizer_context_t *)context; + int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t); + int32_t *param_tmp = (int32_t *)p->data; + int32_t param = *param_tmp++; + int32_t param2; + char *name; + void *value = p->data + voffset; + int i; + + ALOGV("%s: ctxt %p, param %d", __func__, eq_ctxt, param); + + p->status = 0; + + switch (param) { + case EQ_PARAM_NUM_BANDS: + case EQ_PARAM_CUR_PRESET: + case EQ_PARAM_GET_NUM_OF_PRESETS: + case EQ_PARAM_BAND_LEVEL: + case EQ_PARAM_GET_BAND: + if (p->vsize < sizeof(int16_t)) + p->status = -EINVAL; + p->vsize = sizeof(int16_t); + break; + + case EQ_PARAM_LEVEL_RANGE: + if (p->vsize < 2 * sizeof(int16_t)) + p->status = -EINVAL; + p->vsize = 2 * sizeof(int16_t); + break; + case EQ_PARAM_BAND_FREQ_RANGE: + if (p->vsize < 2 * sizeof(int32_t)) + p->status = -EINVAL; + p->vsize = 2 * sizeof(int32_t); + break; + + case EQ_PARAM_CENTER_FREQ: + if (p->vsize < sizeof(int32_t)) + p->status = -EINVAL; + p->vsize = sizeof(int32_t); + break; + + case EQ_PARAM_GET_PRESET_NAME: + break; + + case EQ_PARAM_PROPERTIES: + if (p->vsize < (2 + NUM_EQ_BANDS) * sizeof(uint16_t)) + p->status = -EINVAL; + p->vsize = (2 + NUM_EQ_BANDS) * sizeof(uint16_t); + break; + + default: + p->status = -EINVAL; + } + + *size = sizeof(effect_param_t) + voffset + p->vsize; + + if (p->status != 0) + return 0; + + switch (param) { + case EQ_PARAM_NUM_BANDS: + *(uint16_t *)value = (uint16_t)NUM_EQ_BANDS; + break; + + case EQ_PARAM_LEVEL_RANGE: + *(int16_t *)value = -1500; + *((int16_t *)value + 1) = 1500; + break; + + case EQ_PARAM_BAND_LEVEL: + param2 = *param_tmp; + if (param2 >= NUM_EQ_BANDS) { + p->status = -EINVAL; + break; + } + *(int16_t *)value = (int16_t)equalizer_get_band_level(eq_ctxt, param2); + break; + + case EQ_PARAM_CENTER_FREQ: + param2 = *param_tmp; + if (param2 >= NUM_EQ_BANDS) { + p->status = -EINVAL; + break; + } + *(int32_t *)value = equalizer_get_center_frequency(eq_ctxt, param2); + break; + + case EQ_PARAM_BAND_FREQ_RANGE: + param2 = *param_tmp; + if (param2 >= NUM_EQ_BANDS) { + p->status = -EINVAL; + break; + } + equalizer_get_band_freq_range(eq_ctxt, param2, (uint32_t *)value, + ((uint32_t *)value + 1)); + break; + + case EQ_PARAM_GET_BAND: + param2 = *param_tmp; + *(uint16_t *)value = (uint16_t)equalizer_get_band(eq_ctxt, param2); + break; + + case EQ_PARAM_CUR_PRESET: + *(uint16_t *)value = (uint16_t)equalizer_get_preset(eq_ctxt); + break; + + case EQ_PARAM_GET_NUM_OF_PRESETS: + *(uint16_t *)value = (uint16_t)equalizer_get_num_presets(eq_ctxt); + break; + + case EQ_PARAM_GET_PRESET_NAME: + param2 = *param_tmp; + ALOGV("%s: EQ_PARAM_GET_PRESET_NAME: param2: %d", __func__, param2); + if (param2 >= equalizer_get_num_presets(eq_ctxt)) { + p->status = -EINVAL; + break; + } + name = (char *)value; + strlcpy(name, equalizer_get_preset_name(eq_ctxt, param2), p->vsize - 1); + name[p->vsize - 1] = 0; + p->vsize = strlen(name) + 1; + break; + + case EQ_PARAM_PROPERTIES: { + int16_t *prop = (int16_t *)value; + prop[0] = (int16_t)equalizer_get_preset(eq_ctxt); + prop[1] = (int16_t)NUM_EQ_BANDS; + for (i = 0; i < NUM_EQ_BANDS; i++) { + prop[2 + i] = (int16_t)equalizer_get_band_level(eq_ctxt, i); + } + } break; + + default: + p->status = -EINVAL; + break; + } + + return 0; +} + +int equalizer_set_parameter(effect_context_t *context, effect_param_t *p, + uint32_t size __unused) +{ + equalizer_context_t *eq_ctxt = (equalizer_context_t *)context; + int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t); + void *value = p->data + voffset; + int32_t *param_tmp = (int32_t *)p->data; + int32_t param = *param_tmp++; + int32_t preset; + int32_t band; + int32_t level; + int i; + + ALOGV("%s: ctxt %p, param %d", __func__, eq_ctxt, param); + + p->status = 0; + + switch (param) { + case EQ_PARAM_CUR_PRESET: + preset = (int32_t)(*(uint16_t *)value); + + if ((preset >= equalizer_get_num_presets(eq_ctxt)) || (preset < 0)) { + p->status = -EINVAL; + break; + } + equalizer_set_preset(eq_ctxt, preset); + break; + case EQ_PARAM_BAND_LEVEL: + band = *param_tmp; + level = (int32_t)(*(int16_t *)value); + if (band >= NUM_EQ_BANDS) { + p->status = -EINVAL; + break; + } + equalizer_set_band_level(eq_ctxt, band, level); + break; + case EQ_PARAM_PROPERTIES: { + int16_t *prop = (int16_t *)value; + if ((int)prop[0] >= equalizer_get_num_presets(eq_ctxt)) { + p->status = -EINVAL; + break; + } + if (prop[0] >= 0) { + equalizer_set_preset(eq_ctxt, (int)prop[0]); + } else { + if ((int)prop[1] != NUM_EQ_BANDS) { + p->status = -EINVAL; + break; + } + for (i = 0; i < NUM_EQ_BANDS; i++) { + equalizer_set_band_level(eq_ctxt, i, (int)prop[2 + i]); + } + } + } break; + default: + p->status = -EINVAL; + break; + } + + return 0; +} + +int equalizer_set_device(effect_context_t *context, uint32_t device) +{ + ALOGV("%s: ctxt %p, device: 0x%x", __func__, context, device); + equalizer_context_t *eq_ctxt = (equalizer_context_t *)context; + eq_ctxt->device = device; + offload_eq_set_device(&(eq_ctxt->offload_eq), device); + return 0; +} + +int equalizer_reset(effect_context_t *context) +{ + equalizer_context_t *eq_ctxt = (equalizer_context_t *)context; + + return 0; +} + +int equalizer_init(effect_context_t *context) +{ + ALOGV("%s: ctxt %p", __func__, context); + equalizer_context_t *eq_ctxt = (equalizer_context_t *)context; + + context->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ; + context->config.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; + context->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT; + context->config.inputCfg.samplingRate = 44100; + context->config.inputCfg.bufferProvider.getBuffer = NULL; + context->config.inputCfg.bufferProvider.releaseBuffer = NULL; + context->config.inputCfg.bufferProvider.cookie = NULL; + context->config.inputCfg.mask = EFFECT_CONFIG_ALL; + context->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE; + context->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; + context->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT; + context->config.outputCfg.samplingRate = 44100; + context->config.outputCfg.bufferProvider.getBuffer = NULL; + context->config.outputCfg.bufferProvider.releaseBuffer = NULL; + context->config.outputCfg.bufferProvider.cookie = NULL; + context->config.outputCfg.mask = EFFECT_CONFIG_ALL; + + set_config(context, &context->config); + + memset(&(eq_ctxt->offload_eq), 0, sizeof(struct eq_params)); + offload_eq_set_preset(&(eq_ctxt->offload_eq), INVALID_PRESET); + + return 0; +} + +int equalizer_enable(effect_context_t *context) +{ + equalizer_context_t *eq_ctxt = (equalizer_context_t *)context; + + ALOGV("%s: ctxt %p", __func__, context); + + if (!offload_eq_get_enable_flag(&(eq_ctxt->offload_eq))) { + offload_eq_set_enable_flag(&(eq_ctxt->offload_eq), true); + if (eq_ctxt->ctl) + offload_eq_send_params(eq_ctxt->ctl, eq_ctxt->offload_eq, + OFFLOAD_SEND_EQ_ENABLE_FLAG | + OFFLOAD_SEND_EQ_BANDS_LEVEL); + } + return 0; +} + +int equalizer_disable(effect_context_t *context) +{ + equalizer_context_t *eq_ctxt = (equalizer_context_t *)context; + + ALOGV("%s:ctxt %p", __func__, eq_ctxt); + if (offload_eq_get_enable_flag(&(eq_ctxt->offload_eq))) { + offload_eq_set_enable_flag(&(eq_ctxt->offload_eq), false); + if (eq_ctxt->ctl) + offload_eq_send_params(eq_ctxt->ctl, eq_ctxt->offload_eq, + OFFLOAD_SEND_EQ_ENABLE_FLAG); + } + return 0; +} + +int equalizer_start(effect_context_t *context, output_context_t *output) +{ + equalizer_context_t *eq_ctxt = (equalizer_context_t *)context; + + ALOGV("%s: ctxt %p, ctl %p", __func__, eq_ctxt, output->ctl); + eq_ctxt->ctl = output->ctl; + if (offload_eq_get_enable_flag(&(eq_ctxt->offload_eq))) + if (eq_ctxt->ctl) + offload_eq_send_params(eq_ctxt->ctl, eq_ctxt->offload_eq, + OFFLOAD_SEND_EQ_ENABLE_FLAG | + OFFLOAD_SEND_EQ_BANDS_LEVEL); + return 0; +} + +int equalizer_stop(effect_context_t *context, output_context_t *output __unused) +{ + equalizer_context_t *eq_ctxt = (equalizer_context_t *)context; + + ALOGV("%s: ctxt %p", __func__, eq_ctxt); + eq_ctxt->ctl = NULL; + return 0; +} diff --git a/audio/post_proc/equalizer.h b/audio/post_proc/equalizer.h new file mode 100644 index 0000000..19af186 --- /dev/null +++ b/audio/post_proc/equalizer.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2013 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. + */ + +#ifndef OFFLOAD_EQUALIZER_H_ +#define OFFLOAD_EQUALIZER_H_ + +#include "bundle.h" + +#define NUM_EQ_BANDS 5 +#define INVALID_PRESET -2 +#define PRESET_CUSTOM -1 + +extern const effect_descriptor_t equalizer_descriptor; + +typedef struct equalizer_context_s { + effect_context_t common; + + int preset; + int band_levels[NUM_EQ_BANDS]; + + // Offload vars + struct mixer_ctl *ctl; + uint32_t device; + struct eq_params offload_eq; +} equalizer_context_t; + +int equalizer_get_parameter(effect_context_t *context, effect_param_t *p, + uint32_t *size); + +int equalizer_set_parameter(effect_context_t *context, effect_param_t *p, + uint32_t size); + +int equalizer_set_device(effect_context_t *context, uint32_t device); + +int equalizer_reset(effect_context_t *context); + +int equalizer_init(effect_context_t *context); + +int equalizer_enable(effect_context_t *context); + +int equalizer_disable(effect_context_t *context); + +int equalizer_start(effect_context_t *context, output_context_t *output); + +int equalizer_stop(effect_context_t *context, output_context_t *output); + +#endif /*OFFLOAD_EQUALIZER_H_*/ diff --git a/audio/post_proc/reverb.c b/audio/post_proc/reverb.c new file mode 100644 index 0000000..c89039e --- /dev/null +++ b/audio/post_proc/reverb.c @@ -0,0 +1,613 @@ +/* + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2013 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 "offload_effect_reverb" +#define LOG_NDEBUG 0 + +#include +#include +#include +#include +#include +#include + +#include "effect_api.h" +#include "reverb.h" + +/* Offload auxiliary environmental reverb UUID: 79a18026-18fd-4185-8233-0002a5d5c51b */ +const effect_descriptor_t aux_env_reverb_descriptor = { + { 0xc2e5d5f0, 0x94bd, 0x4763, 0x9cac, { 0x4e, 0x23, 0x4d, 0x06, 0x83, 0x9e } }, + { 0x79a18026, 0x18fd, 0x4185, 0x8233, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } }, + EFFECT_CONTROL_API_VERSION, + (EFFECT_FLAG_TYPE_AUXILIARY | EFFECT_FLAG_HW_ACC_TUNNEL), + 0, /* TODO */ + 1, + "MSM offload Auxiliary Environmental Reverb", + "The Android Open Source Project", +}; + +/* Offload insert environmental reverb UUID: eb64ea04-973b-43d2-8f5e-0002a5d5c51b */ +const effect_descriptor_t ins_env_reverb_descriptor = { + {0xc2e5d5f0, 0x94bd, 0x4763, 0x9cac, {0x4e, 0x23, 0x4d, 0x06, 0x83, 0x9e}}, + {0xeb64ea04, 0x973b, 0x43d2, 0x8f5e, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, + EFFECT_CONTROL_API_VERSION, + (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST | EFFECT_FLAG_HW_ACC_TUNNEL), + 0, /* TODO */ + 1, + "MSM offload Insert Environmental Reverb", + "The Android Open Source Project", +}; + +// Offload auxiliary preset reverb UUID: 6987be09-b142-4b41-9056-0002a5d5c51b */ +const effect_descriptor_t aux_preset_reverb_descriptor = { + {0x47382d60, 0xddd8, 0x11db, 0xbf3a, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, + {0x6987be09, 0xb142, 0x4b41, 0x9056, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, + EFFECT_CONTROL_API_VERSION, + (EFFECT_FLAG_TYPE_AUXILIARY | EFFECT_FLAG_HW_ACC_TUNNEL), + 0, /* TODO */ + 1, + "MSM offload Auxiliary Preset Reverb", + "The Android Open Source Project", +}; + +// Offload insert preset reverb UUID: aa2bebf6-47cf-4613-9bca-0002a5d5c51b */ +const effect_descriptor_t ins_preset_reverb_descriptor = { + {0x47382d60, 0xddd8, 0x11db, 0xbf3a, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, + {0xaa2bebf6, 0x47cf, 0x4613, 0x9bca, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, + EFFECT_CONTROL_API_VERSION, + (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST | EFFECT_FLAG_HW_ACC_TUNNEL), + 0, /* TODO */ + 1, + "MSM offload Insert Preset Reverb", + "The Android Open Source Project", +}; + +static const reverb_settings_t reverb_presets[] = { + // REVERB_PRESET_NONE: values are unused + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + // REVERB_PRESET_SMALLROOM + {-400, -600, 1100, 830, -400, 5, 500, 10, 1000, 1000}, + // REVERB_PRESET_MEDIUMROOM + {-400, -600, 1300, 830, -1000, 20, -200, 20, 1000, 1000}, + // REVERB_PRESET_LARGEROOM + {-400, -600, 1500, 830, -1600, 5, -1000, 40, 1000, 1000}, + // REVERB_PRESET_MEDIUMHALL + {-400, -600, 1800, 700, -1300, 15, -800, 30, 1000, 1000}, + // REVERB_PRESET_LARGEHALL + {-400, -600, 1800, 700, -2000, 30, -1400, 60, 1000, 1000}, + // REVERB_PRESET_PLATE + {-400, -200, 1300, 900, 0, 2, 0, 10, 1000, 750}, +}; + + +void reverb_auxiliary_init(reverb_context_t *context) +{ + context->auxiliary = true; + context->preset = false; +} + +void reverb_preset_init(reverb_context_t *context) +{ + context->auxiliary = false; + context->preset = true; + context->cur_preset = REVERB_PRESET_LAST + 1; + context->next_preset = REVERB_DEFAULT_PRESET; +} + +/* + * Reverb operations + */ +int16_t reverb_get_room_level(reverb_context_t *context) +{ + ALOGV("%s: ctxt %p, room level: %d", __func__, context, context->reverb_settings.roomLevel); + return context->reverb_settings.roomLevel; +} + +void reverb_set_room_level(reverb_context_t *context, int16_t room_level) +{ + ALOGV("%s: ctxt %p, room level: %d", __func__, context, room_level); + context->reverb_settings.roomLevel = room_level; + offload_reverb_set_room_level(&(context->offload_reverb), room_level); + if (context->ctl) + offload_reverb_send_params(context->ctl, context->offload_reverb, + OFFLOAD_SEND_REVERB_ENABLE_FLAG | + OFFLOAD_SEND_REVERB_ROOM_LEVEL); +} + +int16_t reverb_get_room_hf_level(reverb_context_t *context) +{ + ALOGV("%s: ctxt %p, room hf level: %d", __func__, context, + context->reverb_settings.roomHFLevel); + return context->reverb_settings.roomHFLevel; +} + +void reverb_set_room_hf_level(reverb_context_t *context, int16_t room_hf_level) +{ + ALOGV("%s: ctxt %p, room hf level: %d", __func__, context, room_hf_level); + context->reverb_settings.roomHFLevel = room_hf_level; + offload_reverb_set_room_hf_level(&(context->offload_reverb), room_hf_level); + if (context->ctl) + offload_reverb_send_params(context->ctl, context->offload_reverb, + OFFLOAD_SEND_REVERB_ENABLE_FLAG | + OFFLOAD_SEND_REVERB_ROOM_HF_LEVEL); +} + +uint32_t reverb_get_decay_time(reverb_context_t *context) +{ + ALOGV("%s: ctxt %p, decay time: %d", __func__, context, + context->reverb_settings.decayTime); + return context->reverb_settings.decayTime; +} + +void reverb_set_decay_time(reverb_context_t *context, uint32_t decay_time) +{ + ALOGV("%s: ctxt %p, decay_time: %d", __func__, context, decay_time); + context->reverb_settings.decayTime = decay_time; + offload_reverb_set_decay_time(&(context->offload_reverb), decay_time); + if (context->ctl) + offload_reverb_send_params(context->ctl, context->offload_reverb, + OFFLOAD_SEND_REVERB_ENABLE_FLAG | + OFFLOAD_SEND_REVERB_DECAY_TIME); +} + +int16_t reverb_get_decay_hf_ratio(reverb_context_t *context) +{ + ALOGV("%s: ctxt %p, decay hf ratio: %d", __func__, context, + context->reverb_settings.decayHFRatio); + return context->reverb_settings.decayHFRatio; +} + +void reverb_set_decay_hf_ratio(reverb_context_t *context, int16_t decay_hf_ratio) +{ + ALOGV("%s: ctxt %p, decay_hf_ratio: %d", __func__, context, decay_hf_ratio); + context->reverb_settings.decayHFRatio = decay_hf_ratio; + offload_reverb_set_decay_hf_ratio(&(context->offload_reverb), decay_hf_ratio); + if (context->ctl) + offload_reverb_send_params(context->ctl, context->offload_reverb, + OFFLOAD_SEND_REVERB_ENABLE_FLAG | + OFFLOAD_SEND_REVERB_DECAY_HF_RATIO); +} + +int16_t reverb_get_reverb_level(reverb_context_t *context) +{ + ALOGV("%s: ctxt %p, reverb level: %d", __func__, context, + context->reverb_settings.reverbLevel); + return context->reverb_settings.reverbLevel; +} + +void reverb_set_reverb_level(reverb_context_t *context, int16_t reverb_level) +{ + ALOGV("%s: ctxt %p, reverb level: %d", __func__, context, reverb_level); + context->reverb_settings.reverbLevel = reverb_level; + offload_reverb_set_reverb_level(&(context->offload_reverb), reverb_level); + if (context->ctl) + offload_reverb_send_params(context->ctl, context->offload_reverb, + OFFLOAD_SEND_REVERB_ENABLE_FLAG | + OFFLOAD_SEND_REVERB_LEVEL); +} + +int16_t reverb_get_diffusion(reverb_context_t *context) +{ + ALOGV("%s: ctxt %p, diffusion: %d", __func__, context, + context->reverb_settings.diffusion); + return context->reverb_settings.diffusion; +} + +void reverb_set_diffusion(reverb_context_t *context, int16_t diffusion) +{ + ALOGV("%s: ctxt %p, diffusion: %d", __func__, context, diffusion); + context->reverb_settings.diffusion = diffusion; + offload_reverb_set_diffusion(&(context->offload_reverb), diffusion); + if (context->ctl) + offload_reverb_send_params(context->ctl, context->offload_reverb, + OFFLOAD_SEND_REVERB_ENABLE_FLAG | + OFFLOAD_SEND_REVERB_DIFFUSION); +} + +int16_t reverb_get_density(reverb_context_t *context) +{ + ALOGV("%s: ctxt %p, density: %d", __func__, context, + context->reverb_settings.density); + return context->reverb_settings.density; +} + +void reverb_set_density(reverb_context_t *context, int16_t density) +{ + ALOGV("%s: ctxt %p, density: %d", __func__, context, density); + context->reverb_settings.density = density; + offload_reverb_set_density(&(context->offload_reverb), density); + if (context->ctl) + offload_reverb_send_params(context->ctl, context->offload_reverb, + OFFLOAD_SEND_REVERB_ENABLE_FLAG | + OFFLOAD_SEND_REVERB_DENSITY); +} + +void reverb_set_preset(reverb_context_t *context, int16_t preset) +{ + bool enable; + ALOGV("%s: ctxt %p, preset: %d", __func__, context, preset); + context->next_preset = preset; + offload_reverb_set_preset(&(context->offload_reverb), preset); + + enable = (preset == REVERB_PRESET_NONE) ? false: true; + offload_reverb_set_enable_flag(&(context->offload_reverb), enable); + + if (context->ctl) + offload_reverb_send_params(context->ctl, context->offload_reverb, + OFFLOAD_SEND_REVERB_ENABLE_FLAG | + OFFLOAD_SEND_REVERB_PRESET); +} + +void reverb_set_all_properties(reverb_context_t *context, + reverb_settings_t *reverb_settings) +{ + ALOGV("%s: ctxt %p", __func__, context); + context->reverb_settings.roomLevel = reverb_settings->roomLevel; + context->reverb_settings.roomHFLevel = reverb_settings->roomHFLevel; + context->reverb_settings.decayTime = reverb_settings->decayTime; + context->reverb_settings.decayHFRatio = reverb_settings->decayHFRatio; + context->reverb_settings.reverbLevel = reverb_settings->reverbLevel; + context->reverb_settings.diffusion = reverb_settings->diffusion; + context->reverb_settings.density = reverb_settings->density; + if (context->ctl) + offload_reverb_send_params(context->ctl, context->offload_reverb, + OFFLOAD_SEND_REVERB_ENABLE_FLAG | + OFFLOAD_SEND_REVERB_ROOM_LEVEL | + OFFLOAD_SEND_REVERB_ROOM_HF_LEVEL | + OFFLOAD_SEND_REVERB_DECAY_TIME | + OFFLOAD_SEND_REVERB_DECAY_HF_RATIO | + OFFLOAD_SEND_REVERB_LEVEL | + OFFLOAD_SEND_REVERB_DIFFUSION | + OFFLOAD_SEND_REVERB_DENSITY); +} + +void reverb_load_preset(reverb_context_t *context) +{ + context->cur_preset = context->next_preset; + + if (context->cur_preset != REVERB_PRESET_NONE) { + const reverb_settings_t *preset = &reverb_presets[context->cur_preset]; + reverb_set_room_level(context, preset->roomLevel); + reverb_set_room_hf_level(context, preset->roomHFLevel); + reverb_set_decay_time(context, preset->decayTime); + reverb_set_decay_hf_ratio(context, preset->decayHFRatio); + reverb_set_reverb_level(context, preset->reverbLevel); + reverb_set_diffusion(context, preset->diffusion); + reverb_set_density(context, preset->density); + } +} + +int reverb_get_parameter(effect_context_t *context, effect_param_t *p, + uint32_t *size) +{ + reverb_context_t *reverb_ctxt = (reverb_context_t *)context; + int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t); + int32_t *param_tmp = (int32_t *)p->data; + int32_t param = *param_tmp++; + void *value = p->data + voffset; + reverb_settings_t *reverb_settings; + int i; + + ALOGV("%s: ctxt %p, param %d", __func__, reverb_ctxt, param); + + p->status = 0; + + if (reverb_ctxt->preset) { + if (param != REVERB_PARAM_PRESET || p->vsize < sizeof(uint16_t)) + return -EINVAL; + *(uint16_t *)value = reverb_ctxt->next_preset; + ALOGV("get REVERB_PARAM_PRESET, preset %d", reverb_ctxt->next_preset); + return 0; + } + switch (param) { + case REVERB_PARAM_ROOM_LEVEL: + if (p->vsize < sizeof(uint16_t)) + p->status = -EINVAL; + p->vsize = sizeof(uint16_t); + break; + case REVERB_PARAM_ROOM_HF_LEVEL: + if (p->vsize < sizeof(uint16_t)) + p->status = -EINVAL; + p->vsize = sizeof(uint16_t); + break; + case REVERB_PARAM_DECAY_TIME: + if (p->vsize < sizeof(uint32_t)) + p->status = -EINVAL; + p->vsize = sizeof(uint32_t); + break; + case REVERB_PARAM_DECAY_HF_RATIO: + if (p->vsize < sizeof(uint16_t)) + p->status = -EINVAL; + p->vsize = sizeof(uint16_t); + break; + case REVERB_PARAM_REFLECTIONS_LEVEL: + if (p->vsize < sizeof(uint16_t)) + p->status = -EINVAL; + p->vsize = sizeof(uint16_t); + break; + case REVERB_PARAM_REFLECTIONS_DELAY: + if (p->vsize < sizeof(uint32_t)) + p->status = -EINVAL; + p->vsize = sizeof(uint32_t); + break; + case REVERB_PARAM_REVERB_LEVEL: + if (p->vsize < sizeof(uint16_t)) + p->status = -EINVAL; + p->vsize = sizeof(uint16_t); + break; + case REVERB_PARAM_REVERB_DELAY: + if (p->vsize < sizeof(uint32_t)) + p->status = -EINVAL; + p->vsize = sizeof(uint32_t); + break; + case REVERB_PARAM_DIFFUSION: + if (p->vsize < sizeof(uint16_t)) + p->status = -EINVAL; + p->vsize = sizeof(uint16_t); + break; + case REVERB_PARAM_DENSITY: + if (p->vsize < sizeof(uint16_t)) + p->status = -EINVAL; + p->vsize = sizeof(uint16_t); + break; + case REVERB_PARAM_PROPERTIES: + if (p->vsize < sizeof(reverb_settings_t)) + p->status = -EINVAL; + p->vsize = sizeof(reverb_settings_t); + break; + default: + p->status = -EINVAL; + } + + *size = sizeof(effect_param_t) + voffset + p->vsize; + + if (p->status != 0) + return 0; + + switch (param) { + case REVERB_PARAM_PROPERTIES: + reverb_settings = (reverb_settings_t *)value; + reverb_settings->roomLevel = reverb_get_room_level(reverb_ctxt); + reverb_settings->roomHFLevel = reverb_get_room_hf_level(reverb_ctxt); + reverb_settings->decayTime = reverb_get_decay_time(reverb_ctxt); + reverb_settings->decayHFRatio = reverb_get_decay_hf_ratio(reverb_ctxt); + reverb_settings->reflectionsLevel = 0; + reverb_settings->reflectionsDelay = 0; + reverb_settings->reverbDelay = 0; + reverb_settings->reverbLevel = reverb_get_reverb_level(reverb_ctxt); + reverb_settings->diffusion = reverb_get_diffusion(reverb_ctxt); + reverb_settings->density = reverb_get_density(reverb_ctxt); + break; + case REVERB_PARAM_ROOM_LEVEL: + *(int16_t *)value = reverb_get_room_level(reverb_ctxt); + break; + case REVERB_PARAM_ROOM_HF_LEVEL: + *(int16_t *)value = reverb_get_room_hf_level(reverb_ctxt); + break; + case REVERB_PARAM_DECAY_TIME: + *(uint32_t *)value = reverb_get_decay_time(reverb_ctxt); + break; + case REVERB_PARAM_DECAY_HF_RATIO: + *(int16_t *)value = reverb_get_decay_hf_ratio(reverb_ctxt); + break; + case REVERB_PARAM_REVERB_LEVEL: + *(int16_t *)value = reverb_get_reverb_level(reverb_ctxt); + break; + case REVERB_PARAM_DIFFUSION: + *(int16_t *)value = reverb_get_diffusion(reverb_ctxt); + break; + case REVERB_PARAM_DENSITY: + *(int16_t *)value = reverb_get_density(reverb_ctxt); + break; + case REVERB_PARAM_REFLECTIONS_LEVEL: + *(uint16_t *)value = 0; + break; + case REVERB_PARAM_REFLECTIONS_DELAY: + *(uint32_t *)value = 0; + break; + case REVERB_PARAM_REVERB_DELAY: + *(uint32_t *)value = 0; + break; + default: + p->status = -EINVAL; + break; + } + + return 0; +} + +int reverb_set_parameter(effect_context_t *context, effect_param_t *p, + uint32_t size __unused) +{ + reverb_context_t *reverb_ctxt = (reverb_context_t *)context; + int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t); + void *value = p->data + voffset; + int32_t *param_tmp = (int32_t *)p->data; + int32_t param = *param_tmp++; + reverb_settings_t *reverb_settings; + int16_t level; + int16_t ratio; + uint32_t time; + + ALOGV("%s: ctxt %p, param %d", __func__, reverb_ctxt, param); + + p->status = 0; + + if (reverb_ctxt->preset) { + if (param != REVERB_PARAM_PRESET) + return -EINVAL; + uint16_t preset = *(uint16_t *)value; + ALOGV("set REVERB_PARAM_PRESET, preset %d", preset); + if (preset > REVERB_PRESET_LAST) { + return -EINVAL; + } + reverb_set_preset(reverb_ctxt, preset); + return 0; + } + switch (param) { + case REVERB_PARAM_PROPERTIES: + reverb_settings = (reverb_settings_t *)value; + break; + case REVERB_PARAM_ROOM_LEVEL: + level = *(int16_t *)value; + reverb_set_room_level(reverb_ctxt, level); + break; + case REVERB_PARAM_ROOM_HF_LEVEL: + level = *(int16_t *)value; + reverb_set_room_hf_level(reverb_ctxt, level); + break; + case REVERB_PARAM_DECAY_TIME: + time = *(uint32_t *)value; + reverb_set_decay_time(reverb_ctxt, time); + break; + case REVERB_PARAM_DECAY_HF_RATIO: + ratio = *(int16_t *)value; + reverb_set_decay_hf_ratio(reverb_ctxt, ratio); + break; + case REVERB_PARAM_REVERB_LEVEL: + level = *(int16_t *)value; + reverb_set_reverb_level(reverb_ctxt, level); + break; + case REVERB_PARAM_DIFFUSION: + ratio = *(int16_t *)value; + reverb_set_diffusion(reverb_ctxt, ratio); + break; + case REVERB_PARAM_DENSITY: + ratio = *(int16_t *)value; + reverb_set_density(reverb_ctxt, ratio); + break; + case REVERB_PARAM_REFLECTIONS_LEVEL: + case REVERB_PARAM_REFLECTIONS_DELAY: + case REVERB_PARAM_REVERB_DELAY: + break; + default: + p->status = -EINVAL; + break; + } + + return 0; +} + +int reverb_set_device(effect_context_t *context, uint32_t device) +{ + reverb_context_t *reverb_ctxt = (reverb_context_t *)context; + + ALOGV("%s: ctxt %p, device: 0x%x", __func__, reverb_ctxt, device); + reverb_ctxt->device = device; + offload_reverb_set_device(&(reverb_ctxt->offload_reverb), device); + return 0; +} + +int reverb_reset(effect_context_t *context) +{ + reverb_context_t *reverb_ctxt = (reverb_context_t *)context; + + return 0; +} + +int reverb_init(effect_context_t *context) +{ + reverb_context_t *reverb_ctxt = (reverb_context_t *)context; + + ALOGV("%s: ctxt %p", __func__, reverb_ctxt); + context->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ; + /* + FIXME: channel mode is mono for auxiliary. is it needed for offload ? + If so, this set config needs to be updated accordingly + */ + context->config.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; + context->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT; + context->config.inputCfg.samplingRate = 44100; + context->config.inputCfg.bufferProvider.getBuffer = NULL; + context->config.inputCfg.bufferProvider.releaseBuffer = NULL; + context->config.inputCfg.bufferProvider.cookie = NULL; + context->config.inputCfg.mask = EFFECT_CONFIG_ALL; + context->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE; + context->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; + context->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT; + context->config.outputCfg.samplingRate = 44100; + context->config.outputCfg.bufferProvider.getBuffer = NULL; + context->config.outputCfg.bufferProvider.releaseBuffer = NULL; + context->config.outputCfg.bufferProvider.cookie = NULL; + context->config.outputCfg.mask = EFFECT_CONFIG_ALL; + + set_config(context, &context->config); + + memset(&(reverb_ctxt->reverb_settings), 0, sizeof(reverb_settings_t)); + memset(&(reverb_ctxt->offload_reverb), 0, sizeof(struct reverb_params)); + + if (reverb_ctxt->preset && + reverb_ctxt->next_preset != reverb_ctxt->cur_preset) + reverb_load_preset(reverb_ctxt); + + return 0; +} + +int reverb_enable(effect_context_t *context) +{ + reverb_context_t *reverb_ctxt = (reverb_context_t *)context; + + ALOGV("%s: ctxt %p", __func__, reverb_ctxt); + + if (!offload_reverb_get_enable_flag(&(reverb_ctxt->offload_reverb))) + offload_reverb_set_enable_flag(&(reverb_ctxt->offload_reverb), true); + return 0; +} + +int reverb_disable(effect_context_t *context) +{ + reverb_context_t *reverb_ctxt = (reverb_context_t *)context; + + ALOGV("%s: ctxt %p", __func__, reverb_ctxt); + if (offload_reverb_get_enable_flag(&(reverb_ctxt->offload_reverb))) { + offload_reverb_set_enable_flag(&(reverb_ctxt->offload_reverb), false); + if (reverb_ctxt->ctl) + offload_reverb_send_params(reverb_ctxt->ctl, + reverb_ctxt->offload_reverb, + OFFLOAD_SEND_REVERB_ENABLE_FLAG); + } + return 0; +} + +int reverb_start(effect_context_t *context, output_context_t *output) +{ + reverb_context_t *reverb_ctxt = (reverb_context_t *)context; + + ALOGV("%s: ctxt %p, ctl %p", __func__, reverb_ctxt, output->ctl); + reverb_ctxt->ctl = output->ctl; + if (offload_reverb_get_enable_flag(&(reverb_ctxt->offload_reverb))) { + if (reverb_ctxt->ctl && reverb_ctxt->preset) { + offload_reverb_send_params(reverb_ctxt->ctl, reverb_ctxt->offload_reverb, + OFFLOAD_SEND_REVERB_ENABLE_FLAG | + OFFLOAD_SEND_REVERB_PRESET); + } + } + + return 0; +} + +int reverb_stop(effect_context_t *context, output_context_t *output __unused) +{ + reverb_context_t *reverb_ctxt = (reverb_context_t *)context; + + ALOGV("%s: ctxt %p", __func__, reverb_ctxt); + reverb_ctxt->ctl = NULL; + return 0; +} + diff --git a/audio/post_proc/reverb.h b/audio/post_proc/reverb.h new file mode 100644 index 0000000..63192eb --- /dev/null +++ b/audio/post_proc/reverb.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2013 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. + */ + +#ifndef OFFLOAD_REVERB_H_ +#define OFFLOAD_REVERB_H_ + +#include "bundle.h" + +#define REVERB_DEFAULT_PRESET REVERB_PRESET_NONE + +extern const effect_descriptor_t aux_env_reverb_descriptor; +extern const effect_descriptor_t ins_env_reverb_descriptor; +extern const effect_descriptor_t aux_preset_reverb_descriptor; +extern const effect_descriptor_t ins_preset_reverb_descriptor; + +typedef struct reverb_settings_s { + int16_t roomLevel; + int16_t roomHFLevel; + uint32_t decayTime; + int16_t decayHFRatio; + int16_t reflectionsLevel; + uint32_t reflectionsDelay; + int16_t reverbLevel; + uint32_t reverbDelay; + int16_t diffusion; + int16_t density; +} reverb_settings_t; + +typedef struct reverb_context_s { + effect_context_t common; + + // Offload vars + struct mixer_ctl *ctl; + bool auxiliary; + bool preset; + uint16_t cur_preset; + uint16_t next_preset; + reverb_settings_t reverb_settings; + uint32_t device; + struct reverb_params offload_reverb; +} reverb_context_t; + + +void reverb_auxiliary_init(reverb_context_t *context); + +void reverb_preset_init(reverb_context_t *context); + +int reverb_get_parameter(effect_context_t *context, effect_param_t *p, + uint32_t *size); + +int reverb_set_parameter(effect_context_t *context, effect_param_t *p, + uint32_t size); + +int reverb_set_device(effect_context_t *context, uint32_t device); + +int reverb_reset(effect_context_t *context); + +int reverb_init(effect_context_t *context); + +int reverb_enable(effect_context_t *context); + +int reverb_disable(effect_context_t *context); + +int reverb_start(effect_context_t *context, output_context_t *output); + +int reverb_stop(effect_context_t *context, output_context_t *output); + +#endif /* OFFLOAD_REVERB_H_ */ diff --git a/audio/post_proc/virtualizer.c b/audio/post_proc/virtualizer.c new file mode 100644 index 0000000..dcddfe7 --- /dev/null +++ b/audio/post_proc/virtualizer.c @@ -0,0 +1,503 @@ +/* + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2013 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 "offload_effect_virtualizer" +#define LOG_NDEBUG 0 + +#include +#include +#include +#include +#include + +#include "effect_api.h" +#include "virtualizer.h" + +/* Offload Virtualizer UUID: 509a4498-561a-4bea-b3b1-0002a5d5c51b */ +const effect_descriptor_t virtualizer_descriptor = { + {0x37cc2c00, 0xdddd, 0x11db, 0x8577, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, + {0x509a4498, 0x561a, 0x4bea, 0xb3b1, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // uuid + EFFECT_CONTROL_API_VERSION, + (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_DEVICE_IND | EFFECT_FLAG_HW_ACC_TUNNEL), + 0, /* TODO */ + 1, + "MSM offload virtualizer", + "The Android Open Source Project", +}; + +/* + * Virtualizer operations + */ + +int virtualizer_get_strength(virtualizer_context_t *context) +{ + ALOGV("%s: ctxt %p, strength: %d", __func__, context, context->strength); + return context->strength; +} + +int virtualizer_set_strength(virtualizer_context_t *context, uint32_t strength) +{ + ALOGV("%s: ctxt %p, strength: %d", __func__, context, strength); + context->strength = strength; + + offload_virtualizer_set_strength(&(context->offload_virt), strength); + if (context->ctl) + offload_virtualizer_send_params(context->ctl, context->offload_virt, + OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG | + OFFLOAD_SEND_VIRTUALIZER_STRENGTH); + return 0; +} + +/* + * Check if an audio device is supported by this implementation + * + * [in] + * device device that is intented for processing (e.g. for binaural vs transaural) + * [out] + * false device is not applicable for effect + * true device is applicable for effect + */ +bool virtualizer_is_device_supported(audio_devices_t device) { + switch (device) { + case AUDIO_DEVICE_OUT_SPEAKER: + case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT: + case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER: +#ifdef AFE_PROXY_ENABLED + case AUDIO_DEVICE_OUT_PROXY: +#endif + case AUDIO_DEVICE_OUT_AUX_DIGITAL: + case AUDIO_DEVICE_OUT_USB_ACCESSORY: + case AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET: + return false; + default : + return true; + } +} + +/* + * Check if a channel mask + audio device is supported by this implementation + * + * [in] + * channel_mask channel mask of input buffer + * device device that is intented for processing (e.g. for binaural vs transaural) + * [out] + * false if the configuration is not supported or it is unknown + * true if the configuration is supported + */ +bool virtualizer_is_configuration_supported(audio_channel_mask_t channel_mask, + audio_devices_t device) { + uint32_t channelCount = audio_channel_count_from_out_mask(channel_mask); + if ((channelCount == 0) || (channelCount > 2)) { + return false; + } + + return virtualizer_is_device_supported(device); +} + +/* + * Force the virtualization mode to that of the given audio device + * + * [in] + * context effect engine context + * forced_device device whose virtualization mode we'll always use + * [out] + * -EINVAL if the device is not supported or is unknown + * 0 if the device is supported and the virtualization mode forced + */ +int virtualizer_force_virtualization_mode(virtualizer_context_t *context, + audio_devices_t forced_device) { + virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context; + int status = 0; + bool use_virt = false; + int is_virt_enabled = + offload_virtualizer_get_enable_flag(&(virt_ctxt->offload_virt)); + + ALOGV("%s: ctxt %p, forcedDev=0x%x enabled=%d tmpDisabled=%d", __func__, virt_ctxt, + forced_device, is_virt_enabled, virt_ctxt->temp_disabled); + + if (virtualizer_is_device_supported(forced_device) == false) { + if (forced_device != AUDIO_DEVICE_NONE) { + //forced device is not supported, make it behave as a reset of forced mode + forced_device = AUDIO_DEVICE_NONE; + // but return an error + status = -EINVAL; + } + } + + if (forced_device == AUDIO_DEVICE_NONE) { + // disabling forced virtualization mode: + // verify whether the virtualization should be enabled or disabled + if (virtualizer_is_device_supported(virt_ctxt->device)) { + use_virt = (is_virt_enabled == true); + } + virt_ctxt->forced_device = AUDIO_DEVICE_NONE; + } else { + // forcing virtualization mode: + // TODO: we assume device is supported, so hard coded a fixed one. + virt_ctxt->forced_device = AUDIO_DEVICE_OUT_WIRED_HEADPHONE; + // TODO: only enable for a supported mode, when the effect is enabled + use_virt = (is_virt_enabled == true); + } + + if (use_virt) { + if (virt_ctxt->temp_disabled == true) { + if (effect_is_active(&virt_ctxt->common)) { + offload_virtualizer_set_enable_flag(&(virt_ctxt->offload_virt), true); + if (virt_ctxt->ctl) + offload_virtualizer_send_params(virt_ctxt->ctl, + virt_ctxt->offload_virt, + OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG); + } + ALOGV("%s: re-enable VIRTUALIZER", __func__); + virt_ctxt->temp_disabled = false; + } else { + ALOGV("%s: leaving VIRTUALIZER enabled", __func__); + } + } else { + if (virt_ctxt->temp_disabled == false) { + if (effect_is_active(&virt_ctxt->common)) { + offload_virtualizer_set_enable_flag(&(virt_ctxt->offload_virt), false); + if (virt_ctxt->ctl) + offload_virtualizer_send_params(virt_ctxt->ctl, + virt_ctxt->offload_virt, + OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG); + } + ALOGV("%s: disable VIRTUALIZER", __func__); + virt_ctxt->temp_disabled = true; + } else { + ALOGV("%s: leaving VIRTUALIZER disabled", __func__); + } + } + + ALOGV("after %s: ctxt %p, enabled=%d tmpDisabled=%d", __func__, virt_ctxt, + is_virt_enabled, virt_ctxt->temp_disabled); + + return status; +} + +/* + * Get the virtual speaker angles for a channel mask + audio device configuration + * which is guaranteed to be supported by this implementation + * + * [in] + * channel_mask the channel mask of the input to virtualize + * device the type of device that affects the processing (e.g. for binaural vs transaural) + * [in/out] + * speaker_angles the array of integer where each speaker angle is written as a triplet in the + * following format: + * int32_t a bit mask with a single value selected for each speaker, following + * the convention of the audio_channel_mask_t type + * int32_t a value in degrees expressing the speaker azimuth, where 0 is in front + * of the user, 180 behind, -90 to the left, 90 to the right of the user + * int32_t a value in degrees expressing the speaker elevation, where 0 is the + * horizontal plane, +90 is directly above the user, -90 below + * + */ +void virtualizer_get_speaker_angles(audio_channel_mask_t channel_mask __unused, + audio_devices_t device __unused, int32_t *speaker_angles) { + // the channel count is guaranteed to be 1 or 2 + // the device is guaranteed to be of type headphone + // this virtualizer is always 2in with speakers at -90 and 90deg of azimuth, 0deg of elevation + *speaker_angles++ = (int32_t) AUDIO_CHANNEL_OUT_FRONT_LEFT; + *speaker_angles++ = -90; // azimuth + *speaker_angles++ = 0; // elevation + *speaker_angles++ = (int32_t) AUDIO_CHANNEL_OUT_FRONT_RIGHT; + *speaker_angles++ = 90; // azimuth + *speaker_angles = 0; // elevation +} + +/* + * Retrieve the current device whose processing mode is used by this effect + * + * [out] + * AUDIO_DEVICE_NONE if the effect is not virtualizing + * or the device type if the effect is virtualizing + */ +audio_devices_t virtualizer_get_virtualization_mode(virtualizer_context_t *context) { + virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context; + audio_devices_t device = AUDIO_DEVICE_NONE; + + if ((offload_virtualizer_get_enable_flag(&(virt_ctxt->offload_virt))) + && (virt_ctxt->temp_disabled == false)) { + if (virt_ctxt->forced_device != AUDIO_DEVICE_NONE) { + // virtualization mode is forced, return that device + device = virt_ctxt->forced_device; + } else { + // no forced mode, return the current device + device = virt_ctxt->device; + } + } + ALOGV("%s: returning 0x%x", __func__, device); + return device; +} + +int virtualizer_get_parameter(effect_context_t *context, effect_param_t *p, + uint32_t *size) +{ + virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context; + int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t); + int32_t *param_tmp = (int32_t *)p->data; + int32_t param = *param_tmp++; + void *value = p->data + voffset; + int i; + + ALOGV("%s: ctxt %p, param %d", __func__, virt_ctxt, param); + + p->status = 0; + + switch (param) { + case VIRTUALIZER_PARAM_STRENGTH_SUPPORTED: + if (p->vsize < sizeof(uint32_t)) + p->status = -EINVAL; + p->vsize = sizeof(uint32_t); + break; + case VIRTUALIZER_PARAM_STRENGTH: + if (p->vsize < sizeof(int16_t)) + p->status = -EINVAL; + p->vsize = sizeof(int16_t); + break; + case VIRTUALIZER_PARAM_VIRTUAL_SPEAKER_ANGLES: + // return value size can only be interpreted as relative to input value, + // deferring validity check to below + break; + case VIRTUALIZER_PARAM_VIRTUALIZATION_MODE: + if (p->vsize != sizeof(uint32_t)) + p->status = -EINVAL; + p->vsize = sizeof(uint32_t); + break; + default: + p->status = -EINVAL; + } + + *size = sizeof(effect_param_t) + voffset + p->vsize; + + if (p->status != 0) + return 0; + + switch (param) { + case VIRTUALIZER_PARAM_STRENGTH_SUPPORTED: + *(uint32_t *)value = 1; + break; + + case VIRTUALIZER_PARAM_STRENGTH: + *(int16_t *)value = virtualizer_get_strength(virt_ctxt); + break; + + case VIRTUALIZER_PARAM_VIRTUAL_SPEAKER_ANGLES: + { + const audio_channel_mask_t channel_mask = (audio_channel_mask_t) *param_tmp++; + const audio_devices_t device = (audio_devices_t) *param_tmp; + uint32_t channel_cnt = audio_channel_count_from_out_mask(channel_mask); + + if (p->vsize < 3 * channel_cnt * sizeof(int32_t)){ + p->status = -EINVAL; + break; + } + // verify the configuration is supported + if(virtualizer_is_configuration_supported(channel_mask, device)) { + // configuration is supported, get the angles + virtualizer_get_speaker_angles(channel_mask, device, (int32_t *)value); + } else { + p->status = -EINVAL; + } + + break; + } + + case VIRTUALIZER_PARAM_VIRTUALIZATION_MODE: + *(uint32_t *)value = (uint32_t) virtualizer_get_virtualization_mode(virt_ctxt); + break; + + default: + p->status = -EINVAL; + break; + } + + return 0; +} + +int virtualizer_set_parameter(effect_context_t *context, effect_param_t *p, + uint32_t size __unused) +{ + virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context; + int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t); + void *value = p->data + voffset; + int32_t *param_tmp = (int32_t *)p->data; + int32_t param = *param_tmp++; + uint32_t strength; + + ALOGV("%s: ctxt %p, param %d", __func__, virt_ctxt, param); + + p->status = 0; + + switch (param) { + case VIRTUALIZER_PARAM_STRENGTH: + strength = (uint32_t)(*(int16_t *)value); + virtualizer_set_strength(virt_ctxt, strength); + break; + case VIRTUALIZER_PARAM_FORCE_VIRTUALIZATION_MODE: + { + const audio_devices_t device = *(audio_devices_t *)value; + if (0 != virtualizer_force_virtualization_mode(virt_ctxt, device)) { + p->status = -EINVAL; + } + break; + } + default: + p->status = -EINVAL; + break; + } + + return 0; +} + +int virtualizer_set_device(effect_context_t *context, uint32_t device) +{ + virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context; + + ALOGV("%s: ctxt %p, device: 0x%x", __func__, virt_ctxt, device); + virt_ctxt->device = device; + + if (virt_ctxt->forced_device == AUDIO_DEVICE_NONE) { + // default case unless configuration is forced + if (virtualizer_is_device_supported(device) == false) { + if (!virt_ctxt->temp_disabled) { + if (effect_is_active(&virt_ctxt->common)) { + offload_virtualizer_set_enable_flag(&(virt_ctxt->offload_virt), false); + if (virt_ctxt->ctl) + offload_virtualizer_send_params(virt_ctxt->ctl, + virt_ctxt->offload_virt, + OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG); + } + ALOGI("%s: ctxt %p, disabled based on device", __func__, virt_ctxt); + virt_ctxt->temp_disabled = true; + } + } else { + if (virt_ctxt->temp_disabled) { + if (effect_is_active(&virt_ctxt->common)) { + offload_virtualizer_set_enable_flag(&(virt_ctxt->offload_virt), true); + if (virt_ctxt->ctl) + offload_virtualizer_send_params(virt_ctxt->ctl, + virt_ctxt->offload_virt, + OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG); + } + virt_ctxt->temp_disabled = false; + } + } + } + // else virtualization mode is forced to a certain device, nothing to do + + offload_virtualizer_set_device(&(virt_ctxt->offload_virt), device); + return 0; +} + +int virtualizer_reset(effect_context_t *context) +{ + virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context; + + return 0; +} + +int virtualizer_init(effect_context_t *context) +{ + ALOGV("%s: ctxt %p", __func__, context); + virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context; + + context->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ; + context->config.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; + context->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT; + context->config.inputCfg.samplingRate = 44100; + context->config.inputCfg.bufferProvider.getBuffer = NULL; + context->config.inputCfg.bufferProvider.releaseBuffer = NULL; + context->config.inputCfg.bufferProvider.cookie = NULL; + context->config.inputCfg.mask = EFFECT_CONFIG_ALL; + context->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE; + context->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; + context->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT; + context->config.outputCfg.samplingRate = 44100; + context->config.outputCfg.bufferProvider.getBuffer = NULL; + context->config.outputCfg.bufferProvider.releaseBuffer = NULL; + context->config.outputCfg.bufferProvider.cookie = NULL; + context->config.outputCfg.mask = EFFECT_CONFIG_ALL; + + set_config(context, &context->config); + + virt_ctxt->temp_disabled = false; + virt_ctxt->forced_device = AUDIO_DEVICE_NONE; + virt_ctxt->device = AUDIO_DEVICE_NONE; + memset(&(virt_ctxt->offload_virt), 0, sizeof(struct virtualizer_params)); + + return 0; +} + +int virtualizer_enable(effect_context_t *context) +{ + virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context; + + ALOGV("%s: ctxt %p, strength %d", __func__, virt_ctxt, virt_ctxt->strength); + + if (!offload_virtualizer_get_enable_flag(&(virt_ctxt->offload_virt)) && + !(virt_ctxt->temp_disabled)) { + offload_virtualizer_set_enable_flag(&(virt_ctxt->offload_virt), true); + if (virt_ctxt->ctl && virt_ctxt->strength) + offload_virtualizer_send_params(virt_ctxt->ctl, + virt_ctxt->offload_virt, + OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG | + OFFLOAD_SEND_BASSBOOST_STRENGTH); + } + return 0; +} + +int virtualizer_disable(effect_context_t *context) +{ + virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context; + + ALOGV("%s: ctxt %p", __func__, virt_ctxt); + if (offload_virtualizer_get_enable_flag(&(virt_ctxt->offload_virt))) { + offload_virtualizer_set_enable_flag(&(virt_ctxt->offload_virt), false); + if (virt_ctxt->ctl) + offload_virtualizer_send_params(virt_ctxt->ctl, + virt_ctxt->offload_virt, + OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG); + } + return 0; +} + +int virtualizer_start(effect_context_t *context, output_context_t *output) +{ + virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context; + + ALOGV("%s: ctxt %p, ctl %p", __func__, virt_ctxt, output->ctl); + virt_ctxt->ctl = output->ctl; + if (offload_virtualizer_get_enable_flag(&(virt_ctxt->offload_virt))) + if (virt_ctxt->ctl) + offload_virtualizer_send_params(virt_ctxt->ctl, virt_ctxt->offload_virt, + OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG | + OFFLOAD_SEND_VIRTUALIZER_STRENGTH); + return 0; +} + +int virtualizer_stop(effect_context_t *context, output_context_t *output __unused) +{ + virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context; + + ALOGV("%s: ctxt %p", __func__, virt_ctxt); + virt_ctxt->ctl = NULL; + return 0; +} diff --git a/audio/post_proc/virtualizer.h b/audio/post_proc/virtualizer.h new file mode 100644 index 0000000..21bc254 --- /dev/null +++ b/audio/post_proc/virtualizer.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2013 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. + */ + +#ifndef OFFLOAD_VIRTUALIZER_H_ +#define OFFLOAD_VIRTUALIZER_H_ + +#include "bundle.h" + +extern const effect_descriptor_t virtualizer_descriptor; + +typedef struct virtualizer_context_s { + effect_context_t common; + + int strength; + + // Offload vars + struct mixer_ctl *ctl; + bool temp_disabled; + audio_devices_t forced_device; + audio_devices_t device; + struct virtualizer_params offload_virt; +} virtualizer_context_t; + +int virtualizer_get_parameter(effect_context_t *context, effect_param_t *p, + uint32_t *size); + +int virtualizer_set_parameter(effect_context_t *context, effect_param_t *p, + uint32_t size); + +int virtualizer_set_device(effect_context_t *context, uint32_t device); + +int virtualizer_reset(effect_context_t *context); + +int virtualizer_init(effect_context_t *context); + +int virtualizer_enable(effect_context_t *context); + +int virtualizer_disable(effect_context_t *context); + +int virtualizer_start(effect_context_t *context, output_context_t *output); + +int virtualizer_stop(effect_context_t *context, output_context_t *output); + +#endif /* OFFLOAD_VIRTUALIZER_H_ */ diff --git a/audio/visualizer/Android.mk b/audio/visualizer/Android.mk new file mode 100644 index 0000000..393eec3 --- /dev/null +++ b/audio/visualizer/Android.mk @@ -0,0 +1,36 @@ +# Copyright 2013 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. + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + offload_visualizer.c + +LOCAL_CFLAGS+= -O2 -fvisibility=hidden + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + liblog \ + libtinyalsa + +LOCAL_MODULE_RELATIVE_PATH := soundfx +LOCAL_MODULE:= libqcomvisualizer + +LOCAL_C_INCLUDES := \ + external/tinyalsa/include \ + $(call include-path-for, audio-effects) + +include $(BUILD_SHARED_LIBRARY) diff --git a/audio/visualizer/MODULE_LICENSE_APACHE2 b/audio/visualizer/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 diff --git a/audio/visualizer/NOTICE b/audio/visualizer/NOTICE new file mode 100644 index 0000000..ad6ed94 --- /dev/null +++ b/audio/visualizer/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2013, 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. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/audio/visualizer/offload_visualizer.c b/audio/visualizer/offload_visualizer.c new file mode 100644 index 0000000..94c44a5 --- /dev/null +++ b/audio/visualizer/offload_visualizer.c @@ -0,0 +1,1267 @@ +/* + * Copyright (C) 2013 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 "offload_visualizer" +/*#define LOG_NDEBUG 0*/ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +enum { + EFFECT_STATE_UNINITIALIZED, + EFFECT_STATE_INITIALIZED, + EFFECT_STATE_ACTIVE, +}; + +typedef struct effect_context_s effect_context_t; +typedef struct output_context_s output_context_t; + +/* effect specific operations. Only the init() and process() operations must be defined. + * Others are optional. + */ +typedef struct effect_ops_s { + int (*init)(effect_context_t *context); + int (*release)(effect_context_t *context); + int (*reset)(effect_context_t *context); + int (*enable)(effect_context_t *context); + int (*disable)(effect_context_t *context); + int (*start)(effect_context_t *context, output_context_t *output); + int (*stop)(effect_context_t *context, output_context_t *output); + int (*process)(effect_context_t *context, audio_buffer_t *in, audio_buffer_t *out); + int (*set_parameter)(effect_context_t *context, effect_param_t *param, uint32_t size); + int (*get_parameter)(effect_context_t *context, effect_param_t *param, uint32_t *size); + int (*command)(effect_context_t *context, uint32_t cmdCode, uint32_t cmdSize, + void *pCmdData, uint32_t *replySize, void *pReplyData); +} effect_ops_t; + +struct effect_context_s { + const struct effect_interface_s *itfe; + struct listnode effects_list_node; /* node in created_effects_list */ + struct listnode output_node; /* node in output_context_t.effects_list */ + effect_config_t config; + const effect_descriptor_t *desc; + audio_io_handle_t out_handle; /* io handle of the output the effect is attached to */ + uint32_t state; + bool offload_enabled; /* when offload is enabled we process VISUALIZER_CMD_CAPTURE command. + Otherwise non offloaded visualizer has already processed the command + and we must not overwrite the reply. */ + effect_ops_t ops; +}; + +typedef struct output_context_s { + struct listnode outputs_list_node; /* node in active_outputs_list */ + audio_io_handle_t handle; /* io handle */ + struct listnode effects_list; /* list of effects attached to this output */ +} output_context_t; + + +/* maximum time since last capture buffer update before resetting capture buffer. This means + that the framework has stopped playing audio and we must start returning silence */ +#define MAX_STALL_TIME_MS 1000 + +#define CAPTURE_BUF_SIZE 65536 /* "64k should be enough for everyone" */ + +#define DISCARD_MEASUREMENTS_TIME_MS 2000 /* discard measurements older than this number of ms */ + +/* maximum number of buffers for which we keep track of the measurements */ +#define MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS 25 /* note: buffer index is stored in uint8_t */ + +typedef struct buffer_stats_s { + bool is_valid; + uint16_t peak_u16; /* the positive peak of the absolute value of the samples in a buffer */ + float rms_squared; /* the average square of the samples in a buffer */ +} buffer_stats_t; + +typedef struct visualizer_context_s { + effect_context_t common; + + uint32_t capture_idx; + uint32_t capture_size; + uint32_t scaling_mode; + uint32_t last_capture_idx; + uint32_t latency; + struct timespec buffer_update_time; + uint8_t capture_buf[CAPTURE_BUF_SIZE]; + /* for measurements */ + uint8_t channel_count; /* to avoid recomputing it every time a buffer is processed */ + uint32_t meas_mode; + uint8_t meas_wndw_size_in_buffers; + uint8_t meas_buffer_idx; + buffer_stats_t past_meas[MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS]; +} visualizer_context_t; + + +extern const struct effect_interface_s effect_interface; + +/* Offload visualizer UUID: 7a8044a0-1a71-11e3-a184-0002a5d5c51b */ +const effect_descriptor_t visualizer_descriptor = { + {0xe46b26a0, 0xdddd, 0x11db, 0x8afd, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, + {0x7a8044a0, 0x1a71, 0x11e3, 0xa184, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, + EFFECT_CONTROL_API_VERSION, + (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_HW_ACC_TUNNEL ), + 0, /* TODO */ + 1, + "QCOM MSM offload visualizer", + "The Android Open Source Project", +}; + +const effect_descriptor_t *descriptors[] = { + &visualizer_descriptor, + NULL, +}; + + +pthread_once_t once = PTHREAD_ONCE_INIT; +int init_status; + +/* list of created effects. Updated by visualizer_hal_start_output() + * and visualizer_hal_stop_output() */ +struct listnode created_effects_list; +/* list of active output streams. Updated by visualizer_hal_start_output() + * and visualizer_hal_stop_output() */ +struct listnode active_outputs_list; + +/* thread capturing PCM from Proxy port and calling the process function on each enabled effect + * attached to an active output stream */ +pthread_t capture_thread; +/* lock must be held when modifying or accessing created_effects_list or active_outputs_list */ +pthread_mutex_t lock; +/* thread_lock must be held when starting or stopping the capture thread. + * Locking order: thread_lock -> lock */ +pthread_mutex_t thread_lock; +/* cond is signaled when an output is started or stopped or an effect is enabled or disable: the + * capture thread will reevaluate the capture and effect rocess conditions. */ +pthread_cond_t cond; +/* true when requesting the capture thread to exit */ +bool exit_thread; +/* 0 if the capture thread was created successfully */ +int thread_status; + + +#define DSP_OUTPUT_LATENCY_MS 0 /* Fudge factor for latency after capture point in audio DSP */ + +/* Retry for delay for mixer open */ +#define RETRY_NUMBER 10 +#define RETRY_US 500000 + +#define MIXER_CARD 0 +#define SOUND_CARD 0 +#define CAPTURE_DEVICE 8 + +/* Proxy port supports only MMAP read and those fixed parameters*/ +#define AUDIO_CAPTURE_CHANNEL_COUNT 2 +#define AUDIO_CAPTURE_SMP_RATE 48000 +#define AUDIO_CAPTURE_PERIOD_SIZE (768) +#define AUDIO_CAPTURE_PERIOD_COUNT 32 + +struct pcm_config pcm_config_capture = { + .channels = AUDIO_CAPTURE_CHANNEL_COUNT, + .rate = AUDIO_CAPTURE_SMP_RATE, + .period_size = AUDIO_CAPTURE_PERIOD_SIZE, + .period_count = AUDIO_CAPTURE_PERIOD_COUNT, + .format = PCM_FORMAT_S16_LE, + .start_threshold = AUDIO_CAPTURE_PERIOD_SIZE / 4, + .stop_threshold = INT_MAX, + .avail_min = AUDIO_CAPTURE_PERIOD_SIZE / 4, +}; + + +/* + * Local functions + */ + +static void init_once() { + list_init(&created_effects_list); + list_init(&active_outputs_list); + + pthread_mutex_init(&lock, NULL); + pthread_mutex_init(&thread_lock, NULL); + pthread_cond_init(&cond, NULL); + exit_thread = false; + thread_status = -1; + + init_status = 0; +} + +int lib_init() { + pthread_once(&once, init_once); + return init_status; +} + +bool effect_exists(effect_context_t *context) { + struct listnode *node; + + list_for_each(node, &created_effects_list) { + effect_context_t *fx_ctxt = node_to_item(node, + effect_context_t, + effects_list_node); + if (fx_ctxt == context) { + return true; + } + } + return false; +} + +output_context_t *get_output(audio_io_handle_t output) { + struct listnode *node; + + list_for_each(node, &active_outputs_list) { + output_context_t *out_ctxt = node_to_item(node, + output_context_t, + outputs_list_node); + if (out_ctxt->handle == output) { + return out_ctxt; + } + } + return NULL; +} + +void add_effect_to_output(output_context_t * output, effect_context_t *context) { + struct listnode *fx_node; + + list_for_each(fx_node, &output->effects_list) { + effect_context_t *fx_ctxt = node_to_item(fx_node, + effect_context_t, + output_node); + if (fx_ctxt == context) + return; + } + list_add_tail(&output->effects_list, &context->output_node); + if (context->ops.start) + context->ops.start(context, output); +} + +void remove_effect_from_output(output_context_t * output, effect_context_t *context) { + struct listnode *fx_node; + + list_for_each(fx_node, &output->effects_list) { + effect_context_t *fx_ctxt = node_to_item(fx_node, + effect_context_t, + output_node); + if (fx_ctxt == context) { + if (context->ops.stop) + context->ops.stop(context, output); + list_remove(&context->output_node); + return; + } + } +} + +bool effects_enabled() { + struct listnode *out_node; + + list_for_each(out_node, &active_outputs_list) { + struct listnode *fx_node; + output_context_t *out_ctxt = node_to_item(out_node, + output_context_t, + outputs_list_node); + + list_for_each(fx_node, &out_ctxt->effects_list) { + effect_context_t *fx_ctxt = node_to_item(fx_node, + effect_context_t, + output_node); + if (fx_ctxt->state == EFFECT_STATE_ACTIVE && fx_ctxt->ops.process != NULL) + return true; + } + } + return false; +} + +int configure_proxy_capture(struct mixer *mixer, int value) { + const char *proxy_ctl_name = "AFE_PCM_RX Audio Mixer MultiMedia4"; + struct mixer_ctl *ctl; + + ctl = mixer_get_ctl_by_name(mixer, proxy_ctl_name); + if (ctl == NULL) { + ALOGW("%s: could not get %s ctl", __func__, proxy_ctl_name); + return -EINVAL; + } + if (mixer_ctl_set_value(ctl, 0, value) != 0) + ALOGW("%s: error setting value %d on %s ", __func__, value, proxy_ctl_name); + + return 0; +} + + +void *capture_thread_loop(void *arg) +{ + int16_t data[AUDIO_CAPTURE_PERIOD_SIZE * AUDIO_CAPTURE_CHANNEL_COUNT * sizeof(int16_t)]; + audio_buffer_t buf; + buf.frameCount = AUDIO_CAPTURE_PERIOD_SIZE; + buf.s16 = data; + bool capture_enabled = false; + struct mixer *mixer; + struct pcm *pcm = NULL; + int ret; + int retry_num = 0; + + ALOGD("thread enter"); + + prctl(PR_SET_NAME, (unsigned long)"visualizer capture", 0, 0, 0); + + pthread_mutex_lock(&lock); + + mixer = mixer_open(MIXER_CARD); + while (mixer == NULL && retry_num < RETRY_NUMBER) { + usleep(RETRY_US); + mixer = mixer_open(MIXER_CARD); + retry_num++; + } + if (mixer == NULL) { + pthread_mutex_unlock(&lock); + return NULL; + } + + for (;;) { + if (exit_thread) { + break; + } + if (effects_enabled()) { + if (!capture_enabled) { + ret = configure_proxy_capture(mixer, 1); + if (ret == 0) { + pcm = pcm_open(SOUND_CARD, CAPTURE_DEVICE, + PCM_IN|PCM_MMAP|PCM_NOIRQ, &pcm_config_capture); + if (pcm && !pcm_is_ready(pcm)) { + ALOGW("%s: %s", __func__, pcm_get_error(pcm)); + pcm_close(pcm); + pcm = NULL; + configure_proxy_capture(mixer, 0); + } else { + capture_enabled = true; + ALOGD("%s: capture ENABLED", __func__); + } + } + } + } else { + if (capture_enabled) { + if (pcm != NULL) + pcm_close(pcm); + configure_proxy_capture(mixer, 0); + ALOGD("%s: capture DISABLED", __func__); + capture_enabled = false; + } + pthread_cond_wait(&cond, &lock); + } + if (!capture_enabled) + continue; + + pthread_mutex_unlock(&lock); + ret = pcm_mmap_read(pcm, data, sizeof(data)); + pthread_mutex_lock(&lock); + + if (ret == 0) { + struct listnode *out_node; + + list_for_each(out_node, &active_outputs_list) { + output_context_t *out_ctxt = node_to_item(out_node, + output_context_t, + outputs_list_node); + struct listnode *fx_node; + + list_for_each(fx_node, &out_ctxt->effects_list) { + effect_context_t *fx_ctxt = node_to_item(fx_node, + effect_context_t, + output_node); + if (fx_ctxt->ops.process != NULL) + fx_ctxt->ops.process(fx_ctxt, &buf, &buf); + } + } + } else { + ALOGW("%s: read status %d %s", __func__, ret, pcm_get_error(pcm)); + } + } + + if (capture_enabled) { + if (pcm != NULL) + pcm_close(pcm); + configure_proxy_capture(mixer, 0); + } + mixer_close(mixer); + pthread_mutex_unlock(&lock); + + ALOGD("thread exit"); + + return NULL; +} + +/* + * Interface from audio HAL + */ + +__attribute__ ((visibility ("default"))) +int visualizer_hal_start_output(audio_io_handle_t output, int pcm_id) { + int ret; + struct listnode *node; + + ALOGV("%s output %d pcm_id %d", __func__, output, pcm_id); + + if (lib_init() != 0) + return init_status; + + pthread_mutex_lock(&thread_lock); + pthread_mutex_lock(&lock); + if (get_output(output) != NULL) { + ALOGW("%s output already started", __func__); + ret = -ENOSYS; + goto exit; + } + + output_context_t *out_ctxt = (output_context_t *)malloc(sizeof(output_context_t)); + if (out_ctxt == NULL) { + ALOGE("%s fail to allocate memory", __func__); + ret = -ENOMEM; + goto exit; + } + out_ctxt->handle = output; + list_init(&out_ctxt->effects_list); + + list_for_each(node, &created_effects_list) { + effect_context_t *fx_ctxt = node_to_item(node, + effect_context_t, + effects_list_node); + if (fx_ctxt->out_handle == output) { + if (fx_ctxt->ops.start) + fx_ctxt->ops.start(fx_ctxt, out_ctxt); + list_add_tail(&out_ctxt->effects_list, &fx_ctxt->output_node); + } + } + if (list_empty(&active_outputs_list)) { + exit_thread = false; + thread_status = pthread_create(&capture_thread, (const pthread_attr_t *) NULL, + capture_thread_loop, NULL); + } + list_add_tail(&active_outputs_list, &out_ctxt->outputs_list_node); + pthread_cond_signal(&cond); + +exit: + pthread_mutex_unlock(&lock); + pthread_mutex_unlock(&thread_lock); + return ret; +} + +__attribute__ ((visibility ("default"))) +int visualizer_hal_stop_output(audio_io_handle_t output, int pcm_id) { + int ret; + struct listnode *node; + struct listnode *fx_node; + output_context_t *out_ctxt; + + ALOGV("%s output %d pcm_id %d", __func__, output, pcm_id); + + if (lib_init() != 0) + return init_status; + + pthread_mutex_lock(&thread_lock); + pthread_mutex_lock(&lock); + + out_ctxt = get_output(output); + if (out_ctxt == NULL) { + ALOGW("%s output not started", __func__); + ret = -ENOSYS; + goto exit; + } + list_for_each(fx_node, &out_ctxt->effects_list) { + effect_context_t *fx_ctxt = node_to_item(fx_node, + effect_context_t, + output_node); + if (fx_ctxt->ops.stop) + fx_ctxt->ops.stop(fx_ctxt, out_ctxt); + } + list_remove(&out_ctxt->outputs_list_node); + pthread_cond_signal(&cond); + + if (list_empty(&active_outputs_list)) { + if (thread_status == 0) { + exit_thread = true; + pthread_cond_signal(&cond); + pthread_mutex_unlock(&lock); + pthread_join(capture_thread, (void **) NULL); + pthread_mutex_lock(&lock); + thread_status = -1; + } + } + + free(out_ctxt); + +exit: + pthread_mutex_unlock(&lock); + pthread_mutex_unlock(&thread_lock); + return ret; +} + + +/* + * Effect operations + */ + +int set_config(effect_context_t *context, effect_config_t *config) +{ + if (config->inputCfg.samplingRate != config->outputCfg.samplingRate) return -EINVAL; + if (config->inputCfg.channels != config->outputCfg.channels) return -EINVAL; + if (config->inputCfg.format != config->outputCfg.format) return -EINVAL; + if (config->inputCfg.channels != AUDIO_CHANNEL_OUT_STEREO) return -EINVAL; + if (config->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_WRITE && + config->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_ACCUMULATE) return -EINVAL; + if (config->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT) return -EINVAL; + + context->config = *config; + + if (context->ops.reset) + context->ops.reset(context); + + return 0; +} + +void get_config(effect_context_t *context, effect_config_t *config) +{ + *config = context->config; +} + + +/* + * Visualizer operations + */ + +uint32_t visualizer_get_delta_time_ms_from_updated_time(visualizer_context_t* visu_ctxt) { + uint32_t delta_ms = 0; + if (visu_ctxt->buffer_update_time.tv_sec != 0) { + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { + time_t secs = ts.tv_sec - visu_ctxt->buffer_update_time.tv_sec; + long nsec = ts.tv_nsec - visu_ctxt->buffer_update_time.tv_nsec; + if (nsec < 0) { + --secs; + nsec += 1000000000; + } + delta_ms = secs * 1000 + nsec / 1000000; + } + } + return delta_ms; +} + +int visualizer_reset(effect_context_t *context) +{ + visualizer_context_t * visu_ctxt = (visualizer_context_t *)context; + + visu_ctxt->capture_idx = 0; + visu_ctxt->last_capture_idx = 0; + visu_ctxt->buffer_update_time.tv_sec = 0; + visu_ctxt->latency = DSP_OUTPUT_LATENCY_MS; + memset(visu_ctxt->capture_buf, 0x80, CAPTURE_BUF_SIZE); + return 0; +} + +int visualizer_init(effect_context_t *context) +{ + int32_t i; + + visualizer_context_t * visu_ctxt = (visualizer_context_t *)context; + + context->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ; + context->config.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; + context->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT; + context->config.inputCfg.samplingRate = 44100; + context->config.inputCfg.bufferProvider.getBuffer = NULL; + context->config.inputCfg.bufferProvider.releaseBuffer = NULL; + context->config.inputCfg.bufferProvider.cookie = NULL; + context->config.inputCfg.mask = EFFECT_CONFIG_ALL; + context->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE; + context->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; + context->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT; + context->config.outputCfg.samplingRate = 44100; + context->config.outputCfg.bufferProvider.getBuffer = NULL; + context->config.outputCfg.bufferProvider.releaseBuffer = NULL; + context->config.outputCfg.bufferProvider.cookie = NULL; + context->config.outputCfg.mask = EFFECT_CONFIG_ALL; + + visu_ctxt->capture_size = VISUALIZER_CAPTURE_SIZE_MAX; + visu_ctxt->scaling_mode = VISUALIZER_SCALING_MODE_NORMALIZED; + + // measurement initialization + visu_ctxt->channel_count = popcount(context->config.inputCfg.channels); + visu_ctxt->meas_mode = MEASUREMENT_MODE_NONE; + visu_ctxt->meas_wndw_size_in_buffers = MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS; + visu_ctxt->meas_buffer_idx = 0; + for (i=0 ; imeas_wndw_size_in_buffers ; i++) { + visu_ctxt->past_meas[i].is_valid = false; + visu_ctxt->past_meas[i].peak_u16 = 0; + visu_ctxt->past_meas[i].rms_squared = 0; + } + + set_config(context, &context->config); + + return 0; +} + +int visualizer_get_parameter(effect_context_t *context, effect_param_t *p, uint32_t *size) +{ + visualizer_context_t *visu_ctxt = (visualizer_context_t *)context; + + p->status = 0; + *size = sizeof(effect_param_t) + sizeof(uint32_t); + if (p->psize != sizeof(uint32_t)) { + p->status = -EINVAL; + return 0; + } + switch (*(uint32_t *)p->data) { + case VISUALIZER_PARAM_CAPTURE_SIZE: + ALOGV("%s get capture_size = %d", __func__, visu_ctxt->capture_size); + *((uint32_t *)p->data + 1) = visu_ctxt->capture_size; + p->vsize = sizeof(uint32_t); + *size += sizeof(uint32_t); + break; + case VISUALIZER_PARAM_SCALING_MODE: + ALOGV("%s get scaling_mode = %d", __func__, visu_ctxt->scaling_mode); + *((uint32_t *)p->data + 1) = visu_ctxt->scaling_mode; + p->vsize = sizeof(uint32_t); + *size += sizeof(uint32_t); + break; + case VISUALIZER_PARAM_MEASUREMENT_MODE: + ALOGV("%s get meas_mode = %d", __func__, visu_ctxt->meas_mode); + *((uint32_t *)p->data + 1) = visu_ctxt->meas_mode; + p->vsize = sizeof(uint32_t); + *size += sizeof(uint32_t); + break; + default: + p->status = -EINVAL; + } + return 0; +} + +int visualizer_set_parameter(effect_context_t *context, effect_param_t *p, uint32_t size) +{ + visualizer_context_t *visu_ctxt = (visualizer_context_t *)context; + + if (p->psize != sizeof(uint32_t) || p->vsize != sizeof(uint32_t)) + return -EINVAL; + + switch (*(uint32_t *)p->data) { + case VISUALIZER_PARAM_CAPTURE_SIZE: + visu_ctxt->capture_size = *((uint32_t *)p->data + 1); + ALOGV("%s set capture_size = %d", __func__, visu_ctxt->capture_size); + break; + case VISUALIZER_PARAM_SCALING_MODE: + visu_ctxt->scaling_mode = *((uint32_t *)p->data + 1); + ALOGV("%s set scaling_mode = %d", __func__, visu_ctxt->scaling_mode); + break; + case VISUALIZER_PARAM_LATENCY: + /* Ignore latency as we capture at DSP output + * visu_ctxt->latency = *((uint32_t *)p->data + 1); */ + ALOGV("%s set latency = %d", __func__, visu_ctxt->latency); + break; + case VISUALIZER_PARAM_MEASUREMENT_MODE: + visu_ctxt->meas_mode = *((uint32_t *)p->data + 1); + ALOGV("%s set meas_mode = %d", __func__, visu_ctxt->meas_mode); + break; + default: + return -EINVAL; + } + return 0; +} + +/* Real process function called from capture thread. Called with lock held */ +int visualizer_process(effect_context_t *context, + audio_buffer_t *inBuffer, + audio_buffer_t *outBuffer) +{ + visualizer_context_t *visu_ctxt = (visualizer_context_t *)context; + + if (!effect_exists(context)) + return -EINVAL; + + if (inBuffer == NULL || inBuffer->raw == NULL || + outBuffer == NULL || outBuffer->raw == NULL || + inBuffer->frameCount != outBuffer->frameCount || + inBuffer->frameCount == 0) { + return -EINVAL; + } + + // perform measurements if needed + if (visu_ctxt->meas_mode & MEASUREMENT_MODE_PEAK_RMS) { + // find the peak and RMS squared for the new buffer + uint32_t inIdx; + int16_t max_sample = 0; + float rms_squared_acc = 0; + for (inIdx = 0 ; inIdx < inBuffer->frameCount * visu_ctxt->channel_count ; inIdx++) { + if (inBuffer->s16[inIdx] > max_sample) { + max_sample = inBuffer->s16[inIdx]; + } else if (-inBuffer->s16[inIdx] > max_sample) { + max_sample = -inBuffer->s16[inIdx]; + } + rms_squared_acc += (inBuffer->s16[inIdx] * inBuffer->s16[inIdx]); + } + // store the measurement + visu_ctxt->past_meas[visu_ctxt->meas_buffer_idx].peak_u16 = (uint16_t)max_sample; + visu_ctxt->past_meas[visu_ctxt->meas_buffer_idx].rms_squared = + rms_squared_acc / (inBuffer->frameCount * visu_ctxt->channel_count); + visu_ctxt->past_meas[visu_ctxt->meas_buffer_idx].is_valid = true; + if (++visu_ctxt->meas_buffer_idx >= visu_ctxt->meas_wndw_size_in_buffers) { + visu_ctxt->meas_buffer_idx = 0; + } + } + + /* all code below assumes stereo 16 bit PCM output and input */ + int32_t shift; + + if (visu_ctxt->scaling_mode == VISUALIZER_SCALING_MODE_NORMALIZED) { + /* derive capture scaling factor from peak value in current buffer + * this gives more interesting captures for display. */ + shift = 32; + int len = inBuffer->frameCount * 2; + int i; + for (i = 0; i < len; i++) { + int32_t smp = inBuffer->s16[i]; + if (smp < 0) smp = -smp - 1; /* take care to keep the max negative in range */ + int32_t clz = __builtin_clz(smp); + if (shift > clz) shift = clz; + } + /* A maximum amplitude signal will have 17 leading zeros, which we want to + * translate to a shift of 8 (for converting 16 bit to 8 bit) */ + shift = 25 - shift; + /* Never scale by less than 8 to avoid returning unaltered PCM signal. */ + if (shift < 3) { + shift = 3; + } + /* add one to combine the division by 2 needed after summing + * left and right channels below */ + shift++; + } else { + assert(visu_ctxt->scaling_mode == VISUALIZER_SCALING_MODE_AS_PLAYED); + shift = 9; + } + + uint32_t capt_idx; + uint32_t in_idx; + uint8_t *buf = visu_ctxt->capture_buf; + for (in_idx = 0, capt_idx = visu_ctxt->capture_idx; + in_idx < inBuffer->frameCount; + in_idx++, capt_idx++) { + if (capt_idx >= CAPTURE_BUF_SIZE) { + /* wrap around */ + capt_idx = 0; + } + int32_t smp = inBuffer->s16[2 * in_idx] + inBuffer->s16[2 * in_idx + 1]; + smp = smp >> shift; + buf[capt_idx] = ((uint8_t)smp)^0x80; + } + + /* XXX the following two should really be atomic, though it probably doesn't + * matter much for visualization purposes */ + visu_ctxt->capture_idx = capt_idx; + /* update last buffer update time stamp */ + if (clock_gettime(CLOCK_MONOTONIC, &visu_ctxt->buffer_update_time) < 0) { + visu_ctxt->buffer_update_time.tv_sec = 0; + } + + if (context->state != EFFECT_STATE_ACTIVE) { + ALOGV("%s DONE inactive", __func__); + return -ENODATA; + } + + return 0; +} + +int visualizer_command(effect_context_t * context, uint32_t cmdCode, uint32_t cmdSize, + void *pCmdData, uint32_t *replySize, void *pReplyData) +{ + visualizer_context_t * visu_ctxt = (visualizer_context_t *)context; + + switch (cmdCode) { + case VISUALIZER_CMD_CAPTURE: + if (pReplyData == NULL || *replySize != visu_ctxt->capture_size) { + ALOGV("%s VISUALIZER_CMD_CAPTURE error *replySize %d context->capture_size %d", + __func__, *replySize, visu_ctxt->capture_size); + return -EINVAL; + } + + if (!context->offload_enabled) + break; + + if (context->state == EFFECT_STATE_ACTIVE) { + int32_t latency_ms = visu_ctxt->latency; + const uint32_t delta_ms = visualizer_get_delta_time_ms_from_updated_time(visu_ctxt); + latency_ms -= delta_ms; + if (latency_ms < 0) { + latency_ms = 0; + } + const uint32_t delta_smp = context->config.inputCfg.samplingRate * latency_ms / 1000; + + int32_t capture_point = visu_ctxt->capture_idx - visu_ctxt->capture_size - delta_smp; + int32_t capture_size = visu_ctxt->capture_size; + if (capture_point < 0) { + int32_t size = -capture_point; + if (size > capture_size) + size = capture_size; + + memcpy(pReplyData, + visu_ctxt->capture_buf + CAPTURE_BUF_SIZE + capture_point, + size); + pReplyData = (void *)((size_t)pReplyData + size); + capture_size -= size; + capture_point = 0; + } + memcpy(pReplyData, + visu_ctxt->capture_buf + capture_point, + capture_size); + + + /* if audio framework has stopped playing audio although the effect is still + * active we must clear the capture buffer to return silence */ + if ((visu_ctxt->last_capture_idx == visu_ctxt->capture_idx) && + (visu_ctxt->buffer_update_time.tv_sec != 0)) { + if (delta_ms > MAX_STALL_TIME_MS) { + ALOGV("%s capture going to idle", __func__); + visu_ctxt->buffer_update_time.tv_sec = 0; + memset(pReplyData, 0x80, visu_ctxt->capture_size); + } + } + visu_ctxt->last_capture_idx = visu_ctxt->capture_idx; + } else { + memset(pReplyData, 0x80, visu_ctxt->capture_size); + } + break; + + case VISUALIZER_CMD_MEASURE: { + uint16_t peak_u16 = 0; + float sum_rms_squared = 0.0f; + uint8_t nb_valid_meas = 0; + /* reset measurements if last measurement was too long ago (which implies stored + * measurements aren't relevant anymore and shouldn't bias the new one) */ + const int32_t delay_ms = visualizer_get_delta_time_ms_from_updated_time(visu_ctxt); + if (delay_ms > DISCARD_MEASUREMENTS_TIME_MS) { + uint32_t i; + ALOGV("Discarding measurements, last measurement is %dms old", delay_ms); + for (i=0 ; imeas_wndw_size_in_buffers ; i++) { + visu_ctxt->past_meas[i].is_valid = false; + visu_ctxt->past_meas[i].peak_u16 = 0; + visu_ctxt->past_meas[i].rms_squared = 0; + } + visu_ctxt->meas_buffer_idx = 0; + } else { + /* only use actual measurements, otherwise the first RMS measure happening before + * MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS have been played will always be artificially + * low */ + uint32_t i; + for (i=0 ; i < visu_ctxt->meas_wndw_size_in_buffers ; i++) { + if (visu_ctxt->past_meas[i].is_valid) { + if (visu_ctxt->past_meas[i].peak_u16 > peak_u16) { + peak_u16 = visu_ctxt->past_meas[i].peak_u16; + } + sum_rms_squared += visu_ctxt->past_meas[i].rms_squared; + nb_valid_meas++; + } + } + } + float rms = nb_valid_meas == 0 ? 0.0f : sqrtf(sum_rms_squared / nb_valid_meas); + int32_t* p_int_reply_data = (int32_t*)pReplyData; + /* convert from I16 sample values to mB and write results */ + if (rms < 0.000016f) { + p_int_reply_data[MEASUREMENT_IDX_RMS] = -9600; //-96dB + } else { + p_int_reply_data[MEASUREMENT_IDX_RMS] = (int32_t) (2000 * log10(rms / 32767.0f)); + } + if (peak_u16 == 0) { + p_int_reply_data[MEASUREMENT_IDX_PEAK] = -9600; //-96dB + } else { + p_int_reply_data[MEASUREMENT_IDX_PEAK] = (int32_t) (2000 * log10(peak_u16 / 32767.0f)); + } + ALOGV("VISUALIZER_CMD_MEASURE peak=%d (%dmB), rms=%.1f (%dmB)", + peak_u16, p_int_reply_data[MEASUREMENT_IDX_PEAK], + rms, p_int_reply_data[MEASUREMENT_IDX_RMS]); + } + break; + + default: + ALOGW("%s invalid command %d", __func__, cmdCode); + return -EINVAL; + } + return 0; +} + + +/* + * Effect Library Interface Implementation + */ + +int effect_lib_create(const effect_uuid_t *uuid, + int32_t sessionId, + int32_t ioId, + effect_handle_t *pHandle) { + int ret; + int i; + + if (lib_init() != 0) + return init_status; + + if (pHandle == NULL || uuid == NULL) + return -EINVAL; + + for (i = 0; descriptors[i] != NULL; i++) { + if (memcmp(uuid, &descriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) + break; + } + + if (descriptors[i] == NULL) + return -EINVAL; + + effect_context_t *context; + if (memcmp(uuid, &visualizer_descriptor.uuid, sizeof(effect_uuid_t)) == 0) { + visualizer_context_t *visu_ctxt = (visualizer_context_t *)calloc(1, + sizeof(visualizer_context_t)); + if (visu_ctxt == NULL) { + ALOGE("%s fail to allocate memory", __func__); + return -ENOMEM; + } + context = (effect_context_t *)visu_ctxt; + context->ops.init = visualizer_init; + context->ops.reset = visualizer_reset; + context->ops.process = visualizer_process; + context->ops.set_parameter = visualizer_set_parameter; + context->ops.get_parameter = visualizer_get_parameter; + context->ops.command = visualizer_command; + context->desc = &visualizer_descriptor; + } else { + return -EINVAL; + } + + context->itfe = &effect_interface; + context->state = EFFECT_STATE_UNINITIALIZED; + context->out_handle = (audio_io_handle_t)ioId; + + ret = context->ops.init(context); + if (ret < 0) { + ALOGW("%s init failed", __func__); + free(context); + return ret; + } + + context->state = EFFECT_STATE_INITIALIZED; + + pthread_mutex_lock(&lock); + list_add_tail(&created_effects_list, &context->effects_list_node); + output_context_t *out_ctxt = get_output(ioId); + if (out_ctxt != NULL) + add_effect_to_output(out_ctxt, context); + pthread_mutex_unlock(&lock); + + *pHandle = (effect_handle_t)context; + + ALOGV("%s created context %p", __func__, context); + + return 0; + +} + +int effect_lib_release(effect_handle_t handle) { + effect_context_t *context = (effect_context_t *)handle; + int status; + + if (lib_init() != 0) + return init_status; + + ALOGV("%s context %p", __func__, handle); + pthread_mutex_lock(&lock); + status = -EINVAL; + if (effect_exists(context)) { + output_context_t *out_ctxt = get_output(context->out_handle); + if (out_ctxt != NULL) + remove_effect_from_output(out_ctxt, context); + list_remove(&context->effects_list_node); + if (context->ops.release) + context->ops.release(context); + free(context); + status = 0; + } + pthread_mutex_unlock(&lock); + + return status; +} + +int effect_lib_get_descriptor(const effect_uuid_t *uuid, + effect_descriptor_t *descriptor) { + int i; + + if (lib_init() != 0) + return init_status; + + if (descriptor == NULL || uuid == NULL) { + ALOGV("%s called with NULL pointer", __func__); + return -EINVAL; + } + + for (i = 0; descriptors[i] != NULL; i++) { + if (memcmp(uuid, &descriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) { + *descriptor = *descriptors[i]; + return 0; + } + } + + return -EINVAL; +} + +/* + * Effect Control Interface Implementation + */ + + /* Stub function for effect interface: never called for offloaded effects */ +int effect_process(effect_handle_t self, + audio_buffer_t *inBuffer, + audio_buffer_t *outBuffer) +{ + effect_context_t * context = (effect_context_t *)self; + int status = 0; + + ALOGW("%s Called ?????", __func__); + + pthread_mutex_lock(&lock); + if (!effect_exists(context)) { + status = -EINVAL; + goto exit; + } + + if (context->state != EFFECT_STATE_ACTIVE) { + status = -EINVAL; + goto exit; + } + +exit: + pthread_mutex_unlock(&lock); + return status; +} + +int effect_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize, + void *pCmdData, uint32_t *replySize, void *pReplyData) +{ + + effect_context_t * context = (effect_context_t *)self; + int retsize; + int status = 0; + + pthread_mutex_lock(&lock); + + if (!effect_exists(context)) { + status = -EINVAL; + goto exit; + } + + if (context == NULL || context->state == EFFECT_STATE_UNINITIALIZED) { + status = -EINVAL; + goto exit; + } + +// ALOGV_IF(cmdCode != VISUALIZER_CMD_CAPTURE, +// "%s command %d cmdSize %d", __func__, cmdCode, cmdSize); + + switch (cmdCode) { + case EFFECT_CMD_INIT: + if (pReplyData == NULL || *replySize != sizeof(int)) { + status = -EINVAL; + goto exit; + } + if (context->ops.init) + *(int *) pReplyData = context->ops.init(context); + else + *(int *) pReplyData = 0; + break; + case EFFECT_CMD_SET_CONFIG: + if (pCmdData == NULL || cmdSize != sizeof(effect_config_t) + || pReplyData == NULL || *replySize != sizeof(int)) { + status = -EINVAL; + goto exit; + } + *(int *) pReplyData = set_config(context, (effect_config_t *) pCmdData); + break; + case EFFECT_CMD_GET_CONFIG: + if (pReplyData == NULL || + *replySize != sizeof(effect_config_t)) { + status = -EINVAL; + goto exit; + } + if (!context->offload_enabled) { + status = -EINVAL; + goto exit; + } + + get_config(context, (effect_config_t *)pReplyData); + break; + case EFFECT_CMD_RESET: + if (context->ops.reset) + context->ops.reset(context); + break; + case EFFECT_CMD_ENABLE: + if (pReplyData == NULL || *replySize != sizeof(int)) { + status = -EINVAL; + goto exit; + } + if (context->state != EFFECT_STATE_INITIALIZED) { + status = -ENOSYS; + goto exit; + } + context->state = EFFECT_STATE_ACTIVE; + if (context->ops.enable) + context->ops.enable(context); + pthread_cond_signal(&cond); + ALOGV("%s EFFECT_CMD_ENABLE", __func__); + *(int *)pReplyData = 0; + break; + case EFFECT_CMD_DISABLE: + if (pReplyData == NULL || *replySize != sizeof(int)) { + status = -EINVAL; + goto exit; + } + if (context->state != EFFECT_STATE_ACTIVE) { + status = -ENOSYS; + goto exit; + } + context->state = EFFECT_STATE_INITIALIZED; + if (context->ops.disable) + context->ops.disable(context); + pthread_cond_signal(&cond); + ALOGV("%s EFFECT_CMD_DISABLE", __func__); + *(int *)pReplyData = 0; + break; + case EFFECT_CMD_GET_PARAM: { + if (pCmdData == NULL || + cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t)) || + pReplyData == NULL || + *replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t))) { + status = -EINVAL; + goto exit; + } + if (!context->offload_enabled) { + status = -EINVAL; + goto exit; + } + memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(uint32_t)); + effect_param_t *p = (effect_param_t *)pReplyData; + if (context->ops.get_parameter) + context->ops.get_parameter(context, p, replySize); + } break; + case EFFECT_CMD_SET_PARAM: { + if (pCmdData == NULL || + cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t)) || + pReplyData == NULL || *replySize != sizeof(int32_t)) { + status = -EINVAL; + goto exit; + } + *(int32_t *)pReplyData = 0; + effect_param_t *p = (effect_param_t *)pCmdData; + if (context->ops.set_parameter) + *(int32_t *)pReplyData = context->ops.set_parameter(context, p, *replySize); + + } break; + case EFFECT_CMD_SET_DEVICE: + case EFFECT_CMD_SET_VOLUME: + case EFFECT_CMD_SET_AUDIO_MODE: + break; + + case EFFECT_CMD_OFFLOAD: { + output_context_t *out_ctxt; + + if (cmdSize != sizeof(effect_offload_param_t) || pCmdData == NULL + || pReplyData == NULL || *replySize != sizeof(int)) { + ALOGV("%s EFFECT_CMD_OFFLOAD bad format", __func__); + status = -EINVAL; + break; + } + + effect_offload_param_t* offload_param = (effect_offload_param_t*)pCmdData; + + ALOGV("%s EFFECT_CMD_OFFLOAD offload %d output %d", + __func__, offload_param->isOffload, offload_param->ioHandle); + + *(int *)pReplyData = 0; + + context->offload_enabled = offload_param->isOffload; + if (context->out_handle == offload_param->ioHandle) + break; + + out_ctxt = get_output(context->out_handle); + if (out_ctxt != NULL) + remove_effect_from_output(out_ctxt, context); + + context->out_handle = offload_param->ioHandle; + out_ctxt = get_output(offload_param->ioHandle); + if (out_ctxt != NULL) + add_effect_to_output(out_ctxt, context); + + } break; + + + default: + if (cmdCode >= EFFECT_CMD_FIRST_PROPRIETARY && context->ops.command) + status = context->ops.command(context, cmdCode, cmdSize, + pCmdData, replySize, pReplyData); + else { + ALOGW("%s invalid command %d", __func__, cmdCode); + status = -EINVAL; + } + break; + } + +exit: + pthread_mutex_unlock(&lock); + +// ALOGV_IF(cmdCode != VISUALIZER_CMD_CAPTURE,"%s DONE", __func__); + return status; +} + +/* Effect Control Interface Implementation: get_descriptor */ +int effect_get_descriptor(effect_handle_t self, + effect_descriptor_t *descriptor) +{ + effect_context_t *context = (effect_context_t *)self; + + if (!effect_exists(context)) + return -EINVAL; + + if (descriptor == NULL) + return -EINVAL; + + *descriptor = *context->desc; + + return 0; +} + +/* effect_handle_t interface implementation for visualizer effect */ +const struct effect_interface_s effect_interface = { + effect_process, + effect_command, + effect_get_descriptor, + NULL, +}; + +__attribute__ ((visibility ("default"))) +audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { + tag : AUDIO_EFFECT_LIBRARY_TAG, + version : EFFECT_LIBRARY_API_VERSION, + name : "Visualizer Library", + implementor : "The Android Open Source Project", + create_effect : effect_lib_create, + release_effect : effect_lib_release, + get_descriptor : effect_lib_get_descriptor, +}; diff --git a/audio/voice_processing/Android.mk b/audio/voice_processing/Android.mk new file mode 100644 index 0000000..9b86eaf --- /dev/null +++ b/audio/voice_processing/Android.mk @@ -0,0 +1,23 @@ +LOCAL_PATH:= $(call my-dir) + +# audio preprocessing wrapper +include $(CLEAR_VARS) + +LOCAL_MODULE:= libqcomvoiceprocessing +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_RELATIVE_PATH := soundfx + +LOCAL_SRC_FILES:= \ + voice_processing.c + +LOCAL_C_INCLUDES += \ + $(call include-path-for, audio-effects) + +LOCAL_SHARED_LIBRARIES := \ + libcutils + +LOCAL_SHARED_LIBRARIES += libdl + +LOCAL_CFLAGS += -fvisibility=hidden + +include $(BUILD_SHARED_LIBRARY) diff --git a/audio/voice_processing/voice_processing.c b/audio/voice_processing/voice_processing.c new file mode 100644 index 0000000..1d18a3d --- /dev/null +++ b/audio/voice_processing/voice_processing.c @@ -0,0 +1,765 @@ +/* + * Copyright (C) 2013 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 "voice_processing" +/*#define LOG_NDEBUG 0*/ +#include +#include +#include +#include +#include +#include +#include + + +//------------------------------------------------------------------------------ +// local definitions +//------------------------------------------------------------------------------ + +#define EFFECTS_DESCRIPTOR_LIBRARY_PATH "/system/lib/soundfx/libqcomvoiceprocessingdescriptors.so" + +// types of pre processing modules +enum effect_id +{ + AEC_ID, // Acoustic Echo Canceler + NS_ID, // Noise Suppressor +//ENABLE_AGC AGC_ID, // Automatic Gain Control + NUM_ID +}; + +// Session state +enum session_state { + SESSION_STATE_INIT, // initialized + SESSION_STATE_CONFIG // configuration received +}; + +// Effect/Preprocessor state +enum effect_state { + EFFECT_STATE_INIT, // initialized + EFFECT_STATE_CREATED, // webRTC engine created + EFFECT_STATE_CONFIG, // configuration received/disabled + EFFECT_STATE_ACTIVE // active/enabled +}; + +// Effect context +struct effect_s { + const struct effect_interface_s *itfe; + uint32_t id; // type of pre processor (enum effect_id) + uint32_t state; // current state (enum effect_state) + struct session_s *session; // session the effect is on +}; + +// Session context +struct session_s { + struct listnode node; + effect_config_t config; + struct effect_s effects[NUM_ID]; // effects in this session + uint32_t state; // current state (enum session_state) + int id; // audio session ID + int io; // handle of input stream this session is on + uint32_t created_msk; // bit field containing IDs of crested pre processors + uint32_t enabled_msk; // bit field containing IDs of enabled pre processors + uint32_t processed_msk; // bit field containing IDs of pre processors already +}; + + +//------------------------------------------------------------------------------ +// Default Effect descriptors. Device specific descriptors should be defined in +// libqcomvoiceprocessing..so if needed. +//------------------------------------------------------------------------------ + +// UUIDs for effect types have been generated from http://www.itu.int/ITU-T/asn1/uuid.html +// as the pre processing effects are not defined by OpenSL ES + +// Acoustic Echo Cancellation +static const effect_descriptor_t qcom_default_aec_descriptor = { + { 0x7b491460, 0x8d4d, 0x11e0, 0xbd61, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } }, // type + { 0x0f8d0d2a, 0x59e5, 0x45fe, 0xb6e4, { 0x24, 0x8c, 0x8a, 0x79, 0x91, 0x09 } }, // uuid + EFFECT_CONTROL_API_VERSION, + (EFFECT_FLAG_TYPE_PRE_PROC|EFFECT_FLAG_DEVICE_IND), + 0, + 0, + "Acoustic Echo Canceler", + "Qualcomm Fluence" +}; + +// Noise suppression +static const effect_descriptor_t qcom_default_ns_descriptor = { + { 0x58b4b260, 0x8e06, 0x11e0, 0xaa8e, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } }, // type + { 0x1d97bb0b, 0x9e2f, 0x4403, 0x9ae3, { 0x58, 0xc2, 0x55, 0x43, 0x06, 0xf8 } }, // uuid + EFFECT_CONTROL_API_VERSION, + (EFFECT_FLAG_TYPE_PRE_PROC|EFFECT_FLAG_DEVICE_IND), + 0, + 0, + "Noise Suppression", + "Qualcomm Fluence" +}; + +//ENABLE_AGC +// Automatic Gain Control +//static const effect_descriptor_t qcom_default_agc_descriptor = { +// { 0x0a8abfe0, 0x654c, 0x11e0, 0xba26, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } }, // type +// { 0x0dd49521, 0x8c59, 0x40b1, 0xb403, { 0xe0, 0x8d, 0x5f, 0x01, 0x87, 0x5e } }, // uuid +// EFFECT_CONTROL_API_VERSION, +// (EFFECT_FLAG_TYPE_PRE_PROC|EFFECT_FLAG_DEVICE_IND), +// 0, +// 0, +// "Automatic Gain Control", +// "Qualcomm Fluence" +//}; + +const effect_descriptor_t *descriptors[NUM_ID] = { + &qcom_default_aec_descriptor, + &qcom_default_ns_descriptor, +//ENABLE_AGC &qcom_default_agc_descriptor, +}; + + +static int init_status = 1; +struct listnode session_list; +static const struct effect_interface_s effect_interface; +static const effect_uuid_t * uuid_to_id_table[NUM_ID]; + +//------------------------------------------------------------------------------ +// Helper functions +//------------------------------------------------------------------------------ + +static const effect_uuid_t * id_to_uuid(int id) +{ + if (id >= NUM_ID) + return EFFECT_UUID_NULL; + + return uuid_to_id_table[id]; +} + +static uint32_t uuid_to_id(const effect_uuid_t * uuid) +{ + size_t i; + for (i = 0; i < NUM_ID; i++) + if (memcmp(uuid, uuid_to_id_table[i], sizeof(*uuid)) == 0) + break; + + return i; +} + +//------------------------------------------------------------------------------ +// Effect functions +//------------------------------------------------------------------------------ + +static void session_set_fx_enabled(struct session_s *session, uint32_t id, bool enabled); + +#define BAD_STATE_ABORT(from, to) \ + LOG_ALWAYS_FATAL("Bad state transition from %d to %d", from, to); + +static int effect_set_state(struct effect_s *effect, uint32_t state) +{ + int status = 0; + ALOGV("effect_set_state() id %d, new %d old %d", effect->id, state, effect->state); + switch(state) { + case EFFECT_STATE_INIT: + switch(effect->state) { + case EFFECT_STATE_ACTIVE: + session_set_fx_enabled(effect->session, effect->id, false); + case EFFECT_STATE_CONFIG: + case EFFECT_STATE_CREATED: + case EFFECT_STATE_INIT: + break; + default: + BAD_STATE_ABORT(effect->state, state); + } + break; + case EFFECT_STATE_CREATED: + switch(effect->state) { + case EFFECT_STATE_INIT: + break; + case EFFECT_STATE_CREATED: + case EFFECT_STATE_ACTIVE: + case EFFECT_STATE_CONFIG: + ALOGE("effect_set_state() invalid transition"); + status = -ENOSYS; + break; + default: + BAD_STATE_ABORT(effect->state, state); + } + break; + case EFFECT_STATE_CONFIG: + switch(effect->state) { + case EFFECT_STATE_INIT: + ALOGE("effect_set_state() invalid transition"); + status = -ENOSYS; + break; + case EFFECT_STATE_ACTIVE: + session_set_fx_enabled(effect->session, effect->id, false); + break; + case EFFECT_STATE_CREATED: + case EFFECT_STATE_CONFIG: + break; + default: + BAD_STATE_ABORT(effect->state, state); + } + break; + case EFFECT_STATE_ACTIVE: + switch(effect->state) { + case EFFECT_STATE_INIT: + case EFFECT_STATE_CREATED: + ALOGE("effect_set_state() invalid transition"); + status = -ENOSYS; + break; + case EFFECT_STATE_ACTIVE: + // enabling an already enabled effect is just ignored + break; + case EFFECT_STATE_CONFIG: + session_set_fx_enabled(effect->session, effect->id, true); + break; + default: + BAD_STATE_ABORT(effect->state, state); + } + break; + default: + BAD_STATE_ABORT(effect->state, state); + } + + if (status == 0) + effect->state = state; + + return status; +} + +static int effect_init(struct effect_s *effect, uint32_t id) +{ + effect->itfe = &effect_interface; + effect->id = id; + effect->state = EFFECT_STATE_INIT; + return 0; +} + +static int effect_create(struct effect_s *effect, + struct session_s *session, + effect_handle_t *interface) +{ + effect->session = session; + *interface = (effect_handle_t)&effect->itfe; + return effect_set_state(effect, EFFECT_STATE_CREATED); +} + +static int effect_release(struct effect_s *effect) +{ + return effect_set_state(effect, EFFECT_STATE_INIT); +} + + +//------------------------------------------------------------------------------ +// Session functions +//------------------------------------------------------------------------------ + +static int session_init(struct session_s *session) +{ + size_t i; + int status = 0; + + session->state = SESSION_STATE_INIT; + session->id = 0; + session->io = 0; + session->created_msk = 0; + for (i = 0; i < NUM_ID && status == 0; i++) + status = effect_init(&session->effects[i], i); + + return status; +} + + +static int session_create_effect(struct session_s *session, + int32_t id, + effect_handle_t *interface) +{ + int status = -ENOMEM; + + ALOGV("session_create_effect() %s, created_msk %08x", + id == AEC_ID ? "AEC" : id == NS_ID ? "NS" : "?", session->created_msk); + + if (session->created_msk == 0) { + session->config.inputCfg.samplingRate = 16000; + session->config.inputCfg.channels = AUDIO_CHANNEL_IN_MONO; + session->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT; + session->config.outputCfg.samplingRate = 16000; + session->config.outputCfg.channels = AUDIO_CHANNEL_IN_MONO; + session->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT; + session->enabled_msk = 0; + session->processed_msk = 0; + } + status = effect_create(&session->effects[id], session, interface); + if (status < 0) + goto error; + + ALOGV("session_create_effect() OK"); + session->created_msk |= (1<id); + + session->created_msk &= ~(1<id); + if (session->created_msk == 0) + { + ALOGV("session_release_effect() last effect: removing session"); + list_remove(&session->node); + free(session); + } + + return 0; +} + + +static int session_set_config(struct session_s *session, effect_config_t *config) +{ + int status; + + if (config->inputCfg.samplingRate != config->outputCfg.samplingRate || + config->inputCfg.format != config->outputCfg.format || + config->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT) + return -EINVAL; + + ALOGV("session_set_config() sampling rate %d channels %08x", + config->inputCfg.samplingRate, config->inputCfg.channels); + + // if at least one process is enabled, do not accept configuration changes + if (session->enabled_msk) { + if (session->config.inputCfg.samplingRate != config->inputCfg.samplingRate || + session->config.inputCfg.channels != config->inputCfg.channels || + session->config.outputCfg.channels != config->outputCfg.channels) + return -ENOSYS; + else + return 0; + } + + memcpy(&session->config, config, sizeof(effect_config_t)); + + session->state = SESSION_STATE_CONFIG; + return 0; +} + +static void session_get_config(struct session_s *session, effect_config_t *config) +{ + memcpy(config, &session->config, sizeof(effect_config_t)); + + config->inputCfg.mask = config->outputCfg.mask = + (EFFECT_CONFIG_SMP_RATE | EFFECT_CONFIG_CHANNELS | EFFECT_CONFIG_FORMAT); +} + + +static void session_set_fx_enabled(struct session_s *session, uint32_t id, bool enabled) +{ + if (enabled) { + if(session->enabled_msk == 0) { + /* do first enable here */ + } + session->enabled_msk |= (1 << id); + } else { + session->enabled_msk &= ~(1 << id); + if(session->enabled_msk == 0) { + /* do last enable here */ + } + } + ALOGV("session_set_fx_enabled() id %d, enabled %d enabled_msk %08x", + id, enabled, session->enabled_msk); + session->processed_msk = 0; +} + +//------------------------------------------------------------------------------ +// Global functions +//------------------------------------------------------------------------------ + +static struct session_s *get_session(int32_t id, int32_t sessionId, int32_t ioId) +{ + size_t i; + int free = -1; + struct listnode *node; + struct session_s *session; + + list_for_each(node, &session_list) { + session = node_to_item(node, struct session_s, node); + if (session->io == ioId) { + if (session->created_msk & (1 << id)) { + ALOGV("get_session() effect %d already created", id); + return NULL; + } + ALOGV("get_session() found session %p", session); + return session; + } + } + + session = (struct session_s *)calloc(1, sizeof(struct session_s)); + if (session == NULL) { + ALOGE("get_session() fail to allocate memory"); + return NULL; + } + session_init(session); + session->id = sessionId; + session->io = ioId; + list_add_tail(&session_list, &session->node); + + ALOGV("get_session() created session %p", session); + + return session; +} + +static int init() { + void *lib_handle; + const effect_descriptor_t *desc; + + if (init_status <= 0) + return init_status; + + if (access(EFFECTS_DESCRIPTOR_LIBRARY_PATH, R_OK) == 0) { + lib_handle = dlopen(EFFECTS_DESCRIPTOR_LIBRARY_PATH, RTLD_NOW); + if (lib_handle == NULL) { + ALOGE("%s: DLOPEN failed for %s", __func__, EFFECTS_DESCRIPTOR_LIBRARY_PATH); + } else { + ALOGV("%s: DLOPEN successful for %s", __func__, EFFECTS_DESCRIPTOR_LIBRARY_PATH); + desc = (const effect_descriptor_t *)dlsym(lib_handle, + "qcom_product_aec_descriptor"); + if (desc) + descriptors[AEC_ID] = desc; + + desc = (const effect_descriptor_t *)dlsym(lib_handle, + "qcom_product_ns_descriptor"); + if (desc) + descriptors[NS_ID] = desc; + +//ENABLE_AGC +// desc = (const effect_descriptor_t *)dlsym(lib_handle, +// "qcom_product_agc_descriptor"); +// if (desc) +// descriptors[AGC_ID] = desc; + } + } + + uuid_to_id_table[AEC_ID] = FX_IID_AEC; + uuid_to_id_table[NS_ID] = FX_IID_NS; +//ENABLE_AGC uuid_to_id_table[AGC_ID] = FX_IID_AGC; + + list_init(&session_list); + + init_status = 0; + return init_status; +} + +static const effect_descriptor_t *get_descriptor(const effect_uuid_t *uuid) +{ + size_t i; + for (i = 0; i < NUM_ID; i++) + if (memcmp(&descriptors[i]->uuid, uuid, sizeof(effect_uuid_t)) == 0) + return descriptors[i]; + + return NULL; +} + + +//------------------------------------------------------------------------------ +// Effect Control Interface Implementation +//------------------------------------------------------------------------------ + +static int fx_process(effect_handle_t self, + audio_buffer_t *inBuffer, + audio_buffer_t *outBuffer) +{ + struct effect_s *effect = (struct effect_s *)self; + struct session_s *session; + + if (effect == NULL) { + ALOGV("fx_process() ERROR effect == NULL"); + return -EINVAL; + } + + if (inBuffer == NULL || inBuffer->raw == NULL || + outBuffer == NULL || outBuffer->raw == NULL) { + ALOGW("fx_process() ERROR bad pointer"); + return -EINVAL; + } + + session = (struct session_s *)effect->session; + + session->processed_msk |= (1<id); + + if ((session->processed_msk & session->enabled_msk) == session->enabled_msk) { + effect->session->processed_msk = 0; + return 0; + } else + return -ENODATA; +} + +static int fx_command(effect_handle_t self, + uint32_t cmdCode, + uint32_t cmdSize, + void *pCmdData, + uint32_t *replySize, + void *pReplyData) +{ + struct effect_s *effect = (struct effect_s *)self; + + if (effect == NULL) + return -EINVAL; + + //ALOGV("fx_command: command %d cmdSize %d",cmdCode, cmdSize); + + switch (cmdCode) { + case EFFECT_CMD_INIT: + if (pReplyData == NULL || *replySize != sizeof(int)) + return -EINVAL; + + *(int *)pReplyData = 0; + break; + + case EFFECT_CMD_SET_CONFIG: { + if (pCmdData == NULL|| + cmdSize != sizeof(effect_config_t)|| + pReplyData == NULL|| + *replySize != sizeof(int)) { + ALOGV("fx_command() EFFECT_CMD_SET_CONFIG invalid args"); + return -EINVAL; + } + *(int *)pReplyData = session_set_config(effect->session, (effect_config_t *)pCmdData); + if (*(int *)pReplyData != 0) + break; + + if (effect->state != EFFECT_STATE_ACTIVE) + *(int *)pReplyData = effect_set_state(effect, EFFECT_STATE_CONFIG); + + } break; + + case EFFECT_CMD_GET_CONFIG: + if (pReplyData == NULL || + *replySize != sizeof(effect_config_t)) { + ALOGV("fx_command() EFFECT_CMD_GET_CONFIG invalid args"); + return -EINVAL; + } + + session_get_config(effect->session, (effect_config_t *)pReplyData); + break; + + case EFFECT_CMD_RESET: + break; + + case EFFECT_CMD_GET_PARAM: { + if (pCmdData == NULL || + cmdSize < (int)sizeof(effect_param_t) || + pReplyData == NULL || + *replySize < (int)sizeof(effect_param_t)) { + ALOGV("fx_command() EFFECT_CMD_GET_PARAM invalid args"); + return -EINVAL; + } + effect_param_t *p = (effect_param_t *)pCmdData; + + memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + p->psize); + p = (effect_param_t *)pReplyData; + p->status = -ENOSYS; + + } break; + + case EFFECT_CMD_SET_PARAM: { + if (pCmdData == NULL|| + cmdSize < (int)sizeof(effect_param_t) || + pReplyData == NULL || + *replySize != sizeof(int32_t)) { + ALOGV("fx_command() EFFECT_CMD_SET_PARAM invalid args"); + return -EINVAL; + } + effect_param_t *p = (effect_param_t *) pCmdData; + + if (p->psize != sizeof(int32_t)) { + ALOGV("fx_command() EFFECT_CMD_SET_PARAM invalid param format"); + return -EINVAL; + } + *(int *)pReplyData = -ENOSYS; + } break; + + case EFFECT_CMD_ENABLE: + if (pReplyData == NULL || *replySize != sizeof(int)) { + ALOGV("fx_command() EFFECT_CMD_ENABLE invalid args"); + return -EINVAL; + } + *(int *)pReplyData = effect_set_state(effect, EFFECT_STATE_ACTIVE); + break; + + case EFFECT_CMD_DISABLE: + if (pReplyData == NULL || *replySize != sizeof(int)) { + ALOGV("fx_command() EFFECT_CMD_DISABLE invalid args"); + return -EINVAL; + } + *(int *)pReplyData = effect_set_state(effect, EFFECT_STATE_CONFIG); + break; + + case EFFECT_CMD_SET_DEVICE: + case EFFECT_CMD_SET_INPUT_DEVICE: + case EFFECT_CMD_SET_VOLUME: + case EFFECT_CMD_SET_AUDIO_MODE: + if (pCmdData == NULL || + cmdSize != sizeof(uint32_t)) { + ALOGV("fx_command() %s invalid args", + cmdCode == EFFECT_CMD_SET_DEVICE ? "EFFECT_CMD_SET_DEVICE" : + cmdCode == EFFECT_CMD_SET_INPUT_DEVICE ? "EFFECT_CMD_SET_INPUT_DEVICE" : + cmdCode == EFFECT_CMD_SET_VOLUME ? "EFFECT_CMD_SET_VOLUME" : + cmdCode == EFFECT_CMD_SET_AUDIO_MODE ? "EFFECT_CMD_SET_AUDIO_MODE" : + ""); + return -EINVAL; + } + ALOGV("fx_command() %s value %08x", + cmdCode == EFFECT_CMD_SET_DEVICE ? "EFFECT_CMD_SET_DEVICE" : + cmdCode == EFFECT_CMD_SET_INPUT_DEVICE ? "EFFECT_CMD_SET_INPUT_DEVICE" : + cmdCode == EFFECT_CMD_SET_VOLUME ? "EFFECT_CMD_SET_VOLUME" : + cmdCode == EFFECT_CMD_SET_AUDIO_MODE ? "EFFECT_CMD_SET_AUDIO_MODE": + "", + *(int *)pCmdData); + break; + + default: + return -EINVAL; + } + return 0; +} + + +static int fx_get_descriptor(effect_handle_t self, + effect_descriptor_t *pDescriptor) +{ + struct effect_s *effect = (struct effect_s *)self; + + if (effect == NULL || pDescriptor == NULL) + return -EINVAL; + + *pDescriptor = *descriptors[effect->id]; + + return 0; +} + + +// effect_handle_t interface implementation for effect +static const struct effect_interface_s effect_interface = { + fx_process, + fx_command, + fx_get_descriptor, + NULL +}; + +//------------------------------------------------------------------------------ +// Effect Library Interface Implementation +//------------------------------------------------------------------------------ + +static int lib_create(const effect_uuid_t *uuid, + int32_t sessionId, + int32_t ioId, + effect_handle_t *pInterface) +{ + ALOGV("lib_create: uuid: %08x session %d IO: %d", uuid->timeLow, sessionId, ioId); + + int status; + const effect_descriptor_t *desc; + struct session_s *session; + uint32_t id; + + if (init() != 0) + return init_status; + + desc = get_descriptor(uuid); + + if (desc == NULL) { + ALOGW("lib_create: fx not found uuid: %08x", uuid->timeLow); + return -EINVAL; + } + id = uuid_to_id(&desc->type); + if (id >= NUM_ID) { + ALOGW("lib_create: fx not found type: %08x", desc->type.timeLow); + return -EINVAL; + } + + session = get_session(id, sessionId, ioId); + + if (session == NULL) { + ALOGW("lib_create: no more session available"); + return -EINVAL; + } + + status = session_create_effect(session, id, pInterface); + + if (status < 0 && session->created_msk == 0) { + list_remove(&session->node); + free(session); + } + return status; +} + +static int lib_release(effect_handle_t interface) +{ + struct listnode *node; + struct session_s *session; + + ALOGV("lib_release %p", interface); + if (init() != 0) + return init_status; + + struct effect_s *fx = (struct effect_s *)interface; + + list_for_each(node, &session_list) { + session = node_to_item(node, struct session_s, node); + if (session == fx->session) { + session_release_effect(fx->session, fx); + return 0; + } + } + + return -EINVAL; +} + +static int lib_get_descriptor(const effect_uuid_t *uuid, + effect_descriptor_t *pDescriptor) +{ + const effect_descriptor_t *desc; + + if (pDescriptor == NULL || uuid == NULL) + return -EINVAL; + + if (init() != 0) + return init_status; + + desc = get_descriptor(uuid); + if (desc == NULL) { + ALOGV("lib_get_descriptor() not found"); + return -EINVAL; + } + + ALOGV("lib_get_descriptor() got fx %s", desc->name); + + *pDescriptor = *desc; + return 0; +} + +// This is the only symbol that needs to be exported +__attribute__ ((visibility ("default"))) +audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { + tag : AUDIO_EFFECT_LIBRARY_TAG, + version : EFFECT_LIBRARY_API_VERSION, + name : "MSM8960 Audio Preprocessing Library", + implementor : "The Android Open Source Project", + create_effect : lib_create, + release_effect : lib_release, + get_descriptor : lib_get_descriptor +}; -- cgit v1.2.3