From 047927a7a55f62d22b997628e99ff0d4e1e736bf Mon Sep 17 00:00:00 2001 From: evgeny Date: Wed, 14 Oct 2026 23:19:18 +0200 Subject: [PATCH] Check VPS HRD and log actual codec constraints on rejection --- src/call/call_video.c | 3 ++- src/call/call_video_bitstream.c | 24 ++++++++++++++++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/call/call_video.c b/src/call/call_video.c index 686d50e6..535c7ef9 100644 --- a/src/call/call_video.c +++ b/src/call/call_video.c @@ -60,7 +60,8 @@ static int fits(const struct call_video_limit *l, const struct call_video_format } int call_video_choose(const struct call_video_caps *sender, const struct call_video_caps *receiver, struct call_video_format *format) { - if (!call_video_caps_valid(sender) || !call_video_caps_valid(receiver) || !format || format->codec > 2 || format->level) { + if (!call_video_caps_valid(sender) || !call_video_caps_valid(receiver) || !format || format->codec > 2 + || format->level || format->profile > 2 || (format->codec == CALL_VIDEO_H265 && format->profile > 1)) { DEBUG_ERROR(DEBUG_CATEGORY_CALL, "video choose invalid capabilities/request"); return -1; } struct call_video_format f = *format; diff --git a/src/call/call_video_bitstream.c b/src/call/call_video_bitstream.c index 9c8de859..102e0cd6 100644 --- a/src/call/call_video_bitstream.c +++ b/src/call/call_video_bitstream.c @@ -95,7 +95,10 @@ static int hevc_ptl(struct bits *b, struct call_video_bitstream *s) { get(b, 32); // profile compatibility; принимаем только явный Main. get(b, 1); unsigned interlaced = get(b, 1); get(b, 2); get(b, 32); get(b, 12); s->level = get(b, 8); s->profile = CALL_VIDEO_MAIN; - if (space || tier || profile != 1 || interlaced) return bad("HEVC profile/tier/interlace"); + if (space || tier || profile != 1 || interlaced) { + DEBUG_WARN(DEBUG_CATEGORY_CALL, "video HEVC PTL rejected space=%u profile=%u tier=%u level=%u interlaced=%u", + space, profile, tier, s->level, interlaced); return -1; + } return b->error ? bad("truncated HEVC PTL") : 0; } /* HEVC HRD для единственного temporal layer; sub-picture HRD звонкам не требуется. */ @@ -219,13 +222,24 @@ int call_video_inspect_key(int codec, const uint8_t *data, size_t length, struct if (have_sps && (memcmp(&parsed, &stream, sizeof(parsed)) || id != sps_id)) { bad("multiple AVC SPS"); goto done; } stream = parsed; sps_id = id; have_sps = 1; } else if (codec == 2 && type == 32) { + struct call_video_bitstream parsed = {0}; unsigned id = get(&b, 4); get(&b, 2); - if (get(&b, 6) || get(&b, 3) || !get(&b, 1) || get(&b, 16) != 65535 || hevc_ptl(&b, &vp)) { + if (get(&b, 6) || get(&b, 3) || !get(&b, 1) || get(&b, 16) != 65535 || hevc_ptl(&b, &parsed)) { bad("HEVC VPS layers/PTL"); goto done; } get(&b, 1); unsigned dpb = ue(&b) + 1, reorder = ue(&b); ue(&b); - if (b.error || reorder || dpb > 16 || (have_vps && id != vps_id)) { bad("HEVC VPS DPB"); goto done; } - vp.dpb = dpb; vps_id = id; have_vps = 1; + if (b.error || reorder || dpb > 16) { bad("HEVC VPS DPB"); goto done; } + parsed.dpb = dpb; + if (have_vps && (id != vps_id || memcmp(&parsed, &vp, sizeof(vp)))) { bad("multiple HEVC VPS"); goto done; } + if (get(&b, 6) || ue(&b)) { bad("HEVC VPS layer sets"); goto done; } + if (get(&b, 1)) { + get(&b, 32); get(&b, 32); if (get(&b, 1)) ue(&b); + unsigned count = ue(&b); + if (count > 1) { bad("HEVC VPS HRD count"); goto done; } + if (count) { if (ue(&b)) { bad("HEVC VPS HRD layer"); goto done; } hevc_hrd(&b, parsed.level); } + } + if (get(&b, 1) || b.error) { bad("HEVC VPS extension/truncation/HRD"); goto done; } + vp = parsed; vps_id = id; have_vps = 1; } else if (codec == 2 && type == 33) { struct call_video_bitstream parsed = {0}; unsigned id, vps; if (hevc_sps(&b, &parsed, &id, &vps)) goto done; @@ -271,6 +285,8 @@ int call_video_inspect_key(int codec, const uint8_t *data, size_t length, struct || vp.profile != stream.profile || vp.level != stream.level || stream.dpb > vp.dpb))) { bad("missing/inconsistent parameter sets or IDR"); goto done; } + DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "video bitstream verified codec=%d profile=%u level=%u size=%ux%u dpb=%u slices=%u", + codec, stream.profile, stream.level, stream.width, stream.height, stream.dpb, slices); *out = stream; result = 0; done: u_free(scratch); return result;