649 lines
19 KiB
C
Executable File
649 lines
19 KiB
C
Executable File
/*
|
|
* 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.
|
|
*/
|
|
/**
|
|
* @file audio_hw_hdmi.c
|
|
* @brief
|
|
* @author RkAudio
|
|
* @version 1.0.8
|
|
* @date 2015-08-24
|
|
*/
|
|
|
|
#define LOG_TAG "audio_hdmi_parser"
|
|
|
|
#include "audio_hw_hdmi.h"
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <cutils/log.h>
|
|
|
|
#ifdef USE_DRM
|
|
#define HDMI_EDID_NODE "/sys/class/drm/card0-HDMI-A-1/edid"
|
|
#else
|
|
#define HDMI_EDID_NODE "/sys/class/display/HDMI/edid"
|
|
#endif
|
|
|
|
//#define DEBUG
|
|
#ifdef DEBUG
|
|
#define ALOGVV ALOGD
|
|
#else
|
|
#define ALOGVV
|
|
#endif
|
|
|
|
#define HDMI_EDID_BLOCK_SIZE 128
|
|
#define HDMI_MAX_EDID_BLOCK 8
|
|
#define HDMI_AUDIO_LPCM 1
|
|
|
|
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
|
|
|
|
struct HDMI_Audio_Sample {
|
|
int index;
|
|
int sample;
|
|
};
|
|
|
|
const struct HDMI_Audio_Sample HDMI_SAMPLE_TABLE[] =
|
|
{
|
|
{HDMI_AUDIO_FS_32000,32000},
|
|
{HDMI_AUDIO_FS_44100,44100},
|
|
{HDMI_AUDIO_FS_48000,48000},
|
|
{HDMI_AUDIO_FS_88200,88200},
|
|
{HDMI_AUDIO_FS_96000,96000},
|
|
{HDMI_AUDIO_FS_176400,176400},
|
|
{HDMI_AUDIO_FS_192000,192000},
|
|
};
|
|
|
|
struct HDMI_Audio_Speaker_Allocation {
|
|
int index;
|
|
int location;
|
|
char* name;
|
|
};
|
|
|
|
/*
|
|
* the allocation is defined in CEA-861
|
|
*/
|
|
const struct HDMI_Audio_Speaker_Allocation HDMI_SPEAKER_ALLOCATION_TABLE[] =
|
|
{
|
|
{(1<<0), AUDIO_CHANNEL_OUT_STEREO ,"FL/FR"},
|
|
{(1<<1), AUDIO_CHANNEL_OUT_LOW_FREQUENCY ,"LFE"},
|
|
{(1<<2), AUDIO_CHANNEL_OUT_FRONT_CENTER ,"FC"},
|
|
{(1<<3), (AUDIO_CHANNEL_OUT_SIDE_LEFT|AUDIO_CHANNEL_OUT_SIDE_RIGHT), "SL/SR"},
|
|
{(1<<4), AUDIO_CHANNEL_OUT_BACK_CENTER ,"RC"},
|
|
{(1<<5), (AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER|AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER), "FLC/FRC"},
|
|
{(1<<6), (AUDIO_CHANNEL_OUT_BACK_LEFT|AUDIO_CHANNEL_OUT_BACK_RIGHT), "RLC/RRC"},
|
|
{(1<<7), AUDIO_CHANNEL_NONE, "FLW/FRW"}, // no FLW/FRW defined in AUDIO_CHANNEL_OUT_xx
|
|
{(1<<8), (AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT|AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT), "FLH/FRH"},
|
|
{(1<<9), AUDIO_CHANNEL_OUT_TOP_CENTER, "TC"},
|
|
{(1<<10), AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER, "FCH"},
|
|
};
|
|
|
|
int hdmi_edid_checksum(unsigned char *buf)
|
|
{
|
|
int i;
|
|
int checksum = 0;
|
|
if(buf == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
for(i = 0; i < HDMI_EDID_BLOCK_SIZE; i++)
|
|
checksum += buf[i];
|
|
|
|
checksum &= 0xff;
|
|
ALOGVV("%s checksum is %x\n", __FUNCTION__, checksum);
|
|
int result = 0;
|
|
if(checksum == 0) {
|
|
result = 0;
|
|
} else {
|
|
result = -1;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool translate_sample(unsigned char index,unsigned int* sample,int size)
|
|
{
|
|
bool result = false;
|
|
int i = 0, j = 0;
|
|
int sample_array_length = ARRAY_SIZE(HDMI_SAMPLE_TABLE);
|
|
for(i = 0; i < sample_array_length; i++) {
|
|
// every bit mean a samplerate
|
|
if(index & HDMI_SAMPLE_TABLE[i].index) {
|
|
sample[j] = HDMI_SAMPLE_TABLE[i].sample;
|
|
if(j < size){
|
|
j++;
|
|
}
|
|
|
|
result = true;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
int hdmi_edid_parse_cea_audio(unsigned char *buf, struct hdmi_audio_infors *infor)
|
|
{
|
|
int i, count;
|
|
struct hdmi_audio_information* audio = NULL;
|
|
if((buf == NULL) || (infor == NULL)) {
|
|
ALOGD("%s: error, input parameter is NULL",__FUNCTION__);
|
|
return -1;
|
|
}
|
|
|
|
count = buf[0] & 0x1F;
|
|
if(count > 0) {
|
|
audio = malloc(count*sizeof(struct hdmi_audio_information));
|
|
if (audio == NULL) {
|
|
ALOGD("%s: malloc hdmi_audio_information fail",__FUNCTION__);
|
|
return -1;
|
|
}
|
|
|
|
memset(audio,0,count*sizeof(struct hdmi_audio_information));
|
|
int audio_num = count / 3;
|
|
unsigned char sample = 0;
|
|
for (i = 0; i < count; i++) {
|
|
audio[i].type = (buf[i * 3 + 1] >> 3) & 0x0F;
|
|
audio[i].channel = (buf[i * 3 + 1] & 0x07) + 1;
|
|
audio[i].sample = buf[i * 3 + 2];
|
|
audio[i].value = buf[i * 3 + 3];
|
|
// translate_sample(sample,audio[i].sample,8);
|
|
|
|
if (audio[i].type == HDMI_AUDIO_LPCM) {
|
|
audio[i].word_length = buf[i * 3 + 3];
|
|
} else if(audio[i].type >= HDMI_AUDIO_AC3 && audio[i].type <= HDMI_AUDIO_ATARC) {
|
|
audio[i].max_bitrate = 8000*buf[i * 3 + 3];
|
|
}
|
|
|
|
ALOGVV("%s: i = %d, type = %d,channel = %d, sample = %d,value = 0x%x",
|
|
__FUNCTION__,i,audio[i].type,audio[i].channel,audio[i].sample,audio[i].value);
|
|
}
|
|
|
|
infor->number = count;
|
|
infor->audio = audio;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* see document <cea-861-e.pdf> Part: Speaker Allocation Data Block(Page 67)
|
|
* multi LPCM
|
|
*/
|
|
int hdmi_edid_parse_speaker_allocation(unsigned char *buf, struct hdmi_audio_infors *infor)
|
|
{
|
|
if((buf == NULL) || (infor == NULL)) {
|
|
ALOGD("%s: error, input parameter is NULL",__FUNCTION__);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* bytes in buf[1] ~ buf[3] is valid, see CEA-861 part:Speaker Allocation Data Block
|
|
*/
|
|
// int tag = (buf[0]>>5);
|
|
// int length = buf[0] & 0x1f;
|
|
int hightlayout = (buf[2] & 0x07); // hight 5 bit is reseved
|
|
infor->channel_layout = ((hightlayout<<8) | buf[1]);
|
|
ALOGVV("%s : %d,buf[1]= 0x%x,buf[2]= 0x%x, layout = 0x%x",__FUNCTION__,__LINE__,buf[1],buf[2],infor->channel_layout);
|
|
return 0;
|
|
}
|
|
|
|
int hdmi_edid_parse_extensions_cea(unsigned char *buf, struct hdmi_audio_infors *infor)
|
|
{
|
|
unsigned int ddc_offset, native_dtd_num, cur_offset = 4;
|
|
unsigned int tag, IEEEOUI = 0, count, i;
|
|
struct fb_videomode *vmode;
|
|
|
|
if((buf == NULL) || (infor == NULL)) {
|
|
ALOGD("%s: error, input parameter is NULL",__FUNCTION__);
|
|
return -1;
|
|
}
|
|
|
|
/* Check ces extension version */
|
|
if (buf[1] != 3) {
|
|
ALOGD("%s: %d [CEA] error version",__FUNCTION__,__LINE__);
|
|
return -1;
|
|
}
|
|
|
|
ddc_offset = buf[2];
|
|
/* Parse data block */
|
|
while (cur_offset < ddc_offset) {
|
|
tag = buf[cur_offset] >> 5;
|
|
count = buf[cur_offset] & 0x1F;
|
|
switch (tag) {
|
|
case 0x02: /* Video Data Block */
|
|
ALOGVV("%s: [CEA] Video Data Block",__FUNCTION__);
|
|
break;
|
|
case 0x01: /* Audio Data Block */
|
|
ALOGVV("%s [CEA] Audio Data Block",__FUNCTION__);
|
|
hdmi_edid_parse_cea_audio(buf + cur_offset,infor);
|
|
break;
|
|
case 0x04: /* Speaker Allocation Data Block */
|
|
ALOGVV("%s: [CEA] Speaker Allocatio Data Block",__FUNCTION__);
|
|
hdmi_edid_parse_speaker_allocation(buf + cur_offset,infor);
|
|
break;
|
|
case 0x03: /* Vendor Specific Data Block */
|
|
ALOGVV("%s: [CEA] Vendor Specific Data Block",__FUNCTION__);
|
|
break;
|
|
case 0x05: /* VESA DTC Data Block */
|
|
ALOGVV("%s: [CEA] VESA DTC Data Block",__FUNCTION__);
|
|
break;
|
|
case 0x07: /* Use Extended Tag */
|
|
ALOGVV("%s: [CEA] Use Extended Tag Data Block %02x",__FUNCTION__,buf[cur_offset + 1]);
|
|
switch (buf[cur_offset + 1]) {
|
|
case 0x00:
|
|
ALOGVV("%s: [CEA] Video Capability Data Block",__FUNCTION__);
|
|
break;
|
|
case 0x05:
|
|
ALOGVV("%s: [CEA] Colorimetry Data Block",__FUNCTION__);
|
|
break;
|
|
case 0x06:
|
|
ALOGVV("%s: [CEA] HDR Static Metedata data Block",__FUNCTION__);
|
|
break;
|
|
case 0x0e:
|
|
ALOGVV("%s: [CEA] YCBCR 4:2:0 Video Data Block",__FUNCTION__);
|
|
break;
|
|
case 0x0f:
|
|
ALOGVV("%s: [CEA] YCBCR 4:2:0 Capability Map Data",__FUNCTION__);
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
ALOGVV("%s: [CEA] unkowned data block tag",__FUNCTION__);
|
|
break;
|
|
}
|
|
cur_offset += (buf[cur_offset] & 0x1F) + 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int hdmi_edid_parse_extensions(unsigned char *buf, struct hdmi_audio_infors *infor)
|
|
{
|
|
int rc;
|
|
|
|
if((buf == NULL) || (infor == NULL)) {
|
|
ALOGD("%s: error, input parameter is NULL",__FUNCTION__);
|
|
return -1;
|
|
}
|
|
|
|
/* Checksum */
|
|
rc = hdmi_edid_checksum(buf);
|
|
if (rc != 0) {
|
|
ALOGE("%s: [EDID] extensions block checksum error",__FUNCTION__);
|
|
return -1;
|
|
}
|
|
switch (buf[0]) {
|
|
case 0xF0:
|
|
ALOGVV("%s: [EDID-EXTEND] Iextensions block map",__FUNCTION__);
|
|
break;
|
|
case 0x02:
|
|
ALOGVV("%s: [EDID-EXTEND] CEA 861 Series Extension",__FUNCTION__);
|
|
hdmi_edid_parse_extensions_cea(buf, infor);
|
|
break;
|
|
case 0x10:
|
|
ALOGVV("%s: [EDID-EXTEND] Video Timing Block Extension",__FUNCTION__);
|
|
break;
|
|
case 0x40:
|
|
ALOGVV("%s: [EDID-EXTEND] Display Information Extension",__FUNCTION__);
|
|
break;
|
|
case 0x50:
|
|
ALOGVV("%s: [EDID-EXTEND] Localized String Extension",__FUNCTION__);
|
|
break;
|
|
case 0x60:
|
|
ALOGVV("%s: [EDID-EXTEND] Digital Packet Video Link Extension",__FUNCTION__);
|
|
break;
|
|
default:
|
|
ALOGVV("%s: [EDID-EXTEND] Unkowned Extension,tag = 0x%x",__FUNCTION__,buf[0]);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hdmi_parse_base_block(unsigned char *buf, int *extend_num)
|
|
{
|
|
if(buf == NULL || extend_num == NULL)
|
|
return -1;
|
|
|
|
*extend_num = (int)buf[0x7e];
|
|
// skip other informations
|
|
return 0;
|
|
}
|
|
|
|
void init_hdmi_audio(struct hdmi_audio_infors *infor)
|
|
{
|
|
if(infor != NULL) {
|
|
pthread_mutex_init(&infor->lock, NULL);
|
|
infor->number = 0;
|
|
infor->channel_layout = -1;
|
|
infor->audio = NULL;
|
|
}
|
|
}
|
|
|
|
void destory_hdmi_audio(struct hdmi_audio_infors *infor)
|
|
{
|
|
if(infor != NULL) {
|
|
pthread_mutex_lock(&infor->lock);
|
|
if(infor->audio != NULL) {
|
|
free(infor->audio);
|
|
infor->audio = NULL;
|
|
}
|
|
|
|
infor->number = 0;
|
|
infor->channel_layout = -1;
|
|
pthread_mutex_unlock(&infor->lock);
|
|
pthread_mutex_destroy(&infor->lock);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* get the speaker allocation
|
|
* only valid when output stream is pcm
|
|
*/
|
|
int get_hdmi_audio_speaker_allocation(struct hdmi_audio_infors *infor)
|
|
{
|
|
if((infor == NULL) || (infor->channel_layout == -1)) {
|
|
return AUDIO_CHANNEL_OUT_STEREO;
|
|
}
|
|
|
|
pthread_mutex_lock(&infor->lock);
|
|
int layout = AUDIO_CHANNEL_NONE;
|
|
int length = ARRAY_SIZE(HDMI_SPEAKER_ALLOCATION_TABLE);
|
|
for(int i = 0; i < length; i++) {
|
|
if(infor->channel_layout & HDMI_SPEAKER_ALLOCATION_TABLE[i].index) {
|
|
layout |= HDMI_SPEAKER_ALLOCATION_TABLE[i].location;
|
|
}
|
|
}
|
|
pthread_mutex_unlock(&infor->lock);
|
|
return layout;
|
|
}
|
|
|
|
int parse_hdmi_audio(struct hdmi_audio_infors *infor)
|
|
{
|
|
if(infor == NULL) {
|
|
ALOGD("%s: error, input parameter is NULL",__FUNCTION__);
|
|
return -1;
|
|
}
|
|
|
|
FILE* file = fopen(HDMI_EDID_NODE,"rb");
|
|
if(file == NULL) {
|
|
ALOGVV("%s: open %s fail,reason = %s",__FUNCTION__,HDMI_EDID_NODE,strerror(errno));
|
|
return -1;
|
|
}
|
|
pthread_mutex_lock(&infor->lock);
|
|
// there is 128 bytes in every block
|
|
unsigned char buffer[HDMI_EDID_BLOCK_SIZE];
|
|
memset(buffer, 0, HDMI_EDID_BLOCK_SIZE);
|
|
int size = 0;
|
|
int retry = 20;
|
|
do{
|
|
/*
|
|
* using hdmi drm function to get this information is a better way,
|
|
* but using this functions need System permissions.
|
|
* May be this is no information in Node HDMI_EDID_NODE,
|
|
* so if read fail ,we retry to read.
|
|
*/
|
|
size = fread(buffer, 1, HDMI_EDID_BLOCK_SIZE, file);
|
|
if(size == 0){
|
|
usleep(20000);
|
|
retry --;
|
|
}
|
|
}while((size==0) && (retry>0));
|
|
ALOGVV("%s: %d: size = %d",__FUNCTION__,__LINE__,size);
|
|
int extendblock = 0;
|
|
int i = 0;
|
|
|
|
// parse base block, we just need the information of the numbers of extend block
|
|
hdmi_parse_base_block(buffer, &extendblock);
|
|
ALOGVV("%s: %d: extendblock = %d",__FUNCTION__,__LINE__,extendblock);
|
|
// paser extend block
|
|
if(extendblock > 0) {
|
|
for (i = 1; (i < extendblock + 1) && (i < HDMI_MAX_EDID_BLOCK); i++) {
|
|
memset(buffer, 0, HDMI_EDID_BLOCK_SIZE);
|
|
size = fread(buffer, 1, HDMI_EDID_BLOCK_SIZE, file);
|
|
if(size > 0) {
|
|
hdmi_edid_parse_extensions(buffer,infor);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
pthread_mutex_unlock(&infor->lock);
|
|
|
|
dump(infor);
|
|
EXIT:
|
|
if(file != NULL) {
|
|
fclose(file);
|
|
file = NULL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int translate_format(audio_format_t format)
|
|
{
|
|
int type = (int)HDMI_AUDIO_FORMAT_INVALID;
|
|
switch(format)
|
|
{
|
|
case AUDIO_FORMAT_AC3:
|
|
type = (int)HDMI_AUDIO_AC3;
|
|
break;
|
|
case AUDIO_FORMAT_E_AC3:
|
|
type = (int)HDMI_AUDIO_E_AC3;
|
|
break;
|
|
case AUDIO_FORMAT_DTS:
|
|
type = (int)HDMI_AUDIO_DTS;
|
|
break;
|
|
case AUDIO_FORMAT_DTS_HD:
|
|
type = (int)HDMI_AUDIO_DTS_HD;
|
|
break;
|
|
case AUDIO_FORMAT_AAC_LC:
|
|
type = (int)HDMI_AUDIO_AAC_LC;
|
|
break;
|
|
case AUDIO_FORMAT_DOLBY_TRUEHD:
|
|
type = (int)HDMI_AUDIO_MLP;
|
|
break;
|
|
case AUDIO_FORMAT_AC4:
|
|
type = (int)HDMI_AUDIO_E_AC3;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
bool is_support_ac4(int type,int support)
|
|
{
|
|
/* Bits of byte 3
|
|
* bit0 = 1 Decoding of joint object coding content is supported
|
|
* bit1 = 1 Decoding of joint object coding content with ACMOD 28 is supported
|
|
*/
|
|
if((type == (int)HDMI_AUDIO_E_AC3) && (support & 0x1)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
bool is_support_format(struct hdmi_audio_infors *infor,audio_format_t format)
|
|
{
|
|
if((infor == NULL) || (infor->number <= 0) || (infor->audio == NULL)) {
|
|
return false;
|
|
}
|
|
|
|
pthread_mutex_lock(&infor->lock);
|
|
bool support = false;
|
|
int type = translate_format(format);
|
|
for(int i = 0; i < infor->number; i++) {
|
|
// AC4 have the same value with EAC3, must judge the byte3 if cea_audio
|
|
if(format == AUDIO_FORMAT_AC4){
|
|
support = is_support_ac4((int)infor->audio[i].type,(int)infor->audio[i].value);
|
|
if(support){
|
|
break;
|
|
}
|
|
}else if(type == (int)infor->audio[i].type) {
|
|
support = true;
|
|
break;
|
|
}
|
|
}
|
|
pthread_mutex_unlock(&infor->lock);
|
|
return support;
|
|
}
|
|
|
|
void dump_hdmi_audio_sample(int index,char*name,int size)
|
|
{
|
|
int i = 0;
|
|
bool first = true;
|
|
int offset = 0;
|
|
|
|
if((name == NULL) || (size < 0)){
|
|
return ;
|
|
}
|
|
|
|
int sample_array_length = ARRAY_SIZE(HDMI_SAMPLE_TABLE);
|
|
for(i = 0; i < sample_array_length; i++) {
|
|
// every bit mean a samplerate
|
|
if(index & HDMI_SAMPLE_TABLE[i].index) {
|
|
if(name != NULL) {
|
|
int length = snprintf(name + offset, size-offset, "%s%d",
|
|
first ? "" : ",",
|
|
HDMI_SAMPLE_TABLE[i].sample);
|
|
first = false;
|
|
offset += length;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void dump_hdmi_audio_format(int format,int support,char* buffer,int size)
|
|
{
|
|
if((buffer == NULL) || (size <= 0)){
|
|
return ;
|
|
}
|
|
|
|
char* name = NULL;
|
|
switch(format)
|
|
{
|
|
case HDMI_AUDIO_LPCM:
|
|
name = "Pcm";
|
|
break;
|
|
case HDMI_AUDIO_AC3:
|
|
name = "AC3";
|
|
break;
|
|
case HDMI_AUDIO_MPEG1:
|
|
name = "MPEG1";
|
|
break;
|
|
case HDMI_AUDIO_MP3:
|
|
name = "MP3";
|
|
break;
|
|
case HDMI_AUDIO_MPEG2:
|
|
name = "MP2";
|
|
break;
|
|
case HDMI_AUDIO_AAC_LC:
|
|
name = "AAC_LC";
|
|
break;
|
|
case HDMI_AUDIO_DTS:
|
|
name = "DTS";
|
|
break;
|
|
case HDMI_AUDIO_ATARC:
|
|
name = "ATARC";
|
|
break;
|
|
case HDMI_AUDIO_DSD:
|
|
name = "DSD";
|
|
break;
|
|
case HDMI_AUDIO_E_AC3:
|
|
name = "EAC3";
|
|
if(is_support_ac4(format,support)){
|
|
name = "EAC3/AC4";
|
|
}
|
|
break;
|
|
case HDMI_AUDIO_DTS_HD:
|
|
name = "DTS-HD";
|
|
break;
|
|
case HDMI_AUDIO_MLP:
|
|
name = "MLP";
|
|
break;
|
|
case HDMI_AUDIO_DST:
|
|
name = "DST";
|
|
break;
|
|
case HDMI_AUDIO_WMA_PRO:
|
|
name = "WMA-PRO";
|
|
break;
|
|
default:
|
|
name = "Unknow";
|
|
break;
|
|
}
|
|
|
|
if(name != NULL){
|
|
snprintf(buffer, size, "%s", name);
|
|
}
|
|
}
|
|
|
|
void dump_hdmi_audio_speaker_layout(int layout)
|
|
{
|
|
int i = 0;
|
|
bool first = true;
|
|
int offset = 0;
|
|
char buffer[512];
|
|
int size = 512;
|
|
|
|
memset(buffer,0,size);
|
|
int length = ARRAY_SIZE(HDMI_SPEAKER_ALLOCATION_TABLE);
|
|
for(i = 0; i < length; i++) {
|
|
// every bit mean a samplerate
|
|
if(layout & HDMI_SPEAKER_ALLOCATION_TABLE[i].index) {
|
|
int length = snprintf(buffer + offset, size-offset, "%s%s",
|
|
first ? "" : ",",
|
|
HDMI_SPEAKER_ALLOCATION_TABLE[i].name);
|
|
first = false;
|
|
if(offset+length < size){
|
|
offset += length;
|
|
} else {
|
|
ALOGVV("%s: buffer is not enought",__FUNCTION__);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
ALOGVV("%s: speaker allocation = %s",__FUNCTION__,buffer);
|
|
}
|
|
|
|
void dump(struct hdmi_audio_infors *infor)
|
|
{
|
|
if((infor == NULL) || (infor->number <= 0) || (infor->audio == NULL)) {
|
|
return ;
|
|
}
|
|
|
|
char format[20];
|
|
char channel[50];
|
|
char sample[1024];
|
|
char* name = NULL;
|
|
|
|
pthread_mutex_lock(&infor->lock);
|
|
for(int i = 0; i < infor->number; i++) {
|
|
if(infor->audio[i].type == 0){
|
|
continue;
|
|
}
|
|
|
|
dump_hdmi_audio_format(infor->audio[i].type,infor->audio[i].value,format,20);
|
|
dump_hdmi_audio_sample(infor->audio[i].sample,sample,1024);
|
|
ALOGVV("%s: type = %s,channel = %d,sample = %s",__FUNCTION__,format,infor->audio[i].channel,sample);
|
|
}
|
|
|
|
if(infor->channel_layout != -1){
|
|
dump_hdmi_audio_speaker_layout(infor->channel_layout);
|
|
}
|
|
pthread_mutex_unlock(&infor->lock);
|
|
}
|