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 switch (a->format) {
221 case XTYPE_HE_AAC:
222 a->format = TYPE_HE_AAC;
223 break;
224 case XTYPE_HE_AAC2:
225 a->format = TYPE_HE_AAC2;
226 break;
229 break;
230 default:
231 LOG(VB_AUDIO, LOG_INFO, LOC + QString("audio coding xtype %1 not expected")
232 .arg(a->format));
233 a->format = 0;
234 }
235 break;
236 }
237}
238
239int eld::update_eld(const char *buf, int size)
240{
241 int mnl = 0;
242
243 m_e.eld_valid = false;
244 m_e.eld_ver = GRAB_BITS(buf, 0, 3, 5);
247 {
248 LOG(VB_AUDIO, LOG_INFO, LOC + QString("Unknown ELD version %1").arg(m_e.eld_ver));
249 return -1;
250 }
251
252 m_e.eld_size = size;
253 m_e.baseline_len = GRAB_BITS(buf, 2, 0, 8);
254 mnl = GRAB_BITS(buf, 4, 0, 5);
255 m_e.cea_edid_ver = GRAB_BITS(buf, 4, 5, 3);
256
257 m_e.support_hdcp = GRAB_BITS(buf, 5, 0, 1);
258 m_e.support_ai = GRAB_BITS(buf, 5, 1, 1);
259 m_e.conn_type = GRAB_BITS(buf, 5, 2, 2);
260 m_e.sad_count = GRAB_BITS(buf, 5, 4, 4);
261
262 m_e.aud_synch_delay = GRAB_BITS(buf, 6, 0, 8) * 2;
263 m_e.spk_alloc = GRAB_BITS(buf, 7, 0, 7);
264
265 m_e.port_id = qFromLittleEndian<quint64>(buf + 8);
266
267 /* not specified, but the spec's tendency is little endian */
268 m_e.manufacture_id = qFromLittleEndian<quint16>(buf + 16);
269 m_e.product_id = qFromLittleEndian<quint16>(buf + 18);
270
271 if (ELD_FIXED_BYTES + mnl > size)
272 {
273 LOG(VB_AUDIO, LOG_INFO, LOC + QString("out of range MNL %1").arg(mnl));
274 return -1;
275 }
276
277 std::string tmp(buf + ELD_FIXED_BYTES, mnl);
278 m_e.monitor_name = QString::fromStdString(tmp);
279
280 for (int i = 0; i < m_e.sad_count; i++)
281 {
282 if (ELD_FIXED_BYTES + mnl + (3 * (i + 1)) > size)
283 {
284 LOG(VB_AUDIO, LOG_INFO, LOC + QString("out of range SAD %1").arg(i));
285 return -1;
286 }
287 update_sad(i, buf + ELD_FIXED_BYTES + mnl + (3 * static_cast<ptrdiff_t>(i)));
288 }
289
290 /*
291 * Assume the highest speakers configuration
292 */
293 if (!m_e.spk_alloc)
294 m_e.spk_alloc = 0xffff;
295
296 m_e.eld_valid = true;
297 return 0;
298}
299
304QString eld::print_pcm_rates(int pcm)
305{
306 static const std::array<const uint32_t,12> rates {
307 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200,
308 96000, 176400, 192000 };
309 QString result = QString();
310
311 for (size_t i = 0; i < rates.size(); i++)
312 {
313 if ((pcm & (1 << i)) != 0)
314 {
315 result += QString(" %1").arg(rates[i]);
316 }
317 }
318 return result;
319}
320
325QString eld::print_pcm_bits(int pcm)
326{
327 static const std::array<const uint8_t,3> bits { 16, 20, 24 };
328 QString result = QString();
329
330 for (size_t i = 0; i < bits.size(); i++)
331 {
332 if ((pcm & (1 << i)) != 0)
333 {
334 result += QString(" %1").arg(bits[i]);
335 }
336 }
337 return result;
338}
339
340QString eld::sad_desc(int index)
341{
342 cea_sad *a = m_e.sad + index;
343 if (!a->format)
344 return "";
345
346 QString buf = print_pcm_rates(a->rates);
347 QString buf2 = ", bits =";
348
349 if (a->format == TYPE_LPCM)
350 buf2 += print_pcm_bits(a->sample_bits);
351 else if (a->max_bitrate)
352 buf2 = QString(", max bitrate = %1").arg(a->max_bitrate);
353 else
354 buf2 = "";
355
356 return QString("supports coding type %1:"
357 " channels = %2, rates =%3%4")
358 .arg(audiotype_names[a->format], QString::number(a->channels),
359 buf, buf2);
360}
361
363{
364 QString result = QString();
365
366 for (size_t i = 0; i < cea_speaker_allocation_names.size(); i++)
367 {
368 if ((m_e.spk_alloc & (1 << i)) != 0)
369 {
370 result += QString(" %1").arg(cea_speaker_allocation_names[i]);
371 }
372 }
373 return result;
374}
375
377{
378 switch (m_e.eld_ver)
379 {
380 case 2: return "CEA-861D or below";
381 case 31: return "partial";
382 default: return "reserved";
383 }
384}
385
387{
388 switch (m_e.cea_edid_ver)
389 {
390 case 0: return "no CEA EDID Timing Extension block present";
391 case 1: return "CEA-861";
392 case 2: return "CEA-861-A";
393 case 3: return "CEA-861-B, C or D";
394 default: return "reserved";
395 }
396}
397
398QString eld::info_desc() const
399{
400 QString result = QString("manufacture_id\t\t0x%1\n")
401 .arg(m_e.manufacture_id, 0, 16);
402 result += QString("product_id\t\t0x%1\n").arg(m_e.product_id, 0, 16);
403 result += QString("port_id\t\t\t0x%1\n").arg((long long)m_e.port_id);
404 result += QString("support_hdcp\t\t%1\n").arg(m_e.support_hdcp);
405 result += QString("support_ai\t\t%1\n").arg(m_e.support_ai);
406 result += QString("audio_sync_delay\t%1\n").arg(m_e.aud_synch_delay);
407 result += QString("sad_count\t\t%1\n").arg(m_e.sad_count);
408 return result;
409}
410
411bool eld::isValid() const
412{
413 return m_e.eld_valid;
414}
415
417{
418 if (!isValid())
419 {
420 LOG(VB_AUDIO, LOG_INFO, LOC + "Invalid ELD");
421 return;
422 }
423 LOG(VB_AUDIO, LOG_INFO, LOC + QString("Detected monitor %1 at connection type %2")
424 .arg(product_name().simplified(), connection_name()));
425
426 if (m_e.spk_alloc)
427 {
428 LOG(VB_AUDIO, LOG_INFO, LOC + QString("available speakers:%1")
430 }
431 LOG(VB_AUDIO, LOG_INFO, LOC + QString("max LPCM channels = %1").arg(maxLPCMChannels()));
432 LOG(VB_AUDIO, LOG_INFO, LOC + QString("max channels = %1").arg(maxChannels()));
433 LOG(VB_AUDIO, LOG_INFO, LOC + QString("supported codecs = %1").arg(codecs_desc()));
434 for (int i = 0; i < m_e.sad_count; i++)
435 {
436 LOG(VB_AUDIO, LOG_INFO, LOC + sad_desc(i));
437 }
438}
439
440QString eld::product_name() const
441{
442 return m_e.monitor_name;
443}
444
445QString eld::connection_name() const
446{
448}
449
451{
452 int channels = 2; // assume stereo at the minimum
453 for (int i = 0; i < m_e.sad_count; i++)
454 {
455 struct cea_sad *a = m_e.sad + i;
456 if (a->format == TYPE_LPCM)
457 {
458 channels = std::max(a->channels, channels);
459 }
460 }
461 return channels;
462}
463
465{
466 int channels = 2; // assume stereo at the minimum
467 for (int i = 0; i < m_e.sad_count; i++)
468 {
469 struct cea_sad *a = m_e.sad + i;
470 channels = std::max(a->channels, channels);
471 }
472 return channels;
473}
474
475QString eld::codecs_desc() const
476{
477 QString result = QString();
478 bool found_one = false;
479 for (size_t i = 0; i < audiotype_names.size(); i++)
480 {
481 if ((m_e.formats & (1 << i)) != 0)
482 {
483 if (found_one)
484 result += ", ";
485 result += audiotype_names[i];
486 found_one = true;
487 }
488 }
489 return result;
490}
Definition: eldutils.h:39
void show()
Definition: eldutils.cpp:416
eld & operator=(const eld &)
Definition: eldutils.cpp:159
QString channel_allocation_desc() const
Definition: eldutils.cpp:362
@ TYPE_MP3
Definition: eldutils.h:63
@ TYPE_MPEG_SURROUND
Definition: eldutils.h:78
@ TYPE_AC3
Definition: eldutils.h:61
@ TYPE_AACLC
Definition: eldutils.h:65
@ TYPE_HE_AAC
Definition: eldutils.h:76
@ TYPE_DTS_HD
Definition: eldutils.h:70
@ TYPE_MPEG1
Definition: eldutils.h:62
@ TYPE_REF_CXT
Definition: eldutils.h:74
@ TYPE_SACD
Definition: eldutils.h:68
@ TYPE_MPEG2
Definition: eldutils.h:64
@ TYPE_DST
Definition: eldutils.h:72
@ TYPE_MLP
Definition: eldutils.h:71
@ TYPE_WMAPRO
Definition: eldutils.h:73
@ TYPE_ATRAC
Definition: eldutils.h:67
@ TYPE_LPCM
Definition: eldutils.h:60
@ TYPE_EAC3
Definition: eldutils.h:69
@ TYPE_REF_STREAM_HEADER
Definition: eldutils.h:59
@ TYPE_HE_AAC2
Definition: eldutils.h:77
@ TYPE_DTS
Definition: eldutils.h:66
eld_data m_e
Definition: eldutils.h:126
static QString print_pcm_bits(int pcm)
Print the supported PCM fmt bits to the string buffer.
Definition: eldutils.cpp:325
int maxChannels()
Definition: eldutils.cpp:464
QString codecs_desc() const
Definition: eldutils.cpp:475
QString eld_version_name() const
Definition: eldutils.cpp:376
QString edid_version_name() const
Definition: eldutils.cpp:386
bool isValid() const
Definition: eldutils.cpp:411
QString info_desc() const
Definition: eldutils.cpp:398
int maxLPCMChannels()
Definition: eldutils.cpp:450
QString connection_name() const
Definition: eldutils.cpp:445
void update_sad(int index, const char *buf)
Definition: eldutils.cpp:167
QString sad_desc(int index)
Definition: eldutils.cpp:340
int update_eld(const char *buf, int size)
Definition: eldutils.cpp:239
eld()
Definition: eldutils.cpp:153
QString product_name() const
Definition: eldutils.cpp:440
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:304
#define LOC
Definition: eldutils.cpp:39
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
@ 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
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:31
unsigned short uint16_t
Definition: iso6937tables.h:3
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
int channels
Definition: eldutils.h:94
int profile
Definition: eldutils.h:99
int sample_bits
Definition: eldutils.h:97
int max_bitrate
Definition: eldutils.h:98
int format
Definition: eldutils.h:95
int manufacture_id
Definition: eldutils.h:112
int product_id
Definition: eldutils.h:113
QString monitor_name
Definition: eldutils.h:111
int support_ai
Definition: eldutils.h:117
int cea_edid_ver
Definition: eldutils.h:110
int aud_synch_delay
Definition: eldutils.h:119
struct cea_sad sad[ELD_MAX_SAD]
Definition: eldutils.h:123
bool eld_valid
Definition: eldutils.h:106
uint64_t formats
Definition: eldutils.h:115
int baseline_len
Definition: eldutils.h:108
int support_hdcp
Definition: eldutils.h:116
uint64_t port_id
Definition: eldutils.h:114