cta: add support for Speaker Location data blocks

Signed-off-by: Sebastian Wick <sebastian.wick@redhat.com>
This commit is contained in:
Sebastian Wick 2023-01-13 22:21:23 +01:00 committed by Sebastian Wick
parent 3edb81e68d
commit 75b21671d0
3 changed files with 171 additions and 0 deletions

69
cta.c
View file

@ -1301,6 +1301,56 @@ parse_room_config_block(struct di_edid_cta *cta,
return true;
}
static bool
parse_speaker_location_block(struct di_edid_cta *cta,
struct di_cta_speaker_location_block *sldb,
const uint8_t *data, size_t size)
{
struct di_cta_speaker_locations speaker_loc, *slp;
if (size < 2) {
add_failure(cta, "Speaker Location Data Block: Empty Data Block with length %u.",
size);
return false;
}
while (size >= 2) {
speaker_loc.has_coords = has_bit(data[0], 6);
speaker_loc.is_active = has_bit(data[0], 5);
speaker_loc.channel_index = get_bit_range(data[0], 4, 0);
speaker_loc.speaker_id = get_bit_range(data[1], 4, 0);
if (has_bit(data[0], 7) || get_bit_range(data[1], 7, 5) != 0) {
add_failure(cta, "Speaker Location Data Block: Bits F27-F25, F17 must be 0.");
}
if (speaker_loc.has_coords && size >= 5) {
speaker_loc.x = decode_coord(data[2]);
speaker_loc.y = decode_coord(data[3]);
speaker_loc.z = decode_coord(data[4]);
size -= 5;
data += 5;
} else if (speaker_loc.has_coords) {
add_failure(cta, "Speaker Location Data Block: COORD bit "
"set but contains no Coordinates.");
return false;
} else {
size -= 2;
data += 2;
}
slp = calloc(1, sizeof(*slp));
if (!slp)
return false;
*slp = speaker_loc;
assert(sldb->locations_len < EDID_CTA_MAX_SPEAKER_LOCATION_BLOCK_ENTRIES);
sldb->locations[sldb->locations_len++] = slp;
}
return true;
}
static void
destroy_data_block(struct di_cta_data_block *data_block)
{
@ -1308,6 +1358,7 @@ destroy_data_block(struct di_cta_data_block *data_block)
struct di_cta_video_block *video;
struct di_cta_audio_block *audio;
struct di_cta_infoframe_block_priv *infoframe;
struct di_cta_speaker_location_block *speaker_location;
switch (data_block->tag) {
case DI_CTA_DATA_BLOCK_VIDEO:
@ -1330,6 +1381,11 @@ destroy_data_block(struct di_cta_data_block *data_block)
for (i = 0; i < infoframe->infoframes_len; i++)
free(infoframe->infoframes[i]);
break;
case DI_CTA_DATA_BLOCK_SPEAKER_LOCATION:
speaker_location = &data_block->speaker_location;
for (i = 0; i < speaker_location->locations_len; i++)
free(speaker_location->locations[i]);
break;
default:
break; /* Nothing to do */
}
@ -1455,6 +1511,10 @@ parse_data_block(struct di_edid_cta *cta, uint8_t raw_tag, const uint8_t *data,
break;
case 20:
tag = DI_CTA_DATA_BLOCK_SPEAKER_LOCATION;
if (!parse_speaker_location_block(cta,
&data_block->speaker_location,
data, size))
goto skip;
break;
case 32:
tag = DI_CTA_DATA_BLOCK_INFOFRAME;
@ -1753,6 +1813,15 @@ di_cta_data_block_get_infoframe(const struct di_cta_data_block *block)
return &block->infoframe.block;
}
const struct di_cta_speaker_locations *const *
di_cta_data_block_get_speaker_locations(const struct di_cta_data_block *block)
{
if (block->tag != DI_CTA_DATA_BLOCK_SPEAKER_LOCATION) {
return NULL;
}
return (const struct di_cta_speaker_locations *const *) block->speaker_location.locations;
}
const struct di_edid_detailed_timing_def *const *
di_edid_cta_get_detailed_timing_defs(const struct di_edid_cta *cta)
{

View file

@ -57,6 +57,14 @@
* InfoFrame Descriptor is 1 byte.
*/
#define EDID_CTA_INFOFRAME_BLOCK_ENTRIES 61
/**
* The maximum number of Speaker Location Descriptors in a Speaker Location data
* block.
*
* Each data block has its size described in a 5-bit field, so its maximum size
* is 63 bytes, and each Speaker Location Descriptors uses at least 2 byte.
*/
#define EDID_CTA_MAX_SPEAKER_LOCATION_BLOCK_ENTRIES 31
struct di_edid_cta {
int revision;
@ -128,6 +136,12 @@ struct di_cta_infoframe_block_priv {
size_t infoframes_len;
};
struct di_cta_speaker_location_block {
/* NULL-terminated */
struct di_cta_speaker_locations *locations[EDID_CTA_MAX_SPEAKER_LOCATION_BLOCK_ENTRIES + 1];
size_t locations_len;
};
struct di_cta_data_block {
enum di_cta_data_block_tag tag;
@ -157,6 +171,8 @@ struct di_cta_data_block {
struct di_cta_infoframe_block_priv infoframe;
/* Used for DI_CTA_DATA_BLOCK_ROOM_CONFIG */
struct di_cta_room_configuration room_config;
/* Used for DI_CTA_DATA_BLOCK_SPEAKER_LOCATION */
struct di_cta_speaker_location_block speaker_location;
};
extern const struct di_cta_video_format _di_cta_video_formats[];

View file

@ -962,6 +962,92 @@ struct di_cta_room_configuration {
const struct di_cta_room_configuration *
di_cta_data_block_get_room_configuration(const struct di_cta_data_block *block);
enum di_cta_speaker_placement {
/* FL - Front Left */
DI_CTA_SPEAKER_PLACEMENT_FL = 0x00,
/* FR - Front Right */
DI_CTA_SPEAKER_PLACEMENT_FR = 0x01,
/* FC - Front Center */
DI_CTA_SPEAKER_PLACEMENT_FC = 0x02,
/* LFE1 - Low Frequency Effects 1 */
DI_CTA_SPEAKER_PLACEMENT_LFE1 = 0x03,
/* BL - Back Left */
DI_CTA_SPEAKER_PLACEMENT_BL = 0x04,
/* BR - Back Right */
DI_CTA_SPEAKER_PLACEMENT_BR = 0x05,
/* FLc - Front Left of Center */
DI_CTA_SPEAKER_PLACEMENT_FLC = 0x06,
/* FRc - Front Right of Center */
DI_CTA_SPEAKER_PLACEMENT_FRC = 0x07,
/* BC - Back Center */
DI_CTA_SPEAKER_PLACEMENT_BC = 0x08,
/* LFE2 - Low Frequency Effects 2 */
DI_CTA_SPEAKER_PLACEMENT_LFE2 = 0x09,
/* SiL - Side Left */
DI_CTA_SPEAKER_PLACEMENT_SIL = 0x0a,
/* SiR - Side Right */
DI_CTA_SPEAKER_PLACEMENT_SIR = 0x0b,
/* TpFL - Top Front Left */
DI_CTA_SPEAKER_PLACEMENT_TPFL = 0x0c,
/* TpFR - Top Front Right */
DI_CTA_SPEAKER_PLACEMENT_TPFR = 0x0d,
/* TpFC - Top Front Center */
DI_CTA_SPEAKER_PLACEMENT_TPFC = 0x0e,
/* TpC - Top Center */
DI_CTA_SPEAKER_PLACEMENT_TPC = 0x0f,
/* TpBL - Top Back Left */
DI_CTA_SPEAKER_PLACEMENT_TPBL = 0x10,
/* TpBR - Top Back Right */
DI_CTA_SPEAKER_PLACEMENT_TPBR = 0x11,
/* TpSiL - Top Side Left */
DI_CTA_SPEAKER_PLACEMENT_TPSIL = 0x12,
/* TpSiR - Top Side Right */
DI_CTA_SPEAKER_PLACEMENT_TPSIR = 0x13,
/* TpBC - Top Back Center */
DI_CTA_SPEAKER_PLACEMENT_TPBC = 0x14,
/* BtFC - Bottom Front Center */
DI_CTA_SPEAKER_PLACEMENT_BTFC = 0x15,
/* BtFL - Bottom Front Left */
DI_CTA_SPEAKER_PLACEMENT_BTFL = 0x16,
/* BtFR - Bottom Front Right */
DI_CTA_SPEAKER_PLACEMENT_BRFR = 0x17,
/* FLw - Front Left Wide */
DI_CTA_SPEAKER_PLACEMENT_FLW = 0x18,
/* FRw - Front Right Wide */
DI_CTA_SPEAKER_PLACEMENT_FRW = 0x19,
/* LS - Left Surround */
DI_CTA_SPEAKER_PLACEMENT_LS = 0x1a,
/* RS - Right Surround */
DI_CTA_SPEAKER_PLACEMENT_RS = 0x1b,
};
/**
* Speaker Location Data Block, defined in section 7.5.16.
*/
struct di_cta_speaker_locations {
/* Index of the audio channel where the audio for the described speaker
* is to be transmitted. */
int channel_index;
/* If the channel shall be rendered on a speaker by the Sink. */
bool is_active;
/* If the speaker has coordinates instead of a named speaker placement. */
bool has_coords;
/* The position of the speaker in normalized coordinates. */
double x, y, z;
/* The named location of the speaker. Only valid if has_coords is false. */
enum di_cta_speaker_placement speaker_id;
};
/**
* Get an array of Speaker Locations.
*
* Returns NULL if the data block tag is not DI_CTA_DATA_BLOCK_SPEAKER_LOCATION.
*
* The returned array is NULL-terminated.
*/
const struct di_cta_speaker_locations *const *
di_cta_data_block_get_speaker_locations(const struct di_cta_data_block *block);
/**
* Get a list of EDID detailed timing definitions.
*