From 7d8ac601e8d6d041bcf99f2292bd7b36323ecb0a Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" <114750+alfredh@users.noreply.github.com> Date: Wed, 31 Jul 2024 10:35:55 +0200 Subject: [PATCH] dd: Dependency Descriptor RTP header extension (#1170) * dd: Dependency Descriptor RTP header extension Defined in: https://aomediacodec.github.io/av1-rtp-spec/#dependency-descriptor-rtp-header-extension This work was sponsored by Nvidia Corporation * dd: fix windows warning * test: disable dd_print() * dd: use re_printf() * dd: remove debugging * dd: fix for clang analyze * dd: add limit check for count --- CMakeLists.txt | 5 + include/re_dd.h | 109 +++++++++++ include/re_h264.h | 18 ++ src/av1/getbit.c | 61 +++++++ src/dd/dd.c | 432 ++++++++++++++++++++++++++++++++++++++++++++ src/dd/dd_enc.c | 275 ++++++++++++++++++++++++++++ src/dd/putbit.c | 108 +++++++++++ src/h264/getbit.c | 64 +++++++ src/h264/h264.h | 11 -- test/CMakeLists.txt | 1 + test/dd.c | 333 ++++++++++++++++++++++++++++++++++ test/test.c | 1 + test/test.h | 1 + 13 files changed, 1408 insertions(+), 11 deletions(-) create mode 100644 include/re_dd.h create mode 100644 src/av1/getbit.c create mode 100644 src/dd/dd.c create mode 100644 src/dd/dd_enc.c create mode 100644 src/dd/putbit.c create mode 100644 test/dd.c diff --git a/CMakeLists.txt b/CMakeLists.txt index b61a16ef6..7a547fb01 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -132,6 +132,7 @@ set(HEADERS include/re_convert.h include/re_crc32.h include/re_dbg.h + include/re_dd.h include/re_dns.h include/re_fmt.h include/re_h264.h @@ -226,6 +227,10 @@ set(SRCS src/dbg/dbg.c + src/dd/dd.c + src/dd/dd_enc.c + src/dd/putbit.c + src/dns/client.c src/dns/cstr.c src/dns/dname.c diff --git a/include/re_dd.h b/include/re_dd.h new file mode 100644 index 000000000..9dfecd200 --- /dev/null +++ b/include/re_dd.h @@ -0,0 +1,109 @@ +/** + * @file re_dd.h Dependency Descriptor (DD) + * + * Copyright (C) 2010 - 2023 Alfred E. Heggestad + */ + + +/* + * Put bits wrapper (XXX: move to common place) + */ + +struct putbit { + struct mbuf *mb; + size_t bit_pos; +}; + +void putbit_init(struct putbit *pb, struct mbuf *mb); +int putbit_one(struct putbit *pb, unsigned bit); +int putbit_write(struct putbit *pb, unsigned count, unsigned val); +int putbit_write_ns(struct putbit *pb, unsigned n, unsigned v); + + +/* + * Dependency Descriptor (DD) + * + * DT: Decode Target + * DTI: Decode Target Indication + * SID: Spatial ID + * TID: Temporal ID + */ + +/* Constants. */ +enum { + DD_MAX_SPATIAL_IDS = 4u, + DD_MAX_TEMPORAL_IDS = 4u, + DD_MAX_TEMPLATES = 8u, + DD_MAX_FDIFFS = 16u, + DD_MAX_DECODE_TARGETS= 16u, + DD_MAX_CHAINS = 32u, +}; + + +/* Decode Target Indication (DTI) */ +enum dd_dti { + DD_DTI_NOT_PRESENT = 0, + DD_DTI_DISCARDABLE = 1, + DD_DTI_SWITCH = 2, + DD_DTI_REQUIRED = 3, +}; + +enum dd_next_layer_idc { + DD_SAME_LAYER = 0, + DD_NEXT_TEMPORAL_LAYER = 1, + DD_NEXT_SPATIAL_LAYER = 2, + DD_NO_MORE_TEMPLATES = 3, +}; + +/* + * https://aomediacodec.github.io/av1-rtp-spec/ + * #dependency-descriptor-rtp-header-extension + */ +struct dd { + + /* Mandatory Descriptor Fields */ + unsigned start_of_frame:1; + unsigned end_of_frame:1; + unsigned frame_dependency_template_id:6; + uint16_t frame_number; + + // TODO: needed? + bool ext; + + unsigned template_dependency_structure_present_flag:1; + unsigned active_decode_targets_present_flag:1; + unsigned custom_dtis_flag:1; + unsigned custom_fdiffs_flag:1; + unsigned custom_chains_flag:1; + + unsigned active_decode_targets_bitmask; + unsigned template_id_offset:6; + uint8_t dt_cnt; + uint8_t template_cnt; + uint8_t max_spatial_id; + + uint8_t template_spatial_id[DD_MAX_TEMPLATES]; + uint8_t template_temporal_id[DD_MAX_TEMPLATES]; + + /* render_resolutions */ + bool resolutions_present_flag; + uint16_t max_render_width_minus_1[DD_MAX_SPATIAL_IDS]; + uint16_t max_render_height_minus_1[DD_MAX_SPATIAL_IDS]; + uint8_t render_count; + + /* type: enum dd_dti */ + uint8_t template_dti[DD_MAX_TEMPLATES][DD_MAX_DECODE_TARGETS]; + + /* template fdiffs */ + uint8_t template_fdiff[DD_MAX_TEMPLATES][DD_MAX_FDIFFS]; + uint8_t template_fdiff_cnt[DD_MAX_TEMPLATES]; + + /* template chains */ + uint8_t decode_target_protected_by[DD_MAX_DECODE_TARGETS]; + uint8_t template_chain_fdiff[DD_MAX_TEMPLATES][DD_MAX_CHAINS]; + uint8_t chain_cnt; +}; + +int dd_encode(struct mbuf *mb, const struct dd *dd); +int dd_decode(struct dd *dd, const uint8_t *buf, size_t sz); +void dd_print(const struct dd *dd); diff --git a/include/re_h264.h b/include/re_h264.h index e5674ea8e..c1ffbfba6 100644 --- a/include/re_h264.h +++ b/include/re_h264.h @@ -105,3 +105,21 @@ int h264_stap_encode(struct mbuf *mb, const uint8_t *frame, size_t frame_sz); int h264_stap_decode_annexb(struct mbuf *mb_frame, struct mbuf *mb_pkt); int h264_stap_decode_annexb_long(struct mbuf *mb_frame, struct mbuf *mb_pkt); + + +/* + * Get bits wrapper + */ + +struct getbit { + const uint8_t *buf; + size_t pos; + size_t end; +}; + +void getbit_init(struct getbit *gb, const uint8_t *buf, size_t size); +size_t getbit_get_left(const struct getbit *gb); +unsigned get_bit(struct getbit *gb); +int get_ue_golomb(struct getbit *gb, unsigned *valp); +unsigned get_bits(struct getbit *gb, unsigned n); +unsigned getbit_read_ns(struct getbit *gb, unsigned n); diff --git a/src/av1/getbit.c b/src/av1/getbit.c new file mode 100644 index 000000000..cda472201 --- /dev/null +++ b/src/av1/getbit.c @@ -0,0 +1,61 @@ +/** + * @file getbit.c Get bits helper + * + * Copyright (C) 2023 Alfred E. Heggestad + */ + +#include +#include + + +#define DEBUG_MODULE "av1" +#define DEBUG_LEVEL 5 +#include + + +void getbit_init(struct getbit *gb, const uint8_t *buf, size_t size_bits) +{ + if (!gb) + return; + + gb->buf = buf; + gb->pos = 0; + gb->end = size_bits; +} + + +size_t getbit_get_left(const struct getbit *gb) +{ + if (!gb) + return 0; + + if (gb->end > gb->pos) + return gb->end - gb->pos; + else + return 0; +} + + +unsigned get_bit(struct getbit *gb) +{ + register unsigned tmp; + + if (!gb) + return 0; + + if (gb->pos >= gb->end) { + DEBUG_WARNING("get_bit: read past end" + " (%zu >= %zu)\n", gb->pos, gb->end); + assert(0); + return 0; + } + + const uint8_t *p = gb->buf; + tmp = ((*(p + (gb->pos >> 0x3))) >> (0x7 - (gb->pos & 0x7))) & 0x1; + + ++gb->pos; + + return tmp; +} + + diff --git a/src/dd/dd.c b/src/dd/dd.c new file mode 100644 index 000000000..db708c09c --- /dev/null +++ b/src/dd/dd.c @@ -0,0 +1,432 @@ +/** + * @file dd.c Dependency Descriptor (DD) -- decoder + * + * Copyright (C) 2023 Alfred E. Heggestad + */ + +#include +#include +#include + + +#define dd_f(n) get_bits(gb, (n)) + + +static const char *dti_name(enum dd_dti dti) +{ + switch (dti) { + + case DD_DTI_NOT_PRESENT: return "NOT_PRESENT"; + case DD_DTI_DISCARDABLE: return "DISCARDABLE"; + case DD_DTI_SWITCH: return "SWITCH"; + case DD_DTI_REQUIRED: return "REQUIRED"; + } + + return "???"; +} + + +#if 0 +static const char *next_layer_name(enum dd_next_layer_idc idc) +{ + switch (idc) { + + case DD_SAME_LAYER: return "Same"; + case DD_NEXT_TEMPORAL_LAYER: return "Temporal"; + case DD_NEXT_SPATIAL_LAYER: return "Spatial"; + case DD_NO_MORE_TEMPLATES: return "None"; + } + + return "???"; +} +#endif + + +static int mandatory_descriptor_fields(struct dd *dd, struct getbit *gb) +{ + if (getbit_get_left(gb) < 24) + return EBADMSG; + + dd->start_of_frame = dd_f(1); + dd->end_of_frame = dd_f(1); + dd->frame_dependency_template_id = dd_f(6); + dd->frame_number = dd_f(16); + + return 0; +} + + +/* + * 0 next template has the same spatial ID and temporal ID as current template + * + * 1 next template has the same spatial ID and temporal ID plus 1 compared + * with the current Frame dependency template. + * + * 2 next Frame dependency template has temporal ID equal to 0 and + * spatial ID plus 1 compared with the current Frame dependency template. + * + * 3 No more Frame dependency templates are present in the + * Frame dependency structure. + */ +static int template_layers(struct dd *dd, struct getbit *gb) +{ + uint8_t temporalId = 0; + uint8_t spatialId = 0; + uint8_t TemplateCnt = 0; + uint8_t MaxTemporalId = 0; + uint8_t next_layer_idc = DD_SAME_LAYER; + + do { + if (TemplateCnt >= DD_MAX_TEMPLATES) + return EOVERFLOW; + + dd->template_spatial_id[TemplateCnt] = spatialId; + dd->template_temporal_id[TemplateCnt] = temporalId; + + ++TemplateCnt; + + if (getbit_get_left(gb) < 2) + return EBADMSG; + + next_layer_idc = dd_f(2); + + /* next_layer_idc == 0 - same sid and tid */ + if (next_layer_idc == DD_NEXT_TEMPORAL_LAYER) { + ++temporalId; + if (temporalId > MaxTemporalId) { + MaxTemporalId = temporalId; + } + } + else if (next_layer_idc == DD_NEXT_SPATIAL_LAYER) { + temporalId = 0; + ++spatialId; + } + } + while (next_layer_idc != DD_NO_MORE_TEMPLATES); + + dd->max_spatial_id = spatialId; + dd->template_cnt = TemplateCnt; + + return 0; +} + + +static int template_dtis(struct dd *dd, struct getbit *gb) +{ + for (uint8_t templateIndex = 0; + templateIndex < dd->template_cnt; + templateIndex++) { + + if (templateIndex >= DD_MAX_TEMPLATES) + return EOVERFLOW; + + for (uint8_t dtIndex = 0; dtIndex < dd->dt_cnt; dtIndex++) { + + if (getbit_get_left(gb) < 2) + return EBADMSG; + + /* See table A.1 below for meaning of DTI values. */ + dd->template_dti[templateIndex][dtIndex] = dd_f(2); + } + } + + return 0; +} + + +static int template_fdiffs(struct dd *dd, struct getbit *gb) +{ + for (uint8_t templateIndex = 0; + templateIndex < dd->template_cnt; + templateIndex++) { + + uint8_t fdiffCnt = 0; + + if (getbit_get_left(gb) < 1) + return EBADMSG; + + bool fdiff_follows_flag = dd_f(1); + + while (fdiff_follows_flag) { + + if (getbit_get_left(gb) < 5) + return EBADMSG; + + uint8_t fdiff_minus_one = dd_f(4); + + uint8_t fdiff = fdiff_minus_one + 1; + + dd->template_fdiff[templateIndex][fdiffCnt] = fdiff; + + ++fdiffCnt; + fdiff_follows_flag = dd_f(1); + } + + dd->template_fdiff_cnt[templateIndex] = fdiffCnt; + } + + return 0; +} + + +static int template_chains(struct dd *dd, struct getbit *gb) +{ + /* todo: check bits left */ + dd->chain_cnt = getbit_read_ns(gb, dd->dt_cnt + 1); + + if (dd->chain_cnt == 0) + return 0; + + for (uint8_t dtIndex = 0; dtIndex < dd->dt_cnt; dtIndex++) { + uint8_t v = getbit_read_ns(gb, dd->chain_cnt); + dd->decode_target_protected_by[dtIndex] = v; + } + + for (uint8_t templateIndex = 0; + templateIndex < dd->template_cnt; + templateIndex++) { + + for (uint8_t chainIndex = 0; + chainIndex < dd->chain_cnt; + chainIndex++) { + + if (getbit_get_left(gb) < 4) + return EBADMSG; + + dd->template_chain_fdiff[templateIndex][chainIndex] = + dd_f(4); + } + } + + return 0; +} + + +static int render_resolutions(struct dd *dd, struct getbit *gb) +{ + for (uint8_t spatial_id = 0; + spatial_id <= dd->max_spatial_id; + spatial_id++) { + + if (getbit_get_left(gb) < 32) + return EBADMSG; + + dd->max_render_width_minus_1[spatial_id] = dd_f(16); + dd->max_render_height_minus_1[spatial_id] = dd_f(16); + + ++dd->render_count; + } + + return 0; +} + + +static int template_dependency_structure(struct dd *dd, struct getbit *gb) +{ + if (getbit_get_left(gb) < 11) + return EBADMSG; + + dd->template_id_offset = dd_f(6); + uint8_t dt_cnt_minus_one = dd_f(5); + + dd->dt_cnt = dt_cnt_minus_one + 1; + + int err = template_layers(dd, gb); + if (err) + return err; + + err = template_dtis(dd, gb); + if (err) + return err; + + err = template_fdiffs(dd, gb); + if (err) + return err; + + template_chains(dd, gb); + + /* TODO decode_target_layers() */ + + if (getbit_get_left(gb) < 1) + return EBADMSG; + + dd->resolutions_present_flag = dd_f(1); + if (dd->resolutions_present_flag) { + err = render_resolutions(dd, gb); + if (err) + return err; + } + + return 0; +} + + +static int extended_descriptor_fields(struct dd *dd, struct getbit *gb) +{ + if (getbit_get_left(gb) < 5) + return EBADMSG; + + dd->template_dependency_structure_present_flag = dd_f(1); + dd->active_decode_targets_present_flag = dd_f(1); + dd->custom_dtis_flag = dd_f(1); + dd->custom_fdiffs_flag = dd_f(1); + dd->custom_chains_flag = dd_f(1); + + if (dd->template_dependency_structure_present_flag) { + + int err = template_dependency_structure(dd, gb); + if (err) + return err; + + dd->active_decode_targets_bitmask = (1u << dd->dt_cnt) - 1; + } + + if (dd->active_decode_targets_present_flag) { + + dd->active_decode_targets_bitmask = dd_f(dd->dt_cnt); + } + + return 0; +} + + +static void no_extended_descriptor_fields(struct dd *dd) +{ + dd->custom_dtis_flag = 0; + dd->custom_fdiffs_flag = 0; + dd->custom_chains_flag = 0; +} + + +int dd_decode(struct dd *dd, const uint8_t *buf, size_t sz) +{ + if (!dd || !buf) + return EINVAL; + + memset(dd, 0, sizeof(*dd)); + + struct getbit gb; + + getbit_init(&gb, buf, sz*8); + + int err = mandatory_descriptor_fields(dd, &gb); + if (err) + return err; + + if (sz > 3) { + err = extended_descriptor_fields(dd, &gb); + if (err) + return err; + + dd->ext = true; + } + else { + no_extended_descriptor_fields(dd); + } + + return 0; +} + + +void dd_print(const struct dd *dd) +{ + if (!dd) + return; + + re_printf("~~~~ DD: ~~~~\n"); + + re_printf(".... start=%d, end=%d," + " frame_dependency_template_id=%u, frame_number=%u\n", + dd->start_of_frame, + dd->end_of_frame, + dd->frame_dependency_template_id, + dd->frame_number); + re_printf(".... ext: %d\n", dd->ext); + + if (dd->ext) { + + re_printf(".... template_dependency_structure_present: %u\n", + dd->template_dependency_structure_present_flag); + re_printf(".... active_decode_targets_present_flag: %u\n", + dd->active_decode_targets_present_flag); + re_printf(".... custom_dtis_flag: %u\n", + dd->custom_dtis_flag); + re_printf(".... custom_fdiffs_flag: %u\n", + dd->custom_fdiffs_flag); + re_printf(".... custom_chains_flag: %u\n", + dd->custom_chains_flag); + re_printf("\n"); + + re_printf(".... active_decode_targets_bitmask: 0x%x\n", + dd->active_decode_targets_bitmask); + re_printf(".... template_id_offset: %u\n", + dd->template_id_offset); + re_printf(".... dt_cnt: %u\n", + dd->dt_cnt); + re_printf(".... template_cnt: %u\n", + dd->template_cnt); + re_printf(".... max_spatial_id: %u\n", + dd->max_spatial_id); + re_printf("\n"); + + re_printf(".... template spatial/temporal ids:\n"); + for (uint8_t i=0; itemplate_cnt; i++) { + + re_printf(".... [%u] spatial=%u temporal=%u\n", + i, + dd->template_spatial_id[i], + dd->template_temporal_id[i]); + } + re_printf("\n"); + + re_printf(".... resolutions_present_flag: %u\n", + dd->resolutions_present_flag); + re_printf(".... render_count: %u\n", dd->render_count); + for (uint8_t i = 0; i < dd->render_count; i++) { + + re_printf(".... max_render %u: %u x %u\n", + i, + dd->max_render_width_minus_1[i] + 1, + dd->max_render_height_minus_1[i] + 1); + } + re_printf("\n"); + + for (uint8_t i = 0; i < dd->template_cnt; i++) { + + uint8_t fdiffCnt = dd->template_fdiff_cnt[i]; + + re_printf(".... [%u] template_fdiff_cnt: %u", + i, fdiffCnt); + + for (uint8_t j = 0; j < fdiffCnt; j++) { + + uint8_t fdiff; + + fdiff = dd->template_fdiff[i][j]; + + re_printf(" ", fdiff); + } + + re_printf("\n"); + } + re_printf("\n"); + + re_printf(".... chain_cnt: %u\n", dd->chain_cnt); + re_printf("\n"); + + re_printf(".... template_dti: 2D\n"); + for (uint8_t tix = 0; tix < dd->template_cnt; tix++) { + + for (uint8_t dtix = 0; dtix < dd->dt_cnt; dtix++) { + + uint8_t val = dd->template_dti[tix][dtix]; + + re_printf(".... DTI: [%u][%u] %u %s\n", + tix, dtix, val, dti_name(val)); + } + } + } + + re_printf("~~~~~~~~~~~~\n"); + re_printf("\n"); +} diff --git a/src/dd/dd_enc.c b/src/dd/dd_enc.c new file mode 100644 index 000000000..d56a035bc --- /dev/null +++ b/src/dd/dd_enc.c @@ -0,0 +1,275 @@ +/** + * @file dd_enc.c Dependency Descriptor (DD) -- encoder + * + * Copyright (C) 2023 Alfred E. Heggestad + */ + +#include +#include +#include + + +#define DEBUG_MODULE "dd" +#define DEBUG_LEVEL 5 +#include + + +static int mandatory_descriptor_fields(struct putbit *pb, const struct dd *dd) +{ + int err = 0; + + err |= putbit_one(pb, dd->start_of_frame); + err |= putbit_one(pb, dd->end_of_frame); + err |= putbit_write(pb, 6, dd->frame_dependency_template_id); + err |= putbit_write(pb, 16, dd->frame_number); + + return err; +} + + +static uint8_t next_layer(const struct dd *dd, unsigned prev, unsigned next) +{ + if (dd->template_spatial_id[next] == dd->template_spatial_id[prev] && + dd->template_temporal_id[next] == dd->template_temporal_id[prev]) { + + return DD_SAME_LAYER; + } + else if (dd->template_spatial_id[next] == + dd->template_spatial_id[prev] && + dd->template_temporal_id[next] == + dd->template_temporal_id[prev] + 1) { + + return DD_NEXT_TEMPORAL_LAYER; + } + else if (dd->template_spatial_id[next] == + dd->template_spatial_id[prev] + 1 && + dd->template_temporal_id[next] == 0) { + + return DD_NEXT_SPATIAL_LAYER; + } + + return DD_NO_MORE_TEMPLATES; +} + + +static int template_layers(struct putbit *pb, const struct dd *dd) +{ + int err = 0; + + for (unsigned i = 1; i < dd->template_cnt; ++i) { + + uint8_t next_layer_idc = next_layer(dd, i - 1, i); + if (next_layer_idc == DD_NO_MORE_TEMPLATES) + return EBADMSG; + + err |= putbit_write(pb, 2, next_layer_idc); + } + + /* end of layers */ + err |= putbit_write(pb, 2, DD_NO_MORE_TEMPLATES); + + return err; +} + + +static int template_dtis(struct putbit *pb, const struct dd *dd) +{ + for (uint8_t templateIndex = 0; + templateIndex < dd->template_cnt; + templateIndex++) { + + for (uint8_t dtIndex = 0; dtIndex < dd->dt_cnt; dtIndex++) { + + /* See table A.1 below for meaning of DTI values. */ + + uint8_t v = dd->template_dti[templateIndex][dtIndex]; + + int err = putbit_write(pb, 2, v); + if (err) + return err; + } + } + + return 0; +} + + +static int template_fdiffs(struct putbit *pb, const struct dd *dd) +{ + int err; + + for (uint8_t templateIndex = 0; + templateIndex < dd->template_cnt; + templateIndex++) { + + uint8_t fdiffCnt = dd->template_fdiff_cnt[templateIndex]; + + for (uint8_t j = 0; j < fdiffCnt; j++) { + + uint8_t fdiff; + + fdiff = dd->template_fdiff[templateIndex][j]; + + /* fdiff_follows_flag */ + err = putbit_write(pb, 1, true); + if (err) + return err; + + err = putbit_write(pb, 4, fdiff - 1); + if (err) + return err; + } + + /* fdiff_follows_flag */ + err = putbit_write(pb, 1, false); + if (err) + return err; + } + + return 0; +} + + +static int template_chains(struct putbit *pb, const struct dd *dd) +{ + int err = putbit_write_ns(pb, dd->dt_cnt + 1, dd->chain_cnt); + if (err) + return err; + + if (dd->chain_cnt == 0) + return 0; + + for (uint8_t dtIndex = 0; dtIndex < dd->dt_cnt; dtIndex++) { + + uint8_t val = dd->decode_target_protected_by[dtIndex]; + + err = putbit_write_ns(pb, dd->chain_cnt, val); + if (err) + return err; + } + + for (uint8_t templateIndex = 0; + templateIndex < dd->template_cnt; + templateIndex++) { + + for (uint8_t chainIndex = 0; + chainIndex < dd->chain_cnt; + chainIndex++) { + + uint8_t val; + + val=dd->template_chain_fdiff[templateIndex][chainIndex]; + + err = putbit_write(pb, 4, val); + if (err) + return err; + } + } + + return 0; +} + + +static int render_resolutions(struct putbit *pb, const struct dd *dd) +{ + for (uint8_t i=0; irender_count; i++) { + + putbit_write(pb, 16, dd->max_render_width_minus_1[i]); + putbit_write(pb, 16, dd->max_render_height_minus_1[i]); + } + + return 0; +} + + +static int template_dependency_structure(struct putbit *pb, + const struct dd *dd) +{ + int err; + + uint8_t dt_cnt_minus_one = dd->dt_cnt - 1; + + err = putbit_write(pb, 6, dd->template_id_offset); + err |= putbit_write(pb, 5, dt_cnt_minus_one); + if (err) + return err; + + err = template_layers(pb, dd); + if (err) + return err; + + err = template_dtis(pb, dd); + if (err) + return err; + + err = template_fdiffs(pb, dd); + if (err) + return err; + + err = template_chains(pb, dd); + if (err) + return err; + + err = putbit_one(pb, dd->resolutions_present_flag); + if (err) + return err; + + if (dd->resolutions_present_flag) { + render_resolutions(pb, dd); + } + + /* XXX decode_target_layers() */ + + return 0; +} + + +static int extended_descriptor_fields(struct putbit *pb, const struct dd *dd) +{ + int err = 0; + + err |= putbit_one(pb, dd->template_dependency_structure_present_flag); + err |= putbit_one(pb, dd->active_decode_targets_present_flag); + err |= putbit_one(pb, dd->custom_dtis_flag); + err |= putbit_one(pb, dd->custom_fdiffs_flag); + err |= putbit_one(pb, dd->custom_chains_flag); + if (err) + return err; + + if (dd->template_dependency_structure_present_flag) { + + err = template_dependency_structure(pb, dd); + if (err) + return err; + } + + if (dd->active_decode_targets_present_flag) { + DEBUG_WARNING("no active_decode_targets_present_flag\n"); + return ENOTSUP; + } + + return 0; +} + + +int dd_encode(struct mbuf *mb, const struct dd *dd) +{ + struct putbit pb; + + if (!mb || !dd) + return EINVAL; + + putbit_init(&pb, mb); + + int err = mandatory_descriptor_fields(&pb, dd); + if (err) + return err; + + if (dd->ext) { + err = extended_descriptor_fields(&pb, dd); + if (err) + return err; + } + + return 0; +} diff --git a/src/dd/putbit.c b/src/dd/putbit.c new file mode 100644 index 000000000..82f734da1 --- /dev/null +++ b/src/dd/putbit.c @@ -0,0 +1,108 @@ +/** + * @file putbit.c Put bits helper + * + * Copyright (C) 2023 Alfred E. Heggestad + */ + +#include +#include +#include + + +void putbit_init(struct putbit *pb, struct mbuf *mb) +{ + if (!pb || !mb) + return; + + pb->mb = mb; + pb->bit_pos = 0; + + memset(pb->mb->buf, 0, pb->mb->size); +} + + +int putbit_one(struct putbit *pb, unsigned bit) +{ + if (!pb) + return EINVAL; + + size_t byte_pos = pb->bit_pos >> 0x3; + + /* resize mbuf */ + if (byte_pos >= pb->mb->size) { + + int err = mbuf_resize(pb->mb, pb->mb->size * 2); + if (err) + return err; + } + + uint8_t *p = pb->mb->buf; + size_t bit_pos = (size_t)(1u << (0x7 - (pb->bit_pos & 0x7))); + + if (bit) { + p[byte_pos] |= bit_pos; + } + else { + p[byte_pos] &= ~bit_pos; + } + + ++pb->bit_pos; + + /* NOTE: mb->pos not used */ + mbuf_set_end(pb->mb, (pb->bit_pos + 7) >> 0x3); + + return 0; +} + + +int putbit_write(struct putbit *pb, unsigned count, unsigned val) +{ + if (!pb) + return EINVAL; + + if (count > 32) + return EINVAL; + + for (unsigned i=0; i> shift) & 0x1; + + int err = putbit_one(pb, bit); + if (err) + return err; + } + + return 0; +} + + +int putbit_write_ns(struct putbit *pb, unsigned n, unsigned v) +{ + if (!pb) + return EINVAL; + + int err; + +#if 0 + /* TODO: check this */ + if (n == 1) + return EINVAL; +#endif + + unsigned w = 0; + unsigned x = n; + + while (x != 0) { + x = x >> 1; + ++w; + } + + unsigned m = (1 << w) - n; + if (v < m) + err = putbit_write(pb, w - 1, v); + else + err = putbit_write(pb, w, v + m); + + return err; +} diff --git a/src/h264/getbit.c b/src/h264/getbit.c index f6f35318d..770533838 100644 --- a/src/h264/getbit.c +++ b/src/h264/getbit.c @@ -6,9 +6,15 @@ #include #include +#include #include "h264.h" +#define DEBUG_MODULE "getbit" +#define DEBUG_LEVEL 5 +#include + + void getbit_init(struct getbit *gb, const uint8_t *buf, size_t size) { if (!gb) @@ -90,3 +96,61 @@ int get_ue_golomb(struct getbit *gb, unsigned *valp) return 0; } + + +/* + x = 0 + for ( i = 0; i < n; i++ ) { + x = 2 * x + read_bit() + } + TotalConsumedBits += n + return x + */ +unsigned get_bits(struct getbit *gb, unsigned n) +{ + unsigned x = 0; + + if (getbit_get_left(gb) < n) { + DEBUG_WARNING("get_bits: read past end" + " (n=%zu, left=%zu)\n", n, getbit_get_left(gb)); + return 0; + } + + for (unsigned i=0; i> 1; + ++w; + } + + unsigned m = (1u << w) - n; + unsigned v = dd_f(w - 1); + + if (v < m) + return v; + + unsigned extra_bit = dd_f(1); + + return (v << 1) - m + extra_bit; +} diff --git a/src/h264/h264.h b/src/h264/h264.h index 105e3b57b..5ed49f883 100644 --- a/src/h264/h264.h +++ b/src/h264/h264.h @@ -5,14 +5,3 @@ */ -struct getbit { - const uint8_t *buf; - size_t pos; - size_t end; -}; - - -void getbit_init(struct getbit *gb, const uint8_t *buf, size_t size); -size_t getbit_get_left(const struct getbit *gb); -unsigned get_bit(struct getbit *gb); -int get_ue_golomb(struct getbit *gb, unsigned *valp); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8a1bbf043..213e5d4a3 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -80,6 +80,7 @@ set(SRCS conf.c convert.c crc32.c + dd.c dns.c dsp.c dtmf.c diff --git a/test/dd.c b/test/dd.c new file mode 100644 index 000000000..a2fae38a5 --- /dev/null +++ b/test/dd.c @@ -0,0 +1,333 @@ +/** + * @file test/dd.c Dependency Descriptor (DD) testcode + * + * Copyright (C) 2010 - 2023 Alfred E. Heggestad + */ + +#include +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "ddtest" +#define DEBUG_LEVEL 5 +#include + + +static int test_dd_mand(void) +{ + struct dd dd = { 0 }; + struct mbuf *mb = mbuf_alloc(512); + + if (!mb) + return ENOMEM; + + int err = dd_encode(mb, &dd); + TEST_ERR(err); + + /* Mandatory Descriptor Fields -- 3 bytes only */ + static const uint8_t buf_exp[] = { 0, 0, 0 }; + + TEST_MEMCMP(buf_exp, sizeof(buf_exp), mb->buf, mb->end); + + struct dd dd_dec; + + err = dd_decode(&dd_dec, mb->buf, mb->end); + TEST_ERR(err); + + ASSERT_EQ(0, dd_dec.start_of_frame); + ASSERT_EQ(0, dd_dec.end_of_frame); + ASSERT_EQ(0, dd_dec.frame_dependency_template_id); + ASSERT_EQ(0, dd_dec.frame_number); + + ASSERT_TRUE(!dd_dec.ext); + + out: + mem_deref(mb); + return err; +} + + +/* + +80012f800214eaa860414d1410208426 + + +"startOfFrame":true, +"endOfFrame":false, +"frameDependencyTemplateId":0, +"frameNumber":303, +"templateStructure":{ + "templateIdOffset":0, + "templateInfo":{ + "0":{ + "spatialId":0, + "temporalId":0, + "dti":[ + "SWITCH", + "SWITCH", + "SWITCH" + ], + "fdiff":[ + + ], + "chains":[ + 0 + ] + }, + "1":{ + "spatialId":0, + "temporalId":0, + "dti":[ + "SWITCH", + "SWITCH", + "SWITCH" + ], + "fdiff":[ + 4 + ], + "chains":[ + 4 + ] + }, + "2":{ + "spatialId":0, + "temporalId":1, + "dti":[ + "NOT_PRESENT", + "DISCARDABLE", + "SWITCH" + ], + "fdiff":[ + 2 + ], + "chains":[ + 2 + ] + }, + "3":{ + "spatialId":0, + "temporalId":2, + "dti":[ + "NOT_PRESENT", + "NOT_PRESENT", + "DISCARDABLE" + ], + "fdiff":[ + 1 + ], + "chains":[ + 1 + ] + }, + "4":{ + "spatialId":0, + "temporalId":2, + "dti":[ + "NOT_PRESENT", + "NOT_PRESENT", + "DISCARDABLE" + ], + "fdiff":[ + 1 + ], + "chains":[ + 3 + ] + } + }, + "decodeTargetInfo":{ + "0":{ + "protectedBy":0, + "spatialId":0, + "temporalId":0 + }, + "1":{ + "protectedBy":0, + "spatialId":0, + "temporalId":1 + }, + "2":{ + "protectedBy":0, + "spatialId":0, + "temporalId":2 + } + }, + "maxSpatialId":0, + "maxTemporalId":2 +} +} + + + */ +static int test_dd_decode(void) +{ + static const char *str = "80012f800214eaa860414d1410208426"; + struct mbuf *mb = mbuf_alloc(8); + uint8_t buf[16]; + int err; + + if (!mb) + return ENOMEM; + + err = str_hex(buf, sizeof(buf), str); + TEST_ERR(err); + + struct dd dd; + + err = dd_decode(&dd, buf, sizeof(buf)); + TEST_ERR(err); + +#if 0 + dd_print(&dd); +#endif + + ASSERT_EQ(1, dd.start_of_frame); + ASSERT_EQ(0, dd.end_of_frame); + ASSERT_EQ(0, dd.frame_dependency_template_id); + ASSERT_EQ(303, dd.frame_number); + + ASSERT_EQ(1, dd.template_dependency_structure_present_flag); + ASSERT_EQ(0, dd.active_decode_targets_present_flag); + ASSERT_EQ(0, dd.custom_dtis_flag); + ASSERT_EQ(0, dd.custom_fdiffs_flag); + ASSERT_EQ(0, dd.custom_chains_flag); + + ASSERT_EQ(7, dd.active_decode_targets_bitmask); + ASSERT_EQ(0, dd.template_id_offset); + ASSERT_EQ(3, dd.dt_cnt); + ASSERT_EQ(5, dd.template_cnt); + ASSERT_EQ(0, dd.max_spatial_id); + + ASSERT_EQ(0, dd.template_spatial_id[0]); + ASSERT_EQ(0, dd.template_spatial_id[1]); + ASSERT_EQ(0, dd.template_spatial_id[2]); + ASSERT_EQ(0, dd.template_spatial_id[3]); + ASSERT_EQ(0, dd.template_spatial_id[4]); + + ASSERT_EQ(0, dd.template_temporal_id[0]); + ASSERT_EQ(0, dd.template_temporal_id[1]); + ASSERT_EQ(1, dd.template_temporal_id[2]); + ASSERT_EQ(2, dd.template_temporal_id[3]); + ASSERT_EQ(2, dd.template_temporal_id[4]); + + ASSERT_TRUE(!dd.resolutions_present_flag); + ASSERT_EQ(0, dd.render_count); + + ASSERT_EQ(2, dd.template_dti[0][0]); + ASSERT_EQ(2, dd.template_dti[0][1]); + ASSERT_EQ(2, dd.template_dti[0][2]); + ASSERT_EQ(2, dd.template_dti[1][0]); + ASSERT_EQ(2, dd.template_dti[1][1]); + ASSERT_EQ(2, dd.template_dti[1][2]); + ASSERT_EQ(0, dd.template_dti[2][0]); + ASSERT_EQ(1, dd.template_dti[2][1]); + ASSERT_EQ(2, dd.template_dti[2][2]); + ASSERT_EQ(0, dd.template_dti[3][0]); + ASSERT_EQ(0, dd.template_dti[3][1]); + ASSERT_EQ(1, dd.template_dti[3][2]); + ASSERT_EQ(0, dd.template_dti[4][0]); + ASSERT_EQ(0, dd.template_dti[4][1]); + ASSERT_EQ(1, dd.template_dti[4][2]); + + ASSERT_EQ(1, dd.chain_cnt); + + err = dd_encode(mb, &dd); + TEST_ERR(err); + + TEST_MEMCMP(buf, sizeof(buf), mb->buf, mb->end); + + out: + mem_deref(mb); + return err; +} + + +/* + * Interop test with Chrome Version 118.0.5993.70 + */ +static int test_dd_chrome(void) +{ + static const char *str = "80000180003a40813f80ef80"; + struct mbuf *mb = mbuf_alloc(16); + uint8_t buf[12]; + int err; + + if (!mb) + return ENOMEM; + + err = str_hex(buf, sizeof(buf), str); + TEST_ERR(err); + + struct dd dd; + + err = dd_decode(&dd, buf, sizeof(buf)); + TEST_ERR(err); + +#if 0 + dd_print(&dd); +#endif + + ASSERT_EQ(1, dd.start_of_frame); + ASSERT_EQ(0, dd.end_of_frame); + ASSERT_EQ(0, dd.frame_dependency_template_id); + ASSERT_EQ(1, dd.frame_number); + + ASSERT_EQ(1, dd.template_dependency_structure_present_flag); + ASSERT_EQ(0, dd.active_decode_targets_present_flag); + ASSERT_EQ(0, dd.custom_dtis_flag); + ASSERT_EQ(0, dd.custom_fdiffs_flag); + ASSERT_EQ(0, dd.custom_chains_flag); + + ASSERT_EQ(1, dd.active_decode_targets_bitmask); + ASSERT_EQ(0, dd.template_id_offset); + ASSERT_EQ(1, dd.dt_cnt); + ASSERT_EQ(2, dd.template_cnt); + ASSERT_EQ(0, dd.max_spatial_id); + + ASSERT_EQ(0, dd.template_spatial_id[0]); + ASSERT_EQ(0, dd.template_spatial_id[1]); + + ASSERT_EQ(0, dd.template_temporal_id[0]); + ASSERT_EQ(0, dd.template_temporal_id[1]); + + ASSERT_TRUE(dd.resolutions_present_flag); + ASSERT_EQ(1, dd.render_count); + ASSERT_EQ(639, dd.max_render_width_minus_1[0]); + ASSERT_EQ(479, dd.max_render_height_minus_1[0]); + + ASSERT_EQ(2, dd.template_dti[0][0]); + ASSERT_EQ(2, dd.template_dti[1][0]); + + ASSERT_EQ(0, dd.chain_cnt); + + err = dd_encode(mb, &dd); + TEST_ERR(err); + + TEST_MEMCMP(buf, sizeof(buf), mb->buf, mb->end); + + out: + mem_deref(mb); + return err; +} + + +int test_dd(void) +{ + int err; + + err = test_dd_mand(); + if (err) + return err; + + err = test_dd_decode(); + if (err) + return err; + + err = test_dd_chrome(); + if (err) + return err; + + return 0; +} diff --git a/test/test.c b/test/test.c index 967f5dca5..66d2d377e 100644 --- a/test/test.c +++ b/test/test.c @@ -59,6 +59,7 @@ static const struct test tests[] = { TEST(test_auresamp), TEST(test_async), TEST(test_av1), + TEST(test_dd), TEST(test_base64), TEST(test_bfcp), TEST(test_bfcp_bin), diff --git a/test/test.h b/test/test.h index 04b2639f2..aa04d92b3 100644 --- a/test/test.h +++ b/test/test.h @@ -164,6 +164,7 @@ int test_auposition(void); int test_auresamp(void); int test_async(void); int test_av1(void); +int test_dd(void); int test_base64(void); int test_bfcp(void); int test_bfcp_bin(void);