blob: 4bde8bea54b6ba0c1969d3a742761baaf4dcaedf [file] [edit]
#!/bin/sh
##
## Multistream Tests
##
## multistream_mux_tests:
## Test 1.1: Multiplex two streams (stream_id 0,6), verify per-stream ordering
## (basic two-stream multiplexing)
## Test 1.2: Multiplex two streams (stream_id 7,1), verify ordering
## (same as Test 1.1 with different stream_ids)
## Test 1.3: Multiplex two streams (stream_id 0,6) with redundant MSDO, verify identical to Test 1.1
## (tests decoder handles redundant MSDO OBUs)
## Test 1.4: Concatenate the Test 1.1 multistream with itself, verify output is Test 1.1 x 2
## (tests CVS boundary handling within a stream_id)
## Test 1.5: Concatenate Test 1.1 multistream with Test 1.2 multistream, verify pixel-exact
## (tests CVS boundary with different stream_id sets)
##
## multistream_cmvs_tests:
## Test 2.1: Concatenate two singlestreams, verify pixel-exact match
## (tests CVS boundary handling)
## Test 2.2: Multiplex a 20-frame singlestream with the Test 2.1 concatenated stream (stream_id 0,6), verify ordering
## (tests muxing a singlestream with a multi-CVS singlestream)
## Test 2.3: Concatenate 20-frame singlestream with Test 1.1-style multistream (stream_id 0,6), verify strict order
## (tests singlestream followed by multistream)
## Test 2.4: Concatenate 20-frame singlestream with Test 1.2-style multistream (stream_id 7,1), verify strict order
## (same as Test 2.3 with different stream_ids)
## Test 2.5: Concatenate Test 1.1-style multistream (stream_id 0,6) with 20-frame singlestream
## (tests multistream followed by singlestream)
## Test 2.6: Concatenate Test 1.2-style multistream (stream_id 7,1) with 20-frame singlestream
## (same as Test 2.5 with different stream_ids)
##
## multistream_three_and_four_stream_tests:
## Test 3.1: Multiplex three streams (stream_id 12,5,2), verify per-stream ordering
## (tests multiplexing with three sub-streams)
## Test 3.2: Multiplex four streams (stream_id 3,8,4,9), verify per-stream ordering
## (tests multiplexing with four sub-streams)
## Test 3.3: Concatenate the Test 3.1 multistream with Test 3.2 multistream, verify strict order
## (tests CMVS boundary between two different multistreams)
## Test 3.4: Concatenate the Test 3.2 multistream with Test 3.1 multistream, verify strict order
## (same as Test 3.3 with reversed order)
##
. $(dirname $0)/tools_common.sh
multistream_tests_verify_environment() {
if [ -z "$(avm_tool_path avmenc)" ]; then
elog "avmenc not found."
return 1
fi
if [ -z "$(avm_tool_path avmdec)" ]; then
elog "avmdec not found."
return 1
fi
if [ -z "$(avm_tool_path stream_multiplexer)" ]; then
elog "stream_multiplexer not found."
return 1
fi
}
multistream_cmvs_tests() {
local encoder="$(avm_tool_path avmenc)"
local decoder="$(avm_tool_path avmdec)"
local muxer="$(avm_tool_path stream_multiplexer)"
local w=64
local h=64
local frames=10
local frames20=20
local frame_size=$((w * h * 3 / 2))
local input_a="${AVM_TEST_OUTPUT_DIR}/cs_input_a.yuv"
local input_b="${AVM_TEST_OUTPUT_DIR}/cs_input_b.yuv"
local input_c="${AVM_TEST_OUTPUT_DIR}/cs_input_c.yuv"
local bs_a="${AVM_TEST_OUTPUT_DIR}/cs_stream_a.bin"
local bs_b="${AVM_TEST_OUTPUT_DIR}/cs_stream_b.bin"
local bs_c="${AVM_TEST_OUTPUT_DIR}/cs_stream_c.bin"
local bs_concat_ab="${AVM_TEST_OUTPUT_DIR}/cs_concat_ab.bin"
local bs_mux2="${AVM_TEST_OUTPUT_DIR}/cs_mux2.bin"
local bs_mux2a="${AVM_TEST_OUTPUT_DIR}/cs_mux2a.bin"
local dec_a="${AVM_TEST_OUTPUT_DIR}/cs_decoded_a.yuv"
local dec_b="${AVM_TEST_OUTPUT_DIR}/cs_decoded_b.yuv"
local dec_c="${AVM_TEST_OUTPUT_DIR}/cs_decoded_c.yuv"
local dec_concat_ab="${AVM_TEST_OUTPUT_DIR}/cs_decoded_concat_ab.yuv"
local dec_mux2="${AVM_TEST_OUTPUT_DIR}/cs_decoded_mux2.yuv"
local dec_mux2a="${AVM_TEST_OUTPUT_DIR}/cs_decoded_mux2a.yuv"
# Generate inputs: A,B are 10 frames, C is 20 frames
vlog " Generating synthetic YUV inputs..."
python3 -c "
w, h = ${w}, ${h}
for path, n, base, step in [('${input_a}', ${frames}, 16, 8), ('${input_b}', ${frames}, 20, 8), ('${input_c}', ${frames20}, 100, 4)]:
with open(path, 'wb') as f:
for i in range(n):
f.write(bytes([base + i * step]) * (w * h))
f.write(bytes([128]) * (w * h // 2))
" || return 1
# Encode A, B (10 frames each), C (20 frames, TIP disabled)
vlog " Encoding sequence A (${frames} frames)..."
"${encoder}" \
--obu --limit=${frames} --width=${w} --height=${h} --fps=30/1 \
--auto-alt-ref=1 --lag-in-frames=16 \
--gf-min-pyr-height=3 --gf-max-pyr-height=5 \
--cpu-used=8 -o "${bs_a}" "${input_a}" > /dev/null 2>&1 || return 1
vlog " Encoding sequence B (${frames} frames)..."
"${encoder}" \
--obu --limit=${frames} --width=${w} --height=${h} --fps=30/1 \
--auto-alt-ref=1 --lag-in-frames=16 \
--gf-min-pyr-height=3 --gf-max-pyr-height=5 \
--cpu-used=8 -o "${bs_b}" "${input_b}" > /dev/null 2>&1 || return 1
vlog " Encoding sequence C (${frames20} frames, TIP disabled)..."
"${encoder}" \
--obu --limit=${frames20} --width=${w} --height=${h} --fps=30/1 \
--auto-alt-ref=1 --lag-in-frames=16 \
--gf-min-pyr-height=3 --gf-max-pyr-height=5 \
--cpu-used=8 --enable-tip=0 -o "${bs_c}" "${input_c}" > /dev/null 2>&1 || return 1
# Prepare shared artifacts
vlog " Concatenating A+B..."
cat "${bs_a}" "${bs_b}" > "${bs_concat_ab}" || return 1
vlog " Multiplexing A+B (stream_id 0,6)..."
"${muxer}" "${bs_a}" 0 1 "${bs_b}" 6 1 "${bs_mux2}" > /dev/null 2>&1 || return 1
vlog " Multiplexing A+B (stream_id 7,1)..."
"${muxer}" "${bs_a}" 7 1 "${bs_b}" 1 1 "${bs_mux2a}" > /dev/null 2>&1 || return 1
# Decode shared references
vlog " Decoding stream A..."
"${decoder}" --codec=av2 -o "${dec_a}" "${bs_a}" > /dev/null 2>&1 || return 1
vlog " Decoding stream B..."
"${decoder}" --codec=av2 -o "${dec_b}" "${bs_b}" > /dev/null 2>&1 || return 1
vlog " Decoding stream C..."
"${decoder}" --codec=av2 -o "${dec_c}" "${bs_c}" > /dev/null 2>&1 || return 1
vlog " Decoding concat(A+B)..."
"${decoder}" --codec=av2 -o "${dec_concat_ab}" "${bs_concat_ab}" > /dev/null 2>&1 || return 1
vlog " Decoding mux2..."
"${decoder}" --codec=av2 -o "${dec_mux2}" "${bs_mux2}" > /dev/null 2>&1 || return 1
vlog " Decoding mux2a..."
"${decoder}" --codec=av2 -o "${dec_mux2a}" "${bs_mux2a}" > /dev/null 2>&1 || return 1
# --- Test 2.1: Verify concat(A+B) == dec_A + dec_B ---
vlog " [Test 2.1] Verifying concatenation pixel-exact match..."
python3 -c "
def extract_frames(filename, frame_size):
with open(filename, 'rb') as f:
f.readline()
frames = []
while True:
line = f.readline()
if not line: break
if line.startswith(b'FRAME'): frames.append(f.read(frame_size))
return frames
frame_size = ${frame_size}
a = extract_frames('${dec_a}', frame_size)
b = extract_frames('${dec_b}', frame_size)
c = extract_frames('${dec_concat_ab}', frame_size)
assert len(c) == len(a) + len(b), 'Frame count mismatch: %d != %d + %d' % (len(c), len(a), len(b))
assert len(a) == len(set(a)), 'Stream A has duplicate frames'
assert len(b) == len(set(b)), 'Stream B has duplicate frames'
assert b''.join(c) == b''.join(a) + b''.join(b), 'Pixel data mismatch'
print('Test 2.1 passed: %d + %d = %d frames, pixel-exact match' % (len(a), len(b), len(c)))
" || return 1
# --- Test 2.2: Mux C with concat(A+B), verify per-stream ordering ---
local bs_t5="${AVM_TEST_OUTPUT_DIR}/cs_t5_muxed.bin"
local dec_t5="${AVM_TEST_OUTPUT_DIR}/cs_t5_decoded.yuv"
vlog " [Test 2.2] Multiplexing C with concat(A+B)..."
"${muxer}" "${bs_c}" 0 1 "${bs_concat_ab}" 6 1 "${bs_t5}" > /dev/null 2>&1 || return 1
vlog " [Test 2.2] Decoding multiplexed bitstream..."
"${decoder}" --codec=av2 -o "${dec_t5}" "${bs_t5}" > /dev/null 2>&1 || return 1
vlog " [Test 2.2] Verifying per-stream ordering..."
python3 -c "
def extract_frames(f, fs):
with open(f, 'rb') as fh:
fh.readline()
frames = []
while True:
l = fh.readline()
if not l: break
if l.startswith(b'FRAME'): frames.append(fh.read(fs))
return frames
def check_sub(muxed, sf):
it = iter(muxed)
for f in sf:
while True:
m = next(it, None)
if m is None: return False
if m == f: break
return True
fs = ${frame_size}
muxed = extract_frames('${dec_t5}', fs)
c = extract_frames('${dec_c}', fs)
ab = extract_frames('${dec_concat_ab}', fs)
assert len(muxed) == len(c) + len(ab), 'Frame count mismatch: %d != %d + %d' % (len(muxed), len(c), len(ab))
assert sorted(muxed) == sorted(c + ab), 'Frame sets differ'
assert check_sub(muxed, c), 'Stream C not in correct order'
assert check_sub(muxed, ab), 'Concat(A+B) not in correct order'
print('Test 2.2 passed: %d + %d = %d frames, per-stream ordering correct' % (len(c), len(ab), len(muxed)))
" || return 1
# --- Test 2.3: cat(C, mux2) -> verify strict concat order ---
local bs_t6="${AVM_TEST_OUTPUT_DIR}/cs_t6_concat.bin"
local dec_t6="${AVM_TEST_OUTPUT_DIR}/cs_t6_decoded.yuv"
vlog " [Test 2.3] Concatenating C with mux2..."
cat "${bs_c}" "${bs_mux2}" > "${bs_t6}" || return 1
vlog " [Test 2.3] Decoding..."
"${decoder}" --codec=av2 -o "${dec_t6}" "${bs_t6}" > /dev/null 2>&1 || return 1
vlog " [Test 2.3] Verifying strict concatenation order..."
python3 -c "
def extract_pixels(f, fs):
with open(f, 'rb') as fh:
fh.readline()
p = bytearray()
while True:
l = fh.readline()
if not l: break
if l.startswith(b'FRAME'): p.extend(fh.read(fs))
return bytes(p)
fs = ${frame_size}
d = extract_pixels('${dec_t6}', fs)
c = extract_pixels('${dec_c}', fs)
m2 = extract_pixels('${dec_mux2}', fs)
assert d == c + m2, 'Test 2.3: pixel mismatch (len %d != %d + %d)' % (len(d), len(c), len(m2))
print('Test 2.3 passed: cat(C, mux2) strict concatenation order correct')
" || return 1
# --- Test 2.4: cat(C, mux2a) -> verify strict concat order ---
local bs_t6a="${AVM_TEST_OUTPUT_DIR}/cs_t6a_concat.bin"
local dec_t6a="${AVM_TEST_OUTPUT_DIR}/cs_t6a_decoded.yuv"
vlog " [Test 2.4] Concatenating C with mux2a..."
cat "${bs_c}" "${bs_mux2a}" > "${bs_t6a}" || return 1
vlog " [Test 2.4] Decoding..."
"${decoder}" --codec=av2 -o "${dec_t6a}" "${bs_t6a}" > /dev/null 2>&1 || return 1
vlog " [Test 2.4] Verifying strict concatenation order..."
python3 -c "
def extract_pixels(f, fs):
with open(f, 'rb') as fh:
fh.readline()
p = bytearray()
while True:
l = fh.readline()
if not l: break
if l.startswith(b'FRAME'): p.extend(fh.read(fs))
return bytes(p)
fs = ${frame_size}
d = extract_pixels('${dec_t6a}', fs)
c = extract_pixels('${dec_c}', fs)
m2a = extract_pixels('${dec_mux2a}', fs)
assert d == c + m2a, 'Test 2.4: pixel mismatch'
print('Test 2.4 passed: cat(C, mux2a) strict concatenation order correct')
" || return 1
# --- Test 2.5: multistream followed by singlestream ---
local bs_t7="${AVM_TEST_OUTPUT_DIR}/cs_t7_concat.bin"
local dec_t7="${AVM_TEST_OUTPUT_DIR}/cs_t7_decoded.yuv"
vlog " [Test 2.5] Concatenating mux2 with C..."
cat "${bs_mux2}" "${bs_c}" > "${bs_t7}" || return 1
vlog " [Test 2.5] Decoding..."
"${decoder}" --codec=av2 -o "${dec_t7}" "${bs_t7}" > /dev/null 2>&1 || return 1
vlog " [Test 2.5] Verifying strict concatenation order..."
python3 -c "
def extract_pixels(f, fs):
with open(f, 'rb') as fh:
fh.readline()
p = bytearray()
while True:
l = fh.readline()
if not l: break
if l.startswith(b'FRAME'): p.extend(fh.read(fs))
return bytes(p)
fs = ${frame_size}
d = extract_pixels('${dec_t7}', fs)
m2 = extract_pixels('${dec_mux2}', fs)
c = extract_pixels('${dec_c}', fs)
assert d == m2 + c, 'Test 2.5: pixel mismatch'
print('Test 2.5 passed: cat(mux2, C) strict concatenation order correct')
" || return 1
# --- Test 2.6: same as Test 2.5 with different stream_ids ---
local bs_t7a="${AVM_TEST_OUTPUT_DIR}/cs_t7a_concat.bin"
local dec_t7a="${AVM_TEST_OUTPUT_DIR}/cs_t7a_decoded.yuv"
vlog " [Test 2.6] Concatenating mux2a with C..."
cat "${bs_mux2a}" "${bs_c}" > "${bs_t7a}" || return 1
vlog " [Test 2.6] Decoding..."
"${decoder}" --codec=av2 -o "${dec_t7a}" "${bs_t7a}" > /dev/null 2>&1 || return 1
vlog " [Test 2.6] Verifying strict concatenation order..."
python3 -c "
def extract_pixels(f, fs):
with open(f, 'rb') as fh:
fh.readline()
p = bytearray()
while True:
l = fh.readline()
if not l: break
if l.startswith(b'FRAME'): p.extend(fh.read(fs))
return bytes(p)
fs = ${frame_size}
d = extract_pixels('${dec_t7a}', fs)
m2a = extract_pixels('${dec_mux2a}', fs)
c = extract_pixels('${dec_c}', fs)
assert d == m2a + c, 'Test 2.6: pixel mismatch'
print('Test 2.6 passed: cat(mux2a, C) strict concatenation order correct')
" || return 1
}
multistream_mux_tests() {
local encoder="$(avm_tool_path avmenc)"
local decoder="$(avm_tool_path avmdec)"
local muxer="$(avm_tool_path stream_multiplexer)"
local w=64
local h=64
local frames=10
local frame_size=$((w * h * 3 / 2))
local input_a="${AVM_TEST_OUTPUT_DIR}/mux_input_a.yuv"
local input_b="${AVM_TEST_OUTPUT_DIR}/mux_input_b.yuv"
local bs_a="${AVM_TEST_OUTPUT_DIR}/mux_stream_a.bin"
local bs_b="${AVM_TEST_OUTPUT_DIR}/mux_stream_b.bin"
local dec_a="${AVM_TEST_OUTPUT_DIR}/mux_decoded_a.yuv"
local dec_b="${AVM_TEST_OUTPUT_DIR}/mux_decoded_b.yuv"
# Shared encode step
vlog " Generating synthetic YUV inputs (${w}x${h}, ${frames} frames)..."
python3 -c "
w, h = ${w}, ${h}
for path, base, step in [('${input_a}', 24, 8), ('${input_b}', 28, 8)]:
with open(path, 'wb') as f:
for i in range(${frames}):
f.write(bytes([base + i * step]) * (w * h))
f.write(bytes([128]) * (w * h // 2))
" || return 1
vlog " Encoding sequence A..."
"${encoder}" \
--obu --limit=${frames} --width=${w} --height=${h} --fps=30/1 \
--auto-alt-ref=1 --lag-in-frames=16 \
--gf-min-pyr-height=3 --gf-max-pyr-height=5 \
--cpu-used=8 -o "${bs_a}" "${input_a}" > /dev/null 2>&1 || return 1
vlog " Encoding sequence B..."
"${encoder}" \
--obu --limit=${frames} --width=${w} --height=${h} --fps=30/1 \
--auto-alt-ref=1 --lag-in-frames=16 \
--gf-min-pyr-height=3 --gf-max-pyr-height=5 \
--cpu-used=8 -o "${bs_b}" "${input_b}" > /dev/null 2>&1 || return 1
vlog " Decoding stream A..."
"${decoder}" --codec=av2 -o "${dec_a}" "${bs_a}" > /dev/null 2>&1 || return 1
vlog " Decoding stream B..."
"${decoder}" --codec=av2 -o "${dec_b}" "${bs_b}" > /dev/null 2>&1 || return 1
# --- Test 1.1: Mux with stream_id 0 and 6 ---
local bs_mux2="${AVM_TEST_OUTPUT_DIR}/mux2_muxed.bin"
local dec_mux2="${AVM_TEST_OUTPUT_DIR}/mux2_decoded.yuv"
vlog " [Test 1.1] Multiplexing streams (stream_id 0 and 6)..."
"${muxer}" "${bs_a}" 0 1 "${bs_b}" 6 1 "${bs_mux2}" > /dev/null 2>&1 || return 1
vlog " [Test 1.1] Decoding multiplexed bitstream..."
"${decoder}" --codec=av2 -o "${dec_mux2}" "${bs_mux2}" > /dev/null 2>&1 || return 1
vlog " [Test 1.1] Verifying per-stream ordering..."
python3 -c "
def extract_frames(filename, frame_size):
with open(filename, 'rb') as f:
f.readline()
frames = []
while True:
line = f.readline()
if not line:
break
if line.startswith(b'FRAME'):
frames.append(f.read(frame_size))
return frames
def check_subsequence(muxed, stream_frames):
it = iter(muxed)
for frame in stream_frames:
while True:
m = next(it, None)
if m is None:
return False
if m == frame:
break
return True
frame_size = ${frame_size}
muxed = extract_frames('${dec_mux2}', frame_size)
a = extract_frames('${dec_a}', frame_size)
b = extract_frames('${dec_b}', frame_size)
assert len(muxed) == len(a) + len(b), 'Frame count mismatch'
assert len(a) == len(set(a)), 'Stream A has duplicate frames'
assert len(b) == len(set(b)), 'Stream B has duplicate frames'
assert sorted(muxed) == sorted(a + b), 'Frame sets differ'
assert check_subsequence(muxed, a), 'Stream A not in correct order'
assert check_subsequence(muxed, b), 'Stream B not in correct order'
print('Test 1.1 passed: %d + %d = %d frames, per-stream ordering correct' % (len(a), len(b), len(muxed)))
" || return 1
# --- Test 1.2: Mux with stream_id 7 and 1 ---
local bs_mux2a="${AVM_TEST_OUTPUT_DIR}/mux2a_muxed.bin"
local dec_mux2a="${AVM_TEST_OUTPUT_DIR}/mux2a_decoded.yuv"
vlog " [Test 1.2] Multiplexing streams (stream_id 7 and 1)..."
"${muxer}" "${bs_a}" 7 1 "${bs_b}" 1 1 "${bs_mux2a}" > /dev/null 2>&1 || return 1
vlog " [Test 1.2] Decoding multiplexed bitstream..."
"${decoder}" --codec=av2 -o "${dec_mux2a}" "${bs_mux2a}" > /dev/null 2>&1 || return 1
vlog " [Test 1.2] Verifying per-stream ordering..."
python3 -c "
def extract_frames(filename, frame_size):
with open(filename, 'rb') as f:
f.readline()
frames = []
while True:
line = f.readline()
if not line:
break
if line.startswith(b'FRAME'):
frames.append(f.read(frame_size))
return frames
def check_subsequence(muxed, stream_frames):
it = iter(muxed)
for frame in stream_frames:
while True:
m = next(it, None)
if m is None:
return False
if m == frame:
break
return True
frame_size = ${frame_size}
muxed = extract_frames('${dec_mux2a}', frame_size)
a = extract_frames('${dec_a}', frame_size)
b = extract_frames('${dec_b}', frame_size)
assert len(muxed) == len(a) + len(b), 'Frame count mismatch'
assert len(a) == len(set(a)), 'Stream A has duplicate frames'
assert len(b) == len(set(b)), 'Stream B has duplicate frames'
assert sorted(muxed) == sorted(a + b), 'Frame sets differ'
assert check_subsequence(muxed, a), 'Stream A not in correct order'
assert check_subsequence(muxed, b), 'Stream B not in correct order'
print('Test 1.2 passed: %d + %d = %d frames, per-stream ordering correct' % (len(a), len(b), len(muxed)))
" || return 1
# --- Test 1.3: Mux with --redundant-msdo, verify identical to Test 1.1 ---
local bs_mux3="${AVM_TEST_OUTPUT_DIR}/mux3_muxed.bin"
local dec_mux3="${AVM_TEST_OUTPUT_DIR}/mux3_decoded.yuv"
vlog " [Test 1.3] Multiplexing with --redundant-msdo..."
"${muxer}" --redundant-msdo "${bs_a}" 0 1 "${bs_b}" 6 1 "${bs_mux3}" > /dev/null 2>&1 || return 1
vlog " [Test 1.3] Decoding multiplexed bitstream..."
"${decoder}" --codec=av2 -o "${dec_mux3}" "${bs_mux3}" > /dev/null 2>&1 || return 1
vlog " [Test 1.3] Verifying pixel-identical to Test 1.1..."
python3 -c "
def extract_pixels(filename, frame_size):
with open(filename, 'rb') as f:
f.readline()
pixels = bytearray()
while True:
line = f.readline()
if not line:
break
if line.startswith(b'FRAME'):
pixels.extend(f.read(frame_size))
return bytes(pixels)
frame_size = ${frame_size}
t2 = extract_pixels('${dec_mux2}', frame_size)
t3 = extract_pixels('${dec_mux3}', frame_size)
assert t2 == t3, 'Redundant MSDO output differs from Test 1.1 (len %d vs %d)' % (len(t3), len(t2))
print('Test 1.3 passed: redundant MSDO output pixel-identical to Test 1.1')
" || return 1
# --- Test 1.4: Concatenate muxed with itself, verify == Test 1.1 x 2 ---
local bs_cat4="${AVM_TEST_OUTPUT_DIR}/mux4_concat.bin"
local dec_cat4="${AVM_TEST_OUTPUT_DIR}/mux4_decoded.yuv"
vlog " [Test 1.4] Concatenating Test 1.1 muxed with itself..."
cat "${bs_mux2}" "${bs_mux2}" > "${bs_cat4}" || return 1
vlog " [Test 1.4] Decoding concatenated bitstream..."
"${decoder}" --codec=av2 -o "${dec_cat4}" "${bs_cat4}" > /dev/null 2>&1 || return 1
vlog " [Test 1.4] Verifying pixel data == Test 1.1 x 2..."
python3 -c "
def extract_pixels(filename, frame_size):
with open(filename, 'rb') as f:
f.readline()
pixels = bytearray()
while True:
line = f.readline()
if not line:
break
if line.startswith(b'FRAME'):
pixels.extend(f.read(frame_size))
return bytes(pixels)
frame_size = ${frame_size}
t2 = extract_pixels('${dec_mux2}', frame_size)
t4 = extract_pixels('${dec_cat4}', frame_size)
assert t4 == t2 + t2, 'Pixel mismatch: expected Test 1.1 x 2 (len %d vs %d)' % (len(t4), len(t2) * 2)
print('Test 1.4 passed: concatenated muxed output == Test 1.1 x 2')
" || return 1
# --- Test 1.5: Concatenate Test 1.1 muxed + Test 1.2 muxed, verify pixels ---
local bs_cat4a="${AVM_TEST_OUTPUT_DIR}/mux4a_concat.bin"
local dec_cat4a="${AVM_TEST_OUTPUT_DIR}/mux4a_decoded.yuv"
vlog " [Test 1.5] Concatenating Test 1.1 muxed with Test 1.2 muxed..."
cat "${bs_mux2}" "${bs_mux2a}" > "${bs_cat4a}" || return 1
vlog " [Test 1.5] Decoding concatenated bitstream..."
"${decoder}" --codec=av2 -o "${dec_cat4a}" "${bs_cat4a}" > /dev/null 2>&1 || return 1
vlog " [Test 1.5] Verifying pixel data == Test 1.1 + Test 1.2..."
python3 -c "
def extract_pixels(filename, frame_size):
with open(filename, 'rb') as f:
f.readline()
pixels = bytearray()
while True:
line = f.readline()
if not line:
break
if line.startswith(b'FRAME'):
pixels.extend(f.read(frame_size))
return bytes(pixels)
frame_size = ${frame_size}
t2 = extract_pixels('${dec_mux2}', frame_size)
t2a = extract_pixels('${dec_mux2a}', frame_size)
t4a = extract_pixels('${dec_cat4a}', frame_size)
assert t4a == t2 + t2a, 'Pixel mismatch: expected Test 1.1 + Test 1.2 (len %d vs %d)' % (len(t4a), len(t2) + len(t2a))
print('Test 1.5 passed: concatenated output == Test 1.1 + Test 1.2')
" || return 1
}
multistream_three_and_four_stream_tests() {
local encoder="$(avm_tool_path avmenc)"
local decoder="$(avm_tool_path avmdec)"
local muxer="$(avm_tool_path stream_multiplexer)"
local w=64
local h=64
local frames=10
local frame_size=$((w * h * 3 / 2))
local enc_common="--obu --limit=${frames} --width=${w} --height=${h} --fps=30/1 --auto-alt-ref=1 --lag-in-frames=16 --gf-min-pyr-height=3 --gf-max-pyr-height=5 --cpu-used=8"
# Generate 7 inputs: 3 for Test 3.1, 4 for Test 3.2
vlog " Generating synthetic YUV inputs (7 streams)..."
python3 -c "
w, h, n = ${w}, ${h}, ${frames}
specs = [
('${AVM_TEST_OUTPUT_DIR}/mm_8a.yuv', 32, 8),
('${AVM_TEST_OUTPUT_DIR}/mm_8b.yuv', 36, 8),
('${AVM_TEST_OUTPUT_DIR}/mm_8c.yuv', 40, 8),
('${AVM_TEST_OUTPUT_DIR}/mm_9a.yuv', 130, 4),
('${AVM_TEST_OUTPUT_DIR}/mm_9b.yuv', 132, 4),
('${AVM_TEST_OUTPUT_DIR}/mm_9c.yuv', 134, 4),
('${AVM_TEST_OUTPUT_DIR}/mm_9d.yuv', 136, 4),
]
for path, base, step in specs:
with open(path, 'wb') as f:
for i in range(n):
f.write(bytes([base + i * step]) * (w * h))
f.write(bytes([128]) * (w * h // 2))
" || return 1
# Encode all 7 streams
vlog " Encoding 7 streams..."
local s
for s in 8a 8b 8c 9a 9b 9c 9d; do
"${encoder}" ${enc_common} \
-o "${AVM_TEST_OUTPUT_DIR}/mm_${s}.bin" \
"${AVM_TEST_OUTPUT_DIR}/mm_${s}.yuv" > /dev/null 2>&1 || return 1
done
# --- Test 3.1: Mux 3 streams (id 12, 5, 2) ---
local bs_mux8="${AVM_TEST_OUTPUT_DIR}/mm_mux8.bin"
local dec_mux8="${AVM_TEST_OUTPUT_DIR}/mm_dec_mux8.yuv"
vlog " [Test 3.1] Multiplexing 3 streams (stream_id 12, 5, 2)..."
"${muxer}" \
"${AVM_TEST_OUTPUT_DIR}/mm_8a.bin" 12 1 \
"${AVM_TEST_OUTPUT_DIR}/mm_8b.bin" 5 1 \
"${AVM_TEST_OUTPUT_DIR}/mm_8c.bin" 2 1 \
"${bs_mux8}" > /dev/null 2>&1 || return 1
vlog " [Test 3.1] Decoding muxed and individual streams..."
"${decoder}" --codec=av2 -o "${dec_mux8}" "${bs_mux8}" > /dev/null 2>&1 || return 1
for s in 8a 8b 8c; do
"${decoder}" --codec=av2 \
-o "${AVM_TEST_OUTPUT_DIR}/mm_dec_${s}.yuv" \
"${AVM_TEST_OUTPUT_DIR}/mm_${s}.bin" > /dev/null 2>&1 || return 1
done
vlog " [Test 3.1] Verifying per-stream ordering..."
python3 -c "
def extract_frames(f, fs):
with open(f, 'rb') as fh:
fh.readline()
frames = []
while True:
l = fh.readline()
if not l: break
if l.startswith(b'FRAME'): frames.append(fh.read(fs))
return frames
def check_sub(muxed, sf):
it = iter(muxed)
for f in sf:
while True:
m = next(it, None)
if m is None: return False
if m == f: break
return True
fs = ${frame_size}
d = '${AVM_TEST_OUTPUT_DIR}'
muxed = extract_frames('${dec_mux8}', fs)
streams = [extract_frames(d + '/mm_dec_%s.yuv' % s, fs) for s in ['8a', '8b', '8c']]
for i, s in enumerate(streams):
assert len(s) == len(set(s)), 'Stream %d has duplicate frames' % i
assert sorted(muxed) == sorted(sum(streams, [])), 'Frame sets differ'
for i, s in enumerate(streams):
assert check_sub(muxed, s), 'Stream %d not in correct order' % i
print('Test 3.1 passed: 3-stream mux, %d frames, per-stream ordering correct' % len(muxed))
" || return 1
# --- Test 3.2: Mux 4 streams (id 3, 8, 4, 9) ---
local bs_mux9="${AVM_TEST_OUTPUT_DIR}/mm_mux9.bin"
local dec_mux9="${AVM_TEST_OUTPUT_DIR}/mm_dec_mux9.yuv"
vlog " [Test 3.2] Multiplexing 4 streams (stream_id 3, 8, 4, 9)..."
"${muxer}" \
"${AVM_TEST_OUTPUT_DIR}/mm_9a.bin" 3 1 \
"${AVM_TEST_OUTPUT_DIR}/mm_9b.bin" 8 1 \
"${AVM_TEST_OUTPUT_DIR}/mm_9c.bin" 4 1 \
"${AVM_TEST_OUTPUT_DIR}/mm_9d.bin" 9 1 \
"${bs_mux9}" > /dev/null 2>&1 || return 1
vlog " [Test 3.2] Decoding muxed and individual streams..."
"${decoder}" --codec=av2 -o "${dec_mux9}" "${bs_mux9}" > /dev/null 2>&1 || return 1
for s in 9a 9b 9c 9d; do
"${decoder}" --codec=av2 \
-o "${AVM_TEST_OUTPUT_DIR}/mm_dec_${s}.yuv" \
"${AVM_TEST_OUTPUT_DIR}/mm_${s}.bin" > /dev/null 2>&1 || return 1
done
vlog " [Test 3.2] Verifying per-stream ordering..."
python3 -c "
def extract_frames(f, fs):
with open(f, 'rb') as fh:
fh.readline()
frames = []
while True:
l = fh.readline()
if not l: break
if l.startswith(b'FRAME'): frames.append(fh.read(fs))
return frames
def check_sub(muxed, sf):
it = iter(muxed)
for f in sf:
while True:
m = next(it, None)
if m is None: return False
if m == f: break
return True
fs = ${frame_size}
d = '${AVM_TEST_OUTPUT_DIR}'
muxed = extract_frames('${dec_mux9}', fs)
streams = [extract_frames(d + '/mm_dec_%s.yuv' % s, fs) for s in ['9a', '9b', '9c', '9d']]
for i, s in enumerate(streams):
assert len(s) == len(set(s)), 'Stream %d has duplicate frames' % i
assert sorted(muxed) == sorted(sum(streams, [])), 'Frame sets differ'
for i, s in enumerate(streams):
assert check_sub(muxed, s), 'Stream %d not in correct order' % i
print('Test 3.2 passed: 4-stream mux, %d frames, per-stream ordering correct' % len(muxed))
" || return 1
# --- Test 3.3: cat(mux8, mux9) -> verify strict concat order ---
local bs_t10="${AVM_TEST_OUTPUT_DIR}/mm_t10_concat.bin"
local dec_t10="${AVM_TEST_OUTPUT_DIR}/mm_t10_decoded.yuv"
vlog " [Test 3.3] Concatenating mux8 with mux9..."
cat "${bs_mux8}" "${bs_mux9}" > "${bs_t10}" || return 1
vlog " [Test 3.3] Decoding..."
"${decoder}" --codec=av2 -o "${dec_t10}" "${bs_t10}" > /dev/null 2>&1 || return 1
vlog " [Test 3.3] Verifying strict concatenation order..."
python3 -c "
def extract_pixels(f, fs):
with open(f, 'rb') as fh:
fh.readline()
p = bytearray()
while True:
l = fh.readline()
if not l: break
if l.startswith(b'FRAME'): p.extend(fh.read(fs))
return bytes(p)
fs = ${frame_size}
d = extract_pixels('${dec_t10}', fs)
m8 = extract_pixels('${dec_mux8}', fs)
m9 = extract_pixels('${dec_mux9}', fs)
assert d == m8 + m9, 'Test 3.3: pixel mismatch'
print('Test 3.3 passed: cat(mux8, mux9) strict concatenation order correct')
" || return 1
# --- Test 3.4: cat(mux9, mux8) -> verify strict concat order ---
local bs_t10a="${AVM_TEST_OUTPUT_DIR}/mm_t10a_concat.bin"
local dec_t10a="${AVM_TEST_OUTPUT_DIR}/mm_t10a_decoded.yuv"
vlog " [Test 3.4] Concatenating mux9 with mux8..."
cat "${bs_mux9}" "${bs_mux8}" > "${bs_t10a}" || return 1
vlog " [Test 3.4] Decoding..."
"${decoder}" --codec=av2 -o "${dec_t10a}" "${bs_t10a}" > /dev/null 2>&1 || return 1
vlog " [Test 3.4] Verifying strict concatenation order..."
python3 -c "
def extract_pixels(f, fs):
with open(f, 'rb') as fh:
fh.readline()
p = bytearray()
while True:
l = fh.readline()
if not l: break
if l.startswith(b'FRAME'): p.extend(fh.read(fs))
return bytes(p)
fs = ${frame_size}
d = extract_pixels('${dec_t10a}', fs)
m9 = extract_pixels('${dec_mux9}', fs)
m8 = extract_pixels('${dec_mux8}', fs)
assert d == m9 + m8, 'Test 3.4: pixel mismatch'
print('Test 3.4 passed: cat(mux9, mux8) strict concatenation order correct')
" || return 1
}
multistream_tests="multistream_mux_tests multistream_cmvs_tests multistream_three_and_four_stream_tests"
run_tests multistream_tests_verify_environment "${multistream_tests}"