Permalink
Browse files

alsa_sound: Add hdmi audio sink capability discovery

- Add hdmi sink capabilities parsing utility to support
  multi-channel output configuration.
- Update getParameters to calculate supported channels by
  hdmi sink.
- Update alsa_default to calculate channel count to set control
  option.

Bug: 7156174
Change-Id: Iabb9844c1e5a8b7aa7f168992f8beef79b7df8d2
Signed-off-by: Iliyan Malchev <malchev@google.com>
  • Loading branch information...
1 parent 87459f0 commit b357a77dc9c72cb6e327e945421f440052233b51 SathishKumar Mani committed with Iliyan Malchev Sep 26, 2012
Showing with 429 additions and 3 deletions.
  1. +36 −1 alsa_sound/ALSAStreamOps.cpp
  2. +4 −2 alsa_sound/Android.mk
  3. +279 −0 alsa_sound/AudioUtil.cpp
  4. +71 −0 alsa_sound/AudioUtil.h
  5. +39 −0 alsa_sound/alsa_default.cpp
@@ -33,12 +33,16 @@
#include <cutils/properties.h>
#include <media/AudioRecord.h>
#include <hardware_legacy/power.h>
-
+#include "AudioUtil.h"
#include "AudioHardwareALSA.h"
namespace android_audio_legacy
{
+// unused 'enumVal;' is to catch error at compile time if enumVal ever changes
+// or applied on a non-existent enum
+#define ENUM_TO_STRING(var, enumVal) {var = #enumVal; enumVal;}
+
// ----------------------------------------------------------------------------
ALSAStreamOps::ALSAStreamOps(AudioHardwareALSA *parent, alsa_handle_t *handle) :
@@ -254,6 +258,37 @@ String8 ALSAStreamOps::getParameters(const String8& keys)
}
#endif
}
+ key = String8(AUDIO_PARAMETER_STREAM_SUP_CHANNELS);
+ if (param.get(key, value) == NO_ERROR) {
+ EDID_AUDIO_INFO info = { 0 };
+ bool first = true;
+ value = String8();
+ if (AudioUtil::getHDMIAudioSinkCaps(&info)) {
+ for (int i = 0; i < info.nAudioBlocks && i < MAX_EDID_BLOCKS; i++) {
+ String8 append;
+ switch (info.AudioBlocksArray[i].nChannels) {
+ //Do not handle stereo output in Multi-channel cases
+ //Stereo case is handled in normal playback path
+ case 6:
+ ENUM_TO_STRING(append, AUDIO_CHANNEL_OUT_5POINT1);
+ break;
+ case 8:
+ ENUM_TO_STRING(append, AUDIO_CHANNEL_OUT_7POINT1);
+ break;
+ default:
+ ALOGD("Unsupported number of channels %d", info.AudioBlocksArray[i].nChannels);
+ break;
+ }
+ if (!append.isEmpty()) {
+ value += (first ? append : String8("|") + append);
+ first = false;
+ }
+ }
+ } else {
+ ALOGE("Failed to get HDMI sink capabilities");
+ }
+ param.add(key, value);
+ }
ALOGV("getParameters() %s", param.toString().string());
return param.toString();
}
View
@@ -28,7 +28,8 @@ LOCAL_SRC_FILES := \
AudioStreamInALSA.cpp \
ALSAStreamOps.cpp \
audio_hw_hal.cpp \
- AudioUsbALSA.cpp
+ AudioUsbALSA.cpp \
+ AudioUtil.cpp
LOCAL_STATIC_LIBRARIES := \
libmedia_helper \
@@ -127,7 +128,8 @@ LOCAL_C_INCLUDES += $(TARGET_OUT_HEADERS)/mm-audio/libalsa-intf
LOCAL_SRC_FILES:= \
alsa_default.cpp \
- ALSAControl.cpp
+ ALSAControl.cpp \
+ AudioUtil.cpp
LOCAL_SHARED_LIBRARIES := \
libcutils \
View
@@ -0,0 +1,279 @@
+/* AudioUtil.cpp
+ *
+ * Copyright (C) 2012 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 "AudioUtil"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include "AudioUtil.h"
+
+int AudioUtil::printFormatFromEDID(unsigned char format) {
+ switch (format) {
+ case LPCM:
+ ALOGV("Format:LPCM");
+ break;
+ case AC3:
+ ALOGV("Format:AC-3");
+ break;
+ case MPEG1:
+ ALOGV("Format:MPEG1 (Layers 1 & 2)");
+ break;
+ case MP3:
+ ALOGV("Format:MP3 (MPEG1 Layer 3)");
+ break;
+ case MPEG2_MULTI_CHANNEL:
+ ALOGV("Format:MPEG2 (multichannel)");
+ break;
+ case AAC:
+ ALOGV("Format:AAC");
+ break;
+ case DTS:
+ ALOGV("Format:DTS");
+ break;
+ case ATRAC:
+ ALOGV("Format:ATRAC");
+ break;
+ case SACD:
+ ALOGV("Format:One-bit audio aka SACD");
+ break;
+ case DOLBY_DIGITAL_PLUS:
+ ALOGV("Format:Dolby Digital +");
+ break;
+ case DTS_HD:
+ ALOGV("Format:DTS-HD");
+ break;
+ case MAT:
+ ALOGV("Format:MAT (MLP)");
+ break;
+ case DST:
+ ALOGV("Format:DST");
+ break;
+ case WMA_PRO:
+ ALOGV("Format:WMA Pro");
+ break;
+ default:
+ ALOGV("Invalid format ID....");
+ break;
+ }
+ return format;
+}
+
+int AudioUtil::getSamplingFrequencyFromEDID(unsigned char byte) {
+ int nFreq = 0;
+
+ if (byte & BIT(6)) {
+ ALOGV("192kHz");
+ nFreq = 192000;
+ } else if (byte & BIT(5)) {
+ ALOGV("176kHz");
+ nFreq = 176000;
+ } else if (byte & BIT(4)) {
+ ALOGV("96kHz");
+ nFreq = 96000;
+ } else if (byte & BIT(3)) {
+ ALOGV("88.2kHz");
+ nFreq = 88200;
+ } else if (byte & BIT(2)) {
+ ALOGV("48kHz");
+ nFreq = 48000;
+ } else if (byte & BIT(1)) {
+ ALOGV("44.1kHz");
+ nFreq = 44100;
+ } else if (byte & BIT(0)) {
+ ALOGV("32kHz");
+ nFreq = 32000;
+ }
+ return nFreq;
+}
+
+int AudioUtil::getBitsPerSampleFromEDID(unsigned char byte,
+ unsigned char format) {
+ int nBitsPerSample = 0;
+ if (format == 1) {
+ if (byte & BIT(2)) {
+ ALOGV("24bit");
+ nBitsPerSample = 24;
+ } else if (byte & BIT(1)) {
+ ALOGV("20bit");
+ nBitsPerSample = 20;
+ } else if (byte & BIT(0)) {
+ ALOGV("16bit");
+ nBitsPerSample = 16;
+ }
+ } else {
+ ALOGV("not lpcm format, return 0");
+ return 0;
+ }
+ return nBitsPerSample;
+}
+
+bool AudioUtil::getHDMIAudioSinkCaps(EDID_AUDIO_INFO* pInfo) {
+ unsigned char channels[16];
+ unsigned char formats[16];
+ unsigned char frequency[16];
+ unsigned char bitrate[16];
+ unsigned char* data = NULL;
+ unsigned char* original_data_ptr = NULL;
+ int count = 0;
+ bool bRet = false;
+ const char* file = "/sys/class/graphics/fb1/audio_data_block";
+ FILE* fpaudiocaps = fopen(file, "rb");
+ if (fpaudiocaps) {
+ ALOGV("opened audio_caps successfully...");
+ fseek(fpaudiocaps, 0, SEEK_END);
+ long size = ftell(fpaudiocaps);
+ ALOGV("audiocaps size is %ld\n",size);
+ data = (unsigned char*) malloc(size);
+ if (data) {
+ fseek(fpaudiocaps, 0, SEEK_SET);
+ original_data_ptr = data;
+ fread(data, 1, size, fpaudiocaps);
+ }
+ fclose(fpaudiocaps);
+ } else {
+ ALOGE("failed to open audio_caps");
+ }
+
+ if (pInfo && data) {
+ int length = 0;
+ memcpy(&count, data, sizeof(int));
+ data+= sizeof(int);
+ ALOGV("#Audio Block Count is %d",count);
+ memcpy(&length, data, sizeof(int));
+ data += sizeof(int);
+ ALOGV("Total length is %d",length);
+ unsigned int sad[MAX_SHORT_AUDIO_DESC_CNT];
+ int nblockindex = 0;
+ int nCountDesc = 0;
+ while (length >= MIN_AUDIO_DESC_LENGTH && count < MAX_SHORT_AUDIO_DESC_CNT) {
+ sad[nblockindex] = (unsigned int)data[0] + ((unsigned int)data[1] << 8)
+ + ((unsigned int)data[2] << 16);
+ nblockindex+=1;
+ nCountDesc++;
+ length -= MIN_AUDIO_DESC_LENGTH;
+ data += MIN_AUDIO_DESC_LENGTH;
+ }
+ memset(pInfo, 0, sizeof(EDID_AUDIO_INFO));
+ pInfo->nAudioBlocks = nCountDesc;
+ ALOGV("Total # of audio descriptors %d",nCountDesc);
+ int nIndex = 0;
+ while (nCountDesc--) {
+ channels [nIndex] = (sad[nIndex] & 0x7) + 1;
+ formats [nIndex] = (sad[nIndex] & 0xFF) >> 3;
+ frequency[nIndex] = (sad[nIndex] >> 8) & 0xFF;
+ bitrate [nIndex] = (sad[nIndex] >> 16) & 0xFF;
+ nIndex++;
+ }
+ bRet = true;
+ for (int i = 0; i < pInfo->nAudioBlocks; i++) {
+ ALOGV("AUDIO DESC BLOCK # %d\n",i);
+
+ pInfo->AudioBlocksArray[i].nChannels = channels[i];
+ ALOGV("pInfo->AudioBlocksArray[i].nChannels %d\n", pInfo->AudioBlocksArray[i].nChannels);
+
+ ALOGV("Format Byte %d\n", formats[i]);
+ pInfo->AudioBlocksArray[i].nFormatId = (EDID_AUDIO_FORMAT_ID)printFormatFromEDID(formats[i]);
+ ALOGV("pInfo->AudioBlocksArray[i].nFormatId %d",pInfo->AudioBlocksArray[i].nFormatId);
+
+ ALOGV("Frequency Byte %d\n", frequency[i]);
+ pInfo->AudioBlocksArray[i].nSamplingFreq = getSamplingFrequencyFromEDID(frequency[i]);
+ ALOGV("pInfo->AudioBlocksArray[i].nSamplingFreq %d",pInfo->AudioBlocksArray[i].nSamplingFreq);
+
+ ALOGV("BitsPerSample Byte %d\n", bitrate[i]);
+ pInfo->AudioBlocksArray[i].nBitsPerSample = getBitsPerSampleFromEDID(bitrate[i],formats[i]);
+ ALOGV("pInfo->AudioBlocksArray[i].nBitsPerSample %d",pInfo->AudioBlocksArray[i].nBitsPerSample);
+ }
+ getSpeakerAllocation(pInfo);
+ }
+ if (original_data_ptr)
+ free(original_data_ptr);
+
+ return bRet;
+}
+
+bool AudioUtil::getSpeakerAllocation(EDID_AUDIO_INFO* pInfo) {
+ int count = 0;
+ bool bRet = false;
+ unsigned char* data = NULL;
+ unsigned char* original_data_ptr = NULL;
+ const char* spkrfile = "/sys/class/graphics/fb1/spkr_alloc_data_block";
+ FILE* fpspkrfile = fopen(spkrfile, "rb");
+ if(fpspkrfile) {
+ ALOGV("opened spkr_alloc_data_block successfully...");
+ fseek(fpspkrfile,0,SEEK_END);
+ long size = ftell(fpspkrfile);
+ ALOGV("fpspkrfile size is %ld\n",size);
+ data = (unsigned char*)malloc(size);
+ if(data) {
+ original_data_ptr = data;
+ fseek(fpspkrfile,0,SEEK_SET);
+ fread(data,1,size,fpspkrfile);
+ }
+ fclose(fpspkrfile);
+ } else {
+ ALOGE("failed to open fpspkrfile");
+ }
+
+ if(pInfo && data) {
+ int length = 0;
+ memcpy(&count, data, sizeof(int));
+ ALOGV("Count is %d",count);
+ data += sizeof(int);
+ memcpy(&length, data, sizeof(int));
+ ALOGV("Total length is %d",length);
+ data+= sizeof(int);
+ ALOGV("Total speaker allocation Block count # %d\n",count);
+ bRet = true;
+ for (int i = 0; i < count; i++) {
+ ALOGV("Speaker Allocation BLOCK # %d\n",i);
+ pInfo->nSpeakerAllocation[0] = data[0];
+ pInfo->nSpeakerAllocation[1] = data[1];
+ pInfo->nSpeakerAllocation[2] = data[2];
+ ALOGV("pInfo->nSpeakerAllocation %x %x %x\n", data[0],data[1],data[2]);
+
+
+ if (pInfo->nSpeakerAllocation[0] & BIT(7)) {
+ ALOGV("FLW/FRW");
+ } else if (pInfo->nSpeakerAllocation[0] & BIT(6)) {
+ ALOGV("RLC/RRC");
+ } else if (pInfo->nSpeakerAllocation[0] & BIT(5)) {
+ ALOGV("FLC/FRC");
+ } else if (pInfo->nSpeakerAllocation[0] & BIT(4)) {
+ ALOGV("RC");
+ } else if (pInfo->nSpeakerAllocation[0] & BIT(3)) {
+ ALOGV("RL/RR");
+ } else if (pInfo->nSpeakerAllocation[0] & BIT(2)) {
+ ALOGV("FC");
+ } else if (pInfo->nSpeakerAllocation[0] & BIT(1)) {
+ ALOGV("LFE");
+ } else if (pInfo->nSpeakerAllocation[0] & BIT(0)) {
+ ALOGV("FL/FR");
+ }
+
+ if (pInfo->nSpeakerAllocation[1] & BIT(2)) {
+ ALOGV("FCH");
+ } else if (pInfo->nSpeakerAllocation[1] & BIT(1)) {
+ ALOGV("TC");
+ } else if (pInfo->nSpeakerAllocation[1] & BIT(0)) {
+ ALOGV("FLH/FRH");
+ }
+ }
+ }
+ if (original_data_ptr)
+ free(original_data_ptr);
+ return bRet;
+}
Oops, something went wrong.

0 comments on commit b357a77

Please sign in to comment.