aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans Verkuil <hverkuil-cisco@xs4all.nl>2021-01-15 11:02:22 +0100
committerHans Verkuil <hverkuil-cisco@xs4all.nl>2021-01-15 11:02:22 +0100
commitf48427bcd5d079d2ddea31bc26cc919ebabb2b8d (patch)
tree57fbbf5f9743c603e3f73c57bbe799690532bf4c
parent31144f33569bfe2acb50e0c2e52fdf53bd8eb6d8 (diff)
edid-decode: improve RCDB/SLDB checks
CTA-861-H more clearly described the relationship between the RCDB and SLDB Data Blocks, so implement new checks that verify the correctness of these DBs. Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
-rw-r--r--edid-decode.cpp2
-rw-r--r--edid-decode.h34
-rw-r--r--parse-base-block.cpp2
-rw-r--r--parse-cta-block.cpp84
-rw-r--r--parse-displayid-block.cpp14
5 files changed, 100 insertions, 36 deletions
diff --git a/edid-decode.cpp b/edid-decode.cpp
index a8fcb94..b98c941 100644
--- a/edid-decode.cpp
+++ b/edid-decode.cpp
@@ -201,7 +201,7 @@ void calc_ratio(struct timings *t)
std::string edid_state::dtd_type(unsigned cnt)
{
- unsigned len = std::to_string(cta.preparse_total_dtds).length();
+ unsigned len = std::to_string(cta.preparsed_total_dtds).length();
char buf[16];
sprintf(buf, "DTD %*u", len, cnt);
return buf;
diff --git a/edid-decode.h b/edid-decode.h
index faa65c2..666080b 100644
--- a/edid-decode.h
+++ b/edid-decode.h
@@ -120,7 +120,7 @@ struct edid_state {
base.max_display_height_mm = 0;
// CTA-861 block state
- cta.has_vic_1 = cta.first_svd_might_be_preferred =
+ cta.has_vic_1 = cta.first_svd_might_be_preferred = cta.has_sldb =
cta.has_hdmi = cta.has_vcdb = cta.has_vfpdb = false;
cta.last_block_was_hdmi_vsdb = cta.have_hf_vsdb = cta.have_hf_scdb = false;
cta.first_block = cta.first_svd = true;
@@ -128,14 +128,17 @@ struct edid_state {
memset(cta.vics, 0, sizeof(cta.vics));
memset(cta.preparsed_has_vic, 0, sizeof(cta.preparsed_has_vic));
cta.preparsed_phys_addr = 0xffff;
- cta.preparse_total_dtds = 0;
- cta.preparse_total_vtdbs = 0;
- cta.preparse_has_t8vtdb = false;
+ cta.preparsed_speaker_count = 0;
+ cta.preparsed_sld = false;
+ cta.preparsed_sld_has_coord = false;
+ cta.preparsed_total_dtds = 0;
+ cta.preparsed_total_vtdbs = 0;
+ cta.preparsed_has_t8vtdb = false;
// DisplayID block state
dispid.version = 0;
- dispid.preparse_color_ids = dispid.preparse_xfer_ids = 0;
- dispid.preparse_displayid_blocks = 0;
+ dispid.preparsed_color_ids = dispid.preparsed_xfer_ids = 0;
+ dispid.preparsed_displayid_blocks = 0;
dispid.is_base_block = true;
dispid.is_display = dispid.has_product_identification =
dispid.has_display_parameters = dispid.has_type_1_7 =
@@ -196,12 +199,12 @@ struct edid_state {
// CTA-861 block state
struct {
- unsigned preparse_total_dtds;
+ unsigned preparsed_total_dtds;
vec_timings_ext vec_dtds;
- unsigned preparse_total_vtdbs;
+ unsigned preparsed_total_vtdbs;
vec_timings_ext vec_vtdbs;
vec_timings_ext preferred_timings;
- bool preparse_has_t8vtdb;
+ bool preparsed_has_t8vtdb;
timings_ext t8vtdb;
vec_timings_ext native_timings;
bool has_vic_1;
@@ -210,6 +213,10 @@ struct edid_state {
bool has_hdmi;
bool has_vcdb;
bool has_vfpdb;
+ unsigned preparsed_speaker_count;
+ bool preparsed_sld_has_coord;
+ bool preparsed_sld;
+ bool has_sldb;
unsigned short preparsed_phys_addr;
bool last_block_was_hdmi_vsdb;
bool have_hf_vsdb, have_hf_scdb;
@@ -225,9 +232,9 @@ struct edid_state {
// DisplayID block state
struct {
unsigned char version;
- unsigned short preparse_color_ids;
- unsigned short preparse_xfer_ids;
- unsigned preparse_displayid_blocks;
+ unsigned short preparsed_color_ids;
+ unsigned short preparsed_xfer_ids;
+ unsigned preparsed_displayid_blocks;
bool is_base_block;
bool is_display;
bool has_product_identification;
@@ -273,6 +280,9 @@ struct edid_state {
void cta_svd(const unsigned char *x, unsigned n, bool for_ycbcr420);
void cta_y420cmdb(const unsigned char *x, unsigned length);
void cta_vfpdb(const unsigned char *x, unsigned length);
+ void cta_rcdb(const unsigned char *x, unsigned length);
+ void cta_sldb(const unsigned char *x, unsigned length);
+ void cta_preparse_sldb(const unsigned char *x, unsigned length);
void cta_hdmi_block(const unsigned char *x, unsigned length);
void cta_displayid_type_7(const unsigned char *x, unsigned length);
void cta_displayid_type_8(const unsigned char *x, unsigned length);
diff --git a/parse-base-block.cpp b/parse-base-block.cpp
index f18b495..03abdbc 100644
--- a/parse-base-block.cpp
+++ b/parse-base-block.cpp
@@ -1844,7 +1844,7 @@ void edid_state::parse_base_block(const unsigned char *x)
for (unsigned i = 0; i < (base.has_spwg ? 2 : 4); i++)
if (x[0x36 + i * 18] || x[0x37 + i * 18])
- cta.preparse_total_dtds++;
+ cta.preparsed_total_dtds++;
data_block = "Detailed Timing Descriptors";
printf(" %s:\n", data_block.c_str());
diff --git a/parse-cta-block.cpp b/parse-cta-block.cpp
index fe4a8ee..b6519c1 100644
--- a/parse-cta-block.cpp
+++ b/parse-cta-block.cpp
@@ -533,7 +533,7 @@ void edid_state::cta_vfpdb(const unsigned char *x, unsigned length)
} else if (svr >= 129 && svr <= 144) {
sprintf(suffix, "DTD %3u", svr - 128);
- if (svr >= cta.preparse_total_dtds + 129) {
+ if (svr >= cta.preparsed_total_dtds + 129) {
printf(" %s: Invalid\n", suffix);
fail("Invalid DTD %u.\n", svr - 128);
} else {
@@ -542,7 +542,7 @@ void edid_state::cta_vfpdb(const unsigned char *x, unsigned length)
}
} else if (svr >= 145 && svr <= 160) {
sprintf(suffix, "VTDB %3u", svr - 144);
- if (svr >= cta.preparse_total_vtdbs + 145) {
+ if (svr >= cta.preparsed_total_vtdbs + 145) {
printf(" %s: Invalid\n", suffix);
fail("Invalid VTDB %u.\n", svr - 144);
} else {
@@ -551,7 +551,7 @@ void edid_state::cta_vfpdb(const unsigned char *x, unsigned length)
}
} else if (svr == 254) {
sprintf(suffix, "T8VTDB");
- if (!cta.preparse_has_t8vtdb) {
+ if (!cta.preparsed_has_t8vtdb) {
printf(" %s: Invalid\n", suffix);
fail("Invalid T8VTDB.\n");
} else {
@@ -1383,7 +1383,7 @@ static double decode_uchar_as_double(unsigned char x)
return s / 64.0;
}
-static void cta_rcdb(const unsigned char *x, unsigned length)
+void edid_state::cta_rcdb(const unsigned char *x, unsigned length)
{
unsigned spm = ((x[3] << 16) | (x[2] << 8) | x[1]);
unsigned i;
@@ -1393,25 +1393,46 @@ static void cta_rcdb(const unsigned char *x, unsigned length)
return;
}
- if (x[0] & 0x40)
+ if ((x[0] & 0x20) && !cta.has_sldb)
+ fail("'SLD' flag is 1, but no Speaker Location Data Block is found.\n");
+ else if (!(x[0] & 0x20) && cta.has_sldb)
+ fail("'SLD' flag is 0, but a Speaker Location Data Block is present.\n");
+
+ if (x[0] & 0x40) {
printf(" Speaker count: %u\n", (x[0] & 0x1f) + 1);
+ } else {
+ if (x[0] & 0x1f)
+ fail("'Speaker' flag is 0, but 'Speaker Count' is != 0.\n");
+ if (x[0] & 0x20)
+ fail("'SLD' flag is 1, but 'Speaker' is 0.\n");
+ }
printf(" Speaker Presence Mask:\n");
for (i = 0; i < ARRAY_SIZE(speaker_map); i++) {
if ((spm >> i) & 1)
printf(" %s\n", speaker_map[i]);
}
+
if ((x[0] & 0xa0) == 0x80)
fail("'Display' flag set, but not the 'SLD' flag.\n");
- if ((x[0] & 0x20) && length >= 7) {
+
+ bool valid_max = cta.preparsed_sld_has_coord || (x[0] & 0x80);
+
+ if (valid_max && length >= 7) {
printf(" Xmax: %u dm\n", x[4]);
printf(" Ymax: %u dm\n", x[5]);
printf(" Zmax: %u dm\n", x[6]);
+ } else if (!valid_max && length >= 7) {
+ // The RCDB should have been truncated.
+ warn("'Display' flag is 0 and 'Coord' is 0 for all SLDs, but the Max coordinates are still present.\n");
}
if ((x[0] & 0x80) && length >= 10) {
printf(" DisplayX: %.3f * Xmax\n", decode_uchar_as_double(x[7]));
printf(" DisplayY: %.3f * Ymax\n", decode_uchar_as_double(x[8]));
printf(" DisplayZ: %.3f * Zmax\n", decode_uchar_as_double(x[9]));
+ } else if (!(x[0] & 0x80) && length >= 10) {
+ // The RCDB should have been truncated.
+ warn("'Display' flag is 0, but the Display coordinates are still present.\n");
}
}
@@ -1446,15 +1467,26 @@ static const char *speaker_location[] = {
"RS - Right Surround",
};
-static void cta_sldb(const unsigned char *x, unsigned length)
+void edid_state::cta_sldb(const unsigned char *x, unsigned length)
{
if (length < 2) {
fail("Empty Data Block with length %u.\n", length);
return;
}
+
+ unsigned active_cnt = 0;
+ unsigned channel_is_active = 0;
+
while (length >= 2) {
printf(" Channel: %u (%sactive)\n", x[0] & 0x1f,
(x[0] & 0x20) ? "" : "not ");
+ if (x[0] & 0x20) {
+ if (channel_is_active & (1U << (x[0] & 0x1f)))
+ fail("Channel Index %u was already marked 'Active'.\n",
+ x[0] & 0x1f);
+ channel_is_active |= 1U << (x[0] & 0x1f);
+ active_cnt++;
+ }
if ((x[1] & 0x1f) < ARRAY_SIZE(speaker_location))
printf(" Speaker: %s\n", speaker_location[x[1] & 0x1f]);
if (length >= 5 && (x[0] & 0x40)) {
@@ -1468,6 +1500,22 @@ static void cta_sldb(const unsigned char *x, unsigned length)
length -= 2;
x += 2;
}
+ if (active_cnt != cta.preparsed_speaker_count)
+ fail("There are %u active speakers, but 'Speaker Count' is %u.\n",
+ active_cnt, cta.preparsed_speaker_count);
+}
+
+void edid_state::cta_preparse_sldb(const unsigned char *x, unsigned length)
+{
+ cta.has_sldb = true;
+ while (length >= 2) {
+ if (length >= 5 && (x[0] & 0x40)) {
+ cta.preparsed_sld_has_coord = true;
+ return;
+ }
+ length -= 2;
+ x += 2;
+ }
}
void edid_state::cta_vcdb(const unsigned char *x, unsigned length)
@@ -2082,7 +2130,7 @@ void edid_state::preparse_cta_block(const unsigned char *x)
if (memchk(detailed, 18))
break;
if (detailed[0] || detailed[1])
- cta.preparse_total_dtds++;
+ cta.preparsed_total_dtds++;
}
}
@@ -2104,12 +2152,18 @@ void edid_state::preparse_cta_block(const unsigned char *x)
case 0x07:
if (x[i + 1] == 0x0d)
cta.has_vfpdb = true;
+ if (x[i + 1] == 0x13 && (x[i + 2] & 0x40)) {
+ cta.preparsed_speaker_count = 1 + (x[i + 2] & 0x1f);
+ cta.preparsed_sld = x[i + 2] & 0x20;
+ }
+ if (x[i + 1] == 0x14)
+ cta_preparse_sldb(x + i + 2, (x[i] & 0x1f) - 1);
if (x[i + 1] == 0x22)
- cta.preparse_total_vtdbs++;
+ cta.preparsed_total_vtdbs++;
if (x[i + 1] == 0x23)
- cta.preparse_has_t8vtdb = true;
+ cta.preparsed_has_t8vtdb = true;
if (x[i + 1] == 0x32)
- cta.preparse_total_vtdbs +=
+ cta.preparsed_total_vtdbs +=
((x[i] & 0x1f) - 2) / (6 + ((x[i + 2] & 0x70) >> 4));
if (x[i + 1] != 0x0e)
continue;
@@ -2187,12 +2241,12 @@ void edid_state::parse_cta_block(const unsigned char *x)
cta.native_timings.clear();
if (!native_dtds && !cta.has_vfpdb) {
cta.first_svd_might_be_preferred = true;
- } else if (native_dtds > cta.preparse_total_dtds) {
+ } else if (native_dtds > cta.preparsed_total_dtds) {
fail("There are more Native DTDs (%u) than DTDs (%u).\n",
- native_dtds, cta.preparse_total_dtds);
+ native_dtds, cta.preparsed_total_dtds);
}
- if (native_dtds > cta.preparse_total_dtds)
- native_dtds = cta.preparse_total_dtds;
+ if (native_dtds > cta.preparsed_total_dtds)
+ native_dtds = cta.preparsed_total_dtds;
for (unsigned i = 0; i < native_dtds; i++) {
char type[16];
diff --git a/parse-displayid-block.cpp b/parse-displayid-block.cpp
index 312b9c3..035e53d 100644
--- a/parse-displayid-block.cpp
+++ b/parse-displayid-block.cpp
@@ -193,7 +193,7 @@ void edid_state::parse_displayid_color_characteristics(const unsigned char *x)
printf(" Uses %u CIE (x, y) coordinates\n", cie_year);
if (xfer_id) {
printf(" Associated with Transfer Characteristics Data Block with Identifier %u\n", xfer_id);
- if (!(dispid.preparse_xfer_ids & (1 << xfer_id)))
+ if (!(dispid.preparsed_xfer_ids & (1 << xfer_id)))
fail("Missing Transfer Characteristics Data Block with Identifier %u.\n", xfer_id);
}
if (!num_primaries) {
@@ -679,7 +679,7 @@ void edid_state::parse_displayid_transfer_characteristics(const unsigned char *x
if (xfer_id) {
printf(" Transfer Characteristics Data Block Identifier: %u\n", xfer_id);
- if (!(dispid.preparse_color_ids & (1 << xfer_id)))
+ if (!(dispid.preparsed_color_ids & (1 << xfer_id)))
fail("Missing Color Characteristics Data Block using Identifier %u.\n", xfer_id);
}
if (first_is_white)
@@ -1503,17 +1503,17 @@ void edid_state::preparse_displayid_block(const unsigned char *x)
unsigned offset = 5;
- dispid.preparse_displayid_blocks++;
+ dispid.preparsed_displayid_blocks++;
while (length > 0) {
unsigned tag = x[offset];
unsigned len = x[offset + 2];
switch (tag) {
case 0x02:
- dispid.preparse_color_ids |= 1 << ((x[offset + 1] >> 3) & 0x0f);
+ dispid.preparsed_color_ids |= 1 << ((x[offset + 1] >> 3) & 0x0f);
break;
case 0x0e:
- dispid.preparse_xfer_ids |= 1 << ((x[offset + 1] >> 4) & 0x0f);
+ dispid.preparsed_xfer_ids |= 1 << ((x[offset + 1] >> 4) & 0x0f);
break;
default:
break;
@@ -1550,11 +1550,11 @@ void edid_state::parse_displayid_block(const unsigned char *x)
product_type(prod_type, false).c_str());
if (!prod_type)
fail("DisplayID Base Block has no product type.\n");
- if (ext_count != dispid.preparse_displayid_blocks - 1)
+ if (ext_count != dispid.preparsed_displayid_blocks - 1)
fail("Expected %u DisplayID Extension Block%s, but got %u.\n",
ext_count,
ext_count > 1 ? "s" : "",
- dispid.preparse_displayid_blocks - 1);
+ dispid.preparsed_displayid_blocks - 1);
} else {
if (prod_type)
fail("Product Type should be 0 in extension block.\n");

Privacy Policy