. "$BST_LIB" # libopus 1.5.2 (meta-oe recipes-multimedia/libopus) -- the Opus interactive # audio codec of RFC 6716, with upstream's reference driver opus_demo and its # conformance comparator opus_compare, both installed by this layer's # libopus_%.bbappend (upstream builds them as noinst_PROGRAMS and discards them). # # HOW A LOSSY CODEC IS CHECKED OFFLINE, WHICH IS THE WHOLE PROBLEM HERE. flac is # easy: losslessness is an exact property, so `cmp` of input against output is # the gate. Opus throws information away by design, so that assertion does not # exist and the naive alternative -- "it ran and printed a banner" -- says # nothing about whether the arithmetic on this arm is right. Two properties # replace it, and between them they pin the codec without asserting anything a # lossy codec cannot promise: # # 1. DETERMINISM AND A DIGEST. For a fixed build, a fixed configuration and a # fixed input, opus_demo's decoded output is a fixed byte string. Two # independent runs must therefore agree byte for byte, and the digest of # the decoded PCM is a constant this file pins. The decoder half of that is # guaranteed by the specification rather than by luck -- RFC 6716 defines # Opus decoding bit-exactly, which is why the split -e/-d path below # re-decodes the same bitstream twice and demands identity. # 2. BOUNDED DEVIATION FROM THE INPUT. opus_compare is upstream's own # comparator: it scores the weighted spectral distance between a reference # and a test signal and exits non-zero once the result exceeds the # threshold it defines conformance at. Running it over (fixture, round trip) # is the bounded-deviation criterion computed by upstream's code rather than # by an assertion of ours. # # opus_compare IS NOT A GENERAL PCM DIFFER, AND ITS CONTRACT DICTATES THE SHAPE OF # THE SECOND HALF. Three constraints come straight out of src/opus_compare.c and # are the reason the deviation case below runs at 48 kHz stereo while everything # else runs at 8 kHz mono: # # - It ALWAYS reads the reference with `read_pcm16(&x,fin1,2)` -- two channels, # 48 kHz assumed -- and reads the test file at the rate `-r` names, then # requires `xlength == ylength*(48000/rate)` exactly. Handing it an 8 kHz mono # pair cannot satisfy that: the reference is reinterpreted as 48 kHz stereo and # the counts come out 8192 against 98304. Matching rate and channels on both # sides at 48 kHz is the only invocation whose assumptions are the truth. # - Its verdict is Q = 100*(1 - 0.5*log(1+err)/log(1.13)), failing at Q<0, i.e. # err > 0.2769. That is a quality bar, not a bit-exactness bar, so the signal # has to be one Opus can actually carry. # - A PURE TONE IS THE WRONG PROBE, MEASURED RATHER THAN ASSUMED. The metric # normalises error per band, so a signal with energy in one band lets tiny # absolute differences in the near-empty bands dominate: a 750 Hz sine scored # err 0.92 at 96 kb/s and got WORSE with more bits (1.10 at 256 kb/s, 1.15 at # 510 kb/s). The broadband sawtooth scores 0.125 at 256 kb/s and 0.125 at # 510 kb/s -- better than 2x inside the threshold, and flat in the bitrate, # which is the signature of having reached the codec's fidelity ceiling rather # than of starving it. 510 kb/s is used so the number below reports this arm's # arithmetic and not a bitrate choice. # # WHAT A GREEN HERE SAYS ABOUT wasm32. meta-oe passes --enable-asm and # --enable-intrinsics, both of which are auto-detected per architecture and # neither of which recognises wasm32: configure answers no to every SSE and NEON # probe, so celt/ and silk/ contribute their portable C and nothing else. A # round trip that lands on the pinned digest is therefore a statement about this # arm's integer and floating-point arithmetic, not about a SIMD path that was # skipped. # # EVERY CASE IS OFFLINE AND DEVICE-FREE. No sound card, no /dev/dsp, no network # and no peer -- the fixture is generated by shell arithmetic, and the codec # reads and writes plain files. # # THE FIXTURE IS BUILT WITHOUT FORKING, for the reason 57-flac.test states at # length: on this arm each exec is a fresh wasm module instantiation and guest # memory is not returned on process exit, so a `printf '%03o'` subshell per byte # would cost 256 forks before the first case. Every byte here comes out of # arithmetic expansion and the printf builtin. require opus_demo libopus/present tmp=${TMPDIR:-/tmp}/bst-libopus.$$ mkdir -p "$tmp" 2>/dev/null pcm=$tmp/tone.pcm rt1=$tmp/rt1.pcm rt2=$tmp/rt2.pcm bit=$tmp/tone.opus dec1=$tmp/dec1.pcm dec2=$tmp/dec2.pcm pcm48=$tmp/s48.pcm rt48=$tmp/s48.rt.pcm # The digest of the decoded PCM from the single-run round trip below, observed on # the wasm32-hwjs glibc image at port time. It is pinned, not derived: opus_demo # is deterministic for a fixed build, so a change here means the encoder's or # decoder's arithmetic moved -- a finding worth a look, not automatically a bug, # since a libopus version bump or a toolchain change to the floating-point path # legitimately moves it. The determinism and bounded-deviation cases stay valid # either way, which is why this is one case rather than the gate. OPUS_RT_MD5=f78327987246b5c87508f11e77afee8d # ------------------------------------------------------------------ fixture --- # A 256-sample sawtooth, 16-bit signed little-endian mono, repeated 64 times: # 16384 samples / 32768 bytes / 2.048 s at 8 kHz. opus_demo reads and writes # native-endian 16-bit PCM and wasm32 is little-endian, so the bytes below are # read back as the samples they were written as. Sawtooth rather than silence # because a constant signal is carried by SILK's lowest rate and would exercise # almost nothing; the low byte is held at zero so the waveform is a clean ramp in # the high byte. i=0; esc="" while [ $i -lt 256 ]; do esc="$esc\\000\\$((i/64))$((i/8%8))$((i%8))" i=$((i+1)) done j=0 while [ $j -lt 64 ]; do printf "$esc"; j=$((j+1)); done > "$pcm" nbytes=$(wc -c < "$pcm" 2>/dev/null | tr -d ' ') check_eq libopus/fixture-size 32768 "$nbytes" # A fixture the shell failed to write would make every case below fail for a # reason that has nothing to do with libopus, so stop here rather than emit a # dozen misleading reds against the wrong subject. if [ "$nbytes" != "32768" ]; then bst_notok libopus/fixture "the shell could not emit the 32768-byte PCM fixture -- remaining cases not run" rm -rf "$tmp" exit 0 fi # opus_demo with no arguments prints its usage and exits non-zero, which is the # cheapest proof that the binary is a real executable on this arm and got as far # as its own argument parser. usage=$(opus_demo 2>&1) if bst_execfail "$usage"; then bst_unrun libopus/usage "$usage" else case "$usage" in *[Uu]sage*) bst_ok libopus/usage ;; *) bst_notok libopus/usage "opus_demo printed no usage: $(_bst_1line "$usage")" ;; esac fi # --------------------------------------------- the ticket's own criterion ---- # One run, encode and decode together, exactly as the ticket describes it: # `opus_demo ` with neither # -e nor -d. voip is the application SILK serves at 8 kHz; 24000 bit/s is well # inside its range, so the deviation below is a statement about the codec rather # than about starving it. check libopus/roundtrip opus_demo voip 8000 1 24000 "$pcm" "$rt1" rtbytes=$(wc -c < "$rt1" 2>/dev/null | tr -d ' ') if [ -n "$rtbytes" ] && [ "$rtbytes" -gt 0 ]; then bst_ok libopus/roundtrip-nonempty else bst_notok libopus/roundtrip-nonempty "the round trip produced ${rtbytes:-no} bytes of decoded PCM" fi # The decoder emits whole frames, so the output is the input rounded up to a # frame multiple -- never shorter than the input, and never more than one 20 ms # frame (320 samples / 640 bytes at 8 kHz) longer. Bounding it both ways catches # a truncated decode without pinning a number that a default frame-size change # would move. if [ -n "$rtbytes" ] && [ "$rtbytes" -ge 32768 ] && [ "$rtbytes" -le 33408 ]; then bst_ok libopus/roundtrip-length else bst_notok libopus/roundtrip-length "decoded $rtbytes bytes from a 32768-byte fixture; want 32768..33408" fi # Determinism: same binary, same input, same options, twice. This is the property # the pinned digest below rests on, and it is asserted separately so that a # digest change can be read as "the arithmetic moved" rather than "the codec is # no longer reproducible" -- two very different findings. check libopus/roundtrip-again opus_demo voip 8000 1 24000 "$pcm" "$rt2" check libopus/deterministic cmp "$rt1" "$rt2" if command -v md5sum >/dev/null 2>&1; then check_eq libopus/decoded-digest "$OPUS_RT_MD5" "$(md5sum < "$rt1" 2>/dev/null | cut -d' ' -f1)" else bst_skip libopus/decoded-digest "md5sum not installed on this image" fi # ------------------------------------------------------ bounded deviation ---- # 48 kHz stereo, because that is the only shape opus_compare's reference reader # agrees with (see the header). 256 frames of sawtooth -- left ramping up, right # offset by half a period so the two channels are not identical -- repeated 180 # times: 46080 frames / 184320 bytes / 0.96 s, a whole number of 20 ms frames so # the decoder returns exactly what it was given and the length check below is an # equality rather than a range. i=0; esc="" while [ $i -lt 256 ]; do r=$(( (i+128) % 256 )) esc="$esc\\000\\$((i/64))$((i/8%8))$((i%8))\\000\\$((r/64))$((r/8%8))$((r%8))" i=$((i+1)) done j=0 while [ $j -lt 180 ]; do printf "$esc"; j=$((j+1)); done > "$pcm48" check_eq libopus/fixture48-size 184320 "$(wc -c < "$pcm48" 2>/dev/null | tr -d ' ')" check libopus/roundtrip48 opus_demo audio 48000 2 510000 "$pcm48" "$rt48" # opus_compare refuses a pair whose sample counts disagree, and reports that as # "Sample counts do not match" -- a message that reads like a codec fault when it # is really a fixture fault. Asserting the equality here means a length drift is # named for what it is instead of surfacing as a confusing deviation red. rt48bytes=$(wc -c < "$rt48" 2>/dev/null | tr -d ' ') if [ "$rt48bytes" = "184320" ]; then bst_ok libopus/roundtrip48-length else bst_notok libopus/roundtrip48-length "decoded $rt48bytes bytes from a 184320-byte fixture; opus_compare requires the two to match exactly" fi # upstream's own comparator, over the fixture and its round trip. A pass is # agreement inside the threshold opus_compare defines; a fail prints the weighted # error it measured, so a red here carries the number rather than just the verdict. if command -v opus_compare >/dev/null 2>&1; then check libopus/bounded-deviation opus_compare -s "$pcm48" "$rt48" else bst_notok libopus/bounded-deviation "opus_compare is not in /usr/bin, though it is packaged with opus_demo" fi # ------------------------------------------------------- the split -e/-d path --- # The single run above proves the pair; this proves each half separately, and it # is where the bitstream itself becomes observable. It is also the only place # RFC 6716's bit-exactness clause can be asserted directly: the decoder is # specified exactly, so decoding one fixed bitstream twice must give identical # bytes on any conforming implementation, this arm included. check libopus/encode opus_demo -e voip 8000 1 24000 "$pcm" "$bit" encbytes=$(wc -c < "$bit" 2>/dev/null | tr -d ' ') if [ -n "$encbytes" ] && [ "$encbytes" -gt 0 ] && [ "$encbytes" -lt 32768 ]; then bst_ok libopus/compresses else bst_notok libopus/compresses "encoded ${encbytes:-no} bytes from a 32768-byte fixture -- no compression" fi check libopus/decode opus_demo -d 8000 1 "$bit" "$dec1" check libopus/decode-again opus_demo -d 8000 1 "$bit" "$dec2" check libopus/decode-bit-exact cmp "$dec1" "$dec2" # ----------------------------------------------------------- the payload ------ # The library half of the port. libopus ships no shared object on a distro that # configures --disable-shared image-wide; one appearing here would mean the # static-only rule silently stopped holding. if [ -e /usr/lib/libopus.so ] || [ -e /usr/lib/libopus.so.0 ]; then bst_notok libopus/static-only "a shared libopus shipped on a --disable-shared distro" else bst_ok libopus/static-only fi for f in /usr/lib/libopus.a /usr/include/opus/opus.h /usr/lib/pkgconfig/opus.pc; do if [ -s "$f" ]; then bst_ok "libopus/payload$(printf '%s' "$f" | tr / -)" else bst_notok "libopus/payload$(printf '%s' "$f" | tr / -)" "$f is missing or empty" fi done rm -rf "$tmp"