aboutsummaryrefslogtreecommitdiff
path: root/xbmc/cores/AudioEngine/Utils/AEELDParser.cpp
blob: da5c6d320b71db7236ca896c991b1c9f56772c32 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
/*
 *      Copyright (C) 2012 Team XBMC
 *      http://xbmc.org
 *
 *  This Program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  This Program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with XBMC; see the file COPYING.  If not, see
 *  <http://www.gnu.org/licenses/>.
 *
 */

#include "AEELDParser.h"
#include "AEDeviceInfo.h"
#include "utils/EndianSwap.h"
#include <string.h>

#include <stdio.h>

#define GRAB_BITS(buf, byte, lowbit, bits) ((buf[byte] >> (lowbit)) & ((1 << (bits)) - 1))

typedef struct
{
  uint8_t     eld_ver;
  uint8_t     baseline_eid_len;
  uint8_t     cea_edid_ver;
  uint8_t     monitor_name_length;
  uint8_t     sad_count;
  uint8_t     conn_type;
  bool        s_ai;
  bool        hdcp;
  uint8_t     audio_sync_delay;
  bool        rlrc; /* rear left and right of center */
  bool        flrc; /* front left and right of center */
  bool        rc;   /* rear center */
  bool        rlr;  /* rear left and right */
  bool        fc;   /* front center */
  bool        lfe;  /* LFE */
  bool        flr;  /* front left and right */
  uint64_t    port_id;
  char        mfg_name[4];
  uint16_t    product_code;
  std::string monitor_name;
} ELDHeader;

#define ELD_VER_CEA_816D         2
#define ELD_VER_PARTIAL          31

#define ELD_EDID_VER_NONE        0
#define ELD_EDID_VER_CEA_861     1
#define ELD_EDID_VER_CEA_861_A   2
#define ELD_EDID_VER_CEA_861_BCD 3

#define ELD_CONN_TYPE_HDMI       0
#define ELD_CONN_TYPE_DP         1
#define ELD_CONN_TYPE_RESERVED1  2
#define ELD_CONN_TYPE_RESERVED2  3

#define CEA_861_FORMAT_RESERVED1 0
#define CEA_861_FORMAT_LPCM      1
#define CEA_861_FORMAT_AC3       2
#define CEA_861_FORMAT_MPEG1     3
#define CEA_861_FORMAT_MP3       4
#define CEA_861_FORMAT_MPEG2     5
#define CEA_861_FORMAT_AAC       6
#define CEA_861_FORMAT_DTS       7
#define CEA_861_FORMAT_ATRAC     8
#define CEA_861_FORMAT_SACD      9
#define CEA_861_FORMAT_EAC3      10
#define CEA_861_FORMAT_DTSHD     11
#define CEA_861_FORMAT_MLP       12
#define CEA_861_FORMAT_DST       13
#define CEA_861_FORMAT_WMAPRO    14
#define CEA_861_FORMAT_RESERVED2 15

#define rtrim(s) s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end())

