2 changed files with 240 additions and 0 deletions
@ -0,0 +1,220 @@
|
||||
#include "call_video_bitstream.h" |
||||
#include "../../lib/debug_config.h" |
||||
#include "../../lib/mem.h" |
||||
#include <string.h> |
||||
|
||||
struct bits { const uint8_t *data; size_t size, at; int error; }; |
||||
static unsigned get(struct bits *b, unsigned count) { |
||||
if (count > 32 || b->at + count > b->size * 8) { b->error = 1; return 0; } |
||||
unsigned value = 0; |
||||
while (count--) { value = (value << 1) | ((b->data[b->at / 8] >> (7 - b->at % 8)) & 1); ++b->at; } |
||||
return value; |
||||
} |
||||
static unsigned ue(struct bits *b) { |
||||
unsigned zeros = 0; |
||||
while (!b->error && !get(b, 1)) if (++zeros > 20) { b->error = 1; break; } |
||||
return b->error ? 0 : (1u << zeros) - 1 + get(b, zeros); |
||||
} |
||||
static int se(struct bits *b) { unsigned v = ue(b); return v & 1 ? (int)((v + 1) / 2) : -(int)(v / 2); } |
||||
static int bad(const char *reason) { |
||||
DEBUG_WARN(DEBUG_CATEGORY_CALL, "video bitstream rejected reason=%s", reason); return -1; |
||||
} |
||||
|
||||
/* RBSP без emulation prevention; некорректные escape и размеры отклоняются. */ |
||||
static int rbsp(const uint8_t *data, size_t size, uint8_t *out, struct bits *bits) { |
||||
unsigned zeros = 0; size_t n = 0; |
||||
for (size_t i = 0; i < size; ++i) { |
||||
if (zeros == 2 && data[i] == 3) { |
||||
if (i + 1 == size || data[i + 1] > 3) return bad("emulation prevention"); |
||||
zeros = 0; continue; |
||||
} |
||||
out[n++] = data[i]; zeros = data[i] == 0 ? zeros + 1 : 0; |
||||
if (zeros > 2) zeros = 2; |
||||
} |
||||
*bits = (struct bits){out, n, 0, 0}; return 0; |
||||
} |
||||
static size_t start_code(const uint8_t *data, size_t size, size_t at) { |
||||
for (; at + 2 < size; ++at) if (!data[at] && !data[at + 1] && data[at + 2] == 1) return at; |
||||
return size; |
||||
} |
||||
|
||||
/* HRD AVC: проверяем заявленные BR/CPB по уровню, не обещаем их при отсутствии HRD. */ |
||||
static void avc_hrd(struct bits *b, unsigned level) { |
||||
unsigned count = ue(b) + 1, br_scale = get(b, 4), cpb_scale = get(b, 4); |
||||
if (count > 32) { b->error = 1; return; } |
||||
static const unsigned levels[] = {10,11,12,13,20,21,22,30,31,32,40,41,42,50,51,52,60,61,62}; |
||||
static const unsigned br[] = {64,192,384,768,2000,4000,4000,10000,14000,20000,20000,50000,50000, |
||||
135000,240000,240000,240000,480000,800000}; |
||||
static const unsigned cpb[] = {175,500,1000,2000,2000,4000,4000,10000,14000,20000,25000,62500,62500, |
||||
135000,240000,240000,240000,480000,800000}; |
||||
unsigned index = 0; while (index < sizeof(levels) / sizeof(*levels) && levels[index] != level) ++index; |
||||
if (index == sizeof(levels) / sizeof(*levels)) { b->error = 1; return; } |
||||
for (unsigned i = 0; i < count; ++i) { |
||||
uint64_t bitrate = ((uint64_t)ue(b) + 1) << (6 + br_scale); |
||||
uint64_t buffer = ((uint64_t)ue(b) + 1) << (4 + cpb_scale); |
||||
get(b, 1); |
||||
if (bitrate > (uint64_t)br[index] * 1200 || buffer > (uint64_t)cpb[index] * 1200) b->error = 1; |
||||
} |
||||
get(b, 20); |
||||
} |
||||
static int avc_sps(struct bits *b, struct call_video_bitstream *s, unsigned *id) { |
||||
unsigned profile = get(b, 8), constraints = get(b, 8); s->level = get(b, 8); |
||||
if (profile != 66 || (constraints & 3)) return bad("AVC profile/constraints"); |
||||
s->profile = constraints & 64 ? CALL_VIDEO_CONSTRAINED_BASELINE : CALL_VIDEO_BASELINE; |
||||
*id = ue(b); if (*id > 31 || ue(b) > 12) return bad("AVC SPS id/frame_num"); |
||||
unsigned poc = ue(b); |
||||
if (poc == 0) { if (ue(b) > 12) return bad("AVC POC size"); } |
||||
else if (poc == 1) { |
||||
get(b, 1); se(b); se(b); unsigned count = ue(b); |
||||
if (count > 255) return bad("AVC POC cycle"); |
||||
for (unsigned i = 0; i < count; ++i) se(b); |
||||
} else if (poc != 2) return bad("AVC POC type"); |
||||
s->dpb = ue(b); get(b, 1); |
||||
unsigned w = ue(b) + 1, h = ue(b) + 1; |
||||
if (w > 256 || h > 256 || !get(b, 1)) return bad("AVC coded geometry/interlace"); |
||||
get(b, 1); unsigned left = 0, right = 0, top = 0, bottom = 0; |
||||
if (get(b, 1)) { left = ue(b); right = ue(b); top = ue(b); bottom = ue(b); } |
||||
if ((uint64_t)(left + right) * 2 >= w * 16 || (uint64_t)(top + bottom) * 2 >= h * 16) return bad("AVC crop"); |
||||
s->width = w * 16 - (left + right) * 2; s->height = h * 16 - (top + bottom) * 2; |
||||
unsigned limit = call_video_dpb_limit(CALL_VIDEO_H264, s->level, w * 16, h * 16); |
||||
if (!limit || s->dpb > limit) return bad("AVC DPB"); |
||||
if (get(b, 1)) { |
||||
if (get(b, 1)) { if (get(b, 8) == 255) { get(b, 16); get(b, 16); } } |
||||
if (get(b, 1)) get(b, 1); |
||||
if (get(b, 1)) { get(b, 4); if (get(b, 1)) get(b, 24); } |
||||
if (get(b, 1)) { ue(b); ue(b); } |
||||
if (get(b, 1)) { get(b, 32); get(b, 32); get(b, 1); } |
||||
unsigned nal = get(b, 1); if (nal) avc_hrd(b, s->level); |
||||
unsigned vcl = get(b, 1); if (vcl) avc_hrd(b, s->level); |
||||
if (nal || vcl) get(b, 1); |
||||
get(b, 1); |
||||
if (get(b, 1)) { |
||||
get(b, 1); ue(b); ue(b); ue(b); ue(b); |
||||
unsigned reorder = ue(b), buffer = ue(b); |
||||
if (reorder || buffer > limit || buffer < s->dpb) return bad("AVC reorder/DPB restriction"); |
||||
} |
||||
} |
||||
return b->error ? bad("truncated AVC SPS/HRD") : 0; |
||||
} |
||||
static int hevc_ptl(struct bits *b, struct call_video_bitstream *s) { |
||||
unsigned space = get(b, 2), tier = get(b, 1), profile = get(b, 5); |
||||
get(b, 32); // profile compatibility; принимаем только явный Main.
|
||||
unsigned progressive = get(b, 1), 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 || !progressive || interlaced) return bad("HEVC profile/tier/progressive"); |
||||
return b->error ? bad("truncated HEVC PTL") : 0; |
||||
} |
||||
static int hevc_sps(struct bits *b, struct call_video_bitstream *s, unsigned *id, unsigned *vps) { |
||||
*vps = get(b, 4); |
||||
if (get(b, 3) || !get(b, 1) || hevc_ptl(b, s)) return bad("HEVC layers/PTL"); |
||||
*id = ue(b); if (*id > 15 || ue(b) != 1) return bad("HEVC SPS id/chroma"); |
||||
unsigned w = ue(b), h = ue(b), left = 0, right = 0, top = 0, bottom = 0; |
||||
if (!w || !h || w > 4096 || h > 4096) return bad("HEVC coded geometry"); |
||||
if (get(b, 1)) { left = ue(b); right = ue(b); top = ue(b); bottom = ue(b); } |
||||
if ((uint64_t)(left + right) * 2 >= w || (uint64_t)(top + bottom) * 2 >= h) return bad("HEVC crop"); |
||||
s->width = w - (left + right) * 2; s->height = h - (top + bottom) * 2; |
||||
unsigned luma = ue(b), chroma = ue(b), poc = ue(b); |
||||
if (luma || chroma || poc > 12) return bad("HEVC bit depth/POC"); |
||||
get(b, 1); s->dpb = ue(b) + 1; |
||||
unsigned reorder = ue(b); ue(b); |
||||
unsigned limit = call_video_dpb_limit(CALL_VIDEO_H265, s->level, w, h); |
||||
if (!limit || s->dpb > limit || reorder) return bad("HEVC DPB/reorder"); |
||||
return b->error ? bad("truncated HEVC SPS") : 0; |
||||
} |
||||
|
||||
/* Одна активная VPS/SPS/PPS; повтор идентичных параметров в AU разрешён.
|
||||
* IDR и PPS id проверяются по slice header, а не по флагу платформенного API. */ |
||||
int call_video_inspect_key(int codec, const uint8_t *data, size_t length, struct call_video_bitstream *out) { |
||||
if ((codec != 1 && codec != 2) || !data || !out || length < 9 || length > CALL_VIDEO_MAX_FRAME) return bad("KEY arguments"); |
||||
uint32_t config = (uint32_t)data[0] << 24 | (uint32_t)data[1] << 16 | (uint32_t)data[2] << 8 | data[3]; |
||||
if (!config || config > 65536 || config >= length - 4) return bad("KEY configuration length"); |
||||
uint8_t *scratch = u_malloc(length); |
||||
if (!scratch) { DEBUG_ERROR(DEBUG_CATEGORY_CALL, "video RBSP allocation failed bytes=%zu", length); return -1; } |
||||
struct call_video_bitstream stream = {0}, vp = {0}; |
||||
unsigned sps_id = 0, pps_id = 0, vps_id = 0, sps_vps = 0, have_sps = 0, have_pps = 0, have_vps = 0, slices = 0; |
||||
unsigned extra_bits = 0, previous_mb = 0; int result = -1; |
||||
data += 4; length -= 4; |
||||
size_t at = start_code(data, length, 0); |
||||
if (at > 1) { bad("Annex B prefix"); goto done; } |
||||
while (at < length) { |
||||
size_t begin = at + 3, end = start_code(data, length, begin); |
||||
at = end; |
||||
while (end > begin && data[end - 1] == 0) --end; |
||||
unsigned header = codec == 1 ? 1 : 2; |
||||
if (end <= begin + header || (data[begin] & 128)) { bad("NAL header/size"); goto done; } |
||||
unsigned type = codec == 1 ? data[begin] & 31 : (data[begin] >> 1) & 63; |
||||
if (codec == 2 && ((data[begin] & 1) || data[begin + 1] != 1)) { bad("HEVC NAL layer/temporal id"); goto done; } |
||||
struct bits b; |
||||
if (rbsp(data + begin + header, end - begin - header, scratch, &b)) goto done; |
||||
if (codec == 1 && type == 7) { |
||||
struct call_video_bitstream parsed = {0}; unsigned id; |
||||
if (avc_sps(&b, &parsed, &id)) goto done; |
||||
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) { |
||||
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)) { |
||||
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; |
||||
} else if (codec == 2 && type == 33) { |
||||
struct call_video_bitstream parsed = {0}; unsigned id, vps; |
||||
if (hevc_sps(&b, &parsed, &id, &vps)) goto done; |
||||
if (have_sps && (memcmp(&parsed, &stream, sizeof(parsed)) || id != sps_id || vps != sps_vps)) { |
||||
bad("multiple HEVC SPS"); goto done; |
||||
} |
||||
stream = parsed; sps_id = id; sps_vps = vps; have_sps = 1; |
||||
} else if ((codec == 1 && type == 8) || (codec == 2 && type == 34)) { |
||||
unsigned id = ue(&b), sps = ue(&b); |
||||
if (!have_sps || sps != sps_id || id > (codec == 1 ? 255u : 63u) || (have_pps && id != pps_id)) { |
||||
bad("PPS/SPS reference"); goto done; |
||||
} |
||||
if (codec == 1) { |
||||
unsigned entropy = get(&b, 1), fields = get(&b, 1), groups = ue(&b); |
||||
if (entropy || fields || groups || ue(&b) > 15 || ue(&b) > 15) { bad("AVC PPS tools"); goto done; } |
||||
get(&b, 1); get(&b, 2); se(&b); se(&b); se(&b); get(&b, 2); |
||||
if (get(&b, 1)) { bad("AVC redundant pictures"); goto done; } |
||||
} else { |
||||
if (get(&b, 1) || get(&b, 1)) { bad("HEVC dependent slices/output flag"); goto done; } |
||||
extra_bits = get(&b, 3); |
||||
} |
||||
if (b.error) { bad("truncated PPS"); goto done; } |
||||
pps_id = id; have_pps = 1; |
||||
} else if ((codec == 1 && type >= 1 && type <= 5) || (codec == 2 && type <= 31)) { |
||||
if (!have_sps || !have_pps || begin < config) { bad("slice before configuration"); goto done; } |
||||
if (codec == 1) { |
||||
unsigned mb = ue(&b), slice = ue(&b), pps = ue(&b); |
||||
if (type != 5 || slice > 9 || slice % 5 != 2 || pps != pps_id || (!slices && mb) || (slices && mb <= previous_mb)) { |
||||
bad("AVC KEY is not independent ordered IDR"); goto done; |
||||
} |
||||
previous_mb = mb; |
||||
} else { |
||||
if (type != 19 && type != 20) { bad("HEVC KEY is not IDR"); goto done; } |
||||
unsigned first = get(&b, 1); get(&b, 1); |
||||
if (ue(&b) != pps_id || (!slices && !first) || (slices && first)) { bad("HEVC IDR slice/PPS"); goto done; } |
||||
if (first) { get(&b, extra_bits); if (ue(&b) != 2) { bad("HEVC IDR slice type"); goto done; } } |
||||
} |
||||
if (b.error) { bad("truncated IDR"); goto done; } |
||||
++slices; |
||||
} |
||||
} |
||||
if (!slices || !have_sps || !have_pps || (codec == 2 && (!have_vps || sps_vps != vps_id |
||||
|| vp.profile != stream.profile || vp.level != stream.level || stream.dpb > vp.dpb))) { |
||||
bad("missing/inconsistent parameter sets or IDR"); goto done; |
||||
} |
||||
*out = stream; result = 0; |
||||
done: |
||||
u_free(scratch); return result; |
||||
} |
||||
int call_video_check_key(const struct call_video_format *f, const uint8_t *data, size_t length) { |
||||
struct call_video_bitstream s; |
||||
if (!f || call_video_inspect_key(f->codec, data, length, &s)) return -1; |
||||
if (s.width != f->width || s.height != f->height || s.level > f->level |
||||
|| (s.profile != f->profile && !(f->codec == 1 && f->profile == 1 && s.profile == 2))) { |
||||
DEBUG_WARN(DEBUG_CATEGORY_CALL, "video KEY mismatch actual=%ux%u profile=%u level=%u dpb=%u expected=%ux%u profile=%u level=%u", |
||||
s.width, s.height, s.profile, s.level, s.dpb, f->width, f->height, f->profile, f->level); return -1; |
||||
} |
||||
return 0; |
||||
} |
||||
@ -0,0 +1,20 @@
|
||||
/* Общая проверка независимого KEY (configuration + Annex B AU). Не переписывает поток.
|
||||
* Проверяет ограниченный набор для звонков: AVC Baseline/CB, HEVC Main/Main tier, |
||||
* progressive 8-bit 4:2:0, один spatial/temporal layer, без reorder. DPB — по Annex A. |
||||
* Результат публикуется целиком только после успешной проверки VPS/SPS/PPS и IDR. |
||||
* Все функции доступны worker-потокам; состояния и платформенных объектов нет. */ |
||||
#ifndef CALL_VIDEO_BITSTREAM_H |
||||
#define CALL_VIDEO_BITSTREAM_H |
||||
#include "call_video.h" |
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
struct call_video_bitstream { |
||||
unsigned width, height, level, profile, dpb; |
||||
}; |
||||
int call_video_inspect_key(int codec, const uint8_t *data, size_t length, struct call_video_bitstream *out); |
||||
int call_video_check_key(const struct call_video_format *format, const uint8_t *data, size_t length); |
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
#endif |
||||
Loading…
Reference in new issue