mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-05-21 18:20:41 +00:00
520 lines
16 KiB
C++
520 lines
16 KiB
C++
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "../src/cavlc.h"
|
|
|
|
using namespace subcodec::cavlc;
|
|
|
|
/*
|
|
* CAVLC Bitstream Verification Tests
|
|
*
|
|
* These tests verify that CAVLC encoding produces correct bitstreams by checking
|
|
* the output against known expected values from the H.264 specification.
|
|
*
|
|
* Note: Full FFmpeg round-trip verification (encode -> decode -> compare pixels)
|
|
* requires complete macroblock encoding which is implemented in Tasks 7-9.
|
|
* Once write_p_frame_ex() is complete in Task 9, we can add end-to-end decode
|
|
* verification using libavcodec.
|
|
*/
|
|
|
|
// Helper to calculate bits written (bytes * 8 - unused bits in current byte)
|
|
static size_t bs_bits_written(bs_t* b) {
|
|
size_t bytes = b->p - b->start;
|
|
size_t bits = bytes * 8 + (8 - b->bits_left);
|
|
return bits;
|
|
}
|
|
|
|
static int test_zero_block(void) {
|
|
uint8_t buf[64];
|
|
memset(buf, 0, sizeof(buf));
|
|
bs_t b;
|
|
bs_init(&b, buf, sizeof(buf));
|
|
|
|
int16_t coeffs[16] = {0};
|
|
int tc = write_block(&b, coeffs, 0, 16);
|
|
|
|
if (tc != 0) {
|
|
printf("FAIL: test_zero_block - expected TC=0, got %d\n", tc);
|
|
return 1;
|
|
}
|
|
printf("PASS: test_zero_block\n");
|
|
return 0;
|
|
}
|
|
|
|
static int test_dc_only(void) {
|
|
uint8_t buf[64];
|
|
memset(buf, 0, sizeof(buf));
|
|
bs_t b;
|
|
bs_init(&b, buf, sizeof(buf));
|
|
|
|
int16_t coeffs[16] = {5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
int tc = write_block(&b, coeffs, 0, 16);
|
|
|
|
if (tc != 1) {
|
|
printf("FAIL: test_dc_only - expected TC=1, got %d\n", tc);
|
|
return 1;
|
|
}
|
|
printf("PASS: test_dc_only\n");
|
|
return 0;
|
|
}
|
|
|
|
static int test_trailing_ones(void) {
|
|
uint8_t buf[64];
|
|
memset(buf, 0, sizeof(buf));
|
|
bs_t b;
|
|
bs_init(&b, buf, sizeof(buf));
|
|
|
|
// 3, 0, 1, -1 -> TotalCoeff=3, TrailingOnes=2
|
|
int16_t coeffs[16] = {3, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
int tc = write_block(&b, coeffs, 0, 16);
|
|
|
|
if (tc != 3) {
|
|
printf("FAIL: test_trailing_ones - expected TC=3, got %d\n", tc);
|
|
return 1;
|
|
}
|
|
printf("PASS: test_trailing_ones\n");
|
|
return 0;
|
|
}
|
|
|
|
static int test_three_trailing_ones(void) {
|
|
uint8_t buf[64];
|
|
memset(buf, 0, sizeof(buf));
|
|
bs_t b;
|
|
bs_init(&b, buf, sizeof(buf));
|
|
|
|
// 1, 1, -1 -> TotalCoeff=3, TrailingOnes=3
|
|
int16_t coeffs[16] = {1, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
int tc = write_block(&b, coeffs, 0, 16);
|
|
|
|
if (tc != 3) {
|
|
printf("FAIL: test_three_trailing_ones - expected TC=3, got %d\n", tc);
|
|
return 1;
|
|
}
|
|
printf("PASS: test_three_trailing_ones\n");
|
|
return 0;
|
|
}
|
|
|
|
static int test_large_level(void) {
|
|
uint8_t buf[64];
|
|
memset(buf, 0, sizeof(buf));
|
|
bs_t b;
|
|
bs_init(&b, buf, sizeof(buf));
|
|
|
|
// Large coefficient value to test escape coding
|
|
int16_t coeffs[16] = {100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
int tc = write_block(&b, coeffs, 0, 16);
|
|
|
|
if (tc != 1) {
|
|
printf("FAIL: test_large_level - expected TC=1, got %d\n", tc);
|
|
return 1;
|
|
}
|
|
printf("PASS: test_large_level\n");
|
|
return 0;
|
|
}
|
|
|
|
static int test_multiple_coeffs_with_zeros(void) {
|
|
uint8_t buf[64];
|
|
memset(buf, 0, sizeof(buf));
|
|
bs_t b;
|
|
bs_init(&b, buf, sizeof(buf));
|
|
|
|
// Multiple non-zero coefficients with zeros between them
|
|
// This tests the run_before encoding
|
|
int16_t coeffs[16] = {5, 0, 0, 3, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
int tc = write_block(&b, coeffs, 0, 16);
|
|
|
|
if (tc != 3) {
|
|
printf("FAIL: test_multiple_coeffs_with_zeros - expected TC=3, got %d\n", tc);
|
|
return 1;
|
|
}
|
|
printf("PASS: test_multiple_coeffs_with_zeros\n");
|
|
return 0;
|
|
}
|
|
|
|
static int test_chroma_dc(void) {
|
|
uint8_t buf[64];
|
|
memset(buf, 0, sizeof(buf));
|
|
bs_t b;
|
|
bs_init(&b, buf, sizeof(buf));
|
|
|
|
// Chroma DC block (4 coefficients max)
|
|
int16_t coeffs[4] = {10, 0, -5, 0};
|
|
int tc = write_block(&b, coeffs, -1, 4);
|
|
|
|
if (tc != 2) {
|
|
printf("FAIL: test_chroma_dc - expected TC=2, got %d\n", tc);
|
|
return 1;
|
|
}
|
|
printf("PASS: test_chroma_dc\n");
|
|
return 0;
|
|
}
|
|
|
|
static int test_full_block(void) {
|
|
uint8_t buf[128];
|
|
memset(buf, 0, sizeof(buf));
|
|
bs_t b;
|
|
bs_init(&b, buf, sizeof(buf));
|
|
|
|
// All 16 coefficients non-zero
|
|
int16_t coeffs[16] = {16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
|
|
int tc = write_block(&b, coeffs, 0, 16);
|
|
|
|
if (tc != 16) {
|
|
printf("FAIL: test_full_block - expected TC=16, got %d\n", tc);
|
|
return 1;
|
|
}
|
|
printf("PASS: test_full_block\n");
|
|
return 0;
|
|
}
|
|
|
|
static int test_negative_coeffs(void) {
|
|
uint8_t buf[128];
|
|
bs_t b;
|
|
bs_init(&b, buf, sizeof(buf));
|
|
|
|
// Mix of positive and negative values
|
|
int16_t coeffs[16] = {-10, 5, -3, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
int tc = write_block(&b, coeffs, 0, 16);
|
|
|
|
if (tc != 5) {
|
|
printf("FAIL: test_negative_coeffs - expected TC=5, got %d\n", tc);
|
|
return 1;
|
|
}
|
|
printf("PASS: test_negative_coeffs\n");
|
|
return 0;
|
|
}
|
|
|
|
static int test_nc_variations(void) {
|
|
int errors = 0;
|
|
|
|
// Test with different nC values to exercise different VLC tables
|
|
int nc_values[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
|
int num_nc = sizeof(nc_values) / sizeof(nc_values[0]);
|
|
|
|
for (int i = 0; i < num_nc; i++) {
|
|
uint8_t buf[64];
|
|
memset(buf, 0, sizeof(buf));
|
|
bs_t b;
|
|
bs_init(&b, buf, sizeof(buf));
|
|
|
|
int16_t coeffs[16] = {5, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
int tc = write_block(&b, coeffs, nc_values[i], 16);
|
|
|
|
if (tc != 2) {
|
|
printf("FAIL: test_nc_variations (nc=%d) - expected TC=2, got %d\n", nc_values[i], tc);
|
|
errors++;
|
|
}
|
|
}
|
|
|
|
if (errors == 0) {
|
|
printf("PASS: test_nc_variations\n");
|
|
}
|
|
return errors;
|
|
}
|
|
|
|
/*
|
|
* Bitstream-level verification tests
|
|
*
|
|
* These tests verify CAVLC encoding by checking that the output bitstream
|
|
* matches expected patterns from the H.264 specification. This is an
|
|
* intermediate verification step before full FFmpeg round-trip testing.
|
|
*/
|
|
|
|
// Test: Verify zero block encoding produces exactly 1 bit (coeff_token for TC=0)
|
|
// For nC=0, TotalCoeff=0, T1=0: coeff_token = 1 (1 bit)
|
|
static int test_bitstream_zero_block(void) {
|
|
uint8_t buf[64];
|
|
memset(buf, 0, sizeof(buf));
|
|
bs_t b;
|
|
bs_init(&b, buf, sizeof(buf));
|
|
|
|
int16_t coeffs[16] = {0};
|
|
int tc = write_block(&b, coeffs, 0, 16);
|
|
|
|
size_t bits = bs_bits_written(&b);
|
|
|
|
// Zero block for nC=0 should be exactly 1 bit (coeff_token = "1")
|
|
if (tc != 0 || bits != 1) {
|
|
printf("FAIL: test_bitstream_zero_block - TC=%d, bits=%zu (expected TC=0, bits=1)\n", tc, bits);
|
|
return 1;
|
|
}
|
|
|
|
// The first byte should be 0x80 (1 followed by 7 zeros)
|
|
if (buf[0] != 0x80) {
|
|
printf("FAIL: test_bitstream_zero_block - byte=0x%02X (expected 0x80)\n", buf[0]);
|
|
return 1;
|
|
}
|
|
|
|
printf("PASS: test_bitstream_zero_block (1 bit)\n");
|
|
return 0;
|
|
}
|
|
|
|
// Test: Verify single DC coefficient encoding produces valid output
|
|
// For nC=0, TotalCoeff=1, T1=0: coeff_token = 000101 (6 bits)
|
|
// Level=4: After T1 adjustment, level=3, level_code = 2*3-2+0 = 4, suffix_length=0
|
|
// level_prefix = 4 (write 4 zeros + 1) = 00001 (5 bits)
|
|
// total_zeros=0 with TC=1: VLC = 1 (1 bit)
|
|
// Total: 6 + 5 + 1 = 12 bits
|
|
static int test_bitstream_single_dc(void) {
|
|
uint8_t buf[64];
|
|
memset(buf, 0, sizeof(buf));
|
|
bs_t b;
|
|
bs_init(&b, buf, sizeof(buf));
|
|
|
|
int16_t coeffs[16] = {4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
int tc = write_block(&b, coeffs, 0, 16);
|
|
|
|
size_t bits = bs_bits_written(&b);
|
|
|
|
if (tc != 1) {
|
|
printf("FAIL: test_bitstream_single_dc - TC=%d (expected 1)\n", tc);
|
|
return 1;
|
|
}
|
|
|
|
// Expected: 12 bits total (coeff_token=6 + level=5 + total_zeros=1)
|
|
// The first non-T1 level gets magnitude reduced by 1, so level=4 becomes level=3
|
|
if (bits != 12) {
|
|
printf("FAIL: test_bitstream_single_dc - bits=%zu (expected 12)\n", bits);
|
|
return 1;
|
|
}
|
|
|
|
printf("PASS: test_bitstream_single_dc (%zu bits)\n", bits);
|
|
return 0;
|
|
}
|
|
|
|
// Test: Verify trailing ones with sign bits
|
|
// Coeffs: [0, 0, 0, 1, 0, -1] in zigzag order
|
|
// In reverse scan: -1 at pos 5, 1 at pos 3
|
|
// TotalCoeff=2, TrailingOnes=2 (both are +/-1)
|
|
// For nC=0, TC=2, T1=2: coeff_token = 001 (3 bits) from Table 9-5(a)
|
|
// Signs: -1 = 1, 1 = 0 -> write in reverse: 0, 1 = 01 (2 bits)
|
|
// total_zeros: TC=2, total_zeros=3 (positions 3,5 have coeffs, so zeros at 0,1,2,4)
|
|
// Actually: last_nz=5, total_zeros = 5+1 - 2 = 4
|
|
// Wait, let me recalculate with exact positions
|
|
static int test_bitstream_trailing_ones_signs(void) {
|
|
uint8_t buf[64];
|
|
memset(buf, 0, sizeof(buf));
|
|
bs_t b;
|
|
bs_init(&b, buf, sizeof(buf));
|
|
|
|
// Two trailing ones at positions 3 and 5
|
|
int16_t coeffs[16] = {0, 0, 0, 1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
int tc = write_block(&b, coeffs, 0, 16);
|
|
|
|
size_t bits = bs_bits_written(&b);
|
|
|
|
if (tc != 2) {
|
|
printf("FAIL: test_bitstream_trailing_ones_signs - TC=%d (expected 2)\n", tc);
|
|
return 1;
|
|
}
|
|
|
|
// Should produce non-trivial output with sign bits
|
|
// coeff_token(2,2,nC=0) = 001 (3 bits)
|
|
// signs: 0 (for +1), 1 (for -1) -> 2 bits
|
|
// total_zeros (TC=2, tz=4): from table
|
|
// run_before for first coeff
|
|
if (bits < 5) {
|
|
printf("FAIL: test_bitstream_trailing_ones_signs - too few bits=%zu\n", bits);
|
|
return 1;
|
|
}
|
|
|
|
printf("PASS: test_bitstream_trailing_ones_signs (%zu bits)\n", bits);
|
|
return 0;
|
|
}
|
|
|
|
// Test: Verify deterministic encoding (same input = same output)
|
|
static int test_bitstream_deterministic(void) {
|
|
uint8_t buf1[64], buf2[64];
|
|
memset(buf1, 0, sizeof(buf1));
|
|
memset(buf2, 0, sizeof(buf2));
|
|
|
|
bs_t b1, b2;
|
|
bs_init(&b1, buf1, sizeof(buf1));
|
|
bs_init(&b2, buf2, sizeof(buf2));
|
|
|
|
int16_t coeffs[16] = {10, 0, -5, 2, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
|
|
int tc1 = write_block(&b1, coeffs, 0, 16);
|
|
int tc2 = write_block(&b2, coeffs, 0, 16);
|
|
|
|
size_t bits1 = bs_bits_written(&b1);
|
|
size_t bits2 = bs_bits_written(&b2);
|
|
|
|
if (tc1 != tc2) {
|
|
printf("FAIL: test_bitstream_deterministic - TC mismatch %d vs %d\n", tc1, tc2);
|
|
return 1;
|
|
}
|
|
|
|
if (bits1 != bits2) {
|
|
printf("FAIL: test_bitstream_deterministic - bit count mismatch %zu vs %zu\n", bits1, bits2);
|
|
return 1;
|
|
}
|
|
|
|
// Compare output bytes
|
|
size_t bytes = (bits1 + 7) / 8;
|
|
if (memcmp(buf1, buf2, bytes) != 0) {
|
|
printf("FAIL: test_bitstream_deterministic - output bytes differ\n");
|
|
return 1;
|
|
}
|
|
|
|
printf("PASS: test_bitstream_deterministic (TC=%d, %zu bits)\n", tc1, bits1);
|
|
return 0;
|
|
}
|
|
|
|
// Test: Verify different nC values produce different coeff_token encodings
|
|
// The same coefficients with different nC should produce different bit counts
|
|
// because different VLC tables are used
|
|
static int test_bitstream_nc_affects_output(void) {
|
|
int16_t coeffs[16] = {5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
size_t bits_nc0, bits_nc4, bits_nc8;
|
|
|
|
// nC = 0 (Table 9-5(a))
|
|
{
|
|
uint8_t buf[64];
|
|
memset(buf, 0, sizeof(buf));
|
|
bs_t b;
|
|
bs_init(&b, buf, sizeof(buf));
|
|
write_block(&b, coeffs, 0, 16);
|
|
bits_nc0 = bs_bits_written(&b);
|
|
}
|
|
|
|
// nC = 4 (Table 9-5(c))
|
|
{
|
|
uint8_t buf[64];
|
|
memset(buf, 0, sizeof(buf));
|
|
bs_t b;
|
|
bs_init(&b, buf, sizeof(buf));
|
|
write_block(&b, coeffs, 4, 16);
|
|
bits_nc4 = bs_bits_written(&b);
|
|
}
|
|
|
|
// nC = 8 (Table 9-5(d) - fixed length)
|
|
{
|
|
uint8_t buf[64];
|
|
memset(buf, 0, sizeof(buf));
|
|
bs_t b;
|
|
bs_init(&b, buf, sizeof(buf));
|
|
write_block(&b, coeffs, 8, 16);
|
|
bits_nc8 = bs_bits_written(&b);
|
|
}
|
|
|
|
// Different VLC tables should produce different bit counts
|
|
// (they might occasionally be the same, but typically differ)
|
|
// For this test, we just verify all produce valid output
|
|
if (bits_nc0 < 1 || bits_nc4 < 1 || bits_nc8 < 1) {
|
|
printf("FAIL: test_bitstream_nc_affects_output - invalid bit counts\n");
|
|
return 1;
|
|
}
|
|
|
|
printf("PASS: test_bitstream_nc_affects_output (nC=0:%zu, nC=4:%zu, nC=8:%zu bits)\n",
|
|
bits_nc0, bits_nc4, bits_nc8);
|
|
return 0;
|
|
}
|
|
|
|
// Test: Verify escape coding for large levels
|
|
// Level 100 requires escape coding (level_prefix >= 15)
|
|
static int test_bitstream_escape_coding(void) {
|
|
uint8_t buf[64];
|
|
memset(buf, 0, sizeof(buf));
|
|
bs_t b;
|
|
bs_init(&b, buf, sizeof(buf));
|
|
|
|
int16_t coeffs[16] = {100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
int tc = write_block(&b, coeffs, 0, 16);
|
|
|
|
size_t bits = bs_bits_written(&b);
|
|
|
|
if (tc != 1) {
|
|
printf("FAIL: test_bitstream_escape_coding - TC=%d (expected 1)\n", tc);
|
|
return 1;
|
|
}
|
|
|
|
// Large level should produce more bits than small level
|
|
// Level 100 with escape coding: level_prefix=15 (16 bits) + level_suffix (12 bits)
|
|
// Plus coeff_token and total_zeros
|
|
if (bits < 25) { // Escape coding produces many bits
|
|
printf("FAIL: test_bitstream_escape_coding - too few bits=%zu for escape coding\n", bits);
|
|
return 1;
|
|
}
|
|
|
|
printf("PASS: test_bitstream_escape_coding (%zu bits for level=100)\n", bits);
|
|
return 0;
|
|
}
|
|
|
|
// Test: Consistency between same coefficients encoded multiple times
|
|
// Verifies that encoding is deterministic with zero-initialized buffers
|
|
static int test_bitstream_consistency(void) {
|
|
int16_t coeffs[16] = {8, -4, 2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
|
|
uint8_t buf1[128], buf2[128];
|
|
// Both buffers must be zero-initialized since bs_write_u1 clears individual bits
|
|
// but doesn't clear trailing bits in the last byte
|
|
memset(buf1, 0, sizeof(buf1));
|
|
memset(buf2, 0, sizeof(buf2));
|
|
|
|
bs_t b1, b2;
|
|
bs_init(&b1, buf1, sizeof(buf1));
|
|
bs_init(&b2, buf2, sizeof(buf2));
|
|
|
|
int tc1 = write_block(&b1, coeffs, 2, 16);
|
|
int tc2 = write_block(&b2, coeffs, 2, 16);
|
|
|
|
size_t bits1 = bs_bits_written(&b1);
|
|
size_t bits2 = bs_bits_written(&b2);
|
|
|
|
if (tc1 != tc2 || bits1 != bits2) {
|
|
printf("FAIL: test_bitstream_consistency - results differ\n");
|
|
return 1;
|
|
}
|
|
|
|
// Compare the actual encoded bits (full bytes only to avoid partial byte issues)
|
|
size_t full_bytes = bits1 / 8;
|
|
if (full_bytes > 0 && memcmp(buf1, buf2, full_bytes) != 0) {
|
|
printf("FAIL: test_bitstream_consistency - encoded bytes differ\n");
|
|
return 1;
|
|
}
|
|
|
|
// Verify the partial byte if any (mask off unused bits)
|
|
if (bits1 % 8 != 0) {
|
|
int used_bits = bits1 % 8;
|
|
uint8_t mask = (0xFF << (8 - used_bits));
|
|
if ((buf1[full_bytes] & mask) != (buf2[full_bytes] & mask)) {
|
|
printf("FAIL: test_bitstream_consistency - partial byte differs\n");
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
printf("PASS: test_bitstream_consistency\n");
|
|
return 0;
|
|
}
|
|
|
|
int main(void) {
|
|
int errors = 0;
|
|
|
|
printf("Running CAVLC block writer tests...\n\n");
|
|
|
|
// Basic functionality tests
|
|
errors += test_zero_block();
|
|
errors += test_dc_only();
|
|
errors += test_trailing_ones();
|
|
errors += test_three_trailing_ones();
|
|
errors += test_large_level();
|
|
errors += test_multiple_coeffs_with_zeros();
|
|
errors += test_chroma_dc();
|
|
errors += test_full_block();
|
|
errors += test_negative_coeffs();
|
|
errors += test_nc_variations();
|
|
|
|
// Bitstream verification tests
|
|
printf("\nRunning bitstream verification tests...\n\n");
|
|
errors += test_bitstream_zero_block();
|
|
errors += test_bitstream_single_dc();
|
|
errors += test_bitstream_trailing_ones_signs();
|
|
errors += test_bitstream_deterministic();
|
|
errors += test_bitstream_nc_affects_output();
|
|
errors += test_bitstream_escape_coding();
|
|
errors += test_bitstream_consistency();
|
|
|
|
printf("\n%d test(s) failed\n", errors);
|
|
return errors;
|
|
}
|