void CAEELDParser::Parse(const uint8_t *data, size_t length, CAEDeviceInfo& info)
{
  ELDHeader header;
  header.eld_ver = (data[0 ] & 0xF8) >> 3;
  if (header.eld_ver != ELD_VER_CEA_816D && header.eld_ver != ELD_VER_PARTIAL)
    return;

  header.baseline_eid_len    =  data[2 ];
  header.cea_edid_ver        = (data[4 ] & 0xE0) >> 5;
  header.monitor_name_length =  data[4 ] & 0x1F;
  header.sad_count           = (data[5 ] & 0xF0) >> 4;
  header.conn_type           = (data[5 ] & 0x0C) >> 2;
  header.s_ai                = (data[5 ] & 0x02) == 0x02;
  header.hdcp                = (data[5 ] & 0x01) == 0x01;
  header.audio_sync_delay    =  data[6 ];
  header.rlrc                = (data[7 ] & 0x40) == 0x40;
  header.flrc                = (data[7 ] & 0x20) == 0x20;
  header.rc                  = (data[7 ] & 0x10) == 0x10;
  header.rlr                 = (data[7 ] & 0x08) == 0x08;
  header.fc                  = (data[7 ] & 0x04) == 0x04;
  header.lfe                 = (data[7 ] & 0x02) == 0x02;
  header.flr                 = (data[7 ] & 0x01) == 0x01;
  header.port_id             = Endian_SwapLE64(*((uint64_t*)(data + 8)));
  header.mfg_name[0]         = 'A' + ((data[16] >> 2) & 0x1F) - 1;
  header.mfg_name[1]         = 'A' + (((data[16] << 3) | (data[17] >> 5)) & 0x1F) - 1;
  header.mfg_name[2]         = 'A' + (data[17] & 0x1F) - 1;
  header.mfg_name[3]         = '\0';
  header.product_code        = Endian_SwapLE16(*((uint16_t*)(data + 18)));

  switch (header.conn_type)
  {
    case ELD_CONN_TYPE_HDMI: info.m_deviceType = AE_DEVTYPE_HDMI; break;
    case ELD_CONN_TYPE_DP  : info.m_deviceType = AE_DEVTYPE_DP  ; break;
  }

  info.m_displayNameExtra = header.mfg_name;
  if (header.monitor_name_length <= 16)
  {
    header.monitor_name.assign((const char *)(data + 20), header.monitor_name_length);
    rtrim(header.monitor_name);
    if (header.monitor_name.length() > 0)
    {
      info.m_displayNameExtra.append(" ");
      info.m_displayNameExtra.append(header.monitor_name);
      if (header.conn_type == ELD_CONN_TYPE_HDMI)
        info.m_displayNameExtra.append(" on HDMI"       );
      else
        info.m_displayNameExtra.append(" on DisplayPort");
    }
  }

  if (header.flr)
  {
    if (!info.m_channels.HasChannel(AE_CH_FL))
      info.m_channels += AE_CH_FL;
    if (!info.m_channels.HasChannel(AE_CH_FR))
      info.m_channels += AE_CH_FR;
  }

  if (header.lfe)
    if (!info.m_channels.HasChannel(AE_CH_LFE))
      info.m_channels += AE_CH_LFE;

  if (header.fc)
    if (!info.m_channels.HasChannel(AE_CH_FC))
      info.m_channels += AE_CH_FC;

  if (header.rlr)
  {
    if (!info.m_channels.HasChannel(AE_CH_BL))
      info.m_channels += AE_CH_BL;
    if (!info.m_channels.HasChannel(AE_CH_BR))
      info.m_channels += AE_CH_BR;
  }

  if (header.rc)
    if (!info.m_channels.HasChannel(AE_CH_BC))
      info.m_channels += AE_CH_BC;

  if (header.flrc)
  {
    if (!info.m_channels.HasChannel(AE_CH_FLOC))
      info.m_channels += AE_CH_FLOC;
    if (!info.m_channels.HasChannel(AE_CH_FROC))
      info.m_channels += AE_CH_FROC;
  }

  if (header.rlrc)
  {
    if (!info.m_channels.HasChannel(AE_CH_BLOC))
      info.m_channels += AE_CH_BLOC;
    if (!info.m_channels.HasChannel(AE_CH_BROC))
      info.m_channels += AE_CH_BROC;
  }

  const uint8_t *sad = data + 20 + header.monitor_name_length;
  for(uint8_t i = 0; i < header.sad_count; ++i)
  {
    uint8_t offset = i * 3;
    uint8_t formatCode   = (sad[offset + 0] >> 3) & 0xF;
    //uint8_t channelCount = (sad[offset + 0] & 0x7) + 1;
    //uint8_t sampleRates  =  sad[offset + 1];

    AEDataFormat fmt = AE_FMT_INVALID;
    switch (formatCode)
    {
      case CEA_861_FORMAT_AAC  : fmt = AE_FMT_AAC   ; break;
      case CEA_861_FORMAT_AC3  : fmt = AE_FMT_AC3   ; break;
      case CEA_861_FORMAT_DTS  : fmt = AE_FMT_DTS   ; break;
      case CEA_861_FORMAT_DTSHD: fmt = AE_FMT_DTSHD ; break;
      case CEA_861_FORMAT_EAC3 : fmt = AE_FMT_EAC3  ; break;
      case CEA_861_FORMAT_LPCM : fmt = AE_FMT_LPCM  ; break;
      case CEA_861_FORMAT_MLP  : fmt = AE_FMT_TRUEHD; break;
    }

    if (fmt == AE_FMT_INVALID)
      continue;

    if (std::find(info.m_dataFormats.begin(), info.m_dataFormats.end(), fmt) == info.m_dataFormats.end())
      info.m_dataFormats.push_back(fmt);
  }
}