#!/usr/bin/env bash # Regenerate the ffprobe fixture clips in crates/arr-probe/tests/fixtures. # # The clips are committed (a few KB each, DESIGN.md §12) so the test suite # never needs ffmpeg. This script exists to document how they were made and to # rebuild them if the set ever needs to change. # # The two Dolby Vision clips cannot be encoded directly: x265 refuses to write # a DV stream without an RPU file. Instead a real HEVC clip is produced and the # `dvcC`/`dvvC` configuration box is spliced into its sample entry, which is # exactly what ffprobe reads to report `dv_profile`. set -euo pipefail root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" out="$root/crates/arr-probe/tests/fixtures" work="$(mktemp -d)" trap 'rm -rf "$work"' EXIT mkdir -p "$out" # 1080p feature: three audio tracks covering all three §5.2 signals, plus two # subtitle tracks. Three seconds, so the runtime sanity check has something to # compare against. printf '1\n00:00:00,000 --> 00:00:01,000\nhello\n\n' > "$work/en.srt" printf '1\n00:00:00,000 --> 00:00:01,000\nolá\n\n' > "$work/pt.srt" ffmpeg -hide_banner -loglevel error -y \ -f lavfi -i "color=c=black:s=1920x1080:r=24:d=3" \ -f lavfi -i "sine=frequency=440:duration=3" \ -f lavfi -i "sine=frequency=660:duration=3" \ -f lavfi -i "sine=frequency=880:duration=3" \ -i "$work/en.srt" -i "$work/pt.srt" \ -map 0:v -map 1:a -map 2:a -map 3:a -map 4:s -map 5:s \ -c:v libx264 -preset ultrafast -crf 51 -pix_fmt yuv420p \ -c:a aac -b:a 8k -c:s srt \ -metadata:s:a:0 language=eng -metadata:s:a:0 title="English" \ -metadata:s:a:1 language=por -metadata:s:a:1 title="Português (Brasil)" \ -metadata:s:a:1 handler_name="Portuguese (Brazil)" \ -metadata:s:a:2 language=pt-PT \ -metadata:s:s:0 language=eng -metadata:s:s:1 language=por \ "$out/movie-1080p.mkv" # The extras reel trap: larger on disk than the feature (noise compresses # badly) but a third of its runtime. ffmpeg -hide_banner -loglevel error -y \ -f lavfi -i "nullsrc=s=640x360:r=12:d=1,geq=random(1)*255:128:128" \ -an -c:v libx264 -preset ultrafast -crf 51 -pix_fmt yuv420p \ "$out/extras-360p.mkv" # HDR10: bt2020 primaries and the PQ transfer, which is all ffprobe reports. ffmpeg -hide_banner -loglevel error -y \ -f lavfi -i "color=c=black:s=3840x2160:r=24:d=1" -an \ -vf "format=yuv420p10le,setparams=color_primaries=bt2020:color_trc=smpte2084:colorspace=bt2020nc" \ -c:v libx265 -preset ultrafast -crf 51 \ "$out/hdr10-2160p.mkv" # HLG and SDR both use HEVC here so transfer function, rather than codec bit # depth, is what distinguishes their probe results. ffmpeg -hide_banner -loglevel error -y \ -f lavfi -i "color=c=black:s=1280x720:r=24:d=1" -an \ -vf "format=yuv420p10le,setparams=color_primaries=bt2020:color_trc=arib-std-b67:colorspace=bt2020nc" \ -c:v libx265 -preset ultrafast -crf 51 \ "$out/hlg-720p.mkv" ffmpeg -hide_banner -loglevel error -y \ -f lavfi -i "color=c=black:s=1280x720:r=24:d=1" -an \ -vf "format=yuv420p10le" \ -c:v libx265 -preset ultrafast -crf 51 \ "$out/sdr-10bit-hevc-720p.mkv" # HEVC base layer for the two Dolby Vision clips. ffmpeg -hide_banner -loglevel error -y \ -f lavfi -i "color=c=black:s=1280x720:r=24:d=1" -an \ -vf "format=yuv420p10le,setparams=color_primaries=bt2020:color_trc=smpte2084:colorspace=bt2020nc" \ -c:v libx265 -preset ultrafast -crf 51 -tag:v hvc1 \ "$work/base.mp4" cat > "$work/dovi.py" <<'PY' """Splice a DolbyVisionConfigurationBox into an MP4 HEVC sample entry.""" import struct import sys CONTAINERS = {"moov", "trak", "mdia", "minf", "stbl", "stsd"} def children(buf, start, end): boxes = [] off = start while off + 8 <= end: size = struct.unpack(">I", buf[off : off + 4])[0] if size == 0: size = end - off boxes.append((off, size, buf[off + 4 : off + 8].decode("latin1"))) off += size return boxes def first_child_offset(off, kind): # stsd carries a version/flags word and an entry count before its children. return off + (16 if kind == "stsd" else 8) def find(buf, start, end, path): for off, size, kind in children(buf, start, end): if kind != path[0]: continue if len(path) == 1: return off, size return find(buf, first_child_offset(off, kind), off + size, path[1:]) return None def configuration(profile, compatibility_id, level=6): bits = (profile & 0x7F) << 41 bits |= (level & 0x3F) << 35 bits |= 1 << 34 # rpu_present_flag bits |= 1 << 32 # bl_present_flag bits |= (compatibility_id & 0xF) << 28 return bytes([1, 0]) + bits.to_bytes(6, "big") + bytes(16) def grow_ancestors(buf, target, growth): def walk(start, end): for off, size, kind in children(buf, start, end): if not off <= target < off + size: continue struct.pack_into(">I", buf, off, size + growth) if kind in CONTAINERS: walk(first_child_offset(off, kind), off + size) return walk(0, len(buf)) def main(source, destination, profile, compatibility_id): buf = bytearray(open(source, "rb").read()) entry = find(buf, 0, len(buf), ["moov", "trak", "mdia", "minf", "stbl", "stsd", "hvc1"]) if entry is None: sys.exit("no hvc1 sample entry") off, size = entry payload = configuration(profile, compatibility_id) kind = b"dvvC" if profile == 5 else b"dvcC" box = struct.pack(">I", 8 + len(payload)) + kind + payload buf[off + 4 : off + 8] = b"dvh1" buf[off + size : off + size] = box grow_ancestors(buf, off, len(box)) open(destination, "wb").write(bytes(buf)) if __name__ == "__main__": main(sys.argv[1], sys.argv[2], int(sys.argv[3]), int(sys.argv[4])) PY python3 "$work/dovi.py" "$work/base.mp4" "$out/dv-profile5.mp4" 5 0 python3 "$work/dovi.py" "$work/base.mp4" "$out/dv-profile8-1.mp4" 8 1 # An audio file with embedded artwork. ffprobe reports the cover as a video # stream, so this is what stops a soundtrack passing for a feature. ffmpeg -hide_banner -loglevel error -y \ -f lavfi -i "color=c=red:s=64x64:d=1" -frames:v 1 "$work/cover.jpg" ffmpeg -hide_banner -loglevel error -y \ -f lavfi -i "sine=frequency=440:duration=2" -i "$work/cover.jpg" \ -map 0:a -map 1:v -c:a aac -b:a 8k -c:v mjpeg -disposition:v attached_pic \ "$out/audio-with-cover.m4a" # Not media at all: the thing a multi-file torrent is full of. printf 'Release notes. Not a video file.\n' > "$out/notes.nfo" ls -l "$out"