You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

162 lines
10 KiB

#include "call/call_video.h"
#include "call/call_video_bitstream.h"
#include "debug_config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define CHECK(x) do { if (!(x)) { fprintf(stderr, "video:%d %s\n", __LINE__, #x); exit(1); } } while (0)
static struct call_video_format mode(int codec, unsigned width, unsigned height, unsigned fps) {
return (struct call_video_format){1, width, height, fps, 0, codec, 0, 4000000, 0};
}
static int choose(const struct call_video_caps *a, const struct call_video_caps *b, int codec, unsigned w, unsigned h, unsigned fps) {
struct call_video_format f = mode(codec, w, h, fps); return call_video_choose(a, b, &f);
}
static void capabilities(void) {
struct call_video_caps a = {0}, b = {0}, decoded;
struct call_video_limit limit = {1920, 1080, 60, 1920u * 1080u * 30u, 4000000, 1, 41};
a.encode[0] = a.encode[1] = b.decode[0] = b.decode[1] = limit;
a.encode[1].level = b.decode[1].level = 123;
CHECK(choose(&a, &b, 0, 1280, 720, 30) == CALL_VIDEO_H265);
CHECK(choose(&b, &a, 0, 1280, 720, 30) == 0);
b.decode[1].pixels_per_second = 1280 * 720 * 15;
CHECK(choose(&a, &b, 0, 1280, 720, 30) == CALL_VIDEO_H264);
CHECK(choose(&a, &b, CALL_VIDEO_H265, 1280, 720, 30) == 0);
CHECK(choose(&a, &b, 0, 1920, 1080, 60) == 0);
CHECK(choose(&a, &b, 0, 1279, 720, 30) == -1);
uint8_t wire[CALL_VIDEO_CAPS_BYTES];
CHECK(call_video_caps_encode(&b, wire) == 0 && call_video_caps_parse(wire, sizeof(wire), &decoded) == 0);
CHECK(decoded.decode[1].pixels_per_second == b.decode[1].pixels_per_second && !decoded.encode[0].width);
CHECK(decoded.decode[1].maximum_bps == 4000000 && decoded.decode[1].profiles == 1 && decoded.decode[1].level == 123);
CHECK(call_video_caps_parse(wire, 40, &decoded) == -1); // Старый wire format не поддерживается.
wire[63] = 2; CHECK(call_video_caps_parse(wire, sizeof(wire), &decoded) == -1); wire[63] = 1;
wire[0] = 0xff; struct call_video_caps saved = decoded;
CHECK(call_video_caps_parse(wire, sizeof(wire), &decoded) == -1 && !memcmp(&saved, &decoded, sizeof(saved)));
puts("video: directional capabilities, HEVC preference, AVC fallback, pixel-rate limits PASS");
}
static void envelopes(void) {
struct call_video_caps a = {0}, b = {0};
a.encode[0] = b.decode[0] = (struct call_video_limit){1920, 1080, 60, 1920u * 1080u * 60u, 4000000, 3, 42};
a.encode[1] = b.decode[1] = (struct call_video_limit){1920, 1080, 60, 1920u * 1080u * 60u, 4000000, 1, 123};
struct call_video_format f = mode(0, 1280, 720, 30);
CHECK(call_video_choose(&a, &b, &f) == CALL_VIDEO_H265 && f.level == 93);
CHECK(call_video_format_supported(&a, &b, &f) && !call_video_format_supported(&b, &a, &f));
b.decode[1].level = 90;
f = mode(0, 1280, 720, 30); CHECK(call_video_choose(&a, &b, &f) == CALL_VIDEO_H264 && f.level == 31);
f = mode(CALL_VIDEO_H265, 1280, 720, 30); struct call_video_format saved = f;
CHECK(call_video_choose(&a, &b, &f) == 0 && !memcmp(&f, &saved, sizeof(f)));
b.decode[1].level = 123; b.decode[1].maximum_bps = 3999999;
CHECK(choose(&a, &b, 0, 1280, 720, 30) == CALL_VIDEO_H264);
b.decode[1].maximum_bps = 4000000;
a.encode[0].profiles = 1; b.decode[0].profiles = 2;
CHECK(choose(&a, &b, CALL_VIDEO_H264, 1280, 720, 30) == 0);
b.decode[0].profiles = 3;
f = mode(CALL_VIDEO_H264, 1280, 720, 30);
CHECK(call_video_choose(&a, &b, &f) == CALL_VIDEO_H264 && f.profile == CALL_VIDEO_BASELINE);
f = mode(CALL_VIDEO_H264, 176, 144, 30); f.maximum_bps = 300000;
CHECK(call_video_choose(&a, &b, &f) == CALL_VIDEO_H264 && f.level == 12);
f.level = 11; CHECK(!call_video_format_valid(&f)); // Геометрия проходит, битрейт — нет.
CHECK(call_video_dpb_limit(CALL_VIDEO_H265, 93, 1280, 720) == 6);
CHECK(call_video_dpb_limit(CALL_VIDEO_H265, 93, 640, 360) == 16);
CHECK(call_video_dpb_limit(CALL_VIDEO_H264, 31, 1280, 720) == 5);
CHECK(call_video_dpb_limit(CALL_VIDEO_H265, 90, 1280, 720) == 0);
f = mode(CALL_VIDEO_H264, 1920, 1080, 60);
CHECK(call_video_choose(&a, &b, &f) == CALL_VIDEO_H264 && f.level == 42);
f.level = 41; CHECK(!call_video_format_valid(&f));
f = mode(CALL_VIDEO_H265, 1920, 1080, 60);
CHECK(call_video_choose(&a, &b, &f) == CALL_VIDEO_H265 && f.level == 123);
f.level = 120; CHECK(!call_video_format_valid(&f));
a.encode[0].level = 99; CHECK(!call_video_caps_valid(&a));
puts("video: negotiated profiles/level, bitrate fallback, DPB/frame-rate boundaries, atomic selection PASS");
}
static struct call_video_frame frame(uint64_t id, uint64_t generation, const uint8_t *data, uint32_t size, int key) {
struct call_video_frame f = {0};
f.call_id = 42; f.frame_id = id; f.pts_us = 1000000 + id * 33333;
f.format = (struct call_video_format){generation, 640, 480, 30, 90, CALL_VIDEO_H265, 93, 4000000, 1};
f.size = size; f.length = size < CALL_VIDEO_FRAGMENT_BYTES ? size : CALL_VIDEO_FRAGMENT_BYTES;
f.flags = key ? CALL_VIDEO_KEY : 0; f.data = data; return f;
}
static void roundtrip(void) {
uint8_t data[5001], wire[CALL_VIDEO_HEADER_BYTES + CALL_VIDEO_FRAGMENT_BYTES];
for (unsigned i = 0; i < sizeof(data); i++) data[i] = (uint8_t)i;
memset(data, 0, 4);
struct call_video_receiver *r = call_video_receiver_create(); CHECK(r);
struct call_video_frame f = frame(1, 1, data, sizeof(data), 1), parsed, complete;
CHECK(call_video_receiver_needs_key(r));
for (unsigned offset = 0; offset < sizeof(data); offset += f.length) {
f.offset = offset; f.data = data + offset;
f.length = sizeof(data) - offset < CALL_VIDEO_FRAGMENT_BYTES ? sizeof(data) - offset : CALL_VIDEO_FRAGMENT_BYTES;
size_t length = 0;
CHECK(call_video_fragment_encode(&f, wire, sizeof(wire), &length) == 0);
CHECK(wire[7] == 42 && wire[40] == 2 && wire[39] == 90 && wire[42] == 93);
CHECK(call_video_fragment_parse(wire, length, &parsed) == 0);
CHECK(parsed.frame_id == f.frame_id && parsed.pts_us == f.pts_us && parsed.offset == offset);
CHECK(parsed.format.maximum_bps == 4000000 && parsed.format.profile == 1 && parsed.format.level == 93);
int done = call_video_receiver_push(r, &parsed, offset, &complete);
CHECK(done == (offset + f.length == sizeof(data) ? 1 : 0));
}
CHECK(!call_video_receiver_needs_key(r) && complete.size == sizeof(data) && complete.length == sizeof(data));
CHECK(!memcmp(complete.data, data, sizeof(data)));
f = frame(2, 1, data, 8, 0); CHECK(call_video_receiver_push(r, &f, 10000, &complete) == 1);
f = frame(3, 2, data, 8, 0); CHECK(call_video_receiver_push(r, &f, 20000, &complete) == 0);
f = frame(4, 2, data, 8, 1); f.format.codec = CALL_VIDEO_H264; f.format.level = 31;
CHECK(call_video_receiver_push(r, &f, 30000, &complete) == 1 && complete.format.codec == CALL_VIDEO_H264);
f = frame(5, 2, data, 8, 1); // Codec меняется без нового поколения.
CHECK(call_video_receiver_push(r, &f, 40000, &complete) == -1);
f.format.codec = CALL_VIDEO_H264; f.format.level = 31; f.format.profile = 2;
CHECK(call_video_receiver_push(r, &f, 50000, &complete) == -1); // Профиль тоже часть generation.
f.format.generation = 3;
CHECK(call_video_receiver_push(r, &f, 60000, &complete) == 1);
call_video_receiver_destroy(r);
puts("video: endian wire, fragmented KEY, delta frames, ordered format switch PASS");
}
static void rejected(void) {
uint8_t data[2400] = {0}, wire[CALL_VIDEO_HEADER_BYTES + CALL_VIDEO_FRAGMENT_BYTES];
struct call_video_receiver *r = call_video_receiver_create(); CHECK(r);
struct call_video_frame f = frame(1, 1, data, sizeof(data), 1), out = {0}, saved;
CHECK(call_video_receiver_push(r, &f, 0, &out) == 0);
f.offset = 1201; f.length = 1199;
CHECK(call_video_receiver_push(r, &f, 10000, &out) == -1 && call_video_receiver_needs_key(r));
f = frame(2, 1, data, sizeof(data), 1); CHECK(call_video_receiver_push(r, &f, 20000, &out) == 0);
f.offset = 1200; CHECK(call_video_receiver_push(r, &f, 2020001, &out) == 0);
CHECK(call_video_receiver_needs_key(r));
f = frame(3, 1, data, 8, 1); CHECK(call_video_receiver_push(r, &f, 2030000, &out) == 1);
CHECK(call_video_receiver_push(r, &f, 2040000, &out) == -1); // Replay.
call_video_receiver_reset(r); CHECK(call_video_receiver_needs_key(r));
data[0] = 0xff; CHECK(call_video_receiver_push(r, &f, 0, &out) == -1); // Неверный config_length.
data[0] = 0;
size_t length = 0; CHECK(call_video_fragment_encode(&f, wire, sizeof(wire), &length) == 0);
saved = out;
for (size_t n = 0; n <= CALL_VIDEO_HEADER_BYTES; n++) CHECK(call_video_fragment_parse(wire, n, &out) == -1);
wire[43] = 2; CHECK(call_video_fragment_parse(wire, length, &out) == -1);
CHECK(!memcmp(&out, &saved, sizeof(saved))); wire[43] = 1;
wire[44] = 0xff; CHECK(call_video_fragment_parse(wire, length, &out) == -1);
f.size = CALL_VIDEO_MAX_FRAME + 1; CHECK(call_video_receiver_push(r, &f, 0, &out) == -1);
call_video_receiver_destroy(r);
puts("video: gap, timeout, replay, malformed KEY, truncated/oversized packets, atomic parse PASS");
}
static void bitstreams(void) {
uint8_t bytes[8192]; struct call_video_bitstream s;
for (int codec = 1; codec <= 2; ++codec) {
for (int profile = 1; profile <= (codec == 1 ? 2 : 1); ++profile) {
int size = call_video_probe_key(codec, profile, bytes, sizeof(bytes)); CHECK(size > 0);
CHECK(!call_video_inspect_key(codec, bytes, size, &s));
CHECK(s.width == 1280 && s.height == 720 && s.profile == (unsigned)profile && s.level == (codec == 1 ? 31u : 93u));
CHECK(s.dpb >= 3);
struct call_video_format f = {1, 1280, 720, 30, 0, codec, s.level, 4000000, profile};
CHECK(!call_video_check_key(&f, bytes, size));
f.width = 640; CHECK(call_video_check_key(&f, bytes, size) < 0);
struct call_video_bitstream saved = s;
for (int i = 0; i < 32; ++i) {
CHECK(call_video_inspect_key(codec, bytes, i, &s) < 0);
CHECK(!memcmp(&saved, &s, sizeof(s)));
}
bytes[0] = 255; CHECK(call_video_inspect_key(codec, bytes, size, &s) < 0);
}
}
puts("video: real AVC Baseline/CB and HEVC IDR, multi-picture DPB, atomic malformed parse PASS");
}
int main(void) {
debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR);
capabilities(); envelopes(); roundtrip(); rejected(); bitstreams(); return 0;
}