|
|
|
@ -6,14 +6,14 @@ |
|
|
|
#define CHECK(x) do { if (!(x)) { fprintf(stderr, "video:%d %s\n", __LINE__, #x); exit(1); } } while (0) |
|
|
|
#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) { |
|
|
|
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, 1000000}; |
|
|
|
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) { |
|
|
|
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); |
|
|
|
struct call_video_format f = mode(codec, w, h, fps); return call_video_choose(a, b, &f); |
|
|
|
} |
|
|
|
} |
|
|
|
static void capabilities(void) { |
|
|
|
static void capabilities(void) { |
|
|
|
struct call_video_caps a = {0}, b = {0}, decoded; |
|
|
|
struct call_video_caps a = {0}, b = {0}, decoded; |
|
|
|
struct call_video_limit limit = {1920, 1080, 60, 1920u * 1080u * 30u, 4000000, 1000000, 41}; |
|
|
|
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[0] = a.encode[1] = b.decode[0] = b.decode[1] = limit; |
|
|
|
a.encode[1].level = b.decode[1].level = 123; |
|
|
|
a.encode[1].level = b.decode[1].level = 123; |
|
|
|
CHECK(choose(&a, &b, 0, 1280, 720, 30) == CALL_VIDEO_H265); |
|
|
|
CHECK(choose(&a, &b, 0, 1280, 720, 30) == CALL_VIDEO_H265); |
|
|
|
@ -26,17 +26,17 @@ static void capabilities(void) { |
|
|
|
uint8_t wire[CALL_VIDEO_CAPS_BYTES]; |
|
|
|
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(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].pixels_per_second == b.decode[1].pixels_per_second && !decoded.encode[0].width); |
|
|
|
CHECK(decoded.decode[1].maximum_bps == 4000000 && decoded.decode[1].maximum_vbv_bits == 1000000 && decoded.decode[1].level == 123); |
|
|
|
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 не поддерживается.
|
|
|
|
CHECK(call_video_caps_parse(wire, 40, &decoded) == -1); // Старый wire format не поддерживается.
|
|
|
|
wire[79] = 1; CHECK(call_video_caps_parse(wire, sizeof(wire), &decoded) == -1); wire[79] = 0; |
|
|
|
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; |
|
|
|
wire[0] = 0xff; struct call_video_caps saved = decoded; |
|
|
|
CHECK(call_video_caps_parse(wire, sizeof(wire), &decoded) == -1 && !memcmp(&saved, &decoded, sizeof(saved))); |
|
|
|
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"); |
|
|
|
puts("video: directional capabilities, HEVC preference, AVC fallback, pixel-rate limits PASS"); |
|
|
|
} |
|
|
|
} |
|
|
|
static void envelopes(void) { |
|
|
|
static void envelopes(void) { |
|
|
|
struct call_video_caps a = {0}, b = {0}; |
|
|
|
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, 16000000, 42}; |
|
|
|
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, 16000000, 123}; |
|
|
|
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); |
|
|
|
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_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)); |
|
|
|
CHECK(call_video_format_supported(&a, &b, &f) && !call_video_format_supported(&b, &a, &f)); |
|
|
|
@ -46,16 +46,19 @@ static void envelopes(void) { |
|
|
|
CHECK(call_video_choose(&a, &b, &f) == 0 && !memcmp(&f, &saved, sizeof(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; |
|
|
|
b.decode[1].level = 123; b.decode[1].maximum_bps = 3999999; |
|
|
|
CHECK(choose(&a, &b, 0, 1280, 720, 30) == CALL_VIDEO_H264); |
|
|
|
CHECK(choose(&a, &b, 0, 1280, 720, 30) == CALL_VIDEO_H264); |
|
|
|
b.decode[1].maximum_bps = 4000000; b.decode[1].maximum_vbv_bits = 999999; |
|
|
|
b.decode[1].maximum_bps = 4000000; |
|
|
|
CHECK(choose(&a, &b, 0, 1280, 720, 30) == CALL_VIDEO_H264); |
|
|
|
a.encode[0].profiles = 1; b.decode[0].profiles = 2; |
|
|
|
b.decode[1].maximum_vbv_bits = 16000000; |
|
|
|
CHECK(choose(&a, &b, CALL_VIDEO_H264, 1280, 720, 30) == 0); |
|
|
|
f = mode(CALL_VIDEO_H264, 176, 144, 30); f.maximum_bps = 300000; f.vbv_bits = 75000; |
|
|
|
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); |
|
|
|
CHECK(call_video_choose(&a, &b, &f) == CALL_VIDEO_H264 && f.level == 12); |
|
|
|
f.level = 11; CHECK(!call_video_format_valid(&f)); // Геометрия проходит, битрейт — нет.
|
|
|
|
f.level = 11; CHECK(!call_video_format_valid(&f)); // Геометрия проходит, битрейт — нет.
|
|
|
|
f = mode(CALL_VIDEO_H265, 1280, 720, 30); f.level = 93; f.vbv_bits = 10000001; |
|
|
|
CHECK(call_video_dpb_limit(CALL_VIDEO_H265, 93, 1280, 720) == 6); |
|
|
|
CHECK(!call_video_format_valid(&f)); // Main tier CPB level 3.1 = 10 Мбит.
|
|
|
|
CHECK(call_video_dpb_limit(CALL_VIDEO_H265, 93, 640, 360) == 16); |
|
|
|
f.level = 120; CHECK(call_video_format_valid(&f)); |
|
|
|
CHECK(call_video_dpb_limit(CALL_VIDEO_H264, 31, 1280, 720) == 5); |
|
|
|
f.vbv_bits = 1; CHECK(!call_video_format_valid(&f)); // Меньше среднего кадра при maximum_bps.
|
|
|
|
CHECK(call_video_dpb_limit(CALL_VIDEO_H265, 90, 1280, 720) == 0); |
|
|
|
f = mode(CALL_VIDEO_H264, 1920, 1080, 60); |
|
|
|
f = mode(CALL_VIDEO_H264, 1920, 1080, 60); |
|
|
|
CHECK(call_video_choose(&a, &b, &f) == CALL_VIDEO_H264 && f.level == 42); |
|
|
|
CHECK(call_video_choose(&a, &b, &f) == CALL_VIDEO_H264 && f.level == 42); |
|
|
|
f.level = 41; CHECK(!call_video_format_valid(&f)); |
|
|
|
f.level = 41; CHECK(!call_video_format_valid(&f)); |
|
|
|
@ -63,12 +66,12 @@ static void envelopes(void) { |
|
|
|
CHECK(call_video_choose(&a, &b, &f) == CALL_VIDEO_H265 && f.level == 123); |
|
|
|
CHECK(call_video_choose(&a, &b, &f) == CALL_VIDEO_H265 && f.level == 123); |
|
|
|
f.level = 120; CHECK(!call_video_format_valid(&f)); |
|
|
|
f.level = 120; CHECK(!call_video_format_valid(&f)); |
|
|
|
a.encode[0].level = 99; CHECK(!call_video_caps_valid(&a)); |
|
|
|
a.encode[0].level = 99; CHECK(!call_video_caps_valid(&a)); |
|
|
|
puts("video: negotiated level, bitrate/VBV fallback, CPB/frame-rate boundaries, atomic selection PASS"); |
|
|
|
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) { |
|
|
|
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}; |
|
|
|
struct call_video_frame f = {0}; |
|
|
|
f.call_id = 42; f.frame_id = id; f.pts_us = 1000000 + id * 33333; |
|
|
|
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, 1000000}; |
|
|
|
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.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; |
|
|
|
f.flags = key ? CALL_VIDEO_KEY : 0; f.data = data; return f; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -87,7 +90,7 @@ static void roundtrip(void) { |
|
|
|
CHECK(wire[7] == 42 && wire[40] == 2 && wire[39] == 90 && wire[42] == 93); |
|
|
|
CHECK(wire[7] == 42 && wire[40] == 2 && wire[39] == 90 && wire[42] == 93); |
|
|
|
CHECK(call_video_fragment_parse(wire, length, &parsed) == 0); |
|
|
|
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.frame_id == f.frame_id && parsed.pts_us == f.pts_us && parsed.offset == offset); |
|
|
|
CHECK(parsed.format.maximum_bps == 4000000 && parsed.format.vbv_bits == 1000000 && parsed.format.level == 93); |
|
|
|
CHECK(parsed.format.maximum_bps == 4000000 && parsed.format.profile == 1 && parsed.format.level == 93); |
|
|
|
int done = call_video_receiver_push(r, &parsed, offset, &complete); |
|
|
|
int done = call_video_receiver_push(r, &parsed, offset, &complete); |
|
|
|
CHECK(done == (offset + f.length == sizeof(data) ? 1 : 0)); |
|
|
|
CHECK(done == (offset + f.length == sizeof(data) ? 1 : 0)); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -99,8 +102,8 @@ static void roundtrip(void) { |
|
|
|
CHECK(call_video_receiver_push(r, &f, 30000, &complete) == 1 && complete.format.codec == CALL_VIDEO_H264); |
|
|
|
CHECK(call_video_receiver_push(r, &f, 30000, &complete) == 1 && complete.format.codec == CALL_VIDEO_H264); |
|
|
|
f = frame(5, 2, data, 8, 1); // Codec меняется без нового поколения.
|
|
|
|
f = frame(5, 2, data, 8, 1); // Codec меняется без нового поколения.
|
|
|
|
CHECK(call_video_receiver_push(r, &f, 40000, &complete) == -1); |
|
|
|
CHECK(call_video_receiver_push(r, &f, 40000, &complete) == -1); |
|
|
|
f.format.codec = CALL_VIDEO_H264; f.format.level = 31; f.format.vbv_bits = 2000000; |
|
|
|
f.format.codec = CALL_VIDEO_H264; f.format.level = 31; f.format.profile = 2; |
|
|
|
CHECK(call_video_receiver_push(r, &f, 50000, &complete) == -1); // VBV тоже часть generation.
|
|
|
|
CHECK(call_video_receiver_push(r, &f, 50000, &complete) == -1); // Профиль тоже часть generation.
|
|
|
|
f.format.generation = 3; |
|
|
|
f.format.generation = 3; |
|
|
|
CHECK(call_video_receiver_push(r, &f, 60000, &complete) == 1); |
|
|
|
CHECK(call_video_receiver_push(r, &f, 60000, &complete) == 1); |
|
|
|
call_video_receiver_destroy(r); |
|
|
|
call_video_receiver_destroy(r); |
|
|
|
@ -124,8 +127,8 @@ static void rejected(void) { |
|
|
|
size_t length = 0; CHECK(call_video_fragment_encode(&f, wire, sizeof(wire), &length) == 0); |
|
|
|
size_t length = 0; CHECK(call_video_fragment_encode(&f, wire, sizeof(wire), &length) == 0); |
|
|
|
saved = out; |
|
|
|
saved = out; |
|
|
|
for (size_t n = 0; n <= CALL_VIDEO_HEADER_BYTES; n++) CHECK(call_video_fragment_parse(wire, n, &out) == -1); |
|
|
|
for (size_t n = 0; n <= CALL_VIDEO_HEADER_BYTES; n++) CHECK(call_video_fragment_parse(wire, n, &out) == -1); |
|
|
|
wire[43] = 1; CHECK(call_video_fragment_parse(wire, length, &out) == -1); |
|
|
|
wire[43] = 2; CHECK(call_video_fragment_parse(wire, length, &out) == -1); |
|
|
|
CHECK(!memcmp(&out, &saved, sizeof(saved))); wire[43] = 0; |
|
|
|
CHECK(!memcmp(&out, &saved, sizeof(saved))); wire[43] = 1; |
|
|
|
wire[44] = 0xff; CHECK(call_video_fragment_parse(wire, length, &out) == -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); |
|
|
|
f.size = CALL_VIDEO_MAX_FRAME + 1; CHECK(call_video_receiver_push(r, &f, 0, &out) == -1); |
|
|
|
call_video_receiver_destroy(r); |
|
|
|
call_video_receiver_destroy(r); |
|
|
|
|