. "$BST_LIB" # BST-TIMEOUT: 300 # measured 43s over 136 cases; ~30 execs of a wasm module, # # each a fresh instantiation. 300 is 7x that, which is more # # headroom than the corpus norm, but it is kept rather than # # tightened for a reason that is about callers and not about # # liba52: 300 is the value the ticket gate and the nightly # # already pass, so it costs them nothing, and it is the net # # for a bare `brintos-selftest` whose 60s default would leave # # under 2x over a figure measured on an idle host. # liba52 0.7.4 (poky recipes-multimedia/liba52) -- the ATSC A/52 (AC-3) # surround-sound stream decoder: liba52.a, four headers under # /usr/include/a52dec, the `a52dec` command-line decoder and the `extract_a52` # MPEG demuxer. `a52dec -o wav in.ac3 > out.wav` writes a RIFF/WAVE file and # drives no sound card, so the whole of this suite runs offline with no audio # device. # # THE ORACLE IS A STORED REFERENCE DECODE, AND THAT WAS MEASURED BEFORE IT WAS # RELIED ON. Two neighbouring codec suites (57-libvorbis, 57-speexdsp) refuse a # stored cross-machine digest and are right to; liba52 is a different case. It # touches libm in exactly ONE place -- a52_imdct_init() filling the window and # the FFT twiddle tables with sqrt/cos/sin -- and every double it computes there # is stored into a `sample_t`, which is float unless --enable-double (not # passed), so the cast absorbs a last-ulp disagreement between two libm # implementations. There is no -ffast-math in the tree and wasm32 has no FMA to # contract into. The measurement: the same source built with gcc 9.5 -O2, clang # 10 -O2 and gcc -O0 produces BYTE-IDENTICAL decoded PCM for all four fixtures. # So each fixture ships its reference decode and the gate is `cmp`. # # When cmp says equal, max|delta| is 0 and the differing-sample count is 0 BY # CONSTRUCTION, and the two graded cases say so without paying for an od/awk # pass over 6144 frames. When it says unequal they run for real and bound the # damage: <= 1 LSB, and <= 64 samples. A red on the graded pair is an arithmetic # fault; a red on cmp alone with the graded pair green is last-bit drift in the # wasm32 libm and should be re-referenced, not chased. # # WHAT THE FIXTURES ARE. All four are synthetic, built by src/liba52_mkfixtures.py # and encoded with aften 0.0.8 at -b 448 -dynrng 5 -dnorm 31 -m 0 -pad 0; that # file states why none of those is a default and why 448 rather than the maximum. # Each is 4 AC-3 frames = 6144 samples = 128 ms at 48 kHz, and every one decodes # to the SAME geometry -- 2 channels, 48000 Hz, 16-bit, 24576 bytes of data -- # because the wav driver opens with a fixed A52_STEREO whatever the stream is. # # liba52-mono48 1/0 480 Hz @ 9000 # liba52-stereo48 2/0 L 750 Hz @ 9000 / R 500 Hz @ 3000 (+ .mpg) # liba52-surr48 3/2 + LFE L 750 / R 500 / C 480 / Ls 320 / Rs 200, # and a 60 Hz LFE that is the loudest of the six # liba52-trans48 1/0 480 Hz gated 256-on/1536, -s 1 # # EACH OF THE FOUR EXISTS FOR A CODE PATH THE OTHERS DO NOT REACH. # # * mono48 pins the wav driver's fixed output geometry: a 1-channel stream # comes back as two BIT-IDENTICAL channels. Exact, no tolerance, no # reference file needed -- liba52/mono48-upmatrix. # * surr48 is the only fixture that drives downmix.c. liba52 downmixes in the # FREQUENCY domain, before the IMDCT, so even a 3/2+LFE stream costs two # inverse transforms per block and it is the coefficients that the extra # channels exercise. The five tones let the suite ask WHERE EACH CHANNEL # LANDED rather than only that the decode ran. # * trans48 is the only fixture that reaches a52_imdct_256(). a52dec picks the # 256-point transform per block from blksw in the bitstream and an encoder # sets blksw only on a transient, so a corpus of pure tones leaves that half # of imdct.c unexecuted -- upstream's own test list says as much of its "thx" # stream ("that one never uses split imdct"). Measured on this fixture: 4 # blocks of 256 against 20 of 512, so it exercises both transforms AND the # overlap-add that carries delay[] across a change of transform. # * stereo48 is the plain case and the one wrapped in an MPEG-2 program stream # for extract_a52. # # THE SIGNAL CASES USE AN INTEGER PERIOD-FOLD AND NO TRIG AT ALL. Every tone # frequency divides 48000 exactly (750->64 samples, 500->96, 480->100, 320->150, # 200->240), so folding a 4800-frame window into P bins and taking the mean # square of the bin averages isolates everything periodic at P and averages the # rest toward zero. Reported as parts-per-thousand of the window's own mean # square it is a ratio of integer sums: no sqrt, no sin, no cos. That is # deliberate. 57-speexdsp.test found awk's math support silently absent under # busybox awk, where sqrt/sin/cos yield an EMPTY number and take the whole # expression with them -- a red carrying a blank where the measurement should be, # indistinguishable from a port defect. Nothing here can produce one. # # THE PERIODS ARE A DIVISIBILITY ANTICHAIN, AND THAT IS THE WHOLE DESIGN. # fold(P) keeps every component whose period DIVIDES P, so a tone at twice # another's frequency is kept by the lower one's fold as well and the two cannot # be told apart. The first cut of these fixtures used 480/960/240 and would have # read ~1000 ppt on BOTH channels of the stereo fixture -- a decoder that # duplicated one channel would have passed every case. None of 64, 96, 100, 150, # 240 divides any other, so each channel has a fold that isolates it and every # other channel has a fold that must sit at the floor. All five divide 4800, and # so does the control, which is what makes the fold exact rather than # approximate. # # 75 IS THE NEGATIVE CONTROL AND IT IS NOT ARBITRARY. Folding at 75 keeps only # multiples of 640 Hz (48000/75), and none of 750, 500, 480, 320, 200 or 60 is # one -- so a decode that turned into broadband noise lifts fold(75) off the # floor while every positive case could still look plausible. # # THE LFE IS ASSERTED BY ARITHMETIC, NOT BY A SECOND FIXTURE. The wav driver # opens with A52_STEREO, which does not carry A52_LFE, so parse.c takes its # `/* just skip the LFE coefficients */` branch: it reads them into scratch to # keep the bitstream reader in sync and never transforms or downmixes them. The # 60 Hz LFE tone is the loudest channel in surr48 precisely so that this is # checkable -- liba52/surr48-{L,R}-accounted require the three folds that ARE # expected on each side to sum to 1000 ppt, i.e. to account for the whole window. # A leak of the loudest channel cannot hide inside that. (The obvious control -- # the same stream with a silent LFE, decoding byte-identically -- was built and # discarded: at CBR the encoder spends the freed LFE bits on the other channels, # so the two streams differ in far more than the LFE and the comparison measures # aften, not liba52.) # # READING A RED. A wrong *-data-size, *-channels, *-rate or *-frames is a # decoder or packaging fault and nothing else; those are integer and cannot # drift. A red on liba52/*-pcm-exact alone is discussed above. A red on # liba52/no-oss-driver or liba52/default-driver-is-wav means the bbappend's # --disable-oss stopped taking, and every decode case can still pass while it is # red -- which is why they are asserted rather than assumed. A red on the fold # cases with the byte comparisons green is impossible and means this suite is # broken. require a52dec liba52/present tmp=${TMPDIR:-/tmp}/bst-liba52.$$ mkdir -p "$tmp" 2>/dev/null size() { wc -c < "$1" 2>/dev/null | tr -d ' '; } md5of() { md5sum "$1" 2>/dev/null | cut -d' ' -f1; } have_md5=0; command -v md5sum >/dev/null 2>&1 && have_md5=1 # --------------------------------------------------------------- what shipped --- check liba52/extract-present test -x /usr/bin/extract_a52 check liba52/lib-static test -f /usr/lib/liba52.a check liba52/header-a52 test -f /usr/include/a52dec/a52.h check liba52/header-attributes test -f /usr/include/a52dec/attributes.h check liba52/header-audio-out test -f /usr/include/a52dec/audio_out.h check liba52/header-mm-accel test -f /usr/include/a52dec/mm_accel.h bst_lib_shape liba52/static-only "/usr/lib/liba52*.so*" b=$(a52dec -o nosuchdriver /dev/null 2>&1) case "$b" in *"a52dec-0.7.4"*) bst_ok liba52/banner ;; *) bst_notok liba52/banner "$(_bst_1line "$b")" ;; esac # ------------------------------------------------------ the driver table ------ # These three are the only cases that can see the bbappend's --disable-oss, and # every decode case below would still pass with them red: libao/configure.incl # turns the OSS backend on from a *-linux* HOST TRIPLE match alone, our triple is # wasm32-hwjs-linux, and glibc ships sys/soundcard.h -- so audio_out_oss.c # compiles fine against a target with no sound card and pushes "oss" to the front # of audio_out.c's table, where a52dec's handle_args() picks it up as the default # for a bare invocation. case "$b" in *oss*) bst_notok liba52/no-oss-driver "oss backend is compiled in: $(_bst_1line "$b")" ;; *) bst_ok liba52/no-oss-driver ;; esac first=$(printf '%s\n' "$b" | awk '/audio output mode/{getline; gsub(/[ \t]/,"",$0); print; exit}') check_eq liba52/default-driver-is-wav wav "$first" # ------------------------------------------------------------------ payloads --- # Pinned BEFORE anything is decoded. Without this a later mismatch has two # indistinguishable causes -- the decoder changed, or the payload did -- and the # wrong one gets reported. pin() { # pin if [ "$have_md5" -eq 1 ]; then check_eq "$1" "$2" "$(md5of "$3")" else bst_skip "$1" "no md5sum on this image"; fi } pin liba52/fixture-mono48-digest 1c8bd681b2ebc02b2641d44f9b0eb76b "$BST_DATA/liba52-mono48.ac3" pin liba52/fixture-stereo48-digest acc2351f78be64480ceb5b4e83c6e79a "$BST_DATA/liba52-stereo48.ac3" pin liba52/fixture-surr48-digest 9a82c3d3abadc6bc3d80f8dbd48ba594 "$BST_DATA/liba52-surr48.ac3" pin liba52/fixture-trans48-digest 9c131ea29e5a2f17e5c38f92366e3ef3 "$BST_DATA/liba52-trans48.ac3" pin liba52/fixture-ps-digest a222159089a2370080d9ab881b25e75c "$BST_DATA/liba52-stereo48.mpg" pin liba52/reference-mono48-digest a0a30138e4e4604f80836b19a94a08c6 "$BST_DATA/liba52-mono48.ref.wav" pin liba52/reference-stereo48-digest 1f192b662f7016458912ebee987853ce "$BST_DATA/liba52-stereo48.ref.wav" pin liba52/reference-surr48-digest 3147aab16df7d4e0c88b6e106af67fff "$BST_DATA/liba52-surr48.ref.wav" pin liba52/reference-trans48-digest 2ad13012e43a4896be50f8a0a725a715 "$BST_DATA/liba52-trans48.ref.wav" for f in mono48 stereo48 surr48 trans48; do if [ ! -f "$BST_DATA/liba52-$f.ac3" ] || [ ! -f "$BST_DATA/liba52-$f.ref.wav" ]; then bst_notok liba52/payloads "missing $BST_DATA/liba52-$f.ac3 or its reference -- remaining cases not run" rm -rf "$tmp"; exit 0 fi done # -------------------------------------------------------- header field reads --- # The 44-byte canonical RIFF/WAVE header, read one byte at a time so this works # under any od the image ships. Fields: channels @22 (u16le), sample rate @24 # (u32le), bits @34 (u16le), RIFF size @4 (u32le), data size @40 (u32le). # # THE THREE u32 FIELDS ARE PRINTED WITH %.0f AND NOT %u, WHICH IS NOT A STYLE # CHOICE. Three of the values this suite asserts are above 2^31 -- the two # unpatched length placeholders and the never-filled-in sample rate -- and # BUSYBOX AWK, which is the awk on this image, saturates both %u and %d at the # signed 32-bit boundary: it prints 4294967292 as 2147483648 and 4294967295 as # -2147483648, while gawk prints both correctly. The arithmetic is fine (busybox # awk carries the value as a double); only the conversion is lossy. %.0f goes # through no integer conversion at all and is byte-identical to %d for every # small value here. The dash dry run cannot show this -- it was found by running # the suite under `busybox ash` as well, which is why both are run. hdr() { # hdr -> "magic wave channels rate bits riffsize datasize" od -An -v -tu1 -N 44 "$1" 2>/dev/null | awk ' { for (i = 1; i <= NF; i++) b[n++] = $i } END { printf "%c%c%c%c %c%c%c%c %d %.0f %d %.0f %.0f\n", b[0], b[1], b[2], b[3], b[8], b[9], b[10], b[11], b[22] + 256*b[23], b[24] + 256*b[25] + 65536*b[26] + 16777216*b[27], b[34] + 256*b[35], b[4] + 256*b[5] + 65536*b[6] + 16777216*b[7], b[40] + 256*b[41] + 65536*b[42] + 16777216*b[43] }' } # fold # -> " " fold() { _r=$1; _c=$2; _s=$3; _n=$4; shift 4 dd if="$_r" bs=4 skip="$_s" count="$_n" 2>/dev/null \ | od -An -v -td2 -w4 2>/dev/null \ | awk -v c="$((_c + 1))" -v plist="$*" ' BEGIN { np = split(plist, p, " ") } { v = $c; n++; s += v*v; for (i = 1; i <= np; i++) acc[i, (n-1) % p[i]] += v } END { if (!n || !s) { printf "0"; for (i = 1; i <= np; i++) printf " 0"; printf "\n"; exit } tot = s / n printf "%d", int(tot/1000 + 0.5) for (i = 1; i <= np; i++) { k = int(n / p[i]); fm = 0 for (j = 0; j < p[i]; j++) { m = acc[i, j] / k; fm += m*m } fm /= p[i] printf " %d", int(1000*fm/tot + 0.5) } printf "\n" }' } inrange() { # inrange if [ -n "$4" ] && [ "$4" -ge "$2" ] 2>/dev/null && [ "$4" -le "$3" ] 2>/dev/null then bst_ok "$1"; else bst_notok "$1" "$5 = ${4:-} (want $2..$3)"; fi } ndiff() { # ndiff -> count of frames whose L != R od -An -v -td2 -w4 "$1" 2>/dev/null | awk '$1!=$2{n++} END{print n+0}' } # ------------------------------------------------------------- the decodes ---- # Every fixture decodes to the same geometry, so the expectations are constants: # 2 channels, 48000 Hz, 16 bit, 24576 bytes of data, 4 AC-3 frames. decode() { _n=$1 _src=$BST_DATA/liba52-$_n.ac3 _wav=$tmp/$_n.wav _raw=$tmp/$_n.raw _e=$(a52dec -o wav "$_src" 2>&1 > "$_wav"); _rc=$? check_eq liba52/$_n-rc 0 "$_rc" check_eq liba52/$_n-wav-size 24620 "$(size "$_wav")" set -- $(hdr "$_wav") check_eq liba52/$_n-riff "RIFF" "$1" check_eq liba52/$_n-wave "WAVE" "$2" check_eq liba52/$_n-channels "2" "$3" check_eq liba52/$_n-rate "48000" "$4" check_eq liba52/$_n-bits "16" "$5" check_eq liba52/$_n-riff-size "24612" "$6" check_eq liba52/$_n-data-size "24576" "$7" # what the decoder reports on the way out -- the ticket's own criterion. The # timing figures on the same line are not asserted; the frame count is. case "$_e" in *"4 frames decoded"*) bst_ok liba52/$_n-frames ;; *) bst_notok liba52/$_n-frames "$(_bst_1line "$_e")" ;; esac tail -c +45 "$_wav" > "$_raw" 2>/dev/null check_eq liba52/$_n-payload-size 24576 "$(size "$_raw")" _ref=$BST_DATA/liba52-$_n.ref.wav if cmp -s "$_wav" "$_ref"; then bst_ok liba52/$_n-pcm-exact bst_ok liba52/$_n-pcm-max-deviation bst_ok liba52/$_n-pcm-exact-fraction echo "==LIBA52-AGREE $_n byte-identical to reference==" else bst_notok liba52/$_n-pcm-exact "decoded WAV differs from liba52-$_n.ref.wav" tail -c +45 "$_ref" > "$tmp/ref.raw" 2>/dev/null od -An -v -td2 -w2 "$_raw" > "$tmp/a.txt" 2>/dev/null od -An -v -td2 -w2 "$tmp/ref.raw" > "$tmp/b.txt" 2>/dev/null set -- $(awk 'NR==FNR{a[FNR]=$1;next} {d=a[FNR]-$1; if(d<0)d=-d; if(d>m)m=d; if(d)n++} END{print m+0, n+0, FNR}' "$tmp/a.txt" "$tmp/b.txt") echo "==LIBA52-AGREE $_n max=${1:-?} ndiff=${2:-?} of ${3:-?}==" if [ -n "$1" ] && [ "$1" -le 1 ] 2>/dev/null; then bst_ok liba52/$_n-pcm-max-deviation else bst_notok liba52/$_n-pcm-max-deviation "max |delta| ${1:-unknown} LSB > 1"; fi if [ -n "$2" ] && [ "$2" -le 64 ] 2>/dev/null; then bst_ok liba52/$_n-pcm-exact-fraction else bst_notok liba52/$_n-pcm-exact-fraction "${2:-unknown} of ${3:-?} samples differ, bound 64"; fi fi } decode mono48 decode stereo48 decode surr48 decode trans48 # -------------------------------------------------------- output geometry ----- # A mono stream decoded to two BIT-IDENTICAL channels, and the gated mono one # likewise. Exact, no tolerance, no reference file. check_eq liba52/mono48-upmatrix 0 "$(ndiff "$tmp/mono48.raw")" check_eq liba52/trans48-upmatrix 0 "$(ndiff "$tmp/trans48.raw")" # ...and the two multi-channel streams that must NOT collapse that way. Measured # 6083 and 6136 of 6144 frames; the shortfall is the samples where two sines # cross zero together and the encoder's priming, which are silent in both # channels. The bound separates "two different signals" from the up-matrix's # exact 0 -- it is not a tight fit to the measurement. for f in stereo48 surr48; do n=$(ndiff "$tmp/$f.raw") if [ -n "$n" ] && [ "$n" -ge 3000 ] 2>/dev/null; then bst_ok liba52/$f-channels-differ else bst_notok liba52/$f-channels-differ "only ${n:-} of 6144 frames have L != R (want >= 3000)"; fi done # ------------------------------------------------------------- the signal ----- # Window: 4800 frames from frame 600. 4800 is a multiple of every period used, # which is what makes the fold exact. Periods: 64=750 Hz, 96=500, 100=480, # 150=320, 240=200, 75=control. # mono48: one tone at 480 Hz, present identically in both output channels. set -- $(fold "$tmp/mono48.raw" 0 600 4800 100 64 96 75) inrange liba52/mono48-level 38000 43000 "$1" "window mean-square/1000" inrange liba52/mono48-fold480 950 1000 "$2" "fold(100) ppt" inrange liba52/mono48-fold750-absent 0 20 "$3" "fold(64) ppt" inrange liba52/mono48-control 0 20 "$4" "fold(75) ppt" # stereo48: each channel isolated by its own fold, and blind to the other's. set -- $(fold "$tmp/stereo48.raw" 0 600 4800 64 96 75) inrange liba52/stereo48-L-level 38000 43000 "$1" "L window mean-square/1000" inrange liba52/stereo48-L-fold750 950 1000 "$2" "L fold(64) ppt" inrange liba52/stereo48-L-fold500-absent 0 20 "$3" "L fold(96) ppt" inrange liba52/stereo48-L-control 0 20 "$4" "L fold(75) ppt" set -- $(fold "$tmp/stereo48.raw" 1 600 4800 96 64 75) inrange liba52/stereo48-R-level 4000 5000 "$1" "R window mean-square/1000" inrange liba52/stereo48-R-fold500 950 1000 "$2" "R fold(96) ppt" inrange liba52/stereo48-R-fold750-absent 0 20 "$3" "R fold(64) ppt" inrange liba52/stereo48-R-control 0 20 "$4" "R fold(75) ppt" # surr48: this is the downmix, stated channel by channel. Left must carry the # front-left tone, the centre, and the left surround -- and NOT the front-right # or right-surround tones. Right is the mirror image. Measured 581/290/129 ppt # on each side, which is the -3 dB centre and -3 dB surround mix acting on # amplitudes of 6000/6000/4000. set -- $(fold "$tmp/surr48.raw" 0 600 4800 64 100 150 96 240 75) inrange liba52/surr48-L-level 4800 5900 "$1" "L window mean-square/1000" inrange liba52/surr48-L-front 520 640 "$2" "L fold(64) ppt -- front left" inrange liba52/surr48-L-centre 250 330 "$3" "L fold(100) ppt -- centre" inrange liba52/surr48-L-surround 100 160 "$4" "L fold(150) ppt -- left surround" inrange liba52/surr48-L-no-front-right 0 20 "$5" "L fold(96) ppt -- front RIGHT must be absent" inrange liba52/surr48-L-no-right-surround 0 20 "$6" "L fold(240) ppt -- right surround must be absent" inrange liba52/surr48-L-control 0 20 "$7" "L fold(75) ppt" # The three folds that ARE expected must account for the whole window. This is # the LFE assertion: the 60 Hz LFE tone is the loudest channel in the stream and # a leak of it into the stereo downmix cannot fit inside a 1000 ppt total. lsum=$(( $2 + $3 + $4 )) inrange liba52/surr48-L-accounted 960 1040 "$lsum" "L fold(64)+fold(100)+fold(150) ppt" set -- $(fold "$tmp/surr48.raw" 1 600 4800 96 100 240 64 150 75) inrange liba52/surr48-R-level 4800 5900 "$1" "R window mean-square/1000" inrange liba52/surr48-R-front 520 640 "$2" "R fold(96) ppt -- front right" inrange liba52/surr48-R-centre 250 330 "$3" "R fold(100) ppt -- centre" inrange liba52/surr48-R-surround 100 160 "$4" "R fold(240) ppt -- right surround" inrange liba52/surr48-R-no-front-left 0 20 "$5" "R fold(64) ppt -- front LEFT must be absent" inrange liba52/surr48-R-no-left-surround 0 20 "$6" "R fold(150) ppt -- left surround must be absent" inrange liba52/surr48-R-control 0 20 "$7" "R fold(75) ppt" rsum=$(( $2 + $3 + $4 )) inrange liba52/surr48-R-accounted 960 1040 "$rsum" "R fold(96)+fold(100)+fold(240) ppt" # trans48: the fold cases are DELIBERATELY WEAK here and are not the evidence. # A gated tone is broadband by construction, so energy lands in every fold and # the control does not sit at zero -- measured 161 ppt at the tone against 4 ppt # at the control. What this fixture is for is a52_imdct_256(), and its assertion # is liba52/trans48-pcm-exact above. These two bound the shape so a decode that # collapsed to silence or to noise is still caught. set -- $(fold "$tmp/trans48.raw" 0 600 4800 100 75) inrange liba52/trans48-level 30000 35000 "$1" "window mean-square/1000" inrange liba52/trans48-fold480 120 210 "$2" "fold(100) ppt" inrange liba52/trans48-control 0 30 "$3" "fold(75) ppt" # ---------------------------------------------------------- determinism ------- # What makes a stored reference an oracle at all, so it is asserted, not assumed. a52dec -o wav "$BST_DATA/liba52-surr48.ac3" > "$tmp/again.wav" 2>/dev/null check liba52/determinism cmp -s "$tmp/surr48.wav" "$tmp/again.wav" # ------------------------------------------------- the default output driver --- # With --disable-oss the wav driver is drivers[0], so a bare invocation writes # the same RIFF/WAVE file that -o wav does. This is the behavioural half of the # driver-table cases above. a52dec "$BST_DATA/liba52-stereo48.ac3" > "$tmp/bare.wav" 2>/dev/null check liba52/bare-equals-wav cmp -s "$tmp/bare.wav" "$tmp/stereo48.wav" # ------------------------------------------------------------ stdout + seek ---- # wav_close() patches the two length fields with `fseek (stdout, 0, SEEK_SET)`. # Redirected to a file that succeeds; through a PIPE it fails, wav_close returns # early, and the lengths stay at the placeholders 0xFFFFFFFC / 0xFFFFFFD8 -- the # streaming convention, not garbage, and identical on the native x86-64 build. # Both halves are pinned: the body is the property anyone piping a52dec depends # on, and the header is upstream behaviour a reader comparing the two files would # otherwise report as a port defect. The file case is also this suite's only # exercise of a backwards fseek on the virtiofs root. a52dec -o wav "$BST_DATA/liba52-stereo48.ac3" 2>/dev/null | cat > "$tmp/pipe.wav" check_eq liba52/pipe-size 24620 "$(size "$tmp/pipe.wav")" tail -c +45 "$tmp/pipe.wav" > "$tmp/pipe.raw" 2>/dev/null check liba52/stdout-body cmp -s "$tmp/pipe.raw" "$tmp/stereo48.raw" set -- $(hdr "$tmp/pipe.wav") check_eq liba52/pipe-header-unpatched "4294967292" "$6" set -- $(hdr "$tmp/stereo48.wav") check_eq liba52/file-header-patched "24612" "$6" # ---------------------------------------------------------- malformed input ---- # Each of these pins CURRENT upstream behaviour. Two are wrong in upstream's own # terms and are pinned anyway, because an unpinned wrong is indistinguishable # from a port defect the first time someone meets it. check_rc liba52/missing-input-rc 1 a52dec -o wav "$tmp/no-such-file.ac3" check_rc liba52/bad-driver-rc 1 a52dec -o nosuchdriver "$BST_DATA/liba52-mono48.ac3" # An empty input exits 0 and still writes a 44-byte WAV -- because wav_close() # writes the header whether or not wav_play() ever ran, and the sample-rate field # it writes is the never-filled-in placeholder 0xFFFFFFFF. A caller checking only # the status believes it has a valid WAV declaring a 4294967295 Hz rate. : > "$tmp/empty.ac3" a52dec -o wav "$tmp/empty.ac3" > "$tmp/empty.wav" 2>/dev/null; rc=$? check_eq liba52/empty-rc 0 "$rc" check_eq liba52/empty-size 44 "$(size "$tmp/empty.wav")" set -- $(hdr "$tmp/empty.wav") check_eq liba52/empty-rate-placeholder "4294967295" "$4" # Data that is not AC-3 at all: same shape, and no syncword is ever found. dd if=/dev/zero bs=1 count=200 2>/dev/null | tr '\0' 'X' > "$tmp/junk.ac3" a52dec -o wav "$tmp/junk.ac3" > "$tmp/junk.wav" 2>/dev/null; rc=$? check_eq liba52/junk-rc 0 "$rc" check_eq liba52/junk-size 44 "$(size "$tmp/junk.wav")" # A truncated stream decodes as many whole frames as it has and exits 0, with no # diagnostic. The output length is deterministic, so it is what is asserted: # 3000 of 7168 bytes is one 1792-byte frame, 5000 is two. head -c 3000 "$BST_DATA/liba52-stereo48.ac3" > "$tmp/t1.ac3" 2>/dev/null a52dec -o wav "$tmp/t1.ac3" > "$tmp/t1.wav" 2>/dev/null; rc=$? check_eq liba52/truncated1-rc 0 "$rc" check_eq liba52/truncated1-size 6188 "$(size "$tmp/t1.wav")" head -c 5000 "$BST_DATA/liba52-stereo48.ac3" > "$tmp/t2.ac3" 2>/dev/null a52dec -o wav "$tmp/t2.ac3" > "$tmp/t2.wav" 2>/dev/null check_eq liba52/truncated2-size 12332 "$(size "$tmp/t2.wav")" # ------------------------------------------------------------- extract_a52 ----- # The second of the ticket's two named tools. There is no MPEG multiplexer in # this image, on the build host, or in a52dec's own tarball -- upstream's # regression corpus is a separate download of DVD trailers -- so # src/liba52_mkfixtures.py builds the program stream itself, from the shape # extract_a52.c's own demuxer expects: an MPEG-2 pack header, private_stream_1 # PES packets each carrying the 4-byte DVD substream header, and a program end # code. Without it this tool could only be asserted to print its usage. if [ -f "$BST_DATA/liba52-stereo48.mpg" ]; then extract_a52 "$BST_DATA/liba52-stereo48.mpg" > "$tmp/x.ac3" 2>/dev/null check_eq liba52/extract-rc 0 "$?" check_eq liba52/extract-size 7168 "$(size "$tmp/x.ac3")" # The demuxed elementary stream is the one that went in, byte for byte. check liba52/extract-roundtrip cmp -s "$tmp/x.ac3" "$BST_DATA/liba52-stereo48.ac3" # ...and end to end: demux, decode, and land on the same reference the direct # decode does. a52dec's stdout here is a file, so the header is patched. extract_a52 "$BST_DATA/liba52-stereo48.mpg" 2>/dev/null \ | a52dec -o wav > "$tmp/pipeline.wav" 2>/dev/null check liba52/extract-pipeline cmp -s "$tmp/pipeline.wav" "$BST_DATA/liba52-stereo48.ref.wav" # Negative control: track 0x81 is not in this stream, so nothing comes out. # Without it, a demuxer that ignored -s entirely would pass the case above. extract_a52 -s 1 "$BST_DATA/liba52-stereo48.mpg" > "$tmp/x1.ac3" 2>/dev/null check_eq liba52/extract-wrong-track 0 "$(size "$tmp/x1.ac3")" else bst_notok liba52/extract-payload "missing $BST_DATA/liba52-stereo48.mpg" fi check_rc liba52/extract-missing-rc 1 extract_a52 "$tmp/no-such-file.mpg" # An empty program stream yields an empty elementary stream and exits 0. : > "$tmp/empty.mpg" extract_a52 "$tmp/empty.mpg" > "$tmp/empty.es" 2>/dev/null check_eq liba52/extract-empty-rc 0 "$?" check_eq liba52/extract-empty-size 0 "$(size "$tmp/empty.es")" # The demuxer's refusal path: a start code carrying a stream id below 0xB9 is a # video elementary stream, not a system stream, and extract_a52 says so and exits # 1 rather than passing bytes through. # # THIS INPUT IS SYNTHESISED RATHER THAN REUSING THE .ac3, AND THAT IS THE POINT. # The case first shipped as `extract_a52 ` and the dry run caught # it: whether a raw A/52 stream is refused depends entirely on which start code # its compressed bytes happen to contain first. This fixture holds 19 of them # and the first is 0x000001C0 -- an id above 0xB9, so the demuxer SKIPS it and # exits 0 -- while the 640 kbps fixture the suite was prototyped against held # 0x00000160 and was refused. Both are correct behaviour; neither is a property # of extract_a52, and asserting either would have pinned an accident of the # encoder's output that any re-cut of the fixtures could flip. printf '\000\000\001\140\000\010\000\000\000\000\000\000\000\000' > "$tmp/video.ps" e=$(extract_a52 "$tmp/video.ps" 2>&1 >/dev/null); rc=$? check_eq liba52/extract-video-rc 1 "$rc" case "$e" in *"not system stream"*) bst_ok liba52/extract-video-diagnostic ;; *) bst_notok liba52/extract-video-diagnostic "$(_bst_1line "$e")" ;; esac rm -rf "$tmp"