MythTV master
eldutils.cpp
Go to the documentation of this file.
1/*
2 * eldutils.cpp (c) Jean-Yves Avenard <jyavenard@mythtv.org>
3 * a utility class to decode EDID Like Data (ELD) byte stream
4 *
5 * Based on ALSA hda_eld.c
6 * Copyright(c) 2008 Intel Corporation.
7 *
8 * Authors:
9 * Wu Fengguang <wfg@linux.intel.com>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25#include "eldutils.h"
26
27#include <algorithm>
28#include <cinttypes>
29#include <limits> // workaround QTBUG-90395
30#include <sys/types.h>
31
32#include <QString>
33#include <QtEndian>
34
35#include "audiooutputbase.h"
36
38
39#define LOC QString("ELDUTILS: ")
40
41enum eld_versions : std::uint8_t
42{
45};
46
47enum cea_edid_versions : std::uint8_t
48{
54};
55
56static const std::array<const QString,11> cea_speaker_allocation_names {
57 /* 0 */ "FL/FR",
58 /* 1 */ "LFE",
59 /* 2 */ "FC",
60 /* 3 */ "RL/RR",
61 /* 4 */ "RC",
62 /* 5 */ "FLC/FRC",
63 /* 6 */ "RLC/RRC",
64 /* 7 */ "FLW/FRW",
65 /* 8 */ "FLH/FRH",
66 /* 9 */ "TC",
67 /* 10 */ "FCH",
68};
69
70static const std::array<const QString,4> eld_connection_type_names {
71 "HDMI",
72 "DisplayPort",
73 "2-reserved",
74 "3-reserved"
75};
76
77enum cea_audio_coding_xtypes : std::uint8_t
78{
84};
85
86static const std::array<const QString,18> audiotype_names {
87 /* 0 */ "undefined",
88 /* 1 */ "LPCM",
89 /* 2 */ "AC3",
90 /* 3 */ "MPEG1",
91 /* 4 */ "MP3",
92 /* 5 */ "MPEG2",
93 /* 6 */ "AAC-LC",
94 /* 7 */ "DTS",
95 /* 8 */ "ATRAC",
96 /* 9 */ "DSD (One Bit Audio)",
97 /* 10 */ "E-AC3",
98 /* 11 */ "DTS-HD",
99 /* 12 */ "TrueHD",
100 /* 13 */ "DST",
101 /* 14 */ "WMAPro",
102 /* 15 */ "HE-AAC",
103 /* 16 */ "HE-AACv2",
104 /* 17 */ "MPEG Surround",
105};
106
107/*
108 * The following two lists are shared between
109 * - HDMI audio InfoFrame (source to sink)
110 * - CEA E-EDID Extension (sink to source)
111 */
112
113/*
114 * SF2:SF1:SF0 index => sampling frequency
115 */
116enum : std::uint16_t {
117 SNDRV_PCM_RATE_5512 = (1<<0), /* 5512Hz */
118 SNDRV_PCM_RATE_8000 = (1<<1), /* 8000Hz */
119 SNDRV_PCM_RATE_11025 = (1<<2), /* 11025Hz */
120 SNDRV_PCM_RATE_16000 = (1<<3), /* 16000Hz */
121 SNDRV_PCM_RATE_22050 = (1<<4), /* 22050Hz */
122 SNDRV_PCM_RATE_32000 = (1<<5), /* 32000Hz */
123 SNDRV_PCM_RATE_44100 = (1<<6), /* 44100Hz */
124 SNDRV_PCM_RATE_48000 = (1<<7), /* 48000Hz */
125 SNDRV_PCM_RATE_64000 = (1<<8), /* 64000Hz */
126 SNDRV_PCM_RATE_88200 = (1<<9), /* 88200Hz */
127 SNDRV_PCM_RATE_96000 = (1<<10), /* 96000Hz */
128 SNDRV_PCM_RATE_176400 = (1<<11), /* 176400Hz */
129 SNDRV_PCM_RATE_192000 = (1<<12), /* 192000Hz */
130};
131
132static const std::array<const int,8> cea_sampling_frequencies {
133 0, /* 0: Refer to Stream Header */
134 SNDRV_PCM_RATE_32000, /* 1: 32000Hz */
135 SNDRV_PCM_RATE_44100, /* 2: 44100Hz */
136 SNDRV_PCM_RATE_48000, /* 3: 48000Hz */
137 SNDRV_PCM_RATE_88200, /* 4: 88200Hz */
138 SNDRV_PCM_RATE_96000, /* 5: 96000Hz */
139 SNDRV_PCM_RATE_176400, /* 6: 176400Hz */
140 SNDRV_PCM_RATE_192000, /* 7: 192000Hz */
141};
142
143static inline int
144GRAB_BITS(const char* buf, size_t byte, uint8_t lowbit, uint8_t bits)
145 { return (buf[byte] >> lowbit) & ((1 << bits) - 1); };
146
147eld::eld(const char *buf, int size)
148{
149 m_e.formats = 0LL;
150 update_eld(buf, size);
151}
152
154{
155 m_e.formats = 0LL;
156 m_e.eld_valid = false;
157}
158
160{
161 if (this == &rhs)
162 return *this;
163 m_e = rhs.m_e;
164 return *this;
165}
166
167void eld::update_sad(int index,
168 const char *buf)
169{
170 cea_sad *a = m_e.sad + index;
171
172 int val = GRAB_BITS(buf, 1, 0, 7);
173 a->rates = 0;
174 for (int i = 0; i < 7; i++)
175 if ((val & (1 << i)) != 0)
176 a->rates |= cea_sampling_frequencies[i + 1];
177
178 a->channels = GRAB_BITS(buf, 0, 0, 3);
179 a->channels++;
180
181 a->sample_bits = 0;
182 a->max_bitrate = 0;
183
184 a->format = GRAB_BITS(buf, 0, 3, 4);
185 m_e.formats |= 1 << a->format;
186 switch (a->format)
187 {
189 LOG(VB_AUDIO, LOG_INFO, LOC + "audio coding type 0 not expected");
190 break;
191
192 case TYPE_LPCM:
193 a->sample_bits = GRAB_BITS(buf, 2, 0, 3);
194 break;
195
196 case TYPE_AC3:
197 case TYPE_MPEG1:
198 case TYPE_MP3:
199 case TYPE_MPEG2:
200 case TYPE_AACLC:
201 case TYPE_DTS:
202 case TYPE_ATRAC:
203 a->max_bitrate = GRAB_BITS(buf, 2, 0, 8);
204 a->max_bitrate *= 8000;
205 break;
206
207 case TYPE_SACD:
208 case TYPE_EAC3:
209 case TYPE_DTS_HD:
210 case TYPE_MLP:
211 case TYPE_DST:
212 break;
213
214 case TYPE_WMAPRO:
215 a->profile = GRAB_BITS(buf, 2, 0, 3);
216 break;
217
218 case TYPE_REF_CXT:
219 a->format = GRAB_BITS(buf, 2, 3, 5);
220 if (a->format == XTYPE_HE_REF_CT ||
222 {
223 LOG(VB_AUDIO, LOG_INFO, LOC + QString("audio coding xtype %1 not expected")
224 .arg(a->format));
225 a->format = 0;
226 }
227 else
228 {
230 }
231 break;
232 }
233}
234
235int eld::update_eld(const char *buf, int size)
236{
237 int mnl = 0;
238
239 m_e.eld_valid = false;
240 m_e.eld_ver = GRAB_BITS(buf, 0, 3, 5);
243 {
244 LOG(VB_AUDIO, LOG_INFO, LOC + QString("Unknown ELD version %1").arg(m_e.eld_ver));
245 return -1;
246 }
247
248 m_e.eld_size = size;
249 m_e.baseline_len = GRAB_BITS(buf, 2, 0, 8);
250 mnl = GRAB_BITS(buf, 4, 0, 5);
251 m_e.cea_edid_ver = GRAB_BITS(buf, 4, 5, 3);
252
253 m_e.support_hdcp = GRAB_BITS(buf, 5, 0, 1);
254 m_e.support_ai = GRAB_BITS(buf, 5, 1, 1);
255 m_e.conn_type = GRAB_BITS(buf, 5, 2, 2);
256 m_e.sad_count = GRAB_BITS(buf, 5, 4, 4);
257
258 m_e.aud_synch_delay = GRAB_BITS(buf, 6, 0, 8) * 2;
259 m_e.spk_alloc = GRAB_BITS(buf, 7, 0, 7);
260
261 m_e.port_id = qFromLittleEndian<quint64>(buf + 8);
262
263 /* not specified, but the spec's tendency is little endian */
264 m_e.manufacture_id = qFromLittleEndian<quint16>(buf + 16);
265 m_e.product_id = qFromLittleEndian<quint16>(buf + 18);
266
267 if (ELD_FIXED_BYTES + mnl > size)
268 {
269 LOG(VB_AUDIO, LOG_INFO, LOC + QString("out of range MNL %1").arg(mnl));
270 return -1;
271 }
272
273 std::string tmp(buf + ELD_FIXED_BYTES, mnl);
274 m_e.monitor_name = QString::fromStdString(tmp);
275
276 for (int i = 0; i < m_e.sad_count; i++)
277 {
278 if (ELD_FIXED_BYTES + mnl + (3 * (i + 1)) > size)
279 {
280 LOG(VB_AUDIO, LOG_INFO, LOC + QString("out of range SAD %1").arg(i));
281 return -1;
282 }
283 update_sad(i, buf + ELD_FIXED_BYTES + mnl + (3 * static_cast<ptrdiff_t>(i)));
284 }
285
286 /*
287 * Assume the highest speakers configuration
288 */
289 if (!m_e.spk_alloc)
290 m_e.spk_alloc = 0xffff;
291
292 m_e.eld_valid = true;
293 return 0;
294}
295
300QString eld::print_pcm_rates(int pcm)
301{
302 static const std::array<const uint32_t,12> rates {
303 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200,
304 96000, 176400, 192000 };
305 QString result = QString();
306
307 for (size_t i = 0; i < rates.size(); i++)
308 {
309 if ((pcm & (1 << i)) != 0)
310 {
311 result += QString(" %1").arg(rates[i]);
312 }
313 }
314 return result;
315}
316
321QString eld::print_pcm_bits(int pcm)
322{
323 static const std::array<const uint8_t,3> bits { 16, 20, 24 };
324 QString result = QString();
325
326 for (size_t i = 0; i < bits.size(); i++)
327 {
328 if ((pcm & (1 << i)) != 0)
329 {
330 result += QString(" %1").arg(bits[i]);
331 }
332 }
333 return result;
334}
335
336QString eld::sad_desc(int index)
337{
338 cea_sad *a = m_e.sad + index;
339 if (!a->format)
340 return "";
341
342 QString buf = print_pcm_rates(a->rates);
343 QString buf2 = ", bits =";
344
345 if (a->format == TYPE_LPCM)
346 buf2 += print_pcm_bits(a->sample_bits);
347 else if (a->max_bitrate)
348 buf2 = QString(", max bitrate = %1").arg(a->max_bitrate);
349 else
350 buf2 = "";
351
352 return QString("supports coding type %1:"
353 " channels = %2, rates =%3%4")
354 .arg(audiotype_names[a->format], QString::number(a->channels),
355 buf, buf2);
356}
357
359{
360 QString result = QString();
361
362 for (size_t i = 0; i < cea_speaker_allocation_names.size(); i++)
363 {
364 if ((m_e.spk_alloc & (1 << i)) != 0)
365 {
366 result += QString(" %1").arg(cea_speaker_allocation_names[i]);
367 }
368 }
369 return result;
370}
371
373{
374 switch (m_e.eld_ver)
375 {
376 case 2: return "CEA-861D or below";
377 case 31: return "partial";
378 default: return "reserved";
379 }
380}
381
383{
384 switch (m_e.cea_edid_ver)
385 {
386 case 0: return "no CEA EDID Timing Extension block present";
387 case 1: return "CEA-861";
388 case 2: return "CEA-861-A";
389 case 3: return "CEA-861-B, C or D";
390 default: return "reserved";
391 }
392}
393
394QString eld::info_desc() const
395{
396 QString result = QString("manufacture_id\t\t0x%1\n")
397 .arg(m_e.manufacture_id, 0, 16);
398 result += QString("product_id\t\t0x%1\n").arg(m_e.product_id, 0, 16);
399 result += QString("port_id\t\t\t0x%1\n").arg((long long)m_e.port_id);
400 result += QString("support_hdcp\t\t%1\n").arg(m_e.support_hdcp);
401 result += QString("support_ai\t\t%1\n").arg(m_e.support_ai);
402 result += QString("audio_sync_delay\t%1\n").arg(m_e.aud_synch_delay);
403 result += QString("sad_count\t\t%1\n").arg(m_e.sad_count);
404 return result;
405}
406
407bool eld::isValid() const
408{
409 return m_e.eld_valid;
410}
411
413{
414 if (!isValid())
415 {
416 LOG(VB_AUDIO, LOG_INFO, LOC + "Invalid ELD");
417 return;
418 }
419 LOG(VB_AUDIO, LOG_INFO, LOC + QString("Detected monitor %1 at connection type %2")
420 .arg(product_name().simplified(), connection_name()));
421
422 if (m_e.spk_alloc)
423 {
424 LOG(VB_AUDIO, LOG_INFO, LOC + QString("available speakers:%1")
426 }
427 LOG(VB_AUDIO, LOG_INFO, LOC + QString("max LPCM channels = %1").arg(maxLPCMChannels()));
428 LOG(VB_AUDIO, LOG_INFO, LOC + QString("max channels = %1").arg(maxChannels()));
429 LOG(VB_AUDIO, LOG_INFO, LOC + QString("supported codecs = %1").arg(codecs_desc()));
430 for (int i = 0; i < m_e.sad_count; i++)
431 {
432 LOG(VB_AUDIO, LOG_INFO, LOC + sad_desc(i));
433 }
434}
435
436QString eld::product_name() const
437{
438 return m_e.monitor_name;
439}
440
441QString eld::connection_name() const
442{
444}
445
447{
448 int channels = 2; // assume stereo at the minimum
449 for (int i = 0; i < m_e.sad_count; i++)
450 {
451 struct cea_sad *a = m_e.sad + i;
452 if (a->format == TYPE_LPCM)
453 {
454 channels = std::max(a->channels, channels);
455 }
456 }
457 return channels;
458}
459
461{
462 int channels = 2; // assume stereo at the minimum
463 for (int i = 0; i < m_e.sad_count; i++)
464 {
465 struct cea_sad *a = m_e.sad + i;
466 channels = std::max(a->channels, channels);
467 }
468 return channels;
469}
470
471QString eld::codecs_desc() const
472{
473 QString result = QString();
474 bool found_one = false;
475 for (size_t i = 0; i < audiotype_names.size(); i++)
476 {
477 if ((m_e.formats & (1 << i)) != 0)
478 {
479 if (found_one)
480 result += ", ";
481 result += audiotype_names[i];
482 found_one = true;
483 }
484 }
485 return result;
486}
Definition: eldutils.h:38
void show()
Definition: eldutils.cpp:412
eld & operator=(const eld &)
Definition: eldutils.cpp:159
QString channel_allocation_desc() const
Definition: eldutils.cpp:358
@ TYPE_MP3
Definition: eldutils.h:62
@ TYPE_AC3
Definition: eldutils.h:60
@ TYPE_AACLC
Definition: eldutils.h:64
@ TYPE_HE_AAC
Definition: eldutils.h:75
@ TYPE_DTS_HD
Definition: eldutils.h:69
@ TYPE_MPEG1
Definition: eldutils.h:61
@ TYPE_REF_CXT
Definition: eldutils.h:73
@ TYPE_SACD
Definition: eldutils.h:67
@ TYPE_MPEG2
Definition: eldutils.h:63
@ TYPE_DST
Definition: eldutils.h:71
@ TYPE_MLP
Definition: eldutils.h:70
@ TYPE_WMAPRO
Definition: eldutils.h:72
@ TYPE_ATRAC
Definition: eldutils.h:66
@ TYPE_LPCM
Definition: eldutils.h:59
@ TYPE_EAC3
Definition: eldutils.h:68
@ TYPE_REF_STREAM_HEADER
Definition: eldutils.h:58
@ TYPE_DTS
Definition: eldutils.h:65
eld_data m_e
Definition: eldutils.h:125
static QString print_pcm_bits(int pcm)
Print the supported PCM fmt bits to the string buffer.
Definition: eldutils.cpp:321
int maxChannels()
Definition: eldutils.cpp:460
QString codecs_desc() const
Definition: eldutils.cpp:471
QString eld_version_name() const
Definition: eldutils.cpp:372
QString edid_version_name() const
Definition: eldutils.cpp:382
bool isValid() const
Definition: eldutils.cpp:407
QString info_desc() const
Definition: eldutils.cpp:394
int maxLPCMChannels()
Definition: eldutils.cpp:446
QString connection_name() const
Definition: eldutils.cpp:441
void update_sad(int index, const char *buf)
Definition: eldutils.cpp:167
QString sad_desc(int index)
Definition: eldutils.cpp:336
int update_eld(const char *buf, int size)
Definition: eldutils.cpp:235
eld()
Definition: eldutils.cpp:153
QString product_name() const
Definition: eldutils.cpp:436
static QString print_pcm_rates(int pcm)
SNDRV_PCM_RATE_* and AC_PAR_PCM values don't match, print correct rates with hdmi-specific routine.
Definition: eldutils.cpp:300
#define LOC
Definition: eldutils.cpp:39
@ SNDRV_PCM_RATE_64000
Definition: eldutils.cpp:125
@ SNDRV_PCM_RATE_5512
Definition: eldutils.cpp:117
@ SNDRV_PCM_RATE_88200
Definition: eldutils.cpp:126
@ SNDRV_PCM_RATE_22050
Definition: eldutils.cpp:121
@ SNDRV_PCM_RATE_176400
Definition: eldutils.cpp:128
@ SNDRV_PCM_RATE_32000
Definition: eldutils.cpp:122
@ SNDRV_PCM_RATE_16000
Definition: eldutils.cpp:120
@ SNDRV_PCM_RATE_192000
Definition: eldutils.cpp:129
@ SNDRV_PCM_RATE_11025
Definition: eldutils.cpp:119
@ SNDRV_PCM_RATE_44100
Definition: eldutils.cpp:123
@ SNDRV_PCM_RATE_48000
Definition: eldutils.cpp:124
@ SNDRV_PCM_RATE_96000
Definition: eldutils.cpp:127
@ SNDRV_PCM_RATE_8000
Definition: eldutils.cpp:118
cea_audio_coding_xtypes
Definition: eldutils.cpp:78
@ XTYPE_FIRST_RESERVED
Definition: eldutils.cpp:83
@ XTYPE_HE_AAC2
Definition: eldutils.cpp:81
@ XTYPE_MPEG_SURROUND
Definition: eldutils.cpp:82
@ XTYPE_HE_REF_CT
Definition: eldutils.cpp:79
@ XTYPE_HE_AAC
Definition: eldutils.cpp:80
static const std::array< const QString, 18 > audiotype_names
Definition: eldutils.cpp:86
static const std::array< const QString, 4 > eld_connection_type_names
Definition: eldutils.cpp:70
eld_versions
Definition: eldutils.cpp:42
@ ELD_VER_PARTIAL
Definition: eldutils.cpp:44
@ ELD_VER_CEA_861D
Definition: eldutils.cpp:43
static const std::array< const QString, 11 > cea_speaker_allocation_names
Definition: eldutils.cpp:56
static int GRAB_BITS(const char *buf, size_t byte, uint8_t lowbit, uint8_t bits)
Definition: eldutils.cpp:144
cea_edid_versions
Definition: eldutils.cpp:48
@ CEA_EDID_VER_NONE
Definition: eldutils.cpp:49
@ CEA_EDID_VER_CEA861BCD
Definition: eldutils.cpp:52
@ CEA_EDID_VER_CEA861A
Definition: eldutils.cpp:51
@ CEA_EDID_VER_CEA861
Definition: eldutils.cpp:50
@ CEA_EDID_VER_RESERVED
Definition: eldutils.cpp:53
static const std::array< const int, 8 > cea_sampling_frequencies
Definition: eldutils.cpp:132
static constexpr uint8_t ELD_FIXED_BYTES
Definition: eldutils.h:30
static guint32 * tmp
Definition: goom_core.cpp:26
unsigned short uint16_t
Definition: iso6937tables.h:3
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
int channels
Definition: eldutils.h:93
int profile
Definition: eldutils.h:98
int sample_bits
Definition: eldutils.h:96
int max_bitrate
Definition: eldutils.h:97
int format
Definition: eldutils.h:94
int manufacture_id
Definition: eldutils.h:111
int product_id
Definition: eldutils.h:112
QString monitor_name
Definition: eldutils.h:110
int support_ai
Definition: eldutils.h:116
int cea_edid_ver
Definition: eldutils.h:109
int aud_synch_delay
Definition: eldutils.h:118
struct cea_sad sad[ELD_MAX_SAD]
Definition: eldutils.h:122
bool eld_valid
Definition: eldutils.h:105
uint64_t formats
Definition: eldutils.h:114
int baseline_len
Definition: eldutils.h:107
int support_hdcp
Definition: eldutils.h:115
uint64_t port_id
Definition: eldutils.h:113