. "$BST_LIB" # libvorbis 1.3.7 (poky recipes-multimedia/libvorbis) -- the reference Ogg Vorbis # encoder and decoder, driven through upstream's own example programs, which this # image installs as vorbis-encoder-example, vorbis-decoder-example and # vorbisfile-example (see recipes-multimedia/libvorbis/libvorbis_%.bbappend for # why a library-only port ships binaries at all, and why they are renamed). # # WHAT A LOSSY CODEC CAN AND CANNOT BE ASSERTED TO DO. flac next door is checked # by `cmp`: lossless means the bytes come back. Vorbis discards information by # design, so there is no equality to test and the temptation is to fall back to # "it exited 0", which would pass for an encoder that emitted silence. Three # separate oracles are used here instead, and each catches something the others # do not: # # 1. DETERMINISM. Vorbis is lossy but it is not approximate: for a given input # and a given build, the encoder emits one exact bitstream and the decoder # reproduces one exact PCM buffer from it. Two encodes must digest # identically and two decodes must digest identically. This is the ticket's # "a digest of the decoded PCM settles it", applied where a digest is # actually a valid oracle -- see the note below on why it is NOT applied as # a stored cross-machine reference. # 2. BOUNDED DEVIATION. The decoded samples are compared against the fixture's # arithmetically -- peak absolute error and RMS error -- so a codec that # round-trips deterministically to the WRONG audio still goes red. Measured # natively at maxabs 689 and RMS 144.5 against a 16383 peak (0.88% of peak); # the bounds below carry ~3x headroom over that. # 3. SIGNAL LEVEL. Error bounds alone would be satisfied by a decoder that # output silence only if the fixture were quiet, but they say nothing about # gain. The RMS of the decoded signal must land within 10% of the RMS of the # input signal, which is the case that fails loudly for silence, for a # channel swap that zeroes one side, and for a 16/8-bit sample-width fault. # # WHY NO STORED REFERENCE DIGEST, THOUGH THE DECODER IS BIT-EXACT. It would be # the strongest assertion available and it was deliberately rejected: a digest # recorded on one machine and asserted on another is a claim that the two # produce bit-identical floating-point results, and two things here make that # claim false for reasons that are not defects. libvorbis's configure.ac selects # CFLAGS by $host and gives *86-*-linux* an -ffast-math -mno-ieee-fp line that # wasm32-hwjs-linux does not get; and the MDCT and psychoacoustic paths call # cos/sin/log/exp out of libm, whose x86-64 glibc has assembly and IFUNC variants # the wasm32 build does not. A red from either would say nothing about this port. # Determinism is asserted where it actually holds -- within one image, across # runs -- and the observed digests are recorded in the design doc instead. # # EVERYTHING IS OFFLINE. No audio device, no ALSA, no /dev/dsp, no network and no # peer: the encoder reads a file and writes a file, and so does the decoder. require vorbis-encoder-example libvorbis/present tmp=${TMPDIR:-/tmp} w=$tmp/bst-libvorbis.$$ mkdir -p "$w" 2>/dev/null fix=$BST_DATA/vorbis-fixture.wav # ------------------------------------------------------------------ fixture --- # Shipped rather than generated in-shell: the geometry encoder_example hardcodes # is 16-bit stereo 44100 Hz, and the content is two band-limited sine tones, # neither of which a POSIX shell can compute without a sin(). data/ is installed # 0644 and read-only at browser runtime, which is fine -- nothing writes to it. # src/vorbis_mkfixture.py regenerates it byte for byte. if [ ! -f "$fix" ]; then bst_notok libvorbis/fixture "$fix is not on the image -- remaining cases not run" rm -rf "$w" exit 0 fi check_eq libvorbis/fixture-size 44144 "$(wc -c < "$fix" 2>/dev/null | tr -d ' ')" # ------------------------------------------------------------------- encode --- vorbis-encoder-example < "$fix" > "$w/a.ogg" 2>"$w/enc1.err"; rc=$? if [ $rc -eq 0 ]; then bst_ok libvorbis/encode else bst_notok libvorbis/encode "rc=$rc $(_bst_1line "$(cat "$w/enc1.err" 2>/dev/null)")"; fi oggbytes=$(wc -c < "$w/a.ogg" 2>/dev/null | tr -d ' ') # An encoder that produced nothing makes every case below fail against the wrong # subject, so stop here rather than emit fifteen misleading reds. if [ -z "$oggbytes" ] || [ "$oggbytes" -lt 64 ]; then bst_notok libvorbis/encode-output "encoder produced ${oggbytes:-0} bytes -- remaining cases not run" rm -rf "$w" exit 0 fi # The container and the codec identification header, read off the stream itself. # Byte 0 of an Ogg page is 'O' of "OggS"; the first packet of a Vorbis logical # stream is \x01 followed by the literal "vorbis". check_eq libvorbis/ogg-magic "OggS" "$(dd if="$w/a.ogg" bs=1 count=4 2>/dev/null)" if dd if="$w/a.ogg" bs=1 count=64 2>/dev/null | grep -q vorbis; then bst_ok libvorbis/ident-header else bst_notok libvorbis/ident-header "no vorbis identification header in the first 64 bytes" fi # It must be a codec, not a container rewrite. 44100 PCM bytes in; natively this # comes out at 4698 (9.4x), so "smaller than the input" is a floor with room, not # a tight claim about the rate mode. if [ "$oggbytes" -lt 44100 ]; then bst_ok libvorbis/compresses else bst_notok libvorbis/compresses "encoded $oggbytes bytes from 44100 bytes of PCM -- no compression" fi # ------------------------------------------------------------ determinism --- # NOT byte equality of the two streams, deliberately. encoder_example does # # srand(time(NULL)); ogg_stream_init(&os, rand()); # # so the Ogg STREAM SERIAL is randomised per run, and two encodes are # byte-identical only when both land inside the same wall-clock second. On a # fast host they do -- which is exactly why measuring this natively reported # "identical", and why that reading was a property of the host's speed rather # than of the encoder. Under emulation the two runs straddle the second and the # files differ, in the serial alone. A case that passes only on fast machines is # worse than no case: it goes red for a reason that is not a regression. # # What IS deterministic is everything the serial does not reach. The encode is a # pure function of the PCM, so two encodes must be the SAME LENGTH and must # DECODE TO THE SAME BYTES. That still catches a genuinely unstable encoder -- # an uninitialised read, a rate decision that drifts -- while asserting nothing # about a field upstream randomises on purpose. vorbis-encoder-example < "$fix" > "$w/b.ogg" 2>/dev/null check_eq libvorbis/encode-same-size "$oggbytes" \ "$(wc -c < "$w/b.ogg" 2>/dev/null | tr -d ' ')" vorbis-decoder-example < "$w/a.ogg" > "$w/da.pcm" 2>/dev/null vorbis-decoder-example < "$w/b.ogg" > "$w/db.pcm" 2>/dev/null # -s on the first: two empty decodes would compare equal and report a green. if [ -s "$w/da.pcm" ] && cmp -s "$w/da.pcm" "$w/db.pcm"; then bst_ok libvorbis/encode-deterministic else bst_notok libvorbis/encode-deterministic "two encodes of one fixture decode to different PCM" fi # ------------------------------------------------------------------- decode --- vorbis-decoder-example < "$w/a.ogg" > "$w/dec.pcm" 2>"$w/dec1.err"; rc=$? if [ $rc -eq 0 ]; then bst_ok libvorbis/decode else bst_notok libvorbis/decode "rc=$rc $(_bst_1line "$(cat "$w/dec1.err" 2>/dev/null)")"; fi # The decoder reports what it found in the identification header. Getting this # wrong while still decoding is exactly the silent-corruption case. check_out libvorbis/stream-info "2 channel, 44100Hz" cat "$w/dec1.err" # Vorbis stores the exact sample count in the final page's granulepos, so the # decoder trims the last block and the round trip preserves length exactly: # 44144 - 44 header bytes = 44100. check_eq libvorbis/decode-length 44100 "$(wc -c < "$w/dec.pcm" 2>/dev/null | tr -d ' ')" vorbis-decoder-example < "$w/a.ogg" > "$w/dec2.pcm" 2>/dev/null if cmp -s "$w/dec.pcm" "$w/dec2.pcm"; then bst_ok libvorbis/decode-deterministic else bst_notok libvorbis/decode-deterministic "two decodes of one stream differ" fi # ------------------------------------------------------------- the deviation --- # The 44-byte canonical header is skipped so the comparison is PCM against PCM. # od -w2 -td2 gives one signed 16-bit sample per line, which awk pairs by line # number; two od runs and one awk over 22050 samples, no per-sample fork. tail -c +45 "$fix" > "$w/in.pcm" od -An -v -td2 -w2 "$w/in.pcm" > "$w/in.txt" 2>/dev/null od -An -v -td2 -w2 "$w/dec.pcm" > "$w/out.txt" 2>/dev/null inl=$(wc -l < "$w/in.txt" 2>/dev/null | tr -d ' ') outl=$(wc -l < "$w/out.txt" 2>/dev/null | tr -d ' ') if [ "${inl:-0}" -ne 22050 ] || [ "${outl:-0}" -ne 22050 ]; then # od is GNU coreutils here; a short or empty listing means the sample stream # never reached awk, which must not be reported as a codec deviation. bst_notok libvorbis/sample-extract "od yielded in=$inl out=$outl lines, expected 22050 each" bst_notok libvorbis/deviation-max "not measured -- sample extraction failed" bst_notok libvorbis/deviation-rms "not measured -- sample extraction failed" bst_notok libvorbis/signal-level "not measured -- sample extraction failed" else bst_ok libvorbis/sample-extract set -- $(awk 'NR==FNR { a[FNR]=$1; next } { n++; x=a[FNR]; y=$1; d=x-y; if (d<0) d=-d; if (d>mx) mx=d; se+=d*d; ex+=x*x; ey+=y*y } END { if (n==0) { print "0 0 0 0"; exit } printf "%d %d %d %d\n", n, mx, sqrt(se/n)*1000, (ex>0 ? 1000*sqrt(ey/ex) : 0) }' \ "$w/in.txt" "$w/out.txt" 2>/dev/null) nsamp=${1:-0}; maxabs=${2:-0}; rms_m=${3:-0}; level_ppt=${4:-0} # maxabs: native 689 against a 16383 peak. 2000 is ~6% of full scale -- loose # enough that a different libm cannot trip it, tight enough that a codec # decoding to the wrong waveform cannot pass. if [ "$nsamp" -eq 22050 ] && [ "$maxabs" -le 2000 ]; then bst_ok libvorbis/deviation-max else bst_notok libvorbis/deviation-max "n=$nsamp maxabs=$maxabs (bound 2000)" fi # RMS error, scaled by 1000 to stay in shell integers: native 144455. if [ "$rms_m" -gt 0 ] && [ "$rms_m" -le 400000 ]; then bst_ok libvorbis/deviation-rms else bst_notok libvorbis/deviation-rms "rms=${rms_m}/1000 (bound 400)" fi # Decoded signal RMS as parts-per-thousand of the input's: 1000 is equality, # native 1002. Zero output lands at 0 and fails; a doubled or halved gain # lands at 2000 or 500 and fails. if [ "$level_ppt" -ge 900 ] && [ "$level_ppt" -le 1100 ]; then bst_ok libvorbis/signal-level else bst_notok libvorbis/signal-level "decoded/input signal RMS = ${level_ppt}/1000 (want 900..1100)" fi fi # Lossy means the bytes must NOT come back identical. A round trip that returned # the input unchanged would satisfy every bound above and would mean the codec # was never invoked. if cmp -s "$w/in.pcm" "$w/dec.pcm"; then bst_notok libvorbis/is-lossy "decoded PCM is byte-identical to the input -- the codec did not run" else bst_ok libvorbis/is-lossy fi # ------------------------------------------------------------ libvorbisfile --- # A third library with its own API surface (ov_open/ov_info/ov_read over a # FILE*), not a second path into the two above. Its output is NOT compared # against vorbis-decoder-example's: ov_read does its own float-to-int # conversion, so the two agree acoustically and differ in the last bit, which # was measured natively rather than assumed. if [ -x /usr/bin/vorbisfile-example ]; then bst_ok libvorbisfile/present vorbisfile-example < "$w/a.ogg" > "$w/vf.pcm" 2>"$w/vf.err"; rc=$? if [ $rc -eq 0 ]; then bst_ok libvorbisfile/decode else bst_notok libvorbisfile/decode "rc=$rc $(_bst_1line "$(cat "$w/vf.err" 2>/dev/null)")"; fi check_out libvorbisfile/stream-info "2 channel, 44100Hz" cat "$w/vf.err" # ov_pcm_total over the whole stream: 11025 frames, the fixture's exact length. check_out libvorbisfile/pcm-total "Decoded length: 11025 samples" cat "$w/vf.err" check_eq libvorbisfile/decode-length 44100 "$(wc -c < "$w/vf.pcm" 2>/dev/null | tr -d ' ')" else bst_notok libvorbisfile/present "vorbisfile-example is not in /usr/bin, though it is installed with libvorbis" fi # ------------------------------------------------------------ negative path --- # Both of these are error handling in the demuxer, and both were confirmed # natively to exit 1 rather than to loop, crash or emit partial audio. The # not-an-Ogg input is the WAV fixture itself, so no random source is needed and # the case is reproducible byte for byte. vorbis-decoder-example < "$fix" > "$w/wrong.pcm" 2>"$w/wrong.err"; rc=$? if [ $rc -ne 0 ]; then bst_ok libvorbis/reject-non-ogg else bst_notok libvorbis/reject-non-ogg "decoder accepted a RIFF/WAVE file as an Ogg stream (rc=0)"; fi check_out libvorbis/reject-non-ogg-msg "does not appear to be an Ogg bitstream" cat "$w/wrong.err" head -c 1200 "$w/a.ogg" > "$w/trunc.ogg" 2>/dev/null vorbis-decoder-example < "$w/trunc.ogg" > "$w/trunc.pcm" 2>"$w/trunc.err"; rc=$? if [ $rc -ne 0 ]; then bst_ok libvorbis/reject-truncated else bst_notok libvorbis/reject-truncated "decoder accepted a stream truncated before its headers (rc=0)"; fi # ------------------------------------------------------------ what shipped --- # libvorbis-dev and libvorbis-staticdev are named in EMCRAFT_TOOLS so the three # archives and their headers are on target for anything that wants to link them. for a in libvorbis libvorbisenc libvorbisfile; do if [ -f "/usr/lib/$a.a" ]; then bst_ok "libvorbis/archive-$a" else bst_notok "libvorbis/archive-$a" "/usr/lib/$a.a is not on the image"; fi done for h in codec.h vorbisenc.h vorbisfile.h; do if [ -f "/usr/include/vorbis/$h" ]; then bst_ok "libvorbis/header-$h" else bst_notok "libvorbis/header-$h" "/usr/include/vorbis/$h is not on the image"; fi done # No shared object on a distro that configures --disable-shared image-wide. One # appearing here would mean the static-only rule quietly stopped holding. if [ -e /usr/lib/libvorbis.so ] || [ -e /usr/lib/libvorbisenc.so ] || [ -e /usr/lib/libvorbisfile.so ]; then bst_notok libvorbis/static-only "a shared libvorbis shipped on a --disable-shared distro" else bst_ok libvorbis/static-only fi rm -rf "$w"