3 changed files with 98 additions and 0 deletions
@ -0,0 +1,90 @@
|
||||
#include "call/call_video.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 void capabilities(void) { |
||||
struct call_video_caps a = {0}, b = {0}, decoded; |
||||
struct call_video_limit mode = {1920, 1080, 60, 1920u * 1080u * 30u}; |
||||
a.encode[0] = a.encode[1] = b.decode[0] = b.decode[1] = mode; |
||||
CHECK(call_video_choose(&a, &b, 0, 1280, 720, 30) == CALL_VIDEO_H265); |
||||
CHECK(call_video_choose(&b, &a, 0, 1280, 720, 30) == 0); |
||||
b.decode[1].pixels_per_second = 1280 * 720 * 15; |
||||
CHECK(call_video_choose(&a, &b, 0, 1280, 720, 30) == CALL_VIDEO_H264); |
||||
CHECK(call_video_choose(&a, &b, CALL_VIDEO_H265, 1280, 720, 30) == 0); |
||||
CHECK(call_video_choose(&a, &b, 0, 1920, 1080, 60) == 0); |
||||
CHECK(call_video_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); |
||||
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 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}; |
||||
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); |
||||
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); |
||||
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; |
||||
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); |
||||
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[42] = 1; CHECK(call_video_fragment_parse(wire, length, &out) == -1); |
||||
CHECK(!memcmp(&out, &saved, sizeof(saved))); wire[42] = 0; |
||||
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"); |
||||
} |
||||
int main(void) { |
||||
debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR); |
||||
capabilities(); roundtrip(); rejected(); return 0; |
||||
} |
||||
Loading…
Reference in new issue