Claude Skill

ffmpeg

Use this skill for local FFmpeg/FFprobe media inspection, remuxing, transcoding, filtering, evidence-bounded video review, transcript-assisted editorial plans, edit decision lists, podcast/audio cleanup, rendering, and output acceptance. It emphasizes explicit stream selection, s

LLM Mart · 0 points · 10 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download magnus919-agent-skills-ffmpeg-addad86.zip · 113 KB
Part of magnus919/agent-skills — 145 skills

Install

skills CLI npx skills add https://github.com/magnus919/agent-skills/tree/main/ffmpeg
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install magnus919-agent-skills@llmmart
Git git clone https://github.com/magnus919/agent-skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole magnus919/agent-skills collection as a plugin from our marketplace. Git is the plain clone.

README

FFmpeg Expert Skill

A practical FFmpeg skill for inspecting local media, planning reviewable video or podcast edits, rendering safely, and accepting outputs from evidence rather than command success alone.

Why Install This Skill

FFmpeg failures often happen at boundaries: the wrong stream is selected, a cut lands on an unexpected keyframe, a filter is absent from the installed build, timestamps drift, or a technically valid output fails in its real destination. Editorial work adds another risk: sparse frames, silence intervals, and imperfect transcripts can look more conclusive than they are.

This skill provides a repeatable intake-to-acceptance workflow. It separates technical measurements from editorial judgment, preserves originals, makes cuts reviewable in an edit decision list, and records what was actually checked.

What You Get

Core guidance

Path Purpose
SKILL.md Trigger boundaries, capability routing, evidence classes, and the core workflow
references/core-model-and-command-anatomy.md Containers, streams, codecs, mapping, option scope, and timestamps
references/filters-and-transformations.md Simple and complex filtergraphs, audio/video filters, and graph debugging
references/intermediate-workflows.md Trimming, concat, metadata, subtitles, scripting, pipes, and streaming
references/advanced-operations-and-safety.md Hardware, synchronization, reproducibility, and operational safety
references/command-cookbook.md Short, assumption-labeled commands
references/learning-summary.md Learning progression and consolidated mental model
references/source-inventory.md Original primary/secondary source inventory and evidence boundaries
references/local-verification.md Version- and host-specific FFmpeg 8.1.2 experiments

Media editing and evidence guidance

Path Purpose
references/media-intake-and-manifest.md Authorization, source identity, probe capture, output contracts, privacy, and preservation
references/video-inspection-and-visual-evidence.md Bounded frame/clip sampling and defensible visual claims
references/editorial-video-editing.md Transcript-assisted decisions, sequencing, treatments, and review gates
references/audio-and-podcast-editing.md Mechanical edits, signal cleanup, editorial audio decisions, and listening checks
references/ffmpeg-edit-decision-lists.md EDL semantics, validation, keyframe status, mapping, and command planning
references/media-verification-and-acceptance.md Layered probe, decode, content, editorial, and downstream acceptance evidence
references/media-failure-modes.md Diagnosis matrix, safe recovery, and stop rules
references/media-research-source-index.md Claim-to-source map for official docs, standards, experiments, and heuristics
references/editorial-workflow-example.md Reproducible synthetic intake-to-acceptance integration workflow
references/synthetic-media-fixtures.md Deterministic real-media fixture coverage and evidence boundaries

Copyable templates

Path Purpose
templates/media-intake.json Parseable source, stream, timing, contract, privacy, and assumption manifest
templates/edit-decision-list.json Parseable source ranges, evidence, confidence, treatments, mapping, and verification
templates/video-inspection-report.md Fixed-section technical and sampled-evidence report
templates/visual-review-packet.md Timestamped review samples with attribution and coverage limits
templates/vision-review-observations.json Machine-readable reviewer attribution, evidence classes, blind spots, and EDL links
templates/podcast-edit-plan.md Mechanical, signal-processing, and editorial audio plan
templates/media-acceptance-report.md Criterion-by-criterion evidence and release verdict
templates/media-acceptance-contract.json Parseable stream, format, evidence, loudness, and downstream criteria
templates/target-compatibility-manifest.json Named consumer, sourced requirements, technical limits, and authorized target lane
templates/research-experiment-record.md Versioned, reproducible command experiment record

Existing automation and evals

Path Purpose
scripts/ffmpeg-preflight Tool status, inventory counts, and named filter/encoder/hwaccel checks
scripts/test_ffmpeg_preflight.py Deterministic tests for the capability preflight
scripts/fixtures/ffmpeg-8.1.2-inventories.json Small version-labeled parser fixture
scripts/media-intake Read-only input inventory with bounded ffprobe metadata
scripts/extract-review-frames Bounded timestamp frame extraction for human or vision review
scripts/vision-review-handoff Privacy-safe bounded frame packet with provenance, limits, hashes, and pending-review manifest
scripts/import-vision-review Validate attributed observations and link them to EDL events without rendering
scripts/render-edl Validate single- or multi-source EDLs and emit non-executing concat-filter or concat-demuxer plans
scripts/audio-inspect Bounded silence, loudness, peak/clipping, transcript-candidate, and podcast-plan evidence
scripts/media-verify Evaluate output probe and review evidence against a declared acceptance contract
scripts/target-compatibility Separate technical probe conformance from one named player's/editor's/host's result
scripts/editorial-workflow-example Generate synthetic audio/video and exercise the complete workflow with durable evidence
scripts/generate-media-fixtures Generate a bounded sanitized fixture battery and versioned evidence manifest
evals/evals.json Output-quality cases for core FFmpeg, media evidence, video, podcast, EDL, safety, and acceptance behavior

Quick Start

Install FFmpeg with your platform package manager and inspect the source before choosing an edit:

ffmpeg -version
ffprobe -version
ffprobe -v error -show_format -show_streams -of json input.mp4

For a media editing task:

  1. Copy templates/media-intake.json into a private task workspace and record the source and output contract.
  2. Collect only the bounded frame, clip, transcript, or signal evidence needed for the decision.
  3. Copy templates/edit-decision-list.json or templates/podcast-edit-plan.md and review consequential cuts.
  4. Render to a new path with overwrite refusal while exploring.
  5. Copy templates/media-acceptance-report.md, probe and review the result, then test the intended player, editor, service, or archive boundary.

Before using a version-sensitive recipe, inspect the local capability:

scripts/ffmpeg-preflight --filter scale --filter loudnorm --encoder libx264 --hwaccel videotoolbox

Named checks report each capability as present or absent. Exit code 1 means a required tool/probe failed; 2 means a requested capability is absent from a usable inventory. Add --json for machine-readable output.

Run the complete synthetic integration example in a new or empty task-local directory:

scripts/editorial-workflow-example /tmp/ffmpeg-editorial-example --json

The resulting acceptance report passes only the exercised component and integration checks; semantic, listening, and downstream-consumer boundaries remain explicitly unverified.

Generate the richer real-media test battery when a change needs timestamp, concat, audio, subtitle, or bounded visual evidence:

scripts/generate-media-fixtures /tmp/ffmpeg-fixtures --json

Generated media remains task-local; commit the generator and test assertions, not the binary outputs.

Produce measured audio evidence and a reviewable plan without changing the source:

scripts/audio-inspect input.wav \
  --measure-silence --silence-threshold=-45dB --silence-duration 0.7 \
  --measure-loudness --measure-clipping --json

Optional timed transcript JSON must disclose alignment quality. Detector intervals and transcript ranges remain candidates until listening review approves an edit.

Evaluate a rendered output against declared criteria:

scripts/media-verify acceptance-contract.json output-probe.json \
  --evidence review-evidence.json --json

Missing fields or review evidence remain UNVERIFIED; blocked reviews remain BLOCKED; local probe/decode success never supplies downstream compatibility evidence.

Prepare visual evidence around proposed edit boundaries without exposing the source path:

ffmpeg/scripts/vision-review-handoff private.mov --asset-id asset-017 \
  --question "Does the sampled boundary preserve title continuity?" \
  --timestamp 12.4 --neighbor-seconds 0.25 --output-dir review-packet --json

The manifest covers only its listed samples. An authorized reviewer must add attributed observations before import-vision-review can link them to an EDL; sparse frames never prove absence throughout a video.

For destination-specific delivery, evaluate a sourced target contract and preserve target evidence separately:

ffmpeg/scripts/target-compatibility target.json output-probe.json \
  --target-evidence target-result.json --json

The verdict applies only to the named target and version. A local FFmpeg decode pass does not substitute for import, playback, or ingest evidence from that consumer.

Triggers

Load this skill for:

  • Media intake, FFprobe manifests, stream/container/timestamp inspection, or output contracts
  • Remuxing, transcoding, filtering, trimming, joining, extraction, subtitles, or synchronization
  • Bounded review-frame or audio-evidence preparation from supplied/authorized local media
  • Transcript-assisted video edits, reviewable EDLs, or deterministic render plans
  • Podcast cutting, silence/noise analysis, loudness measurement, and audio cleanup
  • Build capability checks, overwrite-safe batch work, output verification, or failure diagnosis

Use another capability first for online media/transcript acquisition, semantic image interpretation, HTML-authored HyperFrames composition, platform publishing/API work, DRM, or rights clearance.

Requirements

  • ffmpeg and ffprobe on PATH for execution
  • A shell with careful filename and filter-expression quoting
  • A vision-capable or human reviewer for semantic claims about extracted images
  • Listening playback for editorial audio acceptance
  • Network access only for linked documentation or an explicitly requested network protocol
  • Hardware acceleration only with the relevant device, drivers, compiled support, and a verified end-to-end path

Skill manifest

FFmpeg Expert

Treat FFmpeg as a typed media pipeline and media editing as an evidence-driven workflow. Inspect the actual source, separate measurements from interpretations, make decisions reviewable, render to a new path, and verify at the intended boundary.

When Not to Use

  • Do not use this skill for libav API programming, opaque whole-video semantic understanding, automatic publishing, rights clearance, DRM circumvention, professional broadcast/color certification, or HyperFrames-authored compositions.
  • Route online media or transcript acquisition to the owning source skill, semantic frame interpretation to a vision-capable reviewer, and platform upload/API work to the platform skill.

Boundaries and Routing

  • Use a YouTube/transcript capability to acquire online video or transcripts; return here only for local supplied media and transcript artifacts.
  • Use HyperFrames for HTML-authored motion graphics or composition; use this skill to inspect and preprocess its media inputs or verify rendered outputs.
  • Use the named platform skill for upload, publishing, account, or API operations.
  • FFmpeg can extract bounded frames and audio segments but does not interpret their semantic content. Route visual interpretation to a vision-capable reviewer and preserve its observations as attributed evidence.
  • Do not infer rights, consent, identity, intent, or whole-program meaning from technical metadata, sparse frames, silence intervals, or an unaligned transcript.

Evidence Classes

Label consequential claims so unlike evidence is not blended:

  • Technical contract — behavior documented by an official FFmpeg or standards source.
  • Observed artifact — probe output, measured signal result, extracted frame, listened segment, or downstream test from this source/output.
  • Reproducible experiment — exact version, input identity/generator, command, result, and limits.
  • Editorial heuristic — a reversible judgment that requires human review, not a fact established by FFmpeg.
  • User requirement — the requested output contract, preservation policy, and acceptance threshold.

Media Editing Loop

  1. Intake. Confirm authorization and privacy boundaries; identify every source; use a private workspace copy of templates/media-intake.json to record probe evidence, timing, the output contract, preservation policy, and unresolved assumptions. Keep raw probe output and media in the restricted task workspace, and minimize any shareable derivative.
  2. Inspect. Probe streams and format. Check required local capabilities with inventories or scripts/ffmpeg-preflight; never assume a filter, encoder, or hardware backend exists.
  3. Collect bounded evidence. Extract only the frames, clips, waveform/signal measurements, or transcript spans needed for the decision. Record sample timestamps, count, byte/size limits, and the statement that samples cover sampled times only.
  4. Plan before rendering. For editorial changes, write a reviewable EDL or podcast edit plan. Every consequential cut needs a source range, reason, evidence, confidence, treatment, mapping, and verification state. Leave ambiguous decisions unresolved rather than improvising.
  5. Render safely. Make stream mapping explicit; prefer -n and a new output path; avoid untrusted shell concatenation. Distinguish keyframe-limited stream copy from decoded/re-encoded precise cuts.
  6. Verify in layers. Check exit status, decodeability, output probe, stream/timing contract, bounded frame/audio evidence, editorial review, and the actual downstream consumer as applicable. Treat probe/decode success and destination acceptance as separate gates; a local file can pass the former and fail the latter.
  7. Accept or stop. Use a private workspace copy of templates/media-acceptance-report.md to record pass/fail/blocked per criterion. A valid container or successful command alone is not acceptance.

Start technical inspection with:

ffprobe -v error -show_format -show_streams -of json INPUT

For shipped helper workflows, run the helper from the skill root with explicit output paths and limits; treat its JSON result as a report to verify, not as acceptance by itself. When using extract-review-frames, pass timestamps as separate values (--timestamps 0 5 9), not one comma-separated value.

Route to the Focused Reference

Evidence-driven media work

  • Read references/media-intake-and-manifest.md before handling supplied/generated media, sensitive material, multiple sources, or a defined delivery contract.
  • Read references/video-inspection-and-visual-evidence.md when extracting or reviewing frames/clips, choosing samples, or making visual claims.
  • Read references/editorial-video-editing.md for transcript-assisted selection, sequencing, pacing, transitions, overlays, and reviewable editorial decisions.
  • Read references/audio-and-podcast-editing.md for podcast cuts, signal cleanup, silence/noise analysis, loudness measurement, and listening gates.
  • Read references/ffmpeg-edit-decision-lists.md before creating, validating, or turning an EDL into a command plan.
  • Read references/media-verification-and-acceptance.md before declaring an output complete or compatible.
  • Read references/media-failure-modes.md when evidence is contradictory, a cut drifts, a filter is missing, review samples are sparse, or a workflow repeatedly fails.
  • Read references/media-research-source-index.md when supporting claims, refreshing version-sensitive guidance, or recording a technical experiment.
  • Read references/editorial-workflow-example.md when proving that intake, evidence, EDL, rendering, and acceptance artifacts compose end to end on a synthetic fixture.
  • Read references/synthetic-media-fixtures.md when a change needs bounded real-media fixtures for cuts, cadence, concat, audio, subtitles, or visual-boundary sampling.

Core FFmpeg work

  • Read references/core-model-and-command-anatomy.md for containers, streams, codecs, option scope, mapping, copy/transcode, and timestamps.
  • Read references/filters-and-transformations.md for filtergraphs, labels, audio/video processing, and incremental graph debugging.
  • Read references/intermediate-workflows.md for seeking, trimming, concat, metadata, subtitles, batching, pipes, and streaming.
  • Read references/advanced-operations-and-safety.md for hardware acceleration, synchronization, reproducibility, network safety, and failure boundaries.
  • Read references/command-cookbook.md only after inspection and capability checks; every recipe is conditional.
  • Read references/learning-summary.md for the newcomer-first mental model.
  • Read references/source-inventory.md for the original FFmpeg source survey and references/local-verification.md only for its explicitly host-specific FFmpeg 8.1.2 observations.

Templates

  • templates/media-intake.json — source identities, probes, contract, privacy, preservation, assumptions
  • templates/edit-decision-list.json — reviewable source ranges and treatments
  • templates/video-inspection-report.md — technical inspection and bounded evidence ledger
  • templates/visual-review-packet.md — attributed frame/clip observations and coverage limits
  • templates/vision-review-observations.json — parseable attributed observation block for a prepared packet
  • templates/podcast-edit-plan.md — mechanical, signal, and editorial audio decisions
  • templates/media-acceptance-report.md — layered verification and criterion verdicts
  • templates/media-acceptance-contract.json — machine-readable stream, format, evidence, loudness, and downstream requirements
  • templates/target-compatibility-manifest.json — one named target, sourced requirements, technical constraints, and downstream lane
  • templates/research-experiment-record.md — reproducible version/command/result record

Run scripts/editorial-workflow-example in a new or empty task-local directory when a reproducible synthetic integration proof is required. Its PASS_WITH_UNVERIFIED_BOUNDARIES result is deliberately narrower than editorial or destination acceptance.

Run scripts/generate-media-fixtures when tests need deterministic non-personal media. Keep its generated binaries and manifest in the task workspace; commit the generator and assertions, not the outputs.

Use scripts/render-edl for a non-executing single- or multi-source plan. Default to decoded concat-filter assembly; select concat-demuxer stream copy only with matching probe-derived signatures and verified packet/keyframe boundaries. Unsupported transitions must remain explicit errors.

Use scripts/audio-inspect for bounded silence, EBU R128, peak/clipping, and transcript-alignment evidence. Request each measurement explicitly, preserve unavailable filters as UNAVAILABLE, and treat every interval or transcript range as a listening-review candidate. Its optional report output refuses overwrite.

Use scripts/media-verify with a declared acceptance contract, output FFprobe JSON, and optional evidence JSON. It reports every criterion independently as PASS, FAIL, BLOCKED, UNVERIFIED, or NOT_APPLICABLE; only a report with no failed or missing required evidence is an overall pass.

Use scripts/target-compatibility when acceptance names a real player, editor, host, archive, or service. Keep sourced technical requirements and local probe results separate from evidence produced by that exact consumer; a pass applies only to the named target/version.

Use scripts/vision-review-handoff to prepare bounded, privacy-safe frame packets for an authorized human or vision reviewer. Import only attributed reviewed observations with scripts/import-vision-review; treat proposed editorial consequences as evidence for review, never automatic decisions.

Copy a template into the task workspace and replace its placeholder/example values. Do not put private paths, media, transcripts, or review evidence in the public skill repository.

Non-Negotiable Checks

  • Make stream selection explicit whenever multiple inputs/tracks or a complex graph are involved.
  • Treat option order as significant: options generally apply to the next input or output.
  • Do not call silence useless; silencedetect reports threshold crossings, not editorial value.
  • Claim loudness, clipping, timing, or keyframe status only from an available measurement method and retain its output.
  • A transcript is evidence only for its text and supplied timing quality; spot-check alignment against media before frame-accurate edits.
  • Stop for review when evidence is sparse, ambiguity could remove meaningful content, an optional tool/filter is absent, privacy/authorization is unclear, or two materially different approaches fail.

Completion

Finish only when the requested artifact exists at a new path, required probes and bounded reviews are recorded, the output has been exercised at the relevant downstream boundary, and every acceptance criterion is passed or explicitly blocked. Report untested claims and remaining assumptions instead of filling gaps with plausible output.

Files (agent-skills)
  • evals
    • evals.json 14.9 KB
      {
        "schema_version": 1,
        "skill_name": "ffmpeg",
        "evals": [
          {
            "id": "inspect-before-remux",
            "prompt": "I have an MKV with several tracks and need an MP4 without re-encoding. Give me a safe approach.",
            "expected_output": "Explain that this is a remux/stream-copy operation, probe the input first, make stream selection explicit, use no-overwrite behavior during exploration, and probe the output because container compatibility is not guaranteed.",
            "assertions": [
              "distinguishes remuxing from transcoding",
              "uses ffprobe before the operation",
              "includes explicit mapping or explains why mapping is needed",
              "mentions container and stream compatibility",
              "includes post-run output verification"
            ],
            "case_set": "dev"
          },
          {
            "id": "build-aware-filter",
            "prompt": "This command fails with No such filter: drawtext. Replace it with something that will work everywhere.",
            "expected_output": "Do not claim a universal replacement. Explain that filter availability is build-dependent, show how to inspect the installed filter inventory or use the skill's ffmpeg-preflight named checks (--filter NAME), and offer a conditional path: use an available equivalent, install a build containing the filter, or omit the transformation.",
            "assertions": [
              "does not fabricate universal filter availability",
              "checks ffmpeg filter capabilities",
              "treats the error as a build boundary",
              "offers conditional next steps rather than silently substituting",
              "preserves the need to rerun and verify the output"
            ],
            "case_set": "regression"
          },
          {
            "id": "explicit-complex-mapping",
            "prompt": "Overlay a logo image on the first video stream while keeping the first input's audio. Show the important FFmpeg structure.",
            "expected_output": "Use a complex filtergraph with two labeled video inputs, label the overlay output, map that label exactly once, map the selected audio explicitly, and explain that filtering requires decoding and video re-encoding while audio may be copied.",
            "assertions": [
              "uses filter_complex with two inputs",
              "labels and maps the filtered video output",
              "maps the intended audio stream explicitly",
              "explains filter and stream-copy boundaries",
              "does not imply automatic mapping is safe here"
            ],
            "case_set": "dev"
          },
          {
            "id": "timestamp-concat-diagnosis",
            "prompt": "Two clips have matching extensions but concatenation produces a jump and audio drift. What should I inspect and which concat mechanism should I choose?",
            "expected_output": "Treat the issue as a stream-compatibility and timestamp problem. Compare codecs, dimensions, rates, channel layouts, start times, durations, time bases, and packet/frame behavior; distinguish concat demuxer, concat filter, and concat protocol; normalize timestamps and formats when using the filter.",
            "assertions": [
              "does not use file extension as the compatibility test",
              "lists relevant audio/video stream properties",
              "distinguishes concat demuxer from concat filter and protocol",
              "identifies timestamps as a diagnosis surface",
              "recommends probing inputs and output"
            ],
            "case_set": "regression"
          },
          {
            "id": "safe-batch-network",
            "prompt": "Write a shell-loop recipe that converts every file in a directory and sends results to a network endpoint.",
            "expected_output": "Before giving a recipe, set safe boundaries: use arrays or null-delimited discovery, avoid shell concatenation of untrusted filenames, refuse overwrites by default, record command/version/exit status, probe each output, and treat network protocol, timeout, authentication, and destination validation as explicit requirements.",
            "assertions": [
              "addresses shell quoting and untrusted filenames",
              "uses a deliberate overwrite policy",
              "requires per-output verification and logging",
              "calls out network protocol and timeout assumptions",
              "does not expose credentials in command arguments"
            ],
            "case_set": "release"
          },
          {
            "id": "named-capability-check",
            "prompt": "Before I script a transcode, check whether my local ffmpeg build can scale with libx264 and videotoolbox, and whether drawtext exists. Just tell me what is available.",
            "expected_output": "Run bounded capability checks rather than assuming from memory: the ffmpeg-preflight script with --filter/--encoder/--hwaccel named checks or the corresponding inventory commands. Report each requested capability as present or absent separately, distinguish absence from probe failure, and state a next step for any absent capability without claiming universal availability.",
            "assertions": [
              "uses named capability checks or inventories instead of assumptions",
              "reports each requested capability individually",
              "distinguishes absent capabilities from probe or tool failures",
              "does not claim a capability is present without local evidence",
              "offers a conditional next step for absent capabilities"
            ],
            "case_set": "dev"
          },
          {
            "id": "hardware-pipeline-caveat",
            "prompt": "NVENC is enabled. Does that prove hardware acceleration will make this workflow faster on my Mac?",
            "expected_output": "No. Explain that decoding, filtering, memory transfers, and encoding are separate pipeline decisions, that NVIDIA-specific NVENC guidance does not transfer to macOS, and that the exact local build/device/pixel-format path must be inspected and benchmarked end to end.",
            "assertions": [
              "rejects encoder-only speed conclusions",
              "separates decode/filter/encode and memory-transfer decisions",
              "recognizes platform-specific hardware guidance",
              "requires local capability inspection",
              "requires reproducible end-to-end benchmarking"
            ],
            "case_set": "release"
          },
          {
            "id": "synthetic-end-to-end-proof",
            "prompt": "Prove that the FFmpeg media workflow composes end to end without using any personal media. I need reproducible evidence, not just a list of commands.",
            "expected_output": "Run the bounded synthetic editorial workflow in a new task-local directory, retain the intake, source/output probes, evidence packet, EDL, exact command log, sampled frames, audio-path check, and criterion-level acceptance report, and report semantic, listening, and downstream compatibility as unverified rather than passing them.",
            "assertions": [
              "uses generated synthetic audio and video rather than personal media",
              "preserves source and renders to a new output path",
              "cross-references stable workflow, asset, and event identities",
              "retains exact tool versions, commands, probes, and digest evidence",
              "distinguishes integration success from semantic, listening, and downstream acceptance"
            ],
            "case_set": "release"
          },
          {
            "id": "real-media-fixture-boundaries",
            "prompt": "Add regression fixtures for FFmpeg cuts, concat, silence, subtitles, and visual boundary sampling without publishing any personal media.",
            "expected_output": "Generate the bounded synthetic fixture battery in a task-local directory, retain its versioned manifest and exact generators, assert the intended success or rejection boundary for each selected fixture, and separate portable workflow assertions from build-specific observations and editorial review.",
            "assertions": [
              "uses deterministic synthetic generators or committed non-personal text",
              "records exact FFmpeg and FFprobe builds, commands, probes, and limitations",
              "distinguishes stream-copy boundaries from decoded frame-accurate cuts",
              "tests concat compatibility from stream properties rather than extensions",
              "does not turn silence, clipping, subtitle presence, or sparse frames into editorial truth"
            ],
            "case_set": "release"
          },
          {
            "id": "multi-source-edl-plan",
            "prompt": "Plan a montage from three reviewed ranges across two local files. Two ranges need frame-accurate cuts and the inputs have different dimensions and audio rates. Do not execute FFmpeg.",
            "expected_output": "Validate explicit sources, streams, decimal-second ranges, destination order, duration math, and output normalization, then emit a concat-filter plan with correct reused input indexes, trim/atrim timestamp resets, deliberate video/audio normalization, and exactly-once output mapping. Reject stream-copy concat because probe-derived signatures do not match and distinguish the concat protocol as inapplicable.",
            "assertions": [
              "uses one input index per source and maps every event to the correct input",
              "uses trim or atrim with timestamp reset before concat",
              "normalizes declared video and audio properties before decoded concat",
              "derives expected duration and rejects invalid or overlapping destination intervals",
              "distinguishes concat filter, concat demuxer, and concat protocol without executing"
            ],
            "case_set": "release"
          },
          {
            "id": "podcast-audio-evidence-plan",
            "prompt": "Analyze this supplied podcast WAV for long pauses, loudness, true peak, clipping, and transcript-aligned edit candidates. Save a plan, but do not alter the recording or make editorial cuts.",
            "expected_output": "Probe the source, inventory required filters, run explicitly bounded silence, EBU R128, and peak measurements with recorded build/commands/thresholds, validate transcript timing quality, and create a no-overwrite plan whose ranges retain reasons, evidence, confidence, handles, fades, output contract, and needs-listening-review status.",
            "assertions": [
              "records exact measurement commands, build, window, thresholds, and limitations",
              "reports missing filters or measurements as unavailable rather than inventing values",
              "keeps silence, clipping, and transcript-derived ranges as candidates pending listening review",
              "preserves the raw recording and refuses report overwrite",
              "records source ranges, actions, reasons, evidence, confidence, handles, fades, and output contract"
            ],
            "case_set": "release"
          },
          {
            "id": "criterion-level-media-acceptance",
            "prompt": "Verify this rendered MP4 against the attached delivery contract. The probe is present, but the listening review and destination-player result are missing. Tell me whether it passes.",
            "expected_output": "Evaluate each declared stream, format, timing, subtitle, metadata, decode, signal, review, and downstream criterion independently. Report missing listening and destination evidence as UNVERIFIED, keep local probe/decode evidence at its own boundary, and return an overall UNVERIFIED result rather than a pass.",
            "assertions": [
              "reports criterion, expected and observed values, evidence, boundary, and verdict separately",
              "uses explicit tolerances for duration, timestamps, rates, and measured values",
              "marks absent required evidence unverified or blocked rather than passing it",
              "tests streams, subtitles, timing, audio, video, and downstream evidence independently",
              "does not generalize local probe or decode success into destination compatibility"
            ],
            "case_set": "release"
          },
          {
            "id": "bounded-vision-review-handoff",
            "prompt": "Prepare frames around two proposed cut boundaries for a vision reviewer, then link any reviewed findings to my EDL without exposing the private source path or treating the reviewer as an automatic editor.",
            "expected_output": "Create a bounded packet with opaque asset and stream IDs, explicit and neighboring timestamps, extraction/build/transform provenance, hashes, byte and range limits, and a sparse-coverage warning. Require attributed observations with evidence class, confidence, blind spots, artifact references, and editorial consequences before writing a new EDL whose visual evidence remains subject to editorial approval.",
            "assertions": [
              "records opaque identity, stream, timestamps, build, extraction parameters, transforms, hashes, and coverage",
              "enforces frame-count, timestamp-range, and output-size limits",
              "does not expose the private source path in the shareable manifest",
              "rejects missing attribution and whole-video conclusions from sparse samples",
              "links reviewed observations to named EDL events as evidence rather than automatic truth"
            ],
            "case_set": "release"
          },
          {
            "id": "named-target-compatibility",
            "prompt": "Will this MP4 work everywhere? I have its FFprobe output and a successful local decode, and I need it accepted by a named editor whose documented limits are attached.",
            "expected_output": "Build a versioned manifest for that one editor from official or explicitly observed requirements, evaluate probe-visible codec/profile, dimensions/rates, mapping, subtitles/metadata, duration, and size separately, and require evidence from importing or playing the exact artifact in the named editor. Report missing access or evidence as BLOCKED or UNVERIFIED and do not generalize the result.",
            "assertions": [
              "links each target requirement to official documentation or labeled observed behavior with an access date",
              "checks codec profile, dimensions, rates, stream order, subtitles, metadata, duration, and size",
              "separates local probe and decode evidence from the named target result",
              "uses PASS, FAIL, BLOCKED, or UNVERIFIED for the exact target lane",
              "does not claim compatibility with every platform and leaves upload operations to the owning platform skill"
            ],
            "case_set": "release"
          },
          {
            "id": "media-research-routing-boundary",
            "prompt": "Build a defensible research record for an FFmpeg behavior claim and a sparse-frame editorial claim. I need source-to-claim traceability, not another command recipe.",
            "expected_output": "Route research scope, source/access-date evaluation, exclusions, claim closure, and durable preservation to research-methodology while retaining FFmpeg build, command, probe, fixture, sample, and target evidence in this skill. Keep documented semantics, reproduced behavior, observed artifacts, heuristics, and human review separate, and refuse editorial or whole-asset conclusions beyond sampled evidence.",
            "assertions": [
              "routes general research method to research-methodology and operational media evidence to ffmpeg",
              "records official source URLs and access dates plus exact local build and experiment conditions",
              "preserves rejected, inaccessible, redundant, and untested sources with reasons",
              "audits sampling coverage against the scope of each claim",
              "keeps technical findings separate from attributed editorial approval"
            ],
            "case_set": "release"
          }
        ]
      }
      
  • references
    • advanced-operations-and-safety.md 5.4 KB
      # Advanced Operations and Safety
      
      ## Hardware acceleration is a pipeline decision
      
      Hardware acceleration may affect decoding, filtering, and encoding separately. A hardware encoder alone does not guarantee faster end-to-end processing. Transfers between system memory and device memory can erase gains, and hardware encoders can have different quality, feature, and rate-control behavior from software encoders.
      
      First inspect the local build:
      
      ```sh
      ffmpeg -hwaccels
      ffmpeg -encoders
      ffmpeg -filters | grep -E 'cuda|vaapi|qsv|videotoolbox|vulkan'
      ```
      
      The recorded local macOS build (see `local-verification.md`) lists `videotoolbox`. NVIDIA CUDA examples from the vendor guide do not transfer to other platforms. Verify the exact device, pixel formats, filter path, and encoder on the target build before benchmarking.
      
      ## Timestamp and synchronization diagnosis
      
      When audio drifts, video freezes, concat jumps, or duration is wrong, collect:
      
      ```sh
      ffprobe -v error -show_streams -show_format -of json input
      ffmpeg -loglevel verbose -i input -f null -
      ```
      
      Compare stream start times, durations, time bases, frame rates, sample rates, packet ordering, and whether a muxer is buffering sparse streams. Avoid cargo-culting timestamp flags. Options such as `-start_at_zero`, `-copyts`, `-vsync`/the modern synchronization controls, `setpts`, `asetpts`, `aresample`, and `avoid_negative_ts` solve different problems and can interact.
      
      ### Audio drift: diagnose before adjusting
      
      Audio drift (gradually increasing A/V offset, or audio that ends before/after video) is a timing or rate mismatch, not a volume problem. Diagnose in this order:
      
      1. **Probe both streams.** Compare `start_time`, `duration`, `time_base`, `sample_rate`, and codec with `ffprobe`. A nonzero or mismatched `start_time` between audio and video shifts the whole track; different `sample_rate` or a rate-labeling mismatch causes progressive drift.
      2. **Check the operation history.** Concatenating sources with different sample rates or time bases, trimming without `asetpts`, or re-encoding AAC (encoder delay/priming samples) each produce characteristic offset patterns. AAC priming shifts audio by a fixed amount; rate mismatch grows over time.
      3. **Normalize deliberately.** `aresample=async=1:first_pts=0` resamples and stretches/squeezes audio onto the video clock, compensating small drift; `asetpts=PTS-STARTPTS` resets timestamps after trimming or concat; `aformat` plus an explicit `sample_rate` makes both inputs share one rate before joining. Apply these where the mismatch originates rather than at the final output only.
      
      If drift appears only in a specific player or receiver, verify the container edit lists and stream timestamps there before changing the encode; the muxer may be preserving an offset the encoder did not create.
      
      ## Reproducible experiments
      
      Pin the binary version, record `ffmpeg -version` and `-buildconf`, preserve the exact input or synthetic generator, and probe both sides. Run more than once when measuring speed. Separate wall-clock throughput from output quality and compatibility. If a tutorial omits hardware, build, driver, codec settings, or measurement method, treat its performance claim as incomplete.
      
      ## Security and operational boundaries
      
      Do not feed untrusted media to an experimental decoder or enable permissive protocol behavior without understanding the exposure. Avoid secrets in command-line arguments when process listings or logs can expose them. Restrict network protocols and destinations. Refuse overwrites by default during development, write to a new path, and keep the original until the output is independently verified.
      
      A syntax check, successful process exit, valid container, or local playback test proves only that layer. Acceptance should match the real boundary: target player, editor, streaming receiver, archival standard, or API consumer.
      
      ## Advanced learning resources
      
      Use the official filter, codec, format, protocol, utility, scaler, and resampler manuals as the reference corpus. Use `slhck/ffmpeg-encoding-course` for a structured intermediate bridge, `amiaopensource/ffmprovisr` for preservation-oriented practice, and the NVIDIA guide for a vendor-specific hardware path. The official Trac wiki is useful but was inaccessible during this research pass, so its examples remain leads rather than verified evidence.
      
      SOURCES (LAYER 3 NAVIGATION)
      https://ffmpeg.org/ffmpeg-codecs.html
       -> Codec options, rate control, time bases, error detection, and encoder-specific behavior.
      
      https://ffmpeg.org/ffmpeg-protocols.html
       -> Protocol options, network I/O, timeouts, and whitelists.
      
      https://ffmpeg.org/ffmpeg-formats.html
       -> Probing, interleaving, timestamp shifting, and muxer/demuxer behavior.
      
      https://ffmpeg.org/ffmpeg-resampler.html
       -> Sample-rate conversion, compensation, and the async/first_pts options used for audio drift repair.
      
      https://ffmpeg.org/ffmpeg-filters.html
       -> setpts/asetpts, aformat, and timestamp normalization filters.
      
      https://docs.nvidia.com/video-technologies/video-codec-sdk/13.0/ffmpeg-with-nvidia-gpu/index.html
       -> NVIDIA-specific CUDA/NVENC pipeline examples and performance cautions.
      
      https://github.com/slhck/ffmpeg-encoding-course
       -> Maintained secondary course for encoding concepts and practical progression.
      
      https://github.com/amiaopensource/ffmprovisr
       -> Secondary preservation/media workflow recipe collection.
      
      https://ffmpeg.org/download.html
       -> Official release and source-build guidance.
      
    • audio-and-podcast-editing.md 5.7 KB
      # Audio and Podcast Editing
      
      Separate mechanical assembly, signal processing, and editorial judgment. FFmpeg can measure and transform audio; it cannot decide whether a pause, breath, correction, tone, identity, or statement should be removed.
      
      ## Intake and plan
      
      Probe each candidate stream and record codec, sample format/rate, channel count/layout, time base, start/duration, disposition, and language metadata. Define the output contract, target loudness policy if one exists, required channel layout, allowed repairs, prohibited edits, and review owner.
      
      Divide proposed work into:
      
      - **Mechanical:** explicit range cuts, reorder, fades, slate/tone removal, channel mapping, resampling, and encoding.
      - **Signal repair:** gain, equalization, hum/noise reduction, de-essing, dynamics, clipping prevention, or dropout repair.
      - **Editorial:** removing speech, changing sequence, shortening pauses, selecting takes, or altering context.
      
      Put source ranges and evidence in a podcast edit plan or EDL. A transcript is a navigation aid; verify words, timing, speaker changes, and context by listening to the source.
      
      ## Measurement and candidate detection
      
      Relevant filters include:
      
      - `astats` for time-domain audio statistics;
      - `volumedetect` for volume statistics;
      - `ebur128` for EBU R128 analysis and metadata/log output;
      - `loudnorm` for EBU R128 normalization and measured values;
      - `silencedetect` for threshold- and duration-based silence candidates;
      - `aphasemeter` for channel phase measurements where applicable.
      
      Record the exact interval, filter options, channel mode, FFmpeg build, and unfiltered source. Noise floors, breaths, room tone, music, cross-talk, and codec artifacts can invalidate a generic threshold.
      
      `scripts/audio-inspect` implements a bounded evidence path. Select `--measure-silence`, `--measure-loudness`, and/or `--measure-clipping`; each requested measurement records the command, analysis window, filter, thresholds, and local build. A missing filter produces `UNAVAILABLE`, not a fabricated value. Silence intervals and peak-based clipping flags are candidates with a listening-review gate.
      
      Pass `--transcript` only a timed JSON document with a non-empty `quality` object and `segments` containing `start`, `end`, and `text`. The report retains timing and a text digest rather than reproducing transcript content. A supplied `proposed_action` remains `needs_listening_review`. Output codec/rate/layout and loudness limits form a declared contract; handles and fades are plan fields, not executed edits. `--report-output` creates a new file exclusively and refuses overwrite.
      
      For loudness normalization, a measured first pass followed by a parameterized second pass is more reviewable than assuming one-pass behavior meets a delivery policy. Verify the rendered output again; a filter’s reported target is not acceptance evidence by itself.
      
      ## Editing and processing
      
      - Use `atrim` for ranges and `asetpts=PTS-STARTPTS` when a segment needs a zero-based timeline.
      - Use the concat filter after making sample rate, sample format, and channel layout deliberate.
      - Use `afade`/`acrossfade` only when their duration and overlap are editorially approved.
      - Treat `silenceremove` as an editorial transform, not harmless cleanup.
      - Apply `highpass`, `lowpass`, `afftdn`, `arnndn`, compression, limiting, or normalization only after a bounded comparison. Filter availability and behavior depend on the local build and options.
      - Avoid repeated lossy encoding. Keep a suitable intermediate when multiple review passes are required.
      - Map audio and attached video/subtitle streams explicitly; define metadata and chapter retention.
      
      ## Listening acceptance
      
      Listen to the opening and closing, every cut/fade/transition, all repaired regions, representative loud and quiet passages, channel fold-down if relevant, and any section flagged by measurements. Check speech intelligibility, clicks, truncation, pumping, tonal shifts, room-tone jumps, phase issues, context, and sync with video. Test the intended destination.
      
      Keep review records privacy-safe: use opaque speaker labels, quote only the minimum required text, and do not publish raw transcripts, private paths, or embedded tags.
      
      ## Evidence and heuristic boundary
      
      | Classification | Boundary |
      |---|---|
      | Direct evidence | Probe fields, decoded samples, filter measurements, commands, logs, and attributed listening observations for declared intervals. |
      | Threshold evidence | Silence, loudness, clipping, or phase candidates under explicitly recorded filter settings. |
      | Heuristic | Speaker labels, transcript timing, “noise-only” regions, acceptable pause length, or a preset suitable for another recording. |
      | Editorial judgment | Whether an edit preserves meaning, consent, tone, and continuity. This requires accountable listening review. |
      | Not established | Whole-program quality from a few measurements, speaker identity, legal clearance, accessibility, or destination acceptance from an FFmpeg exit status. |
      
      ## Official FFmpeg sources
      
      - [ffprobe Documentation](https://ffmpeg.org/ffprobe.html) — audio stream and packet/frame inspection.
      - [ffmpeg Documentation](https://ffmpeg.org/ffmpeg.html) — mapping, filtering, codecs, timestamps, and transcoding.
      - [FFmpeg Filters Documentation](https://ffmpeg.org/ffmpeg-filters.html) — `atrim`, `asetpts`, concat, fades, silence, statistics, loudness, equalization, denoising, dynamics, and resampling filters.
      - [FFmpeg Resampler Documentation](https://ffmpeg.org/ffmpeg-resampler.html) — resampling and rematrixing options.
      - [FFmpeg Codecs Documentation](https://ffmpeg.org/ffmpeg-codecs.html) — codec-specific capabilities and options.
      
      Official documentation establishes mechanics, not local component availability, editorial correctness, or compliance with an external delivery specification.
      
    • command-cookbook.md 1.9 KB
      # Command Cookbook
      
      These are learning commands, not universal production defaults. Check the installed build, input streams, target requirements, and output with `ffprobe`.
      
      ## Inspect
      
      ```sh
      ffprobe -v error -show_format -show_streams -of json input.mp4
      ```
      
      ## List local capabilities
      
      ```sh
      ffmpeg -hide_banner -formats
      ffmpeg -hide_banner -codecs
      ffmpeg -hide_banner -filters
      ffmpeg -hide_banner -encoders
      ffmpeg -hide_banner -hwaccels
      ```
      
      ## Remux without re-encoding
      
      ```sh
      ffmpeg -n -i input.mkv -map 0 -c copy output.mp4
      ```
      
      ## Explicit transcode
      
      ```sh
      ffmpeg -n -i input.mov -map 0:v:0 -map 0:a:0 \
        -c:v libx264 -crf 20 -preset medium \
        -c:a aac -b:a 160k output.mp4
      ```
      
      ## Scale and resample
      
      ```sh
      ffmpeg -n -i input.mp4 -vf 'scale=1280:-2' -ar 48000 output.mp4
      ```
      
      ## Overlay a second input
      
      ```sh
      ffmpeg -n -i video.mp4 -i logo.png \
        -filter_complex '[0:v][1:v]overlay=20:20[v]' \
        -map '[v]' -map 0:a:0 -c:v libx264 -c:a copy output.mp4
      ```
      
      ## Fast exploratory cut
      
      ```sh
      ffmpeg -n -ss 00:01:00 -i input.mp4 -t 00:00:20 -c copy cut.mp4
      ```
      
      ## Extract audio
      
      ```sh
      ffmpeg -n -i input.mp4 -map 0:a:0 -vn -c:a flac output.flac
      ```
      
      ## Generate a synthetic test asset
      
      ```sh
      ffmpeg -f lavfi -i 'testsrc2=size=320x180:rate=30' \
        -f lavfi -i 'sine=frequency=440:sample_rate=48000' \
        -t 2 -c:v libx264 -pix_fmt yuv420p -c:a aac test.mp4
      ```
      
      ## Diagnostic null output
      
      ```sh
      ffmpeg -hide_banner -loglevel verbose -i input.mp4 -f null -
      ```
      
      ## Build-aware filter check
      
      ```sh
      ffmpeg -filters | grep -E 'scale|fps|drawtext|subtitles'
      ffmpeg -h filter=scale
      ```
      
      SOURCES (LAYER 3 NAVIGATION)
      https://ffmpeg.org/ffmpeg.html
       -> Primary command syntax, mapping, copy/transcode, seeking, and filtering options.
      
      https://ffmpeg.org/ffprobe.html
       -> Primary inspection and machine-readable output.
      
      references/local-verification.md
       -> Commands actually exercised on the local FFmpeg 8.1.2 build.
      
    • core-model-and-command-anatomy.md 3.8 KB
      # Core Model and Command Anatomy
      
      ## The pipeline
      
      The `ffmpeg` command line accepts global options, one or more input blocks, and one or more output blocks:
      
      ```sh
      ffmpeg [global_options] {[input_options] -i input_url} ... {[output_options] output_url} ...
      ```
      
      Options generally apply to the next input or output, so order matters. Input and output indexes are zero-based. Stream specifiers such as `:v`, `:a`, `:s`, and `:1` narrow an option to a type or stream index.
      
      The conceptual pipeline is:
      
      ```text
      input URL -> protocol/IO -> demuxer -> streams -> decode -> filters -> encode -> muxer -> output URL
      ```
      
      Stream copy skips decode, filtering, and encode for the copied stream. It is therefore fast and lossless with respect to the encoded stream, but cannot perform transformations on that stream.
      
      ## Selection and mapping
      
      Automatic stream selection is convenient for simple files but unsafe for multi-input work. `-map 0:v:0 -map 1:a:0` selects the first video from input 0 and first audio from input 1. `-map 0` asks for all streams from input 0, subject to output-format limits. A complex filtergraph's labeled outputs must be mapped exactly once.
      
      Use per-stream options deliberately:
      
      ```sh
      ffmpeg -i input.mkv -map 0:v:0 -map 0:a:0 -c:v libx264 -crf 20 -c:a aac -b:a 160k output.mp4
      ```
      
      This is an illustrative transcode, not a universal quality or bitrate recommendation. The correct settings depend on source, target, motion, delivery constraints, and playback support.
      
      ## Container and codec are different decisions
      
      A container packages streams. A codec encodes one stream. Renaming a file does not convert it. A remux can change packaging without re-encoding:
      
      ```sh
      ffmpeg -i input.mkv -map 0 -c copy output.mp4
      ```
      
      The command can fail or produce an unsuitable file when the selected streams, metadata, or timing do not fit the target container. Probe both input and output.
      
      ## Quality and generation loss
      
      Re-encoding is required for filtering, changing many codec properties, or adapting an incompatible stream. Each lossy generation may discard information. Prefer stream copy when the operation is only a compatible container change, but do not force copy when the target needs a different codec, pixel format, sample format, or timing structure.
      
      ## Time and timestamps
      
      Video and audio use timestamps expressed in stream-specific time bases. FFmpeg documentation defines a time base as the fundamental time unit for frame timestamps. Fixed-frame-rate video commonly uses a time base related to the frame rate, while an MP4 stream may use a finer muxer time base. Do not compare raw PTS integers from different streams without rescaling them into a common time unit.
      
      Seeking, trimming, synchronization, and concat problems are timestamp problems until proven otherwise. Record the relevant `start_time`, `duration`, `time_base`, frame rate, and packet/frame behavior with `ffprobe`.
      
      ## Probe before and after
      
      ```sh
      ffprobe -v error -show_format -show_streams -of json input.mkv
      ffprobe -v error -show_entries stream=index,codec_type,codec_name,width,height,sample_rate,channels,time_base,duration -of json output.mp4
      ```
      
      Machine-readable output is preferable in scripts. Treat human-readable stderr as diagnostic evidence, not as a stable parsing interface.
      
      SOURCES (LAYER 3 NAVIGATION)
      https://ffmpeg.org/ffmpeg.html
       -> Command syntax, option scope, stream selection, stream copy, transcoding, and mapping.
      
      https://ffmpeg.org/ffprobe.html
       -> Inspection, stream specifiers, machine-readable writers, and intervals.
      
      https://ffmpeg.org/ffmpeg-codecs.html
       -> Codec options, rate control, time bases, and error detection.
      
      https://ffmpeg.org/ffmpeg-formats.html
       -> Demuxers, muxers, probing, interleaving, and timestamp-related format behavior.
      
      https://ffmpeg.org/ffmpeg-utils.html
       -> Duration syntax, rational numbers, expressions, and quoting/escaping.
      
    • editorial-video-editing.md 4.9 KB
      # Editorial Video Editing
      
      Treat an editorial edit as a sequence of reviewable decisions, not as one opaque FFmpeg command. Preserve source media, use an edit decision list (EDL), render to a new path, and separate technical conformance from human approval.
      
      ## Plan before rendering
      
      1. Write the editorial goal, audience, required duration or range, prohibited changes, output contract, and reviewer.
      2. Probe all selected streams and establish their timelines, time bases, start times, frame cadence, audio layout, subtitles, and metadata.
      3. Use transcripts, scene scores, silence events, and frame samples only to navigate. Verify consequential words, cuts, identities, and context against decoded media.
      4. Record each keep/remove/reorder/treatment decision with source range, rationale, evidence locator, confidence, and required review.
      5. Validate the EDL before generating a filtergraph: ranges must be ordered, bounded, and compatible with transitions and linked audio.
      
      ## Cut and assembly choices
      
      - **Filter-based cuts:** `trim` and `atrim` select ranges but do not reset timestamps. Follow them with `setpts=PTS-STARTPTS` and `asetpts=PTS-STARTPTS` when segments must begin at zero before concatenation.
      - **Concat filter:** use for decoded segments in one graph. Corresponding streams must have compatible parameters; normalize geometry, pixel format, sample format/rate, and channel layout deliberately.
      - **Concat demuxer:** use an `ffconcat` list for separate files whose streams are suitable for packet-level concatenation. Its `duration`, `inpoint`, and `outpoint` directives have documented timestamp and packet-boundary caveats.
      - **Stream-copy cuts:** fast and lossless at the packet level, but start/end precision is constrained by seek points, inter-frame dependencies, timestamps, and muxer behavior. Do not promise frame-accurate editorial cuts without checking the decoded result.
      - **Transitions:** overlaps such as `xfade` or `acrossfade` consume timeline duration and require adequate handles. Put the overlap and expected output-duration calculation in the EDL.
      
      Map streams explicitly. Define whether chapters, attachments, data streams, subtitles, language tags, dispositions, and metadata are retained, rewritten, or removed. An omitted `-map` leaves stream selection to automatic rules that may not match editorial intent.
      
      ## Treatments
      
      Apply only treatments required by the brief:
      
      - geometry: `crop`, `scale`, `pad`, rotation/orientation handling;
      - timing: `fps`, `setpts`, `atempo`, or resampling only with an explicit cadence/sync decision;
      - compositing: overlays, masks, titles, or subtitles with rights and readability review;
      - picture: deinterlacing, range/color conversion, or grading with source and target color assumptions recorded;
      - sound: fades, gain, loudness, noise reduction, and channel mapping under the audio plan.
      
      Keep an intermediate render when it improves reviewability, but avoid unnecessary generations. Record codecs and settings at every lossy boundary.
      
      ## Review gates
      
      1. **EDL review:** all consequential cuts and treatments approved.
      2. **Technical render review:** expected streams, timestamps, duration, geometry, cadence, color tags, audio format, and decode behavior.
      3. **Content review:** opening/closing, every join and transition, titles/subtitles, linked audio, and any high-risk treatment.
      4. **Editorial review:** meaning, pacing, continuity, context, accessibility, and approved claims.
      5. **Destination review:** playback or import in the intended player, editor, service, or archive workflow.
      
      ## Evidence and heuristic boundary
      
      - **Direct evidence:** probe/decode results, recorded EDL ranges, commands, logs, and reviewed samples for this build and artifact.
      - **Derived evidence:** expected duration or transition math computed from declared ranges and rounding rules.
      - **Heuristic:** transcript alignment, scene/silence candidates, automated crop choices, inferred continuity, or “visually lossless” judgments. Mark and review them.
      - **Human decision:** editorial suitability and preservation of meaning require an accountable reviewer.
      - **Not established:** a successful render does not prove frame-accurate cuts, correct context, accessibility, rights, sync everywhere, or destination acceptance.
      
      ## Official FFmpeg sources
      
      - [ffmpeg Documentation](https://ffmpeg.org/ffmpeg.html) — seeking, stream selection, mapping, filtering, codecs, metadata, and overwrite controls.
      - [FFmpeg Filters Documentation](https://ffmpeg.org/ffmpeg-filters.html) — `trim`, `atrim`, timestamp filters, concat, transitions, geometry, subtitles, and audio/video treatments.
      - [FFmpeg Formats Documentation](https://ffmpeg.org/ffmpeg-formats.html) — concat demuxer and muxer/container behavior.
      - [FFmpeg Utilities Documentation](https://ffmpeg.org/ffmpeg-utils.html) — timeline and duration expressions.
      
      These sources define mechanics. They do not supply an editorial rationale, validate transcript meaning, or guarantee compatibility with a particular destination.
      
    • editorial-workflow-example.md 2.7 KB
      # Reproducible Editorial Workflow Example
      
      Use this example to prove that the FFmpeg skill artifacts compose from intake through acceptance. It generates synthetic media only; it does not authorize work on user media and it does not establish editorial quality or destination compatibility.
      
      ## Run the example
      
      From the `ffmpeg` skill directory, choose a new or empty task-local directory:
      
      ```sh
      scripts/editorial-workflow-example /tmp/ffmpeg-editorial-example --json
      ```
      
      The helper refuses a non-empty workspace and uses `-n` for every generated media artifact. It creates a short `testsrc2` video with an 880 Hz synthetic audio track, then performs this complete path:
      
      ```text
      generate -> probe/intake -> bounded evidence -> EDL -> decoded trim
               -> output probe/decode -> frame/audio checks -> acceptance
      ```
      
      The example requires locally installed `ffmpeg` and `ffprobe`. It accepts `--ffmpeg` and `--ffprobe` paths and bounds source duration and per-command runtime.
      
      ## Artifact closure
      
      Every durable record uses `workflow_id: synthetic-editorial-example`; the source is `asset-synthetic-001`, and the bounded trim is `event-001`.
      
      | Artifact | Evidence role |
      |---|---|
      | `synthetic-source.mkv` | Deterministically generated source with video and audio streams |
      | `source-probe.json` | Source structure reported by the current FFprobe build |
      | `intake-manifest.json` | Source identity, digest, tool versions, preservation policy, and output contract |
      | `evidence-packet.json` | Bounded observation and explicit semantic/listening/downstream gaps |
      | `edit-decision-list.json` | Reviewed synthetic trim with source/event identity and expected duration |
      | `edited-output.mkv` | New rendered output; the source is not overwritten |
      | `output-probe.json` | Output stream and duration evidence |
      | `review-frame-*.png` | Opening/closing samples that cover only their timestamps |
      | `acceptance-report.json` | Criterion-level component, integration, signal, sampling, and downstream verdicts |
      | `command-log.json` | Exact executed commands for the recorded local build |
      
      ## What the result proves
      
      A successful run proves that the recorded local FFmpeg/FFprobe build completed this synthetic workflow, the output decoded, required streams were present, duration met tolerance, bounded frames were extracted, and the audio processing path completed.
      
      It does **not** prove semantic visual correctness, listening quality, accessibility, rights, or compatibility with any player, editor, host, archive, or upload API. The acceptance report must retain those items as `UNVERIFIED` until the appropriate reviewer or destination supplies evidence.
      
      Do not commit generated media or task-local evidence. Preserve the report package with the task when it is being used as release evidence.
      
    • ffmpeg-edit-decision-lists.md 5.8 KB
      # FFmpeg Edit Decision Lists
      
      An edit decision list (EDL) is the reviewable source of truth between editorial intent and an FFmpeg render plan. Keep it data-oriented, versioned, and independent of private filesystem paths.
      
      ## Canonical semantics
      
      Use opaque asset IDs and identify streams explicitly. For each source, retain probe evidence for stream index, time base, start time, duration, cadence, audio layout, and source digest.
      
      Define ranges as half-open intervals, `[in, out)`, on the selected source stream timeline. State the time unit and precision. Decimal seconds are convenient for review; preserve exact timestamps or integer ticks when frame/sample boundaries matter. Never silently treat timecode, container time, wall-clock time, and frame number as interchangeable.
      
      Each event should record:
      
      - stable event ID and action (`keep`, `remove`, `insert`, or `treatment`);
      - source asset and stream references;
      - source `in` and `out`, plus any transition handles;
      - destination order or lane;
      - linked audio/video policy;
      - rationale, evidence locators, confidence, and reviewer status;
      - transformations, transition type/duration, and expected duration effect;
      - whether the cut requires decoded precision or permits packet-level copy;
      - verification points around the resulting boundary.
      
      Store commands as generated render records, not as the EDL’s only meaning. Raw paths and shell fragments are unsafe substitutes for structured fields.
      
      ## Pre-render validation
      
      Reject or flag an EDL when:
      
      - a source, stream, time unit, or range endpoint is missing;
      - `in >= out`, a range falls outside declared source bounds, or rounding behavior is undefined;
      - events overlap unintentionally or leave an unexplained gap;
      - linked streams use incompatible timelines or omit a sync policy;
      - transitions lack sufficient handles or their overlap is absent from duration math;
      - concat inputs have unresolved format differences;
      - frame/sample-accurate intent is paired with an unverified stream-copy strategy;
      - required editorial decisions have no evidence or review status.
      
      Calculate expected output duration from kept ranges, inserts, speed changes, and transition overlaps. Mark the result as derived and declare a tolerance for timestamp/time-base rounding.
      
      ## Mapping to FFmpeg
      
      For decoded segment assembly, use `trim`/`atrim`, reset segment timestamps with `setpts`/`asetpts` where required, normalize compatible media parameters deliberately, and join with the concat filter. Map output streams explicitly.
      
      For separate compatible files, the concat demuxer consumes an `ffconcat` list. Its `inpoint` and `outpoint` can include packets outside the requested interval because of inter-frame dependencies and packet boundaries; timestamps can also be adjusted globally. Review the decoded joins.
      
      `scripts/render-edl` validates schema-v1 decimal-second EDLs and emits a plan without executing FFmpeg. Its default `concat-filter` strategy:
      
      - assigns one input index per declared source and reuses that index across events;
      - trims and resets timestamps for every selected video/audio segment;
      - applies declared video/audio normalization before concat;
      - maps each generated output label exactly once;
      - derives duration from event ranges and checks the declared tolerance.
      
      Use `--strategy concat-demuxer` only when every source carries the same probe-derived `compatibility_signature` and every event declares `boundary_precision: packet` with `keyframe_status: verified` or `not_applicable`. The plan returns an `edit.ffconcat` payload; write and review that file before execution. The helper never selects the concat protocol, because a structured EDL needs explicit file/range semantics rather than URL-style concatenation.
      
      Transitions are deliberately rejected until a separately validated design supplies transition duration, handles, stream layout, and output-duration math. Source ranges may be reused or reordered; explicit destination ranges may not overlap.
      
      Fast seek and stream copy may choose seek points or packets that do not correspond to an exact visual/audio edit boundary. Label keyframe/packet status as one of `verified`, `not_verified`, or `not_applicable`; never infer it from a round timestamp.
      
      Record FFmpeg/ffprobe versions, complete generated command, mapping, codec settings, environment-sensitive capabilities, output digest, and acceptance report alongside the rendered artifact.
      
      ## Evidence and heuristic boundary
      
      - **Direct evidence:** source probe data, exact EDL fields, reviewed source samples, packet/frame observations, generated command, and output verification records.
      - **Derived evidence:** output order and duration computed from declared EDL semantics and a stated rounding rule.
      - **Heuristic:** transcript-aligned endpoints, scene/silence candidates, guessed keyframes, or assumed concat compatibility. These must be labeled and tested.
      - **Human decision:** rationale, continuity, context, and approval are editorial evidence only when attributed.
      - **Not established:** an internally valid EDL does not prove render precision, sync, semantic correctness, rights, or downstream acceptance.
      
      ## Official FFmpeg sources
      
      - [ffmpeg Documentation](https://ffmpeg.org/ffmpeg.html) — seeking, timestamps, stream selection/mapping, filtering, and codec-copy behavior.
      - [FFmpeg Filters Documentation](https://ffmpeg.org/ffmpeg-filters.html) — `trim`, `atrim`, `setpts`, `asetpts`, concat, `xfade`, and `acrossfade` semantics.
      - [FFmpeg Formats Documentation](https://ffmpeg.org/ffmpeg-formats.html) — concat demuxer syntax, `duration`, `inpoint`, `outpoint`, and format behavior.
      - [FFmpeg Utilities Documentation](https://ffmpeg.org/ffmpeg-utils.html) — duration and time expression syntax.
      
      The sources define FFmpeg’s timeline and assembly mechanisms. The EDL conventions above are workflow rules; they are not an FFmpeg-native interchange standard.
      
    • filters-and-transformations.md 3.6 KB
      # Filters and Media Transformations
      
      ## Simple versus complex graphs
      
      A simple video or audio filter can be attached with `-vf` or `-af`:
      
      ```sh
      ffmpeg -i input.mp4 -vf 'scale=1280:-2,fps=30' -af 'loudnorm' output.mp4
      ```
      
      The filter string is a graph, even when it is a linear chain. Filters consume frames or audio samples and produce new ones, so filtering implies decoding and re-encoding for that stream.
      
      Use `-filter_complex` when multiple inputs, branches, overlays, joins, or separately labeled outputs are involved:
      
      ```sh
      ffmpeg -i video.mp4 -i logo.png \
        -filter_complex '[0:v][1:v]overlay=20:20[vout]' \
        -map '[vout]' -map 0:a:0 -c:v libx264 -c:a copy output.mp4
      ```
      
      Labels are graph edges. Every labeled output that should reach an output file must be mapped. Unmapped filtered output is not a harmless detail: it changes what reaches the muxer or causes an error.
      
      ## Common transformation classes
      
      - `scale` changes dimensions and may require an explicit pixel format or aspect-ratio policy.
      - `fps` changes frame cadence and can affect duration, motion, and synchronization.
      - `crop`, `pad`, `transpose`, and `setsar` change geometry or display interpretation.
      - `trim` and `atrim` select time ranges but generally need timestamp normalization such as `setpts` or `asetpts` before concatenation.
      - `volume`, `loudnorm`, `aresample`, and `aformat` alter audio level, loudness, sample rate, or format.
      - `subtitles` and `drawtext` render text into pixels when available, unlike a copied subtitle stream.
      - `overlay`, `hstack`, `vstack`, `concat`, `amix`, and `amerge` combine streams and therefore require compatible timing and formats.
      
      These names are not guarantees. Confirm local availability:
      
      ```sh
      ffmpeg -filters
      ffmpeg -h filter=scale
      ffmpeg -h filter=drawtext
      ```
      
      ## Audio and video are separate graphs
      
      A video filter cannot repair audio synchronization by itself. Treat audio and video as separate streams with separate clocks and sample/frame formats. When combining or transcoding them, inspect sample rate, channel layout, start timestamps, duration, and encoder delay.
      
      The resampler can convert sample rates, channel layouts, and sample formats. The scaler converts image dimensions and pixel formats. Both have quality and range choices that should be explicit when the result matters.
      
      ## Local failure as evidence
      
      The local FFmpeg 8.1.2 build did not include `drawtext`. A command that combined `scale`, `fps`, and `drawtext` stopped with `No such filter: 'drawtext'` before creating output. This demonstrates why a tutorial's command must be treated as a recipe conditioned on a build, not as a universal API.
      
      ## Debugging filtergraphs
      
      Build graphs incrementally. First transcode without filters, then add one filter, then add labels and branches. Use short synthetic inputs, verbose logging, and explicit `-map`. If a graph fails, distinguish parser errors, missing filters, format negotiation errors, timestamp errors, and encoder/muxer errors.
      
      SOURCES (LAYER 3 NAVIGATION)
      https://ffmpeg.org/ffmpeg-filters.html
       -> Filtergraph syntax, pads, labels, filter families, framesync, and filter options.
      
      https://ffmpeg.org/ffmpeg-scaler.html
       -> Image scaling and pixel-format conversion.
      
      https://ffmpeg.org/ffmpeg-resampler.html
       -> Audio resampling, rematrixing, formats, dithering, and compensation.
      
      https://trac.ffmpeg.org/wiki/FilteringGuide
       -> Official wiki tutorial lead for filtergraphs; access was blocked by Anubis during this research and commands require verification.
      
      references/local-verification.md
       -> Local filter inventory counts and the verified missing-filter failure.
      
    • intermediate-workflows.md 6.1 KB
      # Intermediate Workflows
      
      ## Inspection and selective conversion
      
      Start with `ffprobe`, then make selection explicit. For scripts, use JSON and fail if the expected stream is absent. For a simple conversion:
      
      ```sh
      ffmpeg -i input.mov -map 0:v:0 -map 0:a:0 -c:v libx264 -c:a aac output.mp4
      ```
      
      For a compatible remux:
      
      ```sh
      ffmpeg -i input.mkv -map 0 -c copy output.mp4
      ```
      
      Do not call remuxing a conversion of the encoded media. It changes packaging only.
      
      ## Trim and seek
      
      `-ss` before the input is input seeking: the demuxer jumps to the nearest seek point before the target, which is fast. `-ss` after the input (before the output) is output seeking: FFmpeg decodes and discards from the stream start until the target, which is slow for late cut points. `-t` limits duration; `-to` specifies an endpoint in the relevant command context. Test the actual cut, especially with inter-frame codecs, nonzero start timestamps, and audio.
      
      Decision rule:
      
      - **Re-encoding anyway:** put `-ss` before `-i`. With the default accurate seek, frames between the keyframe and the target are decoded and discarded, so the cut is frame-accurate and still fast.
      - **Stream copy (`-c copy`):** packets cannot be decoded and discarded, so output starts at the packet boundary the demuxer lands on — typically the keyframe at or before the target. Accept keyframe-aligned cuts and verify the actual start time with `ffprobe`, or re-encode for frame accuracy.
      
      ```sh
      ffmpeg -ss 00:01:00 -i input.mp4 -t 00:00:20 -c copy quick-cut.mp4
      ```
      
      Stream-copy cuts may begin on keyframe boundaries and can preserve awkward timestamps. Re-encode when frame-accurate filtering or predictable normalization is more important than speed.
      
      ## Concatenation
      
      There are distinct mechanisms:
      
      - The concat demuxer reads a script describing files and is appropriate when streams are compatible and the files meet its safety and timestamp assumptions.
      - The concat filter operates on decoded audio/video and can join segments after normalizing dimensions, formats, and timestamps.
      - The concat protocol is physical byte/resource concatenation and is not a general-purpose media join.
      
      Decision rule:
      
      - **Streams match (same codecs, parameters, and time bases) and the target container accepts the concat demuxer:** use the demuxer with `-c copy`. It is fast and lossless. Verify the combined duration and stream count afterwards.
      - **Inputs differ in codecs, dimensions, frame rates, pixel formats, sample rates, or start times:** decode and normalize, then use the concat filter. Normalize video with `scale`/`fps` (and pixel format), audio with `aresample`/`aformat` to a common rate, and reset each segment's timestamps with `setpts=PTS-STARTPTS` and `asetpts=PTS-STARTPTS` before joining. This path re-encodes.
      - **Raw byte-concatenatable formats only (for example MPEG-TS segments):** the concat protocol. Do not use it for container files.
      
      Never choose a concat method solely because files share an extension. Inspect codecs, dimensions, frame rates, sample rates, channel layouts, time bases, and metadata.
      
      ## Metadata and subtitles
      
      Metadata can be copied, mapped, or rewritten. FFmpeg's format documentation describes the `ffmetadata` muxer/demuxer for round-tripping metadata.
      
      For subtitles, choose between preserving the stream and rendering pixels — they are different deliverables:
      
      - **Copy (`-c:s copy` or an explicit subtitle codec)** when the text should remain selectable, restylable, or removable and the target container supports the subtitle codec. Matroska accepts text (SRT/ASS) and bitmap (PGS/DVB) subtitles; MP4 text tracks use `mov_text`, so remuxing SRT into MP4 typically requires `-c:s mov_text`, and bitmap subtitles generally do not fit MP4. Verify with `ffprobe` that the subtitle stream survived.
      - **Burn in (`subtitles=` or `ass=` filter)** when the video must render identically in players that ignore subtitle tracks. This requires a build with libass (confirm with `ffmpeg -filters` or `scripts/ffmpeg-preflight --filter subtitles`), decodes and re-encodes the video, and fixes styling at encode time.
      
      Never assume a copied subtitle stream will survive into the target container; probe the output and confirm the intended track count.
      
      ## Batch scripting
      
      Use shell quoting carefully and never construct commands by concatenating untrusted filenames into `sh -c`. Prefer arrays in Bash/Zsh, null-delimited file discovery, and explicit output paths. Probe each result and preserve stderr logs. Use `-n` to refuse overwriting during exploratory runs; use `-y` only when an overwrite policy is intentional.
      
      A robust batch worker records input, exact command, FFmpeg version, exit status, output path, and a post-run probe. A zero exit status is not a complete acceptance gate if the downstream consumer has stricter requirements.
      
      ## Pipes and streaming
      
      An output URL can be a file, pipe, or network protocol. `-f` can force the muxer when the output URL does not provide a useful extension. `-re` is relevant when reading a file at approximately its native rate for streaming demonstrations, not as a universal speed setting. Network operations need bounded timeouts, known protocols, authentication handling, and a safe destination.
      
      The protocols manual documents protocol-specific options, including `rw_timeout`, protocol whitelists, UDP, and concat. Validate the receiving side independently.
      
      SOURCES (LAYER 3 NAVIGATION)
      https://ffmpeg.org/ffmpeg.html
       -> Seeking, stream copy, mapping, input/output option scope, and transcoding.
      
      https://ffmpeg.org/ffmpeg-formats.html
       -> Demuxers, muxers, concat-related format behavior, metadata, probing, and interleaving.
      
      https://ffmpeg.org/ffmpeg-protocols.html
       -> File, pipe, concat, UDP, HTTP, and network protocol options.
      
      https://ffmpeg.org/ffmpeg-utils.html
       -> Time expressions and quoting/escaping needed for scripts.
      
      https://ffmpeg.org/ffmpeg-filters.html
       -> Subtitle rendering filters, setpts/asetpts, and concat filter normalization requirements.
      
      https://shotstack.io/learn/how-to-use-ffmpeg/
       -> Secondary practical examples; verify all commands against current official manuals.
      
      https://en.wikibooks.org/wiki/FFMPEG_An_Intermediate_Guide
       -> Secondary intermediate topic map; examples are version/build-sensitive.
      
    • learning-summary.md 3.4 KB
      # Learning FFmpeg Without Memorizing Recipes
      
      FFmpeg is best understood as a graph of media transformations. An input URL is demuxed into streams, streams may be selected and either copied or decoded, decoded frames can pass through audio/video filters, encoders turn processed frames back into packets, and a muxer writes those packets to an output URL. `ffprobe` is the inspection tool that tells you what the container and streams actually contain.
      
      ## The practical mental model
      
      - A **container** such as MP4, Matroska, MPEG-TS, or WAV packages one or more streams and their metadata.
      - A **codec** describes the encoded elementary stream, such as H.264, AV1, AAC, Opus, or PCM.
      - A **stream** is one typed track, usually video, audio, subtitles, data, or attachments.
      - **Remuxing** changes the container while copying encoded streams. It is fast and avoids generation loss, but only works when the target container accepts those streams.
      - **Transcoding** decodes and re-encodes. It enables filtering and format changes, but costs time and can reduce quality.
      - **Mapping** makes stream selection explicit. Use it whenever multiple inputs, multiple tracks, or complex filters make automatic selection ambiguous.
      - A **filtergraph** is a directed graph of named inputs, filters, and outputs. A labeled output from `-filter_complex` must be mapped explicitly.
      
      ## A reliable operating loop
      
      1. Inspect: `ffprobe -v error -show_format -show_streams -of json input.mkv`.
      2. Decide: remux, stream-copy, transcode, filter, or combine inputs.
      3. Verify capabilities: `ffmpeg -formats`, `-codecs`, `-encoders`, `-filters`, and `-hwaccels`.
      4. Build a minimal command with explicit stream selectors and output options.
      5. Run without overwriting first, capture stderr, and probe the output.
      6. Test playback and the intended downstream consumer. A successful exit code and valid container are not universal compatibility proof.
      
      ## Learning path
      
      Start with inspection and the command line, then understand stream selection and copy/transcode. Add simple filters before learning labeled filtergraphs. Next learn seeking, timestamps, concat, metadata, subtitles, pipes, and shell loops. Only then move to streaming, hardware acceleration, and complex debugging.
      
      ## What local testing changed
      
      The local macOS Homebrew FFmpeg 8.1.2 build successfully generated and probed an H.264/AAC MP4. A subsequent scale/fps/text-filter experiment failed because this build did not contain `drawtext`. That failure is part of the lesson: online recipes are not portable promises. Check the installed build and verify the final artifact.
      
      ## Implications
      
      FFmpeg becomes predictable when commands are treated as typed pipelines rather than incantations. Most difficult failures occur at boundaries: stream selection, option scope, timestamps, filter availability, codec/container constraints, shell escaping, or hardware/software memory transfer. Make those boundaries explicit and debugging becomes a sequence of observable checks.
      
      SOURCES (SKILL NAVIGATION)
      references/core-model-and-command-anatomy.md
       -> Detailed model of containers, streams, codecs, option scope, mapping, and copy/transcode.
      
      references/filters-and-transformations.md
       -> Filtergraph construction and audio/video transformation boundaries.
      
      references/intermediate-workflows.md
       -> Inspection, joining, metadata, scripting, and streaming workflows.
      
      references/advanced-operations-and-safety.md
       -> Hardware acceleration, timestamps, reproducibility, and diagnosis.
      
    • local-verification.md 3.1 KB
      # Local Verification Dossier
      
      **Access/test date:** 2026-09-01
      **Host:** macOS
      **Binary:** Homebrew-installed `ffmpeg`
      **Version:** FFmpeg 8.1.2, libavutil 60.26.102, libavcodec 62.28.102
      **Build evidence:** `ffmpeg -version` reports `--enable-videotoolbox`, `--enable-audiotoolbox`, libx264, libx265, libsvtav1, libvmaf, libopus, libmp3lame, libdav1d, and libvpx.
      
      Every observation below is reproducible with the commands in this document on any build; nothing here relies on an artifact outside this skill. These are host-specific observations from one build, not portable capability claims.
      
      ## Inventory
      
      The local build reported 481 filter entries, 192 encoders, and 1 hardware acceleration method (VideoToolbox) from the inventories listed under "Reproduction commands". Counts and contents are build-specific: never assume a filter, encoder, protocol, or hardware backend exists just because an online example uses it. `scripts/ffmpeg-preflight --filter NAME --encoder NAME --hwaccel NAME` answers the same question for a specific build without printing the full inventories.
      
      ## Successful experiment
      
      A synthetic 320x180, 30 fps test video and 48 kHz mono sine-wave audio were generated with lavfi and encoded to MP4 using libx264 and native AAC:
      
      ```sh
      ffmpeg -f lavfi -i 'testsrc2=size=320x180:rate=30' \
        -f lavfi -i 'sine=frequency=440:sample_rate=48000' \
        -t 2 -c:v libx264 -pix_fmt yuv420p -c:a aac test.mp4
      ```
      
      `ffprobe -v error -show_format -show_streams -of json test.mp4` verified a 2.00-second MP4 containing H.264 video and AAC audio. The command is deterministic (lavfi sources), so rerunning it reproduces the evidence on any build with libx264 and the AAC encoder.
      
      ## Missing-filter experiment
      
      The intended follow-up transcode used `-ss 0.5 -t 0.75`, scaling to 160 pixels wide, reducing to 15 fps, and adding `drawtext`:
      
      ```sh
      ffmpeg -ss 0.5 -t 0.75 -i test.mp4 -vf 'scale=160:-2,fps=15,drawtext=text=Hi' -c:a copy test-small.mp4
      ```
      
      It failed before writing output because this local build reports `No such filter: 'drawtext'`. The same build confirms the absence with `ffmpeg -filters | grep -w drawtext` (no match) and `scripts/ffmpeg-preflight --filter drawtext` (reported absent). This is useful evidence: filter names and compiled capabilities must be checked with `ffmpeg -filters`, `ffmpeg -h filter=<name>`, or the preflight before placing them in automation.
      
      ## Verification lesson
      
      A command that looks portable can still fail at the filter-availability boundary. The correct response is not to silently substitute a different filter or claim success. Inspect the local build, choose an available equivalent, or install/use a build with the required feature, then rerun and probe the resulting media.
      
      ## Reproduction commands
      
      ```sh
      ffmpeg -version
      ffmpeg -hide_banner -filters
      ffmpeg -hide_banner -encoders
      ffmpeg -hide_banner -hwaccels
      ffprobe -v error -show_format -show_streams -of json test.mp4
      ```
      
      SOURCES (LAYER 3 NAVIGATION)
      https://ffmpeg.org/ffprobe.html
       -> Official description of machine-readable media inspection.
      
      https://ffmpeg.org/ffmpeg.html
       -> Official command-line processing model and option semantics.
      
    • media-failure-modes.md 5.8 KB
      # Media Failure Modes
      
      Diagnose from the first discriminating evidence, preserve the source, and change one assumption at a time. Do not “repair” the only copy or hide warnings merely to obtain a zero exit status.
      
      ## Diagnostic matrix
      
      | Symptom | Evidence to collect | Common hypotheses | Safe next action |
      |---|---|---|---|
      | Wrong or missing output stream | Full input/output `ffprobe`; complete command; mapping log | Automatic stream selection, disposition, program, optional map, or unsupported type | Declare `-map` and retention policy explicitly; verify the new output. |
      | Duration/start time is surprising | Format and stream start/duration/time base; chapters; packet/frame sample near boundaries | Container estimate, edit list, timestamp offset, VFR, sparse index, truncation | Keep raw timestamps; compare decoded boundaries; do not overwrite metadata blindly. |
      | Cut begins early/late | Seek placement, keyframe/packet data, decoded frames/audio around the boundary | Input seek point, inter-frame dependency, packet-level copy, time-base rounding | Use a decoded trim for precision or approve the observed packet boundary. |
      | Concat fails or drifts | Probe every segment; concat method; per-stream parameters and timestamps | Codec/parameter mismatch, inconsistent time bases, missing streams, incorrect duration directives | Normalize intentionally or use decoded concat; review every join. |
      | Non-monotonic DTS or timestamp warnings | Full warning context; packet timestamps around event; muxer and sync options | Broken source timestamps, reordered frames, concat offsets, inappropriate passthrough | Isolate the first bad interval; avoid speculative timestamp generation; test a new artifact. |
      | Filter, encoder, or hardware path is unavailable | Local version/build configuration and component inventories | Local build lacks dependency/component; unsupported device/pixel format | Choose a verified available path; software fallback must be explicit and reaccepted. |
      | A/V sync changes over time | Independent stream starts/durations/time bases; declared sync points across the timeline | Clock/cadence mismatch, dropped/duplicated frames, resampling, incorrect trim/concat | Measure drift before choosing timestamp, frame-rate, or resampling correction. |
      | Color, range, or geometry is wrong | Source/output color and aspect metadata; decoded reference frames; filtergraph | Unstated conversion, ignored display metadata, range/matrix mismatch, SAR/DAR error | Declare the conversion and target; compare in the intended display path. |
      | Audio clips, pumps, or changes tone | Source/output measurements plus listening around affected regions | Excess gain, dynamics/denoise settings, resampling, channel rematrixing | Bypass filters, compare one stage at a time, then re-measure and listen. |
      | Probe succeeds but decode fails | Decode-check errors and first failing timestamp/stream | Truncated/corrupt packets, unsupported feature, damaged index, decoder defect | Preserve evidence; isolate stream/interval; seek an alternate authorized source when repair is uncertain. |
      | Local playback succeeds but destination rejects | Exact accepted artifact plus destination error/version/settings | Unsupported container/codec/profile/level, metadata, file limit, ingest policy | Test against documented destination requirements; make a new derivative and reaccept it. |
      
      ## Recovery rules
      
      1. Save the exact command, build, exit status, and unfiltered first relevant warnings.
      2. Probe before changing anything; compare source and failed output.
      3. Reduce to one stream and the smallest failing interval only in a new diagnostic artifact.
      4. Verify local availability before adding codec, filter, format, protocol, or hardware options.
      5. Prefer explicit mapping, formats, time bases, and channel/pixel choices over guessed defaults.
      6. After a change, repeat the checks affected by that change. A workaround is not a diagnosis until evidence distinguishes it.
      
      Stop when authorization is unclear, the only source would be modified, damage/repair would alter meaning, encryption or access controls are encountered, private material would leave its approved boundary, or acceptance requires an unavailable human/destination review.
      
      ## Evidence and heuristic boundary
      
      - **Direct evidence:** exact probe fields, packet/frame observations, logs, commands, decoded samples, and destination errors for the named artifact/build.
      - **Discriminating experiment:** a controlled one-variable comparison; its conclusion is limited to the recorded fixture and environment.
      - **Hypothesis:** every “common cause” in the matrix until evidence rules it in. Similar symptoms can have different causes.
      - **Heuristic:** increasing probe limits, regenerating timestamps, forcing a codec/tag, changing sync mode, or re-encoding “because it usually works.” Never present these as proven repairs.
      - **Not established:** absence of warnings is not proof of integrity, editorial correctness, sync everywhere, or downstream compatibility.
      
      ## Official FFmpeg sources
      
      - [ffmpeg Documentation](https://ffmpeg.org/ffmpeg.html) — option scope, seeking, mapping, timestamps, filtering, codecs, logging, and overwrite controls.
      - [ffprobe Documentation](https://ffmpeg.org/ffprobe.html) — format/stream/packet/frame inspection and bounded intervals.
      - [FFmpeg Formats Documentation](https://ffmpeg.org/ffmpeg-formats.html) — probing, concat, muxer/demuxer, and timestamp-related format options.
      - [FFmpeg Filters Documentation](https://ffmpeg.org/ffmpeg-filters.html) — filter requirements, timeline behavior, analysis filters, and transformations.
      - [FFmpeg Codecs Documentation](https://ffmpeg.org/ffmpeg-codecs.html) — codec options and implementation-specific constraints.
      
      The documentation describes interfaces and known semantics; it does not identify the cause of a particular failure without artifact-specific evidence.
      
    • media-intake-and-manifest.md 4.6 KB
      # Media Intake and Manifest
      
      Use an intake manifest before inspecting or editing supplied or otherwise authorized media. The manifest is a record of what was received, what the local tools reported, and what the output must satisfy; it is not a rights determination or an editorial brief.
      
      ## Minimum intake
      
      1. Assign a non-identifying `asset_id`. Keep real names, account identifiers, and private storage paths out of shareable records.
      2. Record authorization scope, permitted operations, retention limits, and who may review the media. If authority is unclear, stop.
      3. Preserve the source. Work from a copy or render to a new destination; do not normalize, rename, or overwrite the only original.
      4. Record source identity separately from content claims: byte size, whole-file digest if available, acquisition date, and a private locator. A filename extension is only a hint.
      5. Capture the exact `ffprobe` and FFmpeg versions used. Build options and enabled libraries can change available codecs, filters, and behavior.
      6. Probe structure without decoding the entire asset:
      
      ```sh
      ffprobe -v error -show_format -show_streams -show_chapters -of json INPUT
      ```
      
      Preserve absent fields as absent or `null`; do not invent duration, frame rate, language, or channel layout. For ambiguous or damaged inputs, record probe warnings and any non-default `-probesize` or `-analyzeduration` used.
      
      ## Manifest fields that matter
      
      - **Source:** opaque asset ID, private locator, size, digest, preservation status.
      - **Tool context:** `ffprobe` version, FFmpeg version, build identity, command, exit status, warning-log locator.
      - **Container:** reported format names, start time, duration, bit rate, chapters, and tags needed for the task.
      - **Streams:** index, codec type/name, time base, start time, duration, disposition, language, and stream-specific fields such as dimensions, pixel format, field order, color metadata, sample rate, channel layout, and subtitle type.
      - **Timing:** whether durations come from the container, stream metadata, counted packets/frames, or another declared method.
      - **Output contract:** required container, stream set and order, codecs, geometry, frame cadence, audio layout/rate, duration tolerance, subtitle/metadata policy, file-size constraint, and target player/editor/service.
      - **Assumptions and unknowns:** each assumption, why it is being used, risk, and how it will be tested.
      
      Probe output may contain identifying tags, device data, creation times, titles, comments, and private paths. Store raw output in the restricted task workspace; publish only a minimized/redacted derivative.
      
      ## Output-contract discipline
      
      Express acceptance criteria before rendering. Prefer measurable criteria such as “one H.264 video stream and one AAC stereo audio stream,” “1920×1080,” or “duration within the declared tolerance.” Keep subjective criteria such as pacing or intelligibility as separate human-review items. “Plays for me” and “command exited zero” are not output contracts.
      
      ## Evidence and heuristic boundary
      
      | Classification | What may be claimed |
      |---|---|
      | Direct evidence | The exact probe output, warnings, command, build, source digest, and authorization record captured for this asset. |
      | Derived evidence | Values calculated from recorded fields using a stated formula and rounding rule. Label them as derived. |
      | Heuristic | Meaning inferred from an extension, filename, tags, nominal frame rate, sampled content, or prior behavior of a destination. Mark it as an assumption. |
      | Not established | Rights ownership, complete decodability, semantic content, editorial quality, sync, or destination compatibility. Probe metadata alone establishes none of these. |
      
      A detector or probe result is evidence about this input under the recorded build and options. It is not universal evidence about every copy, FFmpeg build, decoder, or playback environment.
      
      ## Official FFmpeg sources
      
      - [ffprobe Documentation](https://ffmpeg.org/ffprobe.html) — stream/container inspection, output writers, counting, intervals, and probe options.
      - [FFmpeg Formats Documentation](https://ffmpeg.org/ffmpeg-formats.html) — demuxer/muxer behavior, probing controls, and format-specific options.
      - [ffmpeg Documentation](https://ffmpeg.org/ffmpeg.html) — option scope, stream selection, mapping, transcoding, and overwrite behavior.
      - [FFmpeg Utilities Documentation](https://ffmpeg.org/ffmpeg-utils.html) — duration, size, rate, and related value syntax.
      
      These official pages define FFmpeg interfaces and documented behavior. They do not prove that a local build includes a component, that metadata is truthful, or that an output meets an editorial or downstream requirement.
      
    • media-research-source-index.md 5.4 KB
      # Media Research Source Index
      
      Use this index to connect a media claim to the strongest available source. Prefer official documentation for FFmpeg semantics, local evidence for installed capability, controlled experiments for uncertain behavior, and attributed review for editorial judgments.
      
      Research scope, source evaluation, exclusion logging, source-to-claim closure, and sampling-completeness audits follow [`research-methodology` media-evidence routing](../../research-methodology/references/media-evidence-routing.md). This FFmpeg reference owns the media-specific source hierarchy and the operational evidence returned to that method; it does not duplicate the general research lifecycle.
      
      ## Official FFmpeg sources
      
      | Claim area | Official source | Supports | Does not establish |
      |---|---|---|---|
      | CLI option scope, seeking, stream selection, mapping, filtering, metadata, overwrite behavior | [ffmpeg Documentation](https://ffmpeg.org/ffmpeg.html) | Documented command-line semantics and processing model | Local component availability, editorial correctness, or destination acceptance |
      | Format, stream, packet, frame, interval, and output-writer inspection | [ffprobe Documentation](https://ffmpeg.org/ffprobe.html) | Probe fields, selection, counting, intervals, and machine-readable output | Truth of embedded metadata, complete decode, or semantic content |
      | Demuxers, muxers, concat, probing, and format-specific behavior | [FFmpeg Formats Documentation](https://ffmpeg.org/ffmpeg-formats.html) | Documented container and format options | Universal player/service support or validity of a specific damaged file |
      | Audio/video filters, timelines, analysis, transforms, and metrics | [FFmpeg Filters Documentation](https://ffmpeg.org/ffmpeg-filters.html) | Filter parameters, outputs, and documented constraints | Suitability of thresholds, perceptual quality, or editorial intent |
      | Encoder and decoder options | [FFmpeg Codecs Documentation](https://ffmpeg.org/ffmpeg-codecs.html) | Codec-specific controls exposed by FFmpeg | Presence in a local build, conformance of every output, or destination policy |
      | Resampling and rematrixing | [FFmpeg Resampler Documentation](https://ffmpeg.org/ffmpeg-resampler.html) | `libswresample` options and documented behavior | Listening quality or correctness of a chosen channel policy |
      | Scaling, pixel conversion, and dithering | [FFmpeg Scaler Documentation](https://ffmpeg.org/ffmpeg-scaler.html) | `libswscale` options and conversion controls | End-to-end color accuracy on an untested display pipeline |
      | Time, duration, rate, size, color, and expression syntax | [FFmpeg Utilities Documentation](https://ffmpeg.org/ffmpeg-utils.html) | Shared value and expression syntax | The correct editorial timebase or rounding policy for a project |
      | Device and protocol interfaces | [FFmpeg Devices Documentation](https://ffmpeg.org/ffmpeg-devices.html) and [FFmpeg Protocols Documentation](https://ffmpeg.org/ffmpeg-protocols.html) | Documented input/output devices and protocol options | Authorization, network safety, availability, or reliability in a given environment |
      
      The website reflects a documented FFmpeg version that may differ from an installed binary. Capture `ffmpeg -version`, `ffprobe -version`, build configuration, and relevant local inventories before making availability claims.
      
      ## Evidence hierarchy
      
      1. **Artifact-specific direct evidence:** exact probe/decode output, packets/frames, measurements, logs, samples, destination results, and attributed review tied to an asset digest.
      2. **Official FFmpeg documentation:** primary source for documented interfaces and semantics; cite the page and, when consequential, the option/filter section and access date.
      3. **Controlled local experiment:** fixture, digest, command, build, environment, raw result, expected result, and limitations. Reproduction supports only the tested conditions.
      4. **External specification or destination documentation:** normative format/delivery requirement. It does not prove a particular encoder output or ingest result; test both.
      5. **Secondary explanation:** useful for discovery, never stronger than the primary source it interprets.
      6. **Heuristic or editorial convention:** label it, explain why it is reasonable, and assign a reviewer/test.
      
      ## Claim-record pattern
      
      For consequential claims, record:
      
      - claim and classification (`direct`, `derived`, `documented`, `experimental`, `heuristic`, or `human_review`);
      - source URL or evidence locator;
      - FFmpeg version/build and command where applicable;
      - asset/fixture ID and bounded interval;
      - observed result, confidence, assumptions, and counter-evidence;
      - what the evidence does **not** establish.
      
      Do not paste private URLs, credentials, absolute paths, personal names, raw transcripts, or identifying metadata into public research records. Use opaque IDs and restricted evidence locators.
      
      ## Evidence and heuristic boundary
      
      Official documentation is evidence for documented behavior, not proof that the local build implements a feature or that a specific command produced the intended artifact. Local inventories establish availability only for that build. Experiments establish observations only for their fixture and conditions. Automated scores and detector events are measurements under declared parameters, not semantic truth. Editorial quality, meaning, identity, consent, and rights require appropriate human or authoritative evidence outside FFmpeg.
      
    • media-verification-and-acceptance.md 7 KB
      # Media Verification and Acceptance
      
      Accept a media artifact from recorded evidence against a declared contract. A zero exit status proves only that one command completed without reporting a fatal error; it does not prove correct streams, complete decode, editorial quality, or destination compatibility.
      
      ## Layered acceptance
      
      ### 1. Artifact and provenance
      
      Record the output asset ID, digest, size, producing command, source/EDL versions, FFmpeg build, completion status, warning log, and whether the path was new or pre-existing. Confirm the accepted file is the file that was reviewed.
      
      ### 2. Structural conformance
      
      Probe the output and compare every required field with the contract:
      
      ```sh
      ffprobe -v error -show_format -show_streams -show_chapters -of json OUTPUT
      ```
      
      Check container, stream count/order, codecs, dispositions, language, dimensions, aspect ratios, pixel format, color metadata, cadence, start times, durations, audio sample rate/layout, subtitles, chapters, and metadata policy. Treat absent or ambiguous fields explicitly.
      
      ### 3. Processing-path check
      
      Exercise all expected audio/video streams through FFmpeg and preserve errors:
      
      ```sh
      ffmpeg -v error -i OUTPUT -map '0:v?' -map '0:a?' -f null -
      ```
      
      This can expose decode or timeline faults in FFmpeg’s processing path. It does not exercise every player, subtitle/data stream, hardware decoder, display pipeline, or network/service ingest path.
      
      ### 4. Signal and timing checks
      
      Use only metrics tied to criteria: frame/packet counts, start/end timestamps, cadence, A/V offset at declared points, black/freeze candidates, audio statistics, loudness, peaks, or silence events. Record filters, thresholds, intervals, and tolerances. Re-measure the rendered artifact rather than assuming encoder/filter targets were met.
      
      ### 5. Content review
      
      Review the opening and closing, every cut/join/transition/treatment, titles/subtitles, high-risk regions, representative motion and detail, loud/quiet passages, and declared sync points. Use full-resolution frames or short clips where contact sheets are insufficient. Attribute reviewer and time.
      
      ### 6. Editorial and downstream acceptance
      
      An accountable reviewer decides whether meaning, pacing, continuity, intelligibility, accessibility, and the brief are satisfied. Then import, play, upload, or validate the exact artifact in the intended destination. Record destination identity/version, settings, result, warnings, and any transformed derivative.
      
      ## Acceptance record
      
      For every criterion, capture:
      
      - criterion and tolerance;
      - evidence method and exact artifact/interval;
      - observed value or attributed observation;
      - status: `pass`, `fail`, `blocked`, or `not_applicable`;
      - reviewer and date;
      - exception owner and rationale, if any.
      
      The final verdict is `accepted`, `rejected`, or `blocked`. Do not convert an untested criterion into a pass. Any post-review change invalidates affected evidence and requires re-verification.
      
      For machine-readable verification, copy `templates/media-acceptance-contract.json` and evaluate it with:
      
      ```sh
      scripts/media-verify CONTRACT.json OUTPUT-PROBE.json --evidence EVIDENCE.json --json
      ```
      
      The contract can require exact stream order/counts, codecs, dimensions, pixel format, rational frame rate with tolerance, sample rate/channels/layout, forbidden stream types, format duration/start tolerances, chapters, and metadata. Its separate evidence requirements cover decode, visual review, audio review, measured loudness/true peak, and a named downstream target.
      
      The verifier emits one record per criterion with expected and observed values, evidence locator, boundary, reason, and one of `PASS`, `FAIL`, `BLOCKED`, `UNVERIFIED`, or `NOT_APPLICABLE`. A required stream that is absent fails; a required field or review artifact that cannot be observed remains unverified. The overall verdict follows the strongest unresolved state: `FAIL`, then `BLOCKED`, then `UNVERIFIED`, otherwise `PASS`.
      
      ## Named-target compatibility
      
      For a real player, editor, host, archive, or service, copy `templates/target-compatibility-manifest.json` and retain requirements provenance separately from the output probe. Every requirement source needs an official URL or an observed-behavior locator, access date, and the exact claim it supports. Run:
      
      ```sh
      scripts/target-compatibility TARGET.json OUTPUT-PROBE.json \
        --target-evidence TARGET-EVIDENCE.json --json
      ```
      
      The helper evaluates codec/profile, dimensions, pixel format, rates, audio layout, stream order, subtitles, chapters, metadata, duration, and size at the technical boundary. It reports import/playback/ingest evidence for exactly one named target in a separate result. Missing target evidence is `UNVERIFIED`; an explicitly unavailable authorized environment is `BLOCKED`; neither local decoding nor a pass in one consumer is generalized to another consumer.
      
      A concrete FFplay lane may use the [official FFplay documentation](https://ffmpeg.org/ffplay.html) and record the installed version, exact artifact digest, invocation, interactive audio/video/subtitle checks, warnings, and result. The repository's automated tests are headless and do not exercise an authorized display/audio session, so that real playback lane remains explicitly unavailable in CI. Tests instead verify the contract mechanics against a recorded named-target evidence fixture. Platform upload/API actions remain in the owning platform skill.
      
      Minimize reports before sharing: remove private paths, personal names, account identifiers, unnecessary transcript excerpts, and embedded metadata.
      
      ## Evidence and heuristic boundary
      
      | Classification | Boundary |
      |---|---|
      | Direct evidence | Probe/decode output, measurements, samples, destination result, and attributed review for the exact accepted artifact. |
      | Derived evidence | Contract comparisons and timing calculations with a stated method and tolerance. |
      | Heuristic | Sparse sampling, automated quality scores, detector events, or compatibility inferred from a similar file. Label as supporting evidence only. |
      | Human judgment | Editorial quality, intelligibility, context, and visual acceptability require attributed review. |
      | Not established | Universal playback, rights, long-term preservation, accessibility, or unsampled-content correctness unless separately tested. |
      
      ## Official FFmpeg sources
      
      - [ffprobe Documentation](https://ffmpeg.org/ffprobe.html) — machine-readable structural, packet, and frame inspection.
      - [ffmpeg Documentation](https://ffmpeg.org/ffmpeg.html) — stream mapping, decoding/transcoding, progress, logging, and exit behavior context.
      - [FFmpeg Filters Documentation](https://ffmpeg.org/ffmpeg-filters.html) — measurable video/audio analysis filters and their parameters.
      - [FFmpeg Formats Documentation](https://ffmpeg.org/ffmpeg-formats.html) — muxer/demuxer behavior and container-specific options.
      - [FFmpeg Codecs Documentation](https://ffmpeg.org/ffmpeg-codecs.html) — decoder/encoder options and capabilities.
      
      Official documentation supports the mechanics of checks. It does not define the project’s acceptance criteria or guarantee behavior outside the recorded build and destination.
      
    • source-inventory.md 6.2 KB
      # FFmpeg Source Inventory
      
      **Research access date:** 2026-09-01
      
      ## Retained primary sources
      
      | # | Source | Authority | Topics supported | Caveat |
      |---|---|---|---|---|
      | 1 | [FFmpeg documentation](https://ffmpeg.org/documentation.html) | Official project index | Current command-line and API documentation; versioned manuals | Online docs track the newest revision; use installed manuals for older builds. |
      | 2 | [ffmpeg tool manual](https://ffmpeg.org/ffmpeg.html) | Official | Command anatomy, input/output order, stream selection, mapping, codecs, filtering, timestamps | Option scope is order-sensitive and many examples are contextual. |
      | 3 | [ffprobe manual](https://ffmpeg.org/ffprobe.html) | Official | Container/stream inspection, machine-readable output, stream specifiers, intervals | Probe output describes the file; it does not prove universal playback compatibility. |
      | 4 | [ffmpeg-all manual](https://ffmpeg.org/ffmpeg-all.html) | Official | Consolidated reference and examples across tools and libraries | Very large; use for lookup after learning the model. |
      | 5 | [Filters manual](https://ffmpeg.org/ffmpeg-filters.html) | Official | Filtergraphs, pads, labels, audio/video filters, framesync, hardware filters | Availability and options depend on build and version. |
      | 6 | [Formats manual](https://ffmpeg.org/ffmpeg-formats.html) | Official | Demuxers, muxers, probing, interleaving, timestamp handling, concat | Container behavior varies; codec and container compatibility are separate. |
      | 7 | [Codecs manual](https://ffmpeg.org/ffmpeg-codecs.html) | Official | Encoder/decoder options, bitrate, time base, rate control, error detection | Private options are encoder-specific. |
      | 8 | [Protocols manual](https://ffmpeg.org/ffmpeg-protocols.html) | Official | File, pipe, network, concat, UDP, HTTP and other I/O protocols | Network commands need explicit timeout, security, and endpoint assumptions. |
      | 9 | [Utilities manual](https://ffmpeg.org/ffmpeg-utils.html) | Official | Duration syntax, quoting/escaping, expressions, rational numbers, channel layouts | Shell quoting is a second language layered over FFmpeg quoting. |
      | 10 | [Scaler manual](https://ffmpeg.org/ffmpeg-scaler.html) | Official | Scaling, pixel-format conversion, algorithms, range and gamma | Quality depends on source/destination formats and range. |
      | 11 | [Resampler manual](https://ffmpeg.org/ffmpeg-resampler.html) | Official | Sample-rate conversion, channel rematrixing, sample formats, dithering, sync compensation | Optional SoX support and defaults are build-dependent. |
      | 12 | [NVIDIA FFmpeg GPU guide](https://docs.nvidia.com/video-technologies/video-codec-sdk/13.0/ffmpeg-with-nvidia-gpu/index.html) | Vendor primary source | CUDA decode, GPU surfaces, scale_cuda/scale_npp, NVENC | NVIDIA-specific; do not transfer commands to macOS or non-NVIDIA hosts. |
      | 13 | [FFmpeg Git repository](https://git.ffmpeg.org/ffmpeg.git) | Official source | Source-level confirmation and release history | Source inspection does not replace testing the installed binary. |
      | 14 | [FFmpeg download/release guidance](https://ffmpeg.org/download.html) | Official | Releases, source builds, release cadence and signed tags | Distribution packages may lag or differ in enabled components. |
      
      ## Secondary learning resources
      
      - [FFmpeg Filtering Guide](https://trac.ffmpeg.org/wiki/FilteringGuide) - official project wiki guide. Automated retrieval was blocked by the site's Anubis proof-of-work page on this date, so treat it as a reading lead and verify commands against the filters manual.
      - [FFmpeg Ultimate Guide](https://img.ly/blog/ultimate-guide-to-ffmpeg/) - readable secondary overview with practical transcoding examples; vendor context means commands should be checked against official manuals.
      - [Shotstack FFmpeg guide](https://shotstack.io/learn/how-to-use-ffmpeg/) - broad examples and scripting orientation; secondary and service-oriented.
      - [FFmpeg intermediate guide on Wikibooks](https://en.wikibooks.org/wiki/FFMPEG_An_Intermediate_Guide) - useful topic map; community-maintained and examples need current-build verification.
      - [FFmpeg short guide and examples](https://github.com/term7/FFmpeg-A-short-Guide) - practical GitHub notes; inspect freshness before relying on a recipe.
      - [slhck FFmpeg encoding course](https://github.com/slhck/ffmpeg-encoding-course) - maintained educational repository discovered during delegated research; strong intermediate bridge, but still secondary to the official manuals.
      - [amiaopensource/ffmprovisr](https://github.com/amiaopensource/ffmprovisr) - preservation-oriented recipe collection; useful for real media workflows, with strong need for format-specific verification.
      
      ## Rejected or limited sources
      
      - FFmpeg Trac pages for Concatenate, H.264, AAC, HWAccelIntro, StreamingGuide, Encode/YouTube, and CompilationGuide were not retained as evidence because retrieval encountered the site's JavaScript proof-of-work page. This is an access limitation, not evidence that the pages are wrong.
      - DeepWiki pages were not used as primary evidence because they are generated/secondary explanations of source code.
      - Search-result snippets and anonymous cheat sheets were used only for discovery, not evidence.
      
      ## Recommended learning order
      
      1. `ffmpeg` synopsis and `ffprobe` inspection.
      2. Containers, streams, codecs, demux/mux, decode/filter/encode, and stream mapping.
      3. Remuxing versus transcoding, codec selection, quality controls, and stream specifiers.
      4. Simple filters, then labeled `-filter_complex` graphs and explicit `-map`.
      5. Time, seeking, trimming, concat demuxer versus concat filter, timestamps, and synchronization.
      6. Metadata, subtitles, image sequences, pipes, and shell scripting.
      7. Streaming protocols and latency controls, with explicit endpoint and timeout handling.
      8. Hardware acceleration only after understanding software pipelines and verifying local capabilities.
      9. Debugging with verbose logs, `ffprobe`, minimal reproductions, and build inventories.
      
      SOURCES (LAYER 3 NAVIGATION)
      https://ffmpeg.org/documentation.html
       -> Official documentation index and version links.
      
      https://ffmpeg.org/ffmpeg.html
       -> Primary command-line semantics.
      
      https://ffmpeg.org/ffmpeg-filters.html
       -> Primary filtergraph reference.
      
      https://ffmpeg.org/ffprobe.html
       -> Primary inspection reference.
      
    • synthetic-media-fixtures.md 2.4 KB
      # Synthetic Media Fixture Battery
      
      Use the fixture battery when an FFmpeg change needs real-media evidence for timestamp, concat, audio, subtitle, or visual-sampling boundaries. The generator creates non-personal media in a new task-local directory; generated binaries are not repository fixtures and must not be committed.
      
      ## Generate fixtures
      
      ```sh
      scripts/generate-media-fixtures /tmp/ffmpeg-fixtures --json
      ```
      
      The command refuses a non-empty directory and bounds every FFmpeg/FFprobe command. `fixture-manifest.json` records the exact build, generators, digests, probes, expected properties, environment-specific observations, and limitations.
      
      ## Covered boundaries
      
      | Fixture group | Intended evidence |
      |---|---|
      | GOP source, stream-copy cut, decoded cut | Packet/keyframe-limited copying remains distinct from decoded precision |
      | Irregular frame selection and offset/drift candidate | Container rates and stream durations are not a complete account of cadence or sync |
      | Compatible pair and concat output | Concat-demuxer success for the exact recorded streams and build |
      | Incompatible concat candidate | Dimensions, cadence, and sample-rate differences cause pre-concat rejection |
      | Audio analysis, speech-like, and faded WAVs | Declared silence/over-range regions, a frequency-modulated voiced-like source, and a mechanical fade output for bounded measurement tests |
      | SRT and subtitle-stream MKV | Subtitle source and explicit stream preservation; burn-in is exercised when the local filter exists and otherwise recorded unavailable |
      | Three boundary PNGs | Samples around one timestamp with an explicit no-whole-video-claim boundary |
      
      Portable assertions describe generator and workflow invariants. Probe values, packet placement, encoded durations, and filter behavior remain environment observations tied to the manifest's FFmpeg build.
      
      ## Test use
      
      Tests should select the smallest fixture group that exercises the claimed behavior. Do not regenerate the full battery for a unit test that can use structured fake probe data. When a test relies on actual FFmpeg behavior:
      
      - retain the manifest and exact command;
      - assert the intended success or rejection boundary;
      - allow declared timestamp/time-base tolerance rather than an invented exact decimal;
      - label silence, clipping, and sparse-frame outputs as candidates;
      - separate subtitle-stream presence from burn-in or player rendering;
      - never generalize one build's result to another environment.
      
    • video-inspection-and-visual-evidence.md 6.8 KB
      # Video Inspection and Visual Evidence
      
      Inspect video in bounded layers: structural metadata, decoded technical signals, sampled frames or clips, then semantic review. Keep the source ID, stream index, timestamps, commands, build, and coverage limits attached to every artifact.
      
      ## Inspection sequence
      
      1. **Probe the stream.** Record dimensions, sample/display aspect ratio, pixel format, field order, nominal and average frame rates, time base, start/duration, disposition, rotation/display metadata, and color fields.
      2. **Define the question.** Examples: “Is there a black interval near the head?” or “Does the approved title appear in the sampled opening?” Avoid an unbounded request to “check the video.”
      3. **Choose declared coverage.** Use specific timestamps, a fixed cadence, bounded intervals, or event-driven candidates. Record the interval, cadence, number of samples, and omissions.
      4. **Decode evidence to a new review location.** A typical single-frame extraction is:
      
      ```sh
      ffmpeg -ss START -i INPUT -map 0:v:0 -frames:v 1 -an REVIEW_FRAME.png
      ```
      
      For cadence sampling, use the documented `fps` filter; for representative candidates, `thumbnail` can select a frame from each batch and `tile` can assemble a contact sheet. Preserve each sample’s source timestamp rather than relying only on sequential filenames.
      5. **Review at the right resolution.** Contact sheets establish overview, not fine detail. Use full-resolution frames or short clips for text, motion, transitions, sync, compression artifacts, and color judgments.
      
      Input `-ss` seeks to a nearby seek point; with transcoding, accurate seek processing normally decodes and discards material before the requested position. Container indexing, timestamps, variable frame rate, and output rounding can still affect the exact frame. Record the observed frame timestamp when precision matters.
      
      ## Technical aids
      
      Useful video filters include:
      
      - `showinfo` for per-frame timestamps, format, type, and checksums.
      - `signalstats` for frame signal statistics.
      - `blackdetect` or `blackframe` for threshold-based black candidates.
      - `freezedetect` for threshold- and duration-based freeze candidates.
      - `scdet` for scene-change scores and candidate events.
      - `cropdetect` for suggested crop values.
      
      Treat every detector as a candidate generator. Record thresholds, durations, filter order, and any color/range conversion performed before the detector. A detector’s log line is not a semantic judgment.
      
      ## Visual review packet
      
      For each sample include:
      
      - opaque asset ID and selected stream;
      - source and observed timestamps;
      - extraction command and FFmpeg build;
      - whether the image was scaled, cropped, deinterlaced, tone-mapped, or color-converted;
      - the question being tested and reviewer observation;
      - coverage statement and known blind spots.
      
      Avoid embedding private source paths, personal names, faces, transcript text, or location metadata unless required and authorized. Share the smallest packet that answers the review question.
      
      ### Structured handoff workflow
      
      Use `scripts/vision-review-handoff` to turn explicit timestamps, a bounded cadence, or neighboring frames around proposed boundaries into a new review packet. The helper enforces frame-count, timestamp-span, and aggregate-byte limits; records the selected stream, local FFmpeg build, extraction parameters, transformations, hashes, and sampling blind spots; and substitutes an opaque asset ID for the private source path in the manifest. It does not perform semantic review.
      
      ```sh
      scripts/vision-review-handoff private-source.mov \
        --asset-id asset-017 --stream 0:v:0 \
        --question "Does the sampled boundary preserve title continuity?" \
        --timestamp 12.4 --timestamp 18.2 --neighbor-seconds 0.25 \
        --max-frames 12 --max-range-seconds 30 --max-output-bytes 12000000 \
        --output-dir review-packet --json
      ```
      
      Give `manifest.json` and its relative frame artifacts to an authorized human or vision-capable reviewer. The reviewer must replace the pending review block with attributed observations using one of four classes: `technical_observation`, `human_or_vision_observation`, `heuristic`, or `unresolved_claim`. Each observation records confidence, blind spots, sampled-artifact scope, and a proposed editorial consequence. If the samples cannot answer the question, request more bounded samples or human review rather than inferring across the gap.
      
      After review, `scripts/import-vision-review MANIFEST EDL --output REVIEWED-EDL` validates the evidence and links it to named EDL events. The importer rejects missing attribution, missing observations, unknown events, and claims covering the whole asset or unsampled intervals. It writes a new EDL and never renders media or accepts the proposed consequence automatically.
      
      This workflow is distinct from transcript acquisition, which belongs to the source/transcription system and supplies text/timing evidence, and from HyperFrames composition, which creates visual experiences. FFmpeg only extracts declared pixels and records technical provenance here; semantic interpretation belongs to the reviewer.
      
      ## Evidence and heuristic boundary
      
      | Classification | Defensible statement |
      |---|---|
      | Direct evidence | Probe fields for the recorded stream; decoded pixels and filter measurements at the listed timestamps under the recorded command/build. |
      | Threshold evidence | A configured detector emitted a candidate under stated thresholds. This is reproducible, but threshold-dependent. |
      | Human observation | A reviewer observed a visible feature in the supplied sample. Attribute it and preserve the sample. |
      | Heuristic | A sparse sample represents an interval; a scene score denotes an editorial cut; a crop suggestion is compositionally correct. Label and review these assumptions. |
      | Not established | Absence throughout unsampled material, speaker/person identity, intent, rights, complete accessibility, exact color on another display, or downstream playback quality. |
      
      Frame samples cannot prove what happens between samples. Extracted stills can also differ from target playback because of scaling, color management, HDR handling, deinterlacing, and display behavior.
      
      ## Official FFmpeg sources
      
      - [ffprobe Documentation](https://ffmpeg.org/ffprobe.html) — stream/frame inspection and bounded read intervals.
      - [ffmpeg Documentation](https://ffmpeg.org/ffmpeg.html) — seeking, stream mapping, frame limits, and transcoding behavior.
      - [FFmpeg Filters Documentation](https://ffmpeg.org/ffmpeg-filters.html) — `fps`, `thumbnail`, `tile`, `showinfo`, `signalstats`, `blackdetect`, `freezedetect`, `scdet`, and `cropdetect`.
      - [FFmpeg Utilities Documentation](https://ffmpeg.org/ffmpeg-utils.html) — time-duration and rate syntax.
      
      The documentation supports option and filter semantics. It does not validate a chosen sampling plan, detector threshold, semantic interpretation, or display pipeline.
      
  • scripts
    • fixtures
      • ffmpeg-8.1.2-inventories.json 1.3 KB
        {
          "version": "FFmpeg 8.1.2",
          "description": "Representative excerpts from FFmpeg 8.1.2 inventory output.",
          "filters": [
            "Filters:",
            "  T.. = Timeline support",
            "  .S. = Slice threading",
            "  ..C = Command support",
            "  A = Audio input/output",
            "  V = Video input/output",
            "  | = Source or sink filter",
            "  ------",
            " .. scale             V->V       Scale the input video size.",
            " TS aap               AA->A      Apply Affine Projection algorithm.",
            " .. anullsrc          |->A       Null audio source, return empty audio frames.",
            " .. abuffersink       A->|       Buffer audio frames."
          ],
          "encoders": [
            "Encoders:",
            " V..... = Video",
            " A..... = Audio",
            " S..... = Subtitle",
            " .F.... = Frame-level multithreading",
            " ..S... = Slice-level multithreading",
            " ...X.. = Codec is experimental",
            " ....B. = Supports draw_horiz_band",
            " .....D = Supports direct rendering method 1",
            " ------",
            " V....D libx264              libx264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 (codec h264)",
            " A....D aac                  AAC (Advanced Audio Coding)",
            " V....D libvpx-vp9           libvpx VP9 (codec vp9)"
          ],
          "hwaccels": [
            "Hardware acceleration methods:",
            "videotoolbox"
          ]
        }
        
    • audio-inspect 16.8 KB · in bundle
    • editorial-workflow-example 14.8 KB · in bundle
    • extract-review-frames 11.6 KB · in bundle
    • ffmpeg-preflight 10.6 KB · in bundle
    • generate-media-fixtures 23.7 KB · in bundle
    • import-vision-review 3.6 KB · in bundle
    • media-intake 8.1 KB · in bundle
    • media-verify 16.5 KB · in bundle
    • render-edl 18.4 KB · in bundle
    • target-compatibility 8.4 KB · in bundle
    • test_ffmpeg_preflight.py 12.1 KB
      #!/usr/bin/env python3
      """Deterministic pytest suite for ffmpeg-preflight.
      
      Fake ffmpeg/ffprobe binaries cover the declared failure matrix without real
      media, network access, GPU hardware, or dependence on a particular CI image.
      """
      
      from __future__ import annotations
      
      import json
      import os
      import shutil
      import subprocess
      import sys
      from pathlib import Path
      
      import pytest
      
      SCRIPT = Path(__file__).resolve().parent / "ffmpeg-preflight"
      INVENTORY_FIXTURE = SCRIPT.parent / "fixtures" / "ffmpeg-8.1.2-inventories.json"
      
      FAKE_FFMPEG_VERSION = "ffmpeg version 8.1.2 fake"
      FAKE_FFPROBE_VERSION = "ffprobe version 8.1.2 fake"
      
      FIXTURE_FILTERS = """Filters:
        T.. = Timeline support
        A = Audio input/output
        V = Video input/output
        | = Source or sink filter
        ------
       .. scale             V->V       Scale the input video size.
       TS aap               AA->A      Apply Affine Projection algorithm.
       .. anullsrc          |->A       Null audio source, return empty audio frames.
       .. abuffersink       A->|       Buffer audio frames,
      """
      
      FIXTURE_ENCODERS = """Encoders:
       V..... = Video
       A..... = Audio
       S..... = Subtitle
       ------
       V....D libx264              libx264 H.264 (codec h264)
       A....D aac                  AAC (Advanced Audio Coding)
       V....D libvpx-vp9           libvpx VP9 (codec vp9)
      """
      
      FIXTURE_HWACCELS = """Hardware acceleration methods:
      videotoolbox
      """
      
      HEADER_ONLY_FILTERS = "Filters:\n"
      MALFORMED_FILTERS = """some preamble noise
      scale V->V without leading fields
        T.. = Timeline support
       ..                    V->V       name token is missing
      not->an-entry
      Filters:
      """
      
      
      def write_fixtures(directory: Path, filters: str, encoders: str, hwaccels: str) -> None:
          (directory / "filters.txt").write_text(filters)
          (directory / "encoders.txt").write_text(encoders)
          (directory / "hwaccels.txt").write_text(hwaccels)
      
      
      def install_tools(
          bin_dir: Path,
          fixtures_dir: Path,
          *,
          with_ffprobe: bool = True,
          version_rc: int = 0,
          filters_rc: int = 0,
          encoders_rc: int = 0,
          hwaccels_rc: int = 0,
      ) -> None:
          emit = (
              "emit() {\n"
              '  while IFS= read -r line || [ -n "$line" ]; do\n'
              "    printf '%s\\n' \"$line\"\n"
              "  done < \"$1\"\n"
              "}\n"
          )
          ffmpeg = (
              "#!/bin/sh\n"
              + emit
              + 'case "$*" in\n'
              f'  *-version*) echo "{FAKE_FFMPEG_VERSION}"; exit {version_rc} ;;\n'
              f'  *-filters*) emit "$FIXTURES/filters.txt"; exit {filters_rc} ;;\n'
              f'  *-encoders*) emit "$FIXTURES/encoders.txt"; exit {encoders_rc} ;;\n'
              f'  *-hwaccels*) emit "$FIXTURES/hwaccels.txt"; exit {hwaccels_rc} ;;\n'
              "esac\n"
              'echo "unexpected arguments: $*" >&2\n'
              "exit 99\n"
          )
          ffmpeg_path = bin_dir / "ffmpeg"
          ffmpeg_path.write_text(ffmpeg)
          ffmpeg_path.chmod(0o755)
          if with_ffprobe:
              ffprobe = bin_dir / "ffprobe"
              ffprobe.write_text(f'#!/bin/sh\necho "{FAKE_FFPROBE_VERSION}"\n')
              ffprobe.chmod(0o755)
      
      
      @pytest.fixture
      def environment(tmp_path: Path):
          """Provide (setup, run) with healthy fake tools pre-installed.
      
          ``setup(**overrides)`` rebuilds the fake tools (use for missing ffprobe,
          failing probes, or alternate fixtures). ``run(*args, bin_dir=None)``
          invokes the preflight script with PATH restricted to the fake bin dir.
          """
      
          bin_dir = tmp_path / "bin"
          fixtures_dir = tmp_path / "fixtures"
      
          def setup(
              *,
              with_ffprobe: bool = True,
              filters: str = FIXTURE_FILTERS,
              encoders: str = FIXTURE_ENCODERS,
              hwaccels: str = FIXTURE_HWACCELS,
              **tool_kwargs,
          ) -> Path:
              shutil.rmtree(bin_dir, ignore_errors=True)
              bin_dir.mkdir()
              fixtures_dir.mkdir(exist_ok=True)
              write_fixtures(fixtures_dir, filters, encoders, hwaccels)
              install_tools(bin_dir, fixtures_dir, with_ffprobe=with_ffprobe, **tool_kwargs)
              return bin_dir
      
          def run(*arguments: str, bin_dir: Path | None = None) -> subprocess.CompletedProcess:
              env = os.environ.copy()
              env["PATH"] = str(bin_dir if bin_dir is not None else tmp_path / "bin")
              env["FIXTURES"] = str(fixtures_dir)
              return subprocess.run(
                  [sys.executable, str(SCRIPT), *arguments],
                  capture_output=True,
                  text=True,
                  env=env,
              )
      
          setup()
          return setup, run
      
      
      def test_success_json_reports_availability_counts(environment):
          setup, run = environment
          result = run("--json")
          assert result.returncode == 0, result.stderr
          report = json.loads(result.stdout)
          assert report["ffmpeg"]["available"] is True
          assert report["ffmpeg"]["first_line"] == FAKE_FFMPEG_VERSION
          assert report["ffprobe"]["available"] is True
          assert report["filters"]["entry_count"] == 4
          assert report["encoders"]["entry_count"] == 3
          assert report["hwaccels"]["entry_count"] == 1
          assert "warning" not in report["filters"]
          assert report["queries"] == {}
      
      
      def test_ffmpeg_8_1_2_fixture_parses_expected_entries(environment):
          setup, run = environment
          fixture = json.loads(INVENTORY_FIXTURE.read_text())
          setup(**{
              kind: "\n".join(fixture[kind]) + "\n"
              for kind in ("filters", "encoders", "hwaccels")
          })
          result = run("--json", "--filter", "scale", "--encoder", "libx264",
                       "--hwaccel", "videotoolbox")
          assert result.returncode == 0, result.stderr
          report = json.loads(result.stdout)
          counts = [report[kind]["entry_count"] for kind in ("filters", "encoders", "hwaccels")]
          assert counts == [4, 3, 1]
          assert report["queries"] == {
              "filter": {"scale": True},
              "encoder": {"libx264": True},
              "hwaccel": {"videotoolbox": True},
          }
      
      
      def test_named_queries_present_exit_zero(environment):
          setup, run = environment
          result = run("--json", "--filter", "scale", "--filter", "anullsrc",
                       "--encoder", "libx264", "--encoder", "libvpx-vp9",
                       "--hwaccel", "videotoolbox")
          assert result.returncode == 0, result.stderr
          report = json.loads(result.stdout)
          assert report["queries"] == {
              "filter": {"scale": True, "anullsrc": True},
              "encoder": {"libx264": True, "libvpx-vp9": True},
              "hwaccel": {"videotoolbox": True},
          }
      
      
      def test_named_queries_absent_exit_two(environment):
          setup, run = environment
          result = run("--json", "--filter", "drawtext", "--encoder", "nosuchenc",
                       "--hwaccel", "cuda")
          assert result.returncode == 2
          report = json.loads(result.stdout)
          assert report["queries"]["filter"]["drawtext"] is False
          assert report["queries"]["encoder"]["nosuchenc"] is False
          assert report["queries"]["hwaccel"]["cuda"] is False
      
      
      def test_mixed_queries_exit_two(environment):
          setup, run = environment
          result = run("--filter", "scale", "--filter", "drawtext")
          assert result.returncode == 2
      
      
      def test_missing_tools_exit_one(environment, tmp_path):
          setup, run = environment
          empty_bin = tmp_path / "empty-bin"
          empty_bin.mkdir()
          result = run("--json", "--filter", "scale", bin_dir=empty_bin)
          assert result.returncode == 1
          report = json.loads(result.stdout)
          assert report["ffmpeg"]["available"] is False
          assert report["ffprobe"]["available"] is False
          assert report["filters"]["available"] is False
          assert report["queries"] == {"filter": {"scale": None}}
      
      
      def test_ffprobe_missing_exit_one(environment):
          setup, run = environment
          setup(with_ffprobe=False)
          result = run("--json")
          assert result.returncode == 1
          report = json.loads(result.stdout)
          assert report["ffmpeg"]["available"] is True
          assert report["ffprobe"]["available"] is False
      
      
      def test_ffprobe_missing_allows_ffmpeg_only_named_query(environment):
          setup, run = environment
          setup(with_ffprobe=False)
          result = run("--json", "--filter", "scale")
          assert result.returncode == 0, result.stderr
          report = json.loads(result.stdout)
          assert report["ffprobe"]["required"] is False
          assert report["queries"]["filter"]["scale"] is True
      
      
      def test_timeout_is_probe_failure(environment, tmp_path):
          _, run = environment
          ffmpeg = tmp_path / "bin" / "ffmpeg"
          ffmpeg.write_text("#!/bin/sh\n/bin/sleep 1\n")
          result = run("--json", "--timeout", "0.01")
          assert result.returncode == 1
          report = json.loads(result.stdout)
          assert report["ffmpeg"]["status"] == "timeout"
          assert report["ffmpeg"]["timeout_seconds"] == 0.01
      
      
      def test_inventory_command_failure_exit_one(environment):
          setup, run = environment
          setup(filters_rc=3)
          result = run("--json")
          assert result.returncode == 1
          report = json.loads(result.stdout)
          assert report["filters"]["available"] is False
          assert report["filters"]["returncode"] == 3
          assert report["encoders"]["available"] is True
      
      
      def test_inventory_failure_takes_precedence_over_absent_query(environment):
          setup, run = environment
          setup(filters_rc=1)
          result = run("--filter", "scale")
          assert result.returncode == 1
      
      
      def test_empty_inventory_warns_and_exits_zero_without_queries(environment):
          setup, run = environment
          setup(filters=HEADER_ONLY_FILTERS, encoders="", hwaccels="")
          result = run("--json")
          assert result.returncode == 0, result.stderr
          report = json.loads(result.stdout)
          assert report["filters"]["entry_count"] == 0
          assert "warning" in report["filters"]
          assert report["encoders"]["entry_count"] == 0
          assert report["hwaccels"]["entry_count"] == 0
      
      
      def test_query_against_empty_inventory_is_probe_failure(environment):
          setup, run = environment
          setup(filters=HEADER_ONLY_FILTERS)
          result = run("--json", "--filter", "scale")
          assert result.returncode == 1
          report = json.loads(result.stdout)
          assert report["filters"]["status"] == "unparseable"
          assert report["queries"]["filter"]["scale"] is None
      
      
      def test_malformed_output_yields_no_entries(environment):
          setup, run = environment
          setup(filters=MALFORMED_FILTERS, encoders="garbage line\nsecond line\n")
          result = run("--json", "--filter", "scale", "--encoder", "libx264")
          assert result.returncode == 1
          report = json.loads(result.stdout)
          assert report["filters"]["entry_count"] == 0
          assert report["encoders"]["entry_count"] == 0
          assert report["queries"]["filter"]["scale"] is None
          assert report["queries"]["encoder"]["libx264"] is None
      
      
      def test_repeated_flags_are_deduplicated(environment):
          setup, run = environment
          result = run("--json", "--filter", "scale", "--filter", "scale", "--filter", "scale")
          assert result.returncode == 0, result.stderr
          report = json.loads(result.stdout)
          assert report["queries"]["filter"] == {"scale": True}
      
      
      def test_stderr_noise_is_not_parsed_as_inventory(environment, tmp_path):
          setup, run = environment
          noisy = tmp_path / "bin" / "ffmpeg"
          body = noisy.read_text()
          noisy.write_text(body.replace(
              '#!/bin/sh\n',
              '#!/bin/sh\necho "static banner noise" >&2\necho "banner on stdout too" \n',
          ))
          result = run("--json")
          assert result.returncode == 0, result.stderr
          report = json.loads(result.stdout)
          assert report["filters"]["entry_count"] == 4
      
      
      def test_text_mode_is_concise_and_names_results(environment):
          setup, run = environment
          result = run("--filter", "scale", "--encoder", "nosuchenc")
          assert result.returncode == 2
          lines = result.stdout.splitlines()
          assert FAKE_FFMPEG_VERSION in result.stdout
          assert any(l.startswith("filters: 4 entries parsed") for l in lines)
          assert any(l.startswith("filter 'scale': present") for l in lines)
          assert any(l.startswith("encoder 'nosuchenc': absent") for l in lines)
          assert "Scale the input video size" not in result.stdout
      
      
      def test_json_mode_stdout_is_a_single_json_document(environment):
          setup, run = environment
          result = run("--json")
          assert result.returncode == 0, result.stderr
          assert result.stdout.lstrip().startswith("{")
          json.loads(result.stdout)
      
      
      def test_human_mode_failure_line_for_missing_binary(environment, tmp_path):
          setup, run = environment
          empty_bin = tmp_path / "nobin"
          empty_bin.mkdir()
          result = run(bin_dir=empty_bin)
          assert result.returncode == 1
          assert "ffmpeg: unavailable" in result.stdout
      
    • test_media_workflows.py 41.1 KB
      """Deterministic subprocess tests for the FFmpeg media workflow scripts."""
      
      from __future__ import annotations
      
      import json
      import shutil
      import subprocess
      import sys
      from pathlib import Path
      
      SCRIPTS = Path(__file__).resolve().parent
      
      
      def run_script(name: str, *arguments: str) -> subprocess.CompletedProcess[str]:
          return subprocess.run(
              [sys.executable, str(SCRIPTS / name), *arguments],
              capture_output=True,
              text=True,
              check=False,
          )
      
      
      def write_json(path: Path, value: object) -> Path:
          path.write_text(json.dumps(value))
          return path
      
      
      def probe(*stream_types: str, duration: str = "5.0") -> dict[str, object]:
          return {
              "streams": [
                  {"index": index, "codec_type": stream_type}
                  for index, stream_type in enumerate(stream_types)
              ],
              "format": {"duration": duration},
          }
      
      
      def edl_document() -> dict[str, object]:
          return {
              "schema_version": 1,
              "timebase": "seconds",
              "sources": [
                  {"asset_id": "camera-a", "source": "camera-a.mkv", "duration": 4.0},
                  {"asset_id": "camera-b", "source": "camera-b.mkv", "duration": 4.0},
              ],
              "events": [
                  {
                      "id": "event-a",
                      "asset_id": "camera-a",
                      "stream_refs": ["0:v:0", "0:a:0"],
                      "in": 1.0,
                      "out": 2.0,
                  },
                  {
                      "id": "event-b",
                      "asset_id": "camera-b",
                      "stream_refs": ["1:v:0", "1:a:0"],
                      "in": 0.5,
                      "out": 2.0,
                  },
              ],
              "output": {
                  "mapping": ["video", "audio"],
                  "video": {"width": 320, "height": 180, "fps": 24, "pixel_format": "yuv420p"},
                  "audio": {"sample_rate": 48000, "channel_layout": "stereo"},
                  "expected_duration": 2.5,
                  "tolerance_seconds": 0.01,
              },
          }
      
      
      def error_code(result: subprocess.CompletedProcess[str]) -> str:
          return json.loads(result.stdout)["error"]["code"]
      
      
      def make_visual_fixture(path: Path) -> None:
          result = subprocess.run(
              [
                  "ffmpeg",
                  "-hide_banner",
                  "-loglevel",
                  "error",
                  "-f",
                  "lavfi",
                  "-i",
                  "testsrc2=size=160x90:rate=10:duration=3",
                  "-c:v",
                  "mpeg4",
                  "-y",
                  str(path),
              ],
              capture_output=True,
              text=True,
              check=False,
          )
          assert result.returncode == 0, result.stderr
      
      
      def test_vision_handoff_orders_neighbors_and_keeps_manifest_private(tmp_path: Path) -> None:
          if shutil.which("ffmpeg") is None:
              import pytest
      
              pytest.skip("ffmpeg is required for visual handoff testing")
          source = tmp_path / "private-person-name.mov"
          make_visual_fixture(source)
          packet = tmp_path / "packet"
      
          result = run_script(
              "vision-review-handoff",
              str(source),
              "--asset-id",
              "asset-opaque-7",
              "--question",
              "Is the sampled boundary visually continuous?",
              "--timestamp",
              "2",
              "--timestamp",
              "1",
              "--neighbor-seconds",
              "0.25",
              "--max-frames",
              "8",
              "--max-range-seconds",
              "2",
              "--output-dir",
              str(packet),
              "--json",
          )
      
          assert result.returncode == 0, result.stdout + result.stderr
          manifest_text = (packet / "manifest.json").read_text()
          manifest = json.loads(manifest_text)
          assert manifest["sampling"]["timestamps_seconds"] == ["0.75", "1", "1.25", "1.75", "2", "2.25"]
          assert manifest["asset_id"] == "asset-opaque-7"
          assert str(source) not in manifest_text
          assert "private-person-name" not in manifest_text
          assert all(item["source_asset_id"] == "asset-opaque-7" for item in manifest["artifacts"])
          assert all((packet / item["artifact"]).is_file() for item in manifest["artifacts"])
          assert (
              sum(item["size_bytes"] for item in manifest["artifacts"]) == manifest["total_output_bytes"]
          )
          assert "not established" in manifest["sampling"]["coverage_statement"]
      
      
      def test_vision_handoff_enforces_neighbor_frame_and_range_limits(tmp_path: Path) -> None:
          source = tmp_path / "source.mov"
          source.write_bytes(b"not decoded because validation fails first")
          frame_limit = run_script(
              "vision-review-handoff",
              str(source),
              "--asset-id",
              "asset-a",
              "--question",
              "boundary",
              "--timestamp",
              "1",
              "--neighbor-seconds",
              "0.25",
              "--max-frames",
              "2",
              "--output-dir",
              str(tmp_path / "packet-a"),
              "--json",
          )
          range_limit = run_script(
              "vision-review-handoff",
              str(source),
              "--asset-id",
              "asset-a",
              "--question",
              "range",
              "--timestamp",
              "1",
              "--timestamp",
              "5",
              "--max-range-seconds",
              "2",
              "--output-dir",
              str(tmp_path / "packet-b"),
              "--json",
          )
      
          assert frame_limit.returncode == 2
          assert error_code(frame_limit) == "frame_limit_exceeded"
          assert range_limit.returncode == 2
          assert error_code(range_limit) == "range_limit_exceeded"
      
      
      def test_import_vision_review_requires_reviewed_evidence(tmp_path: Path) -> None:
          manifest = write_json(
              tmp_path / "manifest.json", {"packet_id": "packet-1", "review": {"status": "pending"}}
          )
          edl = write_json(tmp_path / "edl.json", edl_document())
      
          result = run_script(
              "import-vision-review",
              str(manifest),
              str(edl),
              "--output",
              str(tmp_path / "out.json"),
              "--json",
          )
      
          assert result.returncode == 2
          assert error_code(result) == "review_evidence_missing"
      
      
      def test_import_vision_review_links_attributed_sample_evidence_without_paths(
          tmp_path: Path,
      ) -> None:
          manifest = write_json(
              tmp_path / "manifest.json",
              {
                  "packet_id": "packet-1",
                  "asset_id": "camera-a",
                  "review": {
                      "status": "reviewed",
                      "reviewer": "reviewer-7",
                      "blind_spots": ["unsampled intervals"],
                      "observations": [
                          {
                              "id": "observation-1",
                              "edl_event_id": "event-a",
                              "artifact_refs": ["frames/frame-0001.jpg"],
                              "observation": "The sampled title remains visible.",
                              "evidence_class": "human_or_vision_observation",
                              "confidence": 0.7,
                              "coverage_scope": "sampled_artifacts_only",
                              "editorial_consequence": "Review the proposed cut after the title.",
                          }
                      ],
                  },
              },
          )
          edl = write_json(tmp_path / "edl.json", edl_document())
          output = tmp_path / "reviewed-edl.json"
      
          result = run_script(
              "import-vision-review", str(manifest), str(edl), "--output", str(output), "--json"
          )
      
          assert result.returncode == 0, result.stdout + result.stderr
          evidence = json.loads(output.read_text())["events"][0]["evidence"][0]
          assert evidence["packet_id"] == "packet-1"
          assert evidence["reviewer"] == "reviewer-7"
          assert evidence["coverage_scope"] == "sampled_artifacts_only"
          assert "source" not in evidence
      
      
      def test_render_edl_multi_source_concat_filter_plan_does_not_execute(tmp_path: Path) -> None:
          output = tmp_path / "rendered.mkv"
          edl = write_json(tmp_path / "multi-source.json", edl_document())
      
          result = run_script("render-edl", str(edl), "--output", str(output))
      
          assert result.returncode == 0, result.stdout + result.stderr
          report = json.loads(result.stdout)
          assert report["selected_mechanism"] == "concat_filter"
          assert report["sources"] == [
              {"asset_id": "camera-a", "input_index": 0},
              {"asset_id": "camera-b", "input_index": 1},
          ]
          assert [event["input_index"] for event in report["events"]] == [0, 1]
          assert report["derived_duration"] == 2.5
          assert "[0:v:0]trim=start=1:end=2" in report["filter_complex"]
          assert "[1:a:0]atrim=start=0.5:end=2" in report["filter_complex"]
          assert "concat=n=2:v=1:a=1[vout][aout]" in report["filter_complex"]
          assert report["argv"].count("[vout]") == 1
          assert report["argv"].count("[aout]") == 1
          assert report["executed"] is False
          assert not output.exists()
      
      
      def test_render_edl_concat_filter_plan_executes_against_synthetic_fixtures(tmp_path: Path) -> None:
          if shutil.which("ffmpeg") is None or shutil.which("ffprobe") is None:
              import pytest
      
              pytest.skip("ffmpeg and ffprobe are required for the rendered fixture check")
      
          fixture_workspace = tmp_path / "fixtures"
          fixture_result = run_script("generate-media-fixtures", str(fixture_workspace), "--json")
          assert fixture_result.returncode == 0, fixture_result.stdout + fixture_result.stderr
          output = tmp_path / "rendered.mkv"
          document = {
              "schema_version": 1,
              "timebase": "seconds",
              "sources": [
                  {
                      "asset_id": "red",
                      "source": str(fixture_workspace / "concat-red.mkv"),
                      "duration": 1.0,
                  },
                  {
                      "asset_id": "blue",
                      "source": str(fixture_workspace / "concat-blue.mkv"),
                      "duration": 1.0,
                  },
              ],
              "events": [
                  {"asset_id": "red", "stream_refs": ["0:v:0", "0:a:0"], "in": 0.0, "out": 0.8},
                  {"asset_id": "blue", "stream_refs": ["1:v:0", "1:a:0"], "in": 0.1, "out": 0.9},
              ],
              "output": {
                  "mapping": ["video", "audio"],
                  "video": {
                      "codec": "mpeg4",
                      "width": 160,
                      "height": 90,
                      "fps": 24,
                      "pixel_format": "yuv420p",
                  },
                  "audio": {"codec": "pcm_s16le", "sample_rate": 48000, "channel_layout": "mono"},
                  "expected_duration": 1.6,
                  "tolerance_seconds": 0.01,
              },
          }
          edl = write_json(tmp_path / "executable-plan.json", document)
          plan_result = run_script("render-edl", str(edl), "--output", str(output))
          assert plan_result.returncode == 0, plan_result.stdout + plan_result.stderr
          plan = json.loads(plan_result.stdout)
      
          rendered = subprocess.run(plan["argv"], capture_output=True, text=True, check=False)
      
          assert rendered.returncode == 0, rendered.stderr
          assert output.exists()
          probe_result = subprocess.run(
              [
                  "ffprobe",
                  "-v",
                  "error",
                  "-show_entries",
                  "stream=codec_type",
                  "-of",
                  "json",
                  str(output),
              ],
              capture_output=True,
              text=True,
              check=False,
          )
          assert probe_result.returncode == 0, probe_result.stderr
          assert {item["codec_type"] for item in json.loads(probe_result.stdout)["streams"]} == {
              "video",
              "audio",
          }
      
      
      def test_render_edl_multi_event_same_source_reuses_input_index(tmp_path: Path) -> None:
          document = edl_document()
          document["sources"] = [document["sources"][0]]
          document["events"][1]["asset_id"] = "camera-a"
          document["events"][1]["stream_refs"] = ["0:v:0", "0:a:0"]
          edl = write_json(tmp_path / "multi-event.json", document)
      
          result = run_script("render-edl", str(edl))
      
          assert result.returncode == 0
          report = json.loads(result.stdout)
          assert [event["input_index"] for event in report["events"]] == [0, 0]
          assert report["argv"].count("-i") == 1
      
      
      def test_render_edl_concat_demuxer_requires_matching_probed_signatures(tmp_path: Path) -> None:
          document = edl_document()
          signature = {"video": "mpeg4:320x180:24", "audio": "pcm_s16le:48000:stereo"}
          for source in document["sources"]:
              source["compatibility_signature"] = signature
          for event in document["events"]:
              event["boundary_precision"] = "packet"
              event["keyframe_status"] = "verified"
          edl = write_json(tmp_path / "copy.json", document)
      
          result = run_script("render-edl", str(edl), "--strategy", "concat-demuxer")
      
          assert result.returncode == 0, result.stdout
          report = json.loads(result.stdout)
          assert report["selected_mechanism"] == "concat_demuxer"
          concat_text = report["auxiliary_files"][0]["content"]
          assert "file 'camera-a.mkv'" in concat_text
          assert "file 'camera-b.mkv'" in concat_text
          assert "inpoint 1" in concat_text
          assert report["argv"][-3:] == ["-c", "copy", "output.mkv"]
      
      
      def test_render_edl_rejects_incompatible_concat_signatures(tmp_path: Path) -> None:
          document = edl_document()
          document["sources"][0]["compatibility_signature"] = {"fps": 24}
          document["sources"][1]["compatibility_signature"] = {"fps": 25}
          for event in document["events"]:
              event["boundary_precision"] = "packet"
              event["keyframe_status"] = "verified"
          edl = write_json(tmp_path / "incompatible.json", document)
      
          result = run_script("render-edl", str(edl), "--strategy", "concat-demuxer")
      
          assert result.returncode == 2
          assert error_code(result) == "incompatible_concat_sources"
      
      
      def test_render_edl_rejects_unverified_stream_copy_boundary(tmp_path: Path) -> None:
          document = edl_document()
          for source in document["sources"]:
              source["compatibility_signature"] = {"fps": 24}
          edl = write_json(tmp_path / "unverified.json", document)
      
          result = run_script("render-edl", str(edl), "--strategy", "concat-demuxer")
      
          assert result.returncode == 2
          assert error_code(result) == "unverified_stream_copy_boundary"
      
      
      def test_render_edl_rejects_transition_destination_overlap_and_bad_duration(tmp_path: Path) -> None:
          transition = edl_document()
          transition["events"][1]["treatment"] = {"transition": "xfade"}
          transition_result = run_script(
              "render-edl", str(write_json(tmp_path / "transition.json", transition))
          )
          assert error_code(transition_result) == "unsupported_transition"
      
          overlap = edl_document()
          overlap["events"][1]["destination_start"] = 0.5
          overlap_result = run_script("render-edl", str(write_json(tmp_path / "overlap.json", overlap)))
          assert error_code(overlap_result) == "destination_overlap"
      
          duration = edl_document()
          duration["output"]["expected_duration"] = 9.0
          duration_result = run_script(
              "render-edl", str(write_json(tmp_path / "duration.json", duration))
          )
          assert error_code(duration_result) == "duration_mismatch"
      
      
      def test_render_edl_rejects_missing_source_stream_and_ambiguous_timebase(tmp_path: Path) -> None:
          missing_source = edl_document()
          missing_source["events"][0]["asset_id"] = "missing"
          result = run_script("render-edl", str(write_json(tmp_path / "missing.json", missing_source)))
          assert error_code(result) == "missing_source"
      
          missing_stream = edl_document()
          del missing_stream["events"][0]["stream_refs"]
          result = run_script("render-edl", str(write_json(tmp_path / "stream.json", missing_stream)))
          assert error_code(result) == "missing_stream_refs"
      
          missing_audio = edl_document()
          missing_audio["events"][0]["stream_refs"] = ["0:v:0"]
          result = run_script("render-edl", str(write_json(tmp_path / "audio.json", missing_audio)))
          assert error_code(result) == "missing_mapped_stream_ref"
      
          mismatched_input = edl_document()
          mismatched_input["events"][1]["stream_refs"] = ["0:v:0", "0:a:0"]
          result = run_script("render-edl", str(write_json(tmp_path / "mismatch.json", mismatched_input)))
          assert error_code(result) == "stream_ref_input_mismatch"
      
          timebase = edl_document()
          timebase["timebase"] = "frames"
          result = run_script("render-edl", str(write_json(tmp_path / "timebase.json", timebase)))
          assert error_code(result) == "unsupported_timebase"
      
      
      def test_render_edl_rejects_invalid_interval(tmp_path: Path) -> None:
          document = edl_document()
          document["events"][0]["out"] = 7.0
          result = run_script("render-edl", str(write_json(tmp_path / "invalid.json", document)))
      
          assert result.returncode == 2
          assert error_code(result) == "interval_out_of_bounds"
      
      
      def test_audio_inspect_reports_missing_ffprobe(tmp_path: Path) -> None:
          media = tmp_path / "audio.wav"
          media.write_bytes(b"")
          missing_ffprobe = tmp_path / "missing-ffprobe"
      
          result = run_script(
              "audio-inspect",
              str(media),
              "--ffprobe",
              str(missing_ffprobe),
          )
      
          assert result.returncode == 3
          assert json.loads(result.stdout) == {
              "ok": False,
              "status": "missing_tool",
              "error": f"executable not found: {missing_ffprobe}",
          }
      
      
      def test_audio_inspect_rejects_malformed_probe_output(tmp_path: Path) -> None:
          fake_probe = tmp_path / "ffprobe"
          fake_probe.write_text("#!/bin/sh\nprintf 'not-json\\n'\n")
          fake_probe.chmod(0o755)
      
          result = run_script("audio-inspect", "input.wav", "--ffprobe", str(fake_probe), "--json")
      
          assert result.returncode == 1
          assert json.loads(result.stdout)["status"] == "invalid_json"
      
      
      def test_audio_inspect_reports_missing_measurement_filter(tmp_path: Path) -> None:
          fake_probe = tmp_path / "ffprobe"
          fake_probe.write_text(
              '#!/bin/sh\nprintf \'%s\\n\' \'{"streams":[{"codec_type":"audio"}],"format":{"duration":"3"}}\'\n'
          )
          fake_probe.chmod(0o755)
          fake_ffmpeg = tmp_path / "ffmpeg"
          fake_ffmpeg.write_text(
              "#!/bin/sh\ncase \"$*\" in *-version*) printf 'ffmpeg version fake\\n' ;; *-filters*) printf 'Filters:\\n' ;; *) exit 99 ;; esac\n"
          )
          fake_ffmpeg.chmod(0o755)
      
          result = run_script(
              "audio-inspect",
              "input.wav",
              "--ffprobe",
              str(fake_probe),
              "--ffmpeg",
              str(fake_ffmpeg),
              "--measure-silence",
              "--json",
          )
      
          assert result.returncode == 0, result.stdout
          silence = json.loads(result.stdout)["analysis"]["silence"]
          assert silence["status"] == "UNAVAILABLE"
          assert silence["filter"] == "silencedetect"
          assert "intervals" not in silence
      
      
      def test_audio_inspect_measures_synthetic_candidates_and_builds_plan(tmp_path: Path) -> None:
          if shutil.which("ffmpeg") is None or shutil.which("ffprobe") is None:
              import pytest
      
              pytest.skip("ffmpeg and ffprobe are required for measured audio evidence")
      
          fixture_workspace = tmp_path / "fixtures"
          fixture_result = run_script("generate-media-fixtures", str(fixture_workspace), "--json")
          assert fixture_result.returncode == 0, fixture_result.stdout + fixture_result.stderr
          transcript = write_json(
              tmp_path / "transcript.json",
              {
                  "quality": {
                      "method": "synthetic fixture timing",
                      "alignment": "declared, not speech-recognized",
                  },
                  "segments": [
                      {
                          "id": "segment-1",
                          "start": 0.2,
                          "end": 0.8,
                          "text": "synthetic phrase placeholder",
                          "proposed_action": "keep",
                          "reason": "exercise transcript alignment",
                          "confidence": 1.0,
                      }
                  ],
              },
          )
          report_path = tmp_path / "audio-report.json"
          arguments = (
              str(fixture_workspace / "speech-like-audio.wav"),
              "--measure-silence",
              "--measure-loudness",
              "--measure-clipping",
              "--silence-threshold",
              "-50dB",
              "--silence-duration",
              "0.5",
              "--transcript",
              str(transcript),
              "--target-lufs",
              "-16",
              "--true-peak-limit",
              "-1",
              "--output-codec",
              "pcm_s16le",
              "--output-sample-rate",
              "48000",
              "--output-channel-layout",
              "mono",
              "--report-output",
              str(report_path),
              "--json",
          )
      
          result = run_script("audio-inspect", *arguments)
      
          assert result.returncode == 0, result.stdout + result.stderr
          report = json.loads(result.stdout)
          assert report == json.loads(report_path.read_text())
          assert report["analysis"]["silence"]["status"] == "MEASURED"
          assert any(
              interval["duration"] >= 0.5 for interval in report["analysis"]["silence"]["intervals"]
          )
          assert report["analysis"]["loudness"]["integrated_lufs"] is not None
          assert report["analysis"]["loudness"]["true_peak_dbfs"] is not None
          assert report["analysis"]["clipping"]["peak_level_dbfs"] is not None
          candidate = report["podcast_edit_plan"]["candidates"][0]
          assert candidate["source_range"] == {"in": 0.2, "out": 0.8}
          assert candidate["review_status"] == "needs_listening_review"
          assert candidate["handles_seconds"] == 0.05
          assert candidate["fade_seconds"] == 0.01
          assert "text_sha256" in candidate["evidence"]
          assert report["podcast_edit_plan"]["overwrite_policy"] == "refuse"
          assert "listening quality" in report["unverified"]
      
          repeated_arguments = (*arguments[:-3], "--json")
          repeated = run_script("audio-inspect", *repeated_arguments)
          assert repeated.returncode == 0, repeated.stdout + repeated.stderr
          repeated_report = json.loads(repeated.stdout)
          assert repeated_report == report
      
          overwrite = run_script("audio-inspect", *arguments)
          assert overwrite.returncode == 2
          assert json.loads(overwrite.stdout)["status"] == "output_exists"
      
          clipping_result = run_script(
              "audio-inspect",
              str(fixture_workspace / "audio-analysis.wav"),
              "--measure-clipping",
              "--json",
          )
          assert clipping_result.returncode == 0, clipping_result.stdout + clipping_result.stderr
          assert json.loads(clipping_result.stdout)["analysis"]["clipping"]["clipping_candidate"] is True
      
          treated = tmp_path / "treated.wav"
          render = subprocess.run(
              [
                  "ffmpeg",
                  "-v",
                  "error",
                  "-n",
                  "-i",
                  str(fixture_workspace / "speech-like-audio.wav"),
                  "-af",
                  "afade=t=in:d=0.05,afade=t=out:st=2.95:d=0.05",
                  "-c:a",
                  "pcm_s16le",
                  str(treated),
              ],
              capture_output=True,
              text=True,
              check=False,
          )
          assert render.returncode == 0, render.stderr
      
          probe_result = subprocess.run(
              [
                  "ffprobe",
                  "-v",
                  "error",
                  "-show_format",
                  "-show_streams",
                  "-of",
                  "json",
                  str(treated),
              ],
              capture_output=True,
              text=True,
              check=False,
          )
          assert probe_result.returncode == 0, probe_result.stderr
          treated_probe = write_json(tmp_path / "treated-probe.json", json.loads(probe_result.stdout))
          contract = write_json(
              tmp_path / "treated-contract.json",
              {
                  "schema_version": 1,
                  "required_streams": [
                      {"type": "audio", "codec_name": "pcm_s16le", "sample_rate": "48000", "channels": 1}
                  ],
                  "stream_order": ["audio"],
                  "format": {"duration": 3.0, "duration_tolerance": 0.05},
                  "evidence": {},
                  "downstream": {},
              },
          )
          verify = run_script("media-verify", str(contract), str(treated_probe))
          assert verify.returncode == 0, verify.stdout + verify.stderr
          assert json.loads(verify.stdout)["overall_verdict"] == "PASS"
      
      
      def test_audio_inspect_rejects_invalid_thresholds(tmp_path: Path) -> None:
          result = run_script(
              "audio-inspect",
              str(tmp_path / "missing.wav"),
              "--silence-duration",
              "0",
              "--json",
          )
      
          assert result.returncode == 2
          assert json.loads(result.stdout)["status"] == "invalid_threshold"
      
      
      def acceptance_contract() -> dict[str, object]:
          return {
              "schema_version": 1,
              "required_streams": [
                  {
                      "type": "video",
                      "codec_name": "h264",
                      "width": 1920,
                      "height": 1080,
                      "pix_fmt": "yuv420p",
                      "avg_frame_rate": "30000/1001",
                      "tolerances": {"avg_frame_rate": 0.001},
                  },
                  {
                      "type": "audio",
                      "codec_name": "aac",
                      "sample_rate": "48000",
                      "channels": 2,
                      "channel_layout": "stereo",
                  },
                  {"type": "subtitle", "codec_name": "subrip"},
              ],
              "stream_order": ["video", "audio", "subtitle"],
              "forbidden_stream_types": ["data", "attachment"],
              "format": {
                  "format_name": "matroska,webm",
                  "duration": 5.0,
                  "duration_tolerance": 0.1,
                  "start_time": 0.0,
                  "start_time_tolerance": 0.01,
              },
              "chapters": {"count": 1},
              "metadata": {"required": {"title": "Accepted output"}, "forbidden": ["comment"]},
              "evidence": {"decode": "required", "visual_review": "required", "audio_review": "required"},
              "loudness": {
                  "integrated_lufs": {"target": -16.0, "tolerance": 0.5},
                  "true_peak_max_dbfs": -1.0,
              },
              "downstream": {"target": "Test Player 1.0"},
          }
      
      
      def accepted_probe() -> dict[str, object]:
          return {
              "streams": [
                  {
                      "index": 0,
                      "codec_type": "video",
                      "codec_name": "h264",
                      "width": 1920,
                      "height": 1080,
                      "pix_fmt": "yuv420p",
                      "avg_frame_rate": "60000/2002",
                  },
                  {
                      "index": 1,
                      "codec_type": "audio",
                      "codec_name": "aac",
                      "sample_rate": "48000",
                      "channels": 2,
                      "channel_layout": "stereo",
                  },
                  {"index": 2, "codec_type": "subtitle", "codec_name": "subrip"},
              ],
              "format": {
                  "format_name": "matroska,webm",
                  "duration": "5.04",
                  "start_time": "0.000000",
                  "tags": {"title": "Accepted output"},
              },
              "chapters": [{"id": 0}],
          }
      
      
      def accepted_evidence() -> dict[str, object]:
          return {
              "decode": {"status": "PASS", "command": ["ffmpeg", "-f", "null", "-"]},
              "visual_review": {"status": "PASS", "artifact": "visual-review.json"},
              "audio_review": {"status": "PASS", "artifact": "listening-review.json"},
              "loudness": {
                  "integrated_lufs": -16.2,
                  "true_peak_dbfs": -1.2,
                  "artifact": "loudness.json",
              },
              "downstream": {
                  "status": "PASS",
                  "target": "Test Player 1.0",
                  "artifact": "player-result.json",
              },
          }
      
      
      def test_media_verify_passes_complete_contract(tmp_path: Path) -> None:
          contract = write_json(tmp_path / "contract.json", acceptance_contract())
          output_probe = write_json(tmp_path / "probe.json", accepted_probe())
          evidence = write_json(tmp_path / "evidence.json", accepted_evidence())
      
          result = run_script(
              "media-verify", str(contract), str(output_probe), "--evidence", str(evidence)
          )
      
          assert result.returncode == 0, result.stdout + result.stderr
          report = json.loads(result.stdout)
          assert report["ok"] is True
          assert report["overall_verdict"] == "PASS"
          assert report["summary"]["FAIL"] == 0
          assert report["summary"]["UNVERIFIED"] == 0
          assert all(
              {"criterion", "boundary", "verdict", "expected", "observed", "evidence", "reason"}
              == set(item)
              for item in report["criteria"]
          )
      
      
      def test_media_verify_fails_independent_stream_subtitle_and_tolerance_checks(
          tmp_path: Path,
      ) -> None:
          probe_document = accepted_probe()
          probe_document["streams"] = [probe_document["streams"][0]]
          probe_document["format"]["duration"] = "5.5"
          contract = write_json(tmp_path / "contract.json", acceptance_contract())
          output_probe = write_json(tmp_path / "probe.json", probe_document)
          evidence = write_json(tmp_path / "evidence.json", accepted_evidence())
      
          result = run_script(
              "media-verify", str(contract), str(output_probe), "--evidence", str(evidence)
          )
      
          assert result.returncode == 1
          report = json.loads(result.stdout)
          verdicts = {item["criterion"]: item["verdict"] for item in report["criteria"]}
          assert report["overall_verdict"] == "FAIL"
          assert verdicts["audio_0_present"] == "FAIL"
          assert verdicts["subtitle_0_present"] == "FAIL"
          assert verdicts["stream_order"] == "FAIL"
          assert verdicts["format_duration"] == "FAIL"
      
      
      def test_media_verify_marks_missing_fields_and_evidence_unverified(tmp_path: Path) -> None:
          probe_document = accepted_probe()
          del probe_document["streams"][0]["pix_fmt"]
          contract = write_json(tmp_path / "contract.json", acceptance_contract())
          output_probe = write_json(tmp_path / "probe.json", probe_document)
      
          result = run_script("media-verify", str(contract), str(output_probe))
      
          assert result.returncode == 1
          report = json.loads(result.stdout)
          verdicts = {item["criterion"]: item["verdict"] for item in report["criteria"]}
          assert report["overall_verdict"] == "UNVERIFIED"
          assert verdicts["video_0_pix_fmt"] == "UNVERIFIED"
          assert verdicts["decode"] == "UNVERIFIED"
          assert verdicts["loudness_integrated_lufs"] == "UNVERIFIED"
          assert verdicts["downstream_consumer"] == "UNVERIFIED"
      
      
      def test_media_verify_preserves_blocked_review_status(tmp_path: Path) -> None:
          evidence_document = accepted_evidence()
          evidence_document["visual_review"] = {
              "status": "BLOCKED",
              "reason": "authorized reviewer unavailable",
          }
          contract = write_json(tmp_path / "contract.json", acceptance_contract())
          output_probe = write_json(tmp_path / "probe.json", accepted_probe())
          evidence = write_json(tmp_path / "evidence.json", evidence_document)
      
          result = run_script(
              "media-verify", str(contract), str(output_probe), "--evidence", str(evidence)
          )
      
          assert result.returncode == 1
          report = json.loads(result.stdout)
          assert report["overall_verdict"] == "BLOCKED"
          visual = next(item for item in report["criteria"] if item["criterion"] == "visual_review")
          assert visual["reason"] == "authorized reviewer unavailable"
      
      
      def test_media_verify_rejects_malformed_contract_and_probe(tmp_path: Path) -> None:
          bad_contract = write_json(tmp_path / "contract.json", {"schema_version": 1})
          probe_path = write_json(tmp_path / "probe.json", accepted_probe())
          result = run_script("media-verify", str(bad_contract), str(probe_path))
          assert result.returncode == 2
          assert json.loads(result.stdout)["status"] == "INVALID_INPUT"
      
          malformed = tmp_path / "malformed.json"
          malformed.write_text("not-json")
          contract = write_json(tmp_path / "valid-contract.json", acceptance_contract())
          result = run_script("media-verify", str(contract), str(malformed))
          assert result.returncode == 2
          assert "could not load output probe" in json.loads(result.stdout)["error"]
      
      
      def test_editorial_workflow_example_runs_with_real_tools(tmp_path: Path) -> None:
          if shutil.which("ffmpeg") is None or shutil.which("ffprobe") is None:
              import pytest
      
              pytest.skip("ffmpeg and ffprobe are required for the integration example")
      
          workspace = tmp_path / "workflow"
          result = run_script(
              "editorial-workflow-example",
              str(workspace),
              "--duration",
              "1.25",
              "--json",
          )
      
          assert result.returncode == 0, result.stdout + result.stderr
          report = json.loads(result.stdout)
          assert report["ok"] is True
          assert report["overall_verdict"] == "PASS_WITH_UNVERIFIED_BOUNDARIES"
          assert report["unverified_boundaries"] == [
              "semantic visual review",
              "listening review",
              "downstream consumer compatibility",
          ]
          assert {
              "synthetic-source.mkv",
              "source-probe.json",
              "intake-manifest.json",
              "evidence-packet.json",
              "edit-decision-list.json",
              "edited-output.mkv",
              "output-probe.json",
              "review-frame-1.png",
              "review-frame-2.png",
              "acceptance-report.json",
              "command-log.json",
          }.issubset(report["artifacts"])
      
          intake = json.loads((workspace / "intake-manifest.json").read_text())
          edl = json.loads((workspace / "edit-decision-list.json").read_text())
          acceptance = json.loads((workspace / "acceptance-report.json").read_text())
          assert intake["workflow_id"] == edl["workflow_id"] == acceptance["workflow_id"]
          assert intake["assets"][0]["id"] == edl["sources"][0]["asset_id"]
          assert edl["events"][0]["id"] == acceptance["event_ids"][0]
          assert acceptance["overall_verdict"] == "PASS_WITH_UNVERIFIED_BOUNDARIES"
      
      
      def test_editorial_workflow_example_refuses_nonempty_workspace(tmp_path: Path) -> None:
          workspace = tmp_path / "workflow"
          workspace.mkdir()
          (workspace / "keep.txt").write_text("do not replace")
      
          result = run_script("editorial-workflow-example", str(workspace), "--json")
      
          assert result.returncode == 2
          assert "workspace must be absent or empty" in json.loads(result.stdout)["error"]
          assert (workspace / "keep.txt").read_text() == "do not replace"
      
      
      def test_generate_media_fixtures_covers_real_boundaries(tmp_path: Path) -> None:
          if shutil.which("ffmpeg") is None or shutil.which("ffprobe") is None:
              import pytest
      
              pytest.skip("ffmpeg and ffprobe are required for the synthetic fixture battery")
      
          workspace = tmp_path / "fixtures"
          result = run_script("generate-media-fixtures", str(workspace), "--json")
      
          assert result.returncode == 0, result.stdout + result.stderr
          summary = json.loads(result.stdout)
          assert summary["ok"] is True
          assert summary["fixture_set"] == "ffmpeg-synthetic-media-v1"
          assert summary["fixture_count"] >= 14
          assert summary["concat_incompatible_verdict"] == "REJECTED_BEFORE_CONCAT"
      
          manifest = json.loads((workspace / "fixture-manifest.json").read_text())
          roles = {fixture["role"] for fixture in manifest["fixtures"]}
          assert {
              "non-keyframe-cut-source",
              "packet-boundary-copy-cut",
              "decoded-accurate-cut",
              "variable-frame-cadence",
              "concat-compatible-input",
              "concat-compatible-success",
              "concat-incompatible-input",
              "audio-offset-and-duration-drift-candidate",
              "audio-silence-and-peak-candidates",
              "audio-fade-output",
              "synthetic-speech-like-analysis-source",
              "subtitle-source-text",
              "subtitle-stream-survival",
              "bounded-boundary-frame",
          }.issubset(roles)
          assert manifest["concat"]["compatible_pair"]["verdict"] == "PASS"
          assert manifest["concat"]["incompatible_candidate"]["differences"]["audio_sample_rate"] == [
              "48000",
              "44100",
          ]
          assert manifest["concat"]["incompatible_candidate"]["differences"]["audio_channels"] == [
              1,
              2,
          ]
          assert manifest["subtitle_burn_in"]["status"] in {"EXERCISED", "UNAVAILABLE"}
          if manifest["subtitle_burn_in"]["status"] == "EXERCISED":
              assert "subtitle-burn-in-output" in roles
              assert manifest["subtitle_burn_in"]["subtitle_stream_present"] is False
          assert manifest["review_packet"]["timestamps_seconds"] == [0.4, 0.5, 0.6]
          assert "no whole-video claim" in manifest["review_packet"]["coverage"]
          assert all(fixture["sha256"].startswith("sha256:") for fixture in manifest["fixtures"])
      
      
      def test_generate_media_fixtures_refuses_nonempty_workspace(tmp_path: Path) -> None:
          workspace = tmp_path / "fixtures"
          workspace.mkdir()
          marker = workspace / "keep.txt"
          marker.write_text("preserve")
      
          result = run_script("generate-media-fixtures", str(workspace), "--json")
      
          assert result.returncode == 2
          assert "workspace must be absent or empty" in json.loads(result.stdout)["error"]
          assert marker.read_text() == "preserve"
      
      
      def compatibility_manifest() -> dict[str, object]:
          return {
              "schema_version": 1,
              "target": {
                  "id": "reference-editor-3.2",
                  "name": "Reference Editor 3.2",
                  "requirement_basis": "mixed",
                  "sources": [
                      {
                          "basis": "official_documentation",
                          "locator": "https://vendor.example/3.2/media",
                          "accessed_at": "2026-09-08",
                          "claim": "H.264 High/AAC MP4 import requirements",
                      },
                      {
                          "basis": "observed_behavior",
                          "locator": "test-run:reference-editor-3.2:fixture-7",
                          "accessed_at": "2026-09-08",
                          "claim": "Exact fixture imported and played in the named build",
                      },
                  ],
              },
              "technical_requirements": {
                  "schema_version": 1,
                  "required_streams": [
                      {
                          "type": "video",
                          "codec_name": "h264",
                          "profile": "High",
                          "width": 1920,
                          "height": 1080,
                          "pix_fmt": "yuv420p",
                          "avg_frame_rate": "30/1",
                      },
                      {
                          "type": "audio",
                          "codec_name": "aac",
                          "sample_rate": "48000",
                          "channels": 2,
                          "channel_layout": "stereo",
                      },
                      {"type": "subtitle", "codec_name": "mov_text"},
                  ],
                  "stream_order": ["video", "audio", "subtitle"],
                  "forbidden_stream_types": ["data", "attachment"],
                  "format": {"format_name": "mov,mp4,m4a,3gp,3g2,mj2"},
                  "chapters": {"count": 0},
                  "metadata": {"required": {"title": "Delivery"}, "forbidden": ["comment"]},
                  "evidence": {
                      "decode": "optional",
                      "visual_review": "optional",
                      "audio_review": "optional",
                  },
              },
              "target_limits": {"maximum_duration_seconds": 60, "maximum_file_size_bytes": 5000000},
              "target_lane": {"method": "import and playback", "authorization_required": True},
          }
      
      
      def compatibility_probe() -> dict[str, object]:
          return {
              "streams": [
                  {
                      "index": 0,
                      "codec_type": "video",
                      "codec_name": "h264",
                      "profile": "High",
                      "width": 1920,
                      "height": 1080,
                      "pix_fmt": "yuv420p",
                      "avg_frame_rate": "30/1",
                  },
                  {
                      "index": 1,
                      "codec_type": "audio",
                      "codec_name": "aac",
                      "sample_rate": "48000",
                      "channels": 2,
                      "channel_layout": "stereo",
                  },
                  {"index": 2, "codec_type": "subtitle", "codec_name": "mov_text"},
              ],
              "format": {
                  "format_name": "mov,mp4,m4a,3gp,3g2,mj2",
                  "duration": "30.0",
                  "size": "4000000",
                  "tags": {"title": "Delivery"},
              },
              "chapters": [],
          }
      
      
      def test_target_compatibility_separates_probe_from_named_consumer(tmp_path: Path) -> None:
          manifest = write_json(tmp_path / "target.json", compatibility_manifest())
          output_probe = write_json(tmp_path / "probe.json", compatibility_probe())
          target_evidence = write_json(
              tmp_path / "target-evidence.json",
              {
                  "target_consumer": {
                      "status": "PASS",
                      "target_id": "reference-editor-3.2",
                      "target_version": "3.2.1",
                      "artifact": "sha256:fixture-7",
                      "method": "import, timeline playback, and subtitle toggle",
                      "warnings": [],
                  }
              },
          )
      
          result = run_script(
              "target-compatibility",
              str(manifest),
              str(output_probe),
              "--target-evidence",
              str(target_evidence),
              "--json",
          )
      
          assert result.returncode == 0, result.stdout + result.stderr
          report = json.loads(result.stdout)
          assert report["overall_verdict"] == "PASS"
          assert report["technical_probe_result"]["overall_verdict"] == "PASS"
          assert report["target_consumer_result"]["verdict"] == "PASS"
          criteria = {item["criterion"]: item for item in report["technical_probe_result"]["criteria"]}
          for expected in (
              "video_0_profile",
              "video_0_width",
              "video_0_avg_frame_rate",
              "stream_order",
              "subtitle_0_codec_name",
              "metadata_title",
              "target_maximum_duration",
              "target_maximum_file_size",
          ):
              assert criteria[expected]["verdict"] == "PASS"
          assert "only to target reference-editor-3.2" in report["boundary_statement"]
      
      
      def test_target_compatibility_keeps_unavailable_lane_separate(tmp_path: Path) -> None:
          manifest = compatibility_manifest()
          manifest["target_lane"]["unavailable_reason"] = "headless CI has no authorized editor session"
          probe_document = compatibility_probe()
          probe_document["streams"][0]["profile"] = "Main"
          probe_document["format"]["size"] = "6000000"
          target_path = write_json(tmp_path / "target.json", manifest)
          probe_path = write_json(tmp_path / "probe.json", probe_document)
      
          result = run_script("target-compatibility", str(target_path), str(probe_path), "--json")
      
          assert result.returncode == 1
          report = json.loads(result.stdout)
          assert report["technical_probe_result"]["overall_verdict"] == "FAIL"
          assert report["target_consumer_result"]["verdict"] == "BLOCKED"
          assert (
              report["target_consumer_result"]["reason"] == "headless CI has no authorized editor session"
          )
          assert report["overall_verdict"] == "FAIL"
      
    • vision-review-handoff 12.1 KB · in bundle
  • templates
    • edit-decision-list.json 1.1 KB
      {
        "schema_version": 1,
        "timebase": "seconds",
        "sources": [
          {
            "asset_id": "asset-001",
            "source": "input-a.mkv",
            "duration": 120.0,
            "compatibility_signature": {
              "video": "h264:1920x1080:yuv420p:30000/1001",
              "audio": "aac:48000:stereo"
            }
          }
        ],
        "events": [
          {
            "id": "event-001",
            "action": "keep",
            "asset_id": "asset-001",
            "stream_refs": ["0:v:0", "0:a:0"],
            "in": 10.0,
            "out": 25.0,
            "reason": "<editorial reason>",
            "evidence": [{"type": "transcript", "locator": "00:10-00:25", "confidence": 0.8}],
            "boundary_precision": "not_verified",
            "keyframe_status": "not_verified",
            "treatment": {"transition": null, "audio_fade": true},
            "review_status": "needs_review",
            "verification": []
          }
        ],
        "output": {
          "mapping": ["video", "audio"],
          "video": {
            "codec": "libx264",
            "width": 1920,
            "height": 1080,
            "fps": 29.97,
            "pixel_format": "yuv420p"
          },
          "audio": {"codec": "aac", "sample_rate": 48000, "channel_layout": "stereo"},
          "expected_duration": 15.0,
          "tolerance_seconds": 0.1
        }
      }
      
    • media-acceptance-contract.json 1 KB
      {
        "schema_version": 1,
        "required_streams": [
          {
            "type": "video",
            "codec_name": "h264",
            "width": 1920,
            "height": 1080,
            "pix_fmt": "yuv420p",
            "avg_frame_rate": "30000/1001",
            "tolerances": {"avg_frame_rate": 0.001}
          },
          {
            "type": "audio",
            "codec_name": "aac",
            "sample_rate": "48000",
            "channels": 2,
            "channel_layout": "stereo"
          }
        ],
        "stream_order": ["video", "audio"],
        "forbidden_stream_types": ["data", "attachment"],
        "format": {
          "format_name": "mov,mp4,m4a,3gp,3g2,mj2",
          "duration": 120.0,
          "duration_tolerance": 0.1,
          "start_time": 0.0,
          "start_time_tolerance": 0.05
        },
        "chapters": {"count": 0},
        "metadata": {"required": {}, "forbidden": ["comment"]},
        "evidence": {
          "decode": "required",
          "visual_review": "required",
          "audio_review": "required"
        },
        "loudness": {
          "integrated_lufs": {"target": -16.0, "tolerance": 1.0},
          "true_peak_max_dbfs": -1.0
        },
        "downstream": {"target": "<named player, editor, host, archive, or service>"}
      }
      
    • media-acceptance-report.md 1.1 KB
      # Media Acceptance Report
      
      - Input identity and digest:
      - Output path and digest:
      - Exact render command:
      - FFmpeg/ffprobe version:
      
      ## Criteria
      
      | Criterion | Expected/observed value | Evidence command/artifact | Boundary | Verdict |
      |---|---|---|---|---|
      | Output decodes | | | component | PASS/FAIL/BLOCKED/UNVERIFIED |
      | Streams/codecs/mapping | | | component | PASS/FAIL/BLOCKED/UNVERIFIED |
      | Timing/duration/sync | | | integration | PASS/FAIL/BLOCKED/UNVERIFIED |
      | Visual boundaries/captions | | | editorial | PASS/FAIL/BLOCKED/UNVERIFIED |
      | Audio levels/listening | | | editorial | PASS/FAIL/BLOCKED/UNVERIFIED |
      | Downstream consumer | | | downstream | PASS/FAIL/BLOCKED/UNVERIFIED/NOT_APPLICABLE |
      
      Keep named-target evidence in a separate target-consumer record: target ID/version, exact artifact digest, method, warnings, requirements sources/access dates, and result. A local probe/decode pass is not the target result, and one target result does not establish universal compatibility.
      
      ## Unverified Boundaries
      
      -
      
      ## Decision
      
      - Overall verdict:
      - Follow-up trigger:
      
    • media-intake.json 856 B
      {
        "schema_version": 1,
        "assets": [
          {
            "id": "asset-001",
            "source": "input.mp4",
            "source_digest": "sha256:<record-after-intake>",
            "probe": {
              "command": "ffprobe -v error -show_format -show_streams -of json input.mp4",
              "captured_at": "<UTC timestamp>",
              "result_path": "<task-local path>"
            }
          }
        ],
        "output_contract": {
          "purpose": "<editorial purpose>",
          "container": "mp4",
          "video_codec": "<required or null>",
          "audio_codec": "<required or null>",
          "downstream_consumer": "<player, editor, host, archive, or other>",
          "acceptance_thresholds": ["<measurable condition>"]
        },
        "preservation": {"originals_untouched": true, "overwrite_policy": "refuse"},
        "privacy_boundary": "<where task-local media and evidence must remain>",
        "assumptions": ["<explicit unknown>"]
      }
      
    • podcast-edit-plan.md 961 B
      # Podcast Edit Plan
      
      ## Contract
      
      - Episode/source ID:
      - Intended listener and destination:
      - Output format/loudness requirements:
      - Codec, sample rate, and channel layout:
      - True-peak limit and measurement method:
      - Original preservation path:
      - Overwrite policy: refuse
      
      ## Evidence
      
      - Transcript and timing quality:
      - Audio probe:
      - Silence threshold/minimum duration and exact command:
      - Loudness/true-peak method and exact command:
      - Peak/clipping candidate method and threshold:
      - Listening review segments:
      
      ## Decisions
      
      | Source range | Action | Reason | Evidence | Confidence | Handles/fades | Review status |
      |---|---|---|---|---|---|---|
      | | keep/remove/shorten/treat | | | | | needs listening review |
      
      ## Safety
      
      - Meaningful pauses preserved:
      - Speech/breath/room-tone policy:
      - Handles and fades:
      - Clipping/noise policy:
      - Missing measurement or playback evidence:
      - Voice/music separation, ducking, intro/outro, chapters, and metadata status:
      
    • research-experiment-record.md 433 B
      # FFmpeg Research Experiment Record
      
      - Question/claim:
      - Evidence class: technical contract / reproduced behavior / heuristic / local observation
      - Official source URLs:
      - FFmpeg/ffprobe version and build:
      - Input identity or synthetic generator:
      - Exact command:
      - Expected result:
      - Observed result:
      - Output probe:
      - Downstream check:
      - Limitations and untested variants:
      - Verdict: verified / partial / contradicted / unverified
      
    • target-compatibility-manifest.json 1.5 KB
      {
        "schema_version": 1,
        "target": {
          "id": "named-target-and-version",
          "name": "Named player, editor, host, archive, or service",
          "requirement_basis": "official_documentation",
          "sources": [
            {
              "basis": "official_documentation",
              "locator": "https://official.example/media-requirements",
              "accessed_at": "YYYY-MM-DD",
              "claim": "Exact requirement supported by this source"
            }
          ]
        },
        "technical_requirements": {
          "schema_version": 1,
          "required_streams": [
            {"type": "video", "codec_name": "h264", "profile": "High", "width": 1920, "height": 1080, "pix_fmt": "yuv420p", "avg_frame_rate": "30000/1001", "tolerances": {"avg_frame_rate": 0.001}},
            {"type": "audio", "codec_name": "aac", "sample_rate": "48000", "channels": 2, "channel_layout": "stereo"}
          ],
          "stream_order": ["video", "audio"],
          "forbidden_stream_types": ["data", "attachment"],
          "format": {"format_name": "mov,mp4,m4a,3gp,3g2,mj2"},
          "chapters": {"count": 0},
          "metadata": {"required": {}, "forbidden": ["comment"]},
          "evidence": {"decode": "optional", "visual_review": "optional", "audio_review": "optional"}
        },
        "target_limits": {"maximum_duration_seconds": 3600, "maximum_file_size_bytes": 1000000000},
        "target_lane": {
          "method": "Import, play, validate, or ingest the exact artifact in the named target",
          "authorization_required": true,
          "unavailable_reason": "Remove this field only when an authorized target environment is available"
        }
      }
      
    • video-inspection-report.md 806 B
      # Video Inspection Report
      
      ## Identity and Contract
      
      - Asset ID:
      - Source digest:
      - Editorial objective:
      - Downstream consumer:
      - Privacy/authorization boundary:
      
      ## Technical Probe
      
      - Command:
      - FFmpeg/ffprobe version/build:
      - Container:
      - Streams, codecs, dimensions, frame rate, sample rate, channels:
      - Start times, durations, time bases, VFR/CFR:
      
      ## Bounded Visual Evidence
      
      - Sampling rule and rationale:
      - Sample count/size limit:
      - Sample timestamps:
      - Frame/contact-sheet artifact:
      - Vision or human reviewer:
      - Coverage limitation: samples describe sampled times only.
      
      ## Candidates and Unknowns
      
      | Time | Candidate observation | Evidence class | Confidence | Needs review |
      |---|---|---|---|---|
      | | | | | |
      
      ## Decision
      
      - Proposed next step:
      - Unresolved assumptions:
      - Do not render until:
      
    • vision-review-observations.json 634 B
      {
        "review": {
          "status": "reviewed",
          "reviewer": "human-or-vision-reviewer-id",
          "blind_spots": ["unsampled intervals", "motion between frames"],
          "observations": [
            {
              "id": "observation-001",
              "edl_event_id": "event-001",
              "artifact_refs": ["frames/frame-0001.jpg"],
              "observation": "Describe only what is visible in the referenced sample.",
              "evidence_class": "human_or_vision_observation",
              "confidence": 0.8,
              "coverage_scope": "sampled_artifacts_only",
              "editorial_consequence": "Candidate consequence requiring editorial approval"
            }
          ]
        }
      }
      
    • visual-review-packet.md 1.2 KB
      # Visual Review Packet
      
      ## Coverage
      
      - Packet ID:
      - Source asset ID:
      - Selected stream:
      - FFmpeg build:
      - Sampling timestamps:
      - Sampling method, neighboring offsets, and bounds:
      - Extraction command template:
      - Scaling, cropping, and color transforms:
      - Maximum frame count, timestamp range, and output bytes:
      - Review target:
      - Coverage statement: this packet represents only the listed timestamps and neighboring windows.
      - Blind spots: unsampled intervals, motion between samples, audio/transcript content, and target display behavior.
      
      ## Observations
      
      | Observation ID | Timestamp | Artifact ref | Observation | Reviewer | Evidence class | Confidence | Coverage scope |
      |---|---|---|---|---|---|---|---|
      | | | | | | technical / human-or-vision / heuristic / unresolved | | sampled artifacts only |
      
      ## Editorial Consequence
      
      - Candidate cut/join:
      - Continuity concerns:
      - Missing evidence:
      - More samples requested:
      - Human decision required:
      
      Do not state that a feature is absent throughout the asset unless the whole asset was reviewed using evidence capable of supporting that claim. Link reviewed observations to EDL events as attributed evidence; they do not become automatic editorial truth.
      
  • README.md 10.5 KB
    # FFmpeg Expert Skill
    
    A practical FFmpeg skill for inspecting local media, planning reviewable video or podcast edits, rendering safely, and accepting outputs from evidence rather than command success alone.
    
    ## Why Install This Skill
    
    FFmpeg failures often happen at boundaries: the wrong stream is selected, a cut lands on an unexpected keyframe, a filter is absent from the installed build, timestamps drift, or a technically valid output fails in its real destination. Editorial work adds another risk: sparse frames, silence intervals, and imperfect transcripts can look more conclusive than they are.
    
    This skill provides a repeatable intake-to-acceptance workflow. It separates technical measurements from editorial judgment, preserves originals, makes cuts reviewable in an edit decision list, and records what was actually checked.
    
    ## What You Get
    
    ### Core guidance
    
    | Path | Purpose |
    |---|---|
    | `SKILL.md` | Trigger boundaries, capability routing, evidence classes, and the core workflow |
    | `references/core-model-and-command-anatomy.md` | Containers, streams, codecs, mapping, option scope, and timestamps |
    | `references/filters-and-transformations.md` | Simple and complex filtergraphs, audio/video filters, and graph debugging |
    | `references/intermediate-workflows.md` | Trimming, concat, metadata, subtitles, scripting, pipes, and streaming |
    | `references/advanced-operations-and-safety.md` | Hardware, synchronization, reproducibility, and operational safety |
    | `references/command-cookbook.md` | Short, assumption-labeled commands |
    | `references/learning-summary.md` | Learning progression and consolidated mental model |
    | `references/source-inventory.md` | Original primary/secondary source inventory and evidence boundaries |
    | `references/local-verification.md` | Version- and host-specific FFmpeg 8.1.2 experiments |
    
    ### Media editing and evidence guidance
    
    | Path | Purpose |
    |---|---|
    | `references/media-intake-and-manifest.md` | Authorization, source identity, probe capture, output contracts, privacy, and preservation |
    | `references/video-inspection-and-visual-evidence.md` | Bounded frame/clip sampling and defensible visual claims |
    | `references/editorial-video-editing.md` | Transcript-assisted decisions, sequencing, treatments, and review gates |
    | `references/audio-and-podcast-editing.md` | Mechanical edits, signal cleanup, editorial audio decisions, and listening checks |
    | `references/ffmpeg-edit-decision-lists.md` | EDL semantics, validation, keyframe status, mapping, and command planning |
    | `references/media-verification-and-acceptance.md` | Layered probe, decode, content, editorial, and downstream acceptance evidence |
    | `references/media-failure-modes.md` | Diagnosis matrix, safe recovery, and stop rules |
    | `references/media-research-source-index.md` | Claim-to-source map for official docs, standards, experiments, and heuristics |
    | `references/editorial-workflow-example.md` | Reproducible synthetic intake-to-acceptance integration workflow |
    | `references/synthetic-media-fixtures.md` | Deterministic real-media fixture coverage and evidence boundaries |
    
    ### Copyable templates
    
    | Path | Purpose |
    |---|---|
    | `templates/media-intake.json` | Parseable source, stream, timing, contract, privacy, and assumption manifest |
    | `templates/edit-decision-list.json` | Parseable source ranges, evidence, confidence, treatments, mapping, and verification |
    | `templates/video-inspection-report.md` | Fixed-section technical and sampled-evidence report |
    | `templates/visual-review-packet.md` | Timestamped review samples with attribution and coverage limits |
    | `templates/vision-review-observations.json` | Machine-readable reviewer attribution, evidence classes, blind spots, and EDL links |
    | `templates/podcast-edit-plan.md` | Mechanical, signal-processing, and editorial audio plan |
    | `templates/media-acceptance-report.md` | Criterion-by-criterion evidence and release verdict |
    | `templates/media-acceptance-contract.json` | Parseable stream, format, evidence, loudness, and downstream criteria |
    | `templates/target-compatibility-manifest.json` | Named consumer, sourced requirements, technical limits, and authorized target lane |
    | `templates/research-experiment-record.md` | Versioned, reproducible command experiment record |
    
    ### Existing automation and evals
    
    | Path | Purpose |
    |---|---|
    | `scripts/ffmpeg-preflight` | Tool status, inventory counts, and named filter/encoder/hwaccel checks |
    | `scripts/test_ffmpeg_preflight.py` | Deterministic tests for the capability preflight |
    | `scripts/fixtures/ffmpeg-8.1.2-inventories.json` | Small version-labeled parser fixture |
    | `scripts/media-intake` | Read-only input inventory with bounded `ffprobe` metadata |
    | `scripts/extract-review-frames` | Bounded timestamp frame extraction for human or vision review |
    | `scripts/vision-review-handoff` | Privacy-safe bounded frame packet with provenance, limits, hashes, and pending-review manifest |
    | `scripts/import-vision-review` | Validate attributed observations and link them to EDL events without rendering |
    | `scripts/render-edl` | Validate single- or multi-source EDLs and emit non-executing concat-filter or concat-demuxer plans |
    | `scripts/audio-inspect` | Bounded silence, loudness, peak/clipping, transcript-candidate, and podcast-plan evidence |
    | `scripts/media-verify` | Evaluate output probe and review evidence against a declared acceptance contract |
    | `scripts/target-compatibility` | Separate technical probe conformance from one named player's/editor's/host's result |
    | `scripts/editorial-workflow-example` | Generate synthetic audio/video and exercise the complete workflow with durable evidence |
    | `scripts/generate-media-fixtures` | Generate a bounded sanitized fixture battery and versioned evidence manifest |
    | `evals/evals.json` | Output-quality cases for core FFmpeg, media evidence, video, podcast, EDL, safety, and acceptance behavior |
    
    ## Quick Start
    
    Install FFmpeg with your platform package manager and inspect the source before choosing an edit:
    
    ```sh
    ffmpeg -version
    ffprobe -version
    ffprobe -v error -show_format -show_streams -of json input.mp4
    ```
    
    For a media editing task:
    
    1. Copy `templates/media-intake.json` into a private task workspace and record the source and output contract.
    2. Collect only the bounded frame, clip, transcript, or signal evidence needed for the decision.
    3. Copy `templates/edit-decision-list.json` or `templates/podcast-edit-plan.md` and review consequential cuts.
    4. Render to a new path with overwrite refusal while exploring.
    5. Copy `templates/media-acceptance-report.md`, probe and review the result, then test the intended player, editor, service, or archive boundary.
    
    Before using a version-sensitive recipe, inspect the local capability:
    
    ```sh
    scripts/ffmpeg-preflight --filter scale --filter loudnorm --encoder libx264 --hwaccel videotoolbox
    ```
    
    Named checks report each capability as present or absent. Exit code `1` means a required tool/probe failed; `2` means a requested capability is absent from a usable inventory. Add `--json` for machine-readable output.
    
    Run the complete synthetic integration example in a new or empty task-local directory:
    
    ```sh
    scripts/editorial-workflow-example /tmp/ffmpeg-editorial-example --json
    ```
    
    The resulting acceptance report passes only the exercised component and integration checks; semantic, listening, and downstream-consumer boundaries remain explicitly unverified.
    
    Generate the richer real-media test battery when a change needs timestamp, concat, audio, subtitle, or bounded visual evidence:
    
    ```sh
    scripts/generate-media-fixtures /tmp/ffmpeg-fixtures --json
    ```
    
    Generated media remains task-local; commit the generator and test assertions, not the binary outputs.
    
    Produce measured audio evidence and a reviewable plan without changing the source:
    
    ```sh
    scripts/audio-inspect input.wav \
      --measure-silence --silence-threshold=-45dB --silence-duration 0.7 \
      --measure-loudness --measure-clipping --json
    ```
    
    Optional timed transcript JSON must disclose alignment quality. Detector intervals and transcript ranges remain candidates until listening review approves an edit.
    
    Evaluate a rendered output against declared criteria:
    
    ```sh
    scripts/media-verify acceptance-contract.json output-probe.json \
      --evidence review-evidence.json --json
    ```
    
    Missing fields or review evidence remain `UNVERIFIED`; blocked reviews remain `BLOCKED`; local probe/decode success never supplies downstream compatibility evidence.
    
    Prepare visual evidence around proposed edit boundaries without exposing the source path:
    
    ```sh
    ffmpeg/scripts/vision-review-handoff private.mov --asset-id asset-017 \
      --question "Does the sampled boundary preserve title continuity?" \
      --timestamp 12.4 --neighbor-seconds 0.25 --output-dir review-packet --json
    ```
    
    The manifest covers only its listed samples. An authorized reviewer must add attributed observations before `import-vision-review` can link them to an EDL; sparse frames never prove absence throughout a video.
    
    For destination-specific delivery, evaluate a sourced target contract and preserve target evidence separately:
    
    ```sh
    ffmpeg/scripts/target-compatibility target.json output-probe.json \
      --target-evidence target-result.json --json
    ```
    
    The verdict applies only to the named target and version. A local FFmpeg decode pass does not substitute for import, playback, or ingest evidence from that consumer.
    
    ## Triggers
    
    Load this skill for:
    
    - Media intake, FFprobe manifests, stream/container/timestamp inspection, or output contracts
    - Remuxing, transcoding, filtering, trimming, joining, extraction, subtitles, or synchronization
    - Bounded review-frame or audio-evidence preparation from supplied/authorized local media
    - Transcript-assisted video edits, reviewable EDLs, or deterministic render plans
    - Podcast cutting, silence/noise analysis, loudness measurement, and audio cleanup
    - Build capability checks, overwrite-safe batch work, output verification, or failure diagnosis
    
    Use another capability first for online media/transcript acquisition, semantic image interpretation, HTML-authored HyperFrames composition, platform publishing/API work, DRM, or rights clearance.
    
    ## Requirements
    
    - `ffmpeg` and `ffprobe` on `PATH` for execution
    - A shell with careful filename and filter-expression quoting
    - A vision-capable or human reviewer for semantic claims about extracted images
    - Listening playback for editorial audio acceptance
    - Network access only for linked documentation or an explicitly requested network protocol
    - Hardware acceleration only with the relevant device, drivers, compiled support, and a verified end-to-end path
    
  • SKILL.md 11.7 KB
    ---
    name: ffmpeg
    description: >-
      Use this skill for local FFmpeg/FFprobe media inspection, remuxing, transcoding,
      filtering, evidence-bounded video review, transcript-assisted editorial plans,
      edit decision lists, podcast/audio cleanup, rendering, and output acceptance.
      It emphasizes explicit stream selection, source preservation, build-aware commands,
      bounded evidence, and verified new outputs. Do not use it for libav API programming,
      opaque whole-video understanding, automatic publishing, rights clearance, DRM
      circumvention, professional broadcast/color certification, or HyperFrames-authored
      compositions; route those tasks to their owning capabilities.
    license: MIT
    compatibility: Requires ffmpeg and ffprobe for execution; exact filters, codecs, protocols, and hardware backends vary by build and version.
    ---
    
    # FFmpeg Expert
    
    Treat FFmpeg as a typed media pipeline and media editing as an evidence-driven workflow. Inspect the actual source, separate measurements from interpretations, make decisions reviewable, render to a new path, and verify at the intended boundary.
    
    ## When Not to Use
    
    - Do not use this skill for libav API programming, opaque whole-video semantic understanding, automatic publishing, rights clearance, DRM circumvention, professional broadcast/color certification, or HyperFrames-authored compositions.
    - Route online media or transcript acquisition to the owning source skill, semantic frame interpretation to a vision-capable reviewer, and platform upload/API work to the platform skill.
    
    ## Boundaries and Routing
    
    - Use a YouTube/transcript capability to acquire online video or transcripts; return here only for local supplied media and transcript artifacts.
    - Use HyperFrames for HTML-authored motion graphics or composition; use this skill to inspect and preprocess its media inputs or verify rendered outputs.
    - Use the named platform skill for upload, publishing, account, or API operations.
    - FFmpeg can extract bounded frames and audio segments but does not interpret their semantic content. Route visual interpretation to a vision-capable reviewer and preserve its observations as attributed evidence.
    - Do not infer rights, consent, identity, intent, or whole-program meaning from technical metadata, sparse frames, silence intervals, or an unaligned transcript.
    
    ## Evidence Classes
    
    Label consequential claims so unlike evidence is not blended:
    
    - **Technical contract** — behavior documented by an official FFmpeg or standards source.
    - **Observed artifact** — probe output, measured signal result, extracted frame, listened segment, or downstream test from this source/output.
    - **Reproducible experiment** — exact version, input identity/generator, command, result, and limits.
    - **Editorial heuristic** — a reversible judgment that requires human review, not a fact established by FFmpeg.
    - **User requirement** — the requested output contract, preservation policy, and acceptance threshold.
    
    ## Media Editing Loop
    
    1. **Intake.** Confirm authorization and privacy boundaries; identify every source; use a private workspace copy of `templates/media-intake.json` to record probe evidence, timing, the output contract, preservation policy, and unresolved assumptions. Keep raw probe output and media in the restricted task workspace, and minimize any shareable derivative.
    2. **Inspect.** Probe streams and format. Check required local capabilities with inventories or `scripts/ffmpeg-preflight`; never assume a filter, encoder, or hardware backend exists.
    3. **Collect bounded evidence.** Extract only the frames, clips, waveform/signal measurements, or transcript spans needed for the decision. Record sample timestamps, count, byte/size limits, and the statement that samples cover sampled times only.
    4. **Plan before rendering.** For editorial changes, write a reviewable EDL or podcast edit plan. Every consequential cut needs a source range, reason, evidence, confidence, treatment, mapping, and verification state. Leave ambiguous decisions unresolved rather than improvising.
    5. **Render safely.** Make stream mapping explicit; prefer `-n` and a new output path; avoid untrusted shell concatenation. Distinguish keyframe-limited stream copy from decoded/re-encoded precise cuts.
    6. **Verify in layers.** Check exit status, decodeability, output probe, stream/timing contract, bounded frame/audio evidence, editorial review, and the actual downstream consumer as applicable. Treat probe/decode success and destination acceptance as separate gates; a local file can pass the former and fail the latter.
    7. **Accept or stop.** Use a private workspace copy of `templates/media-acceptance-report.md` to record pass/fail/blocked per criterion. A valid container or successful command alone is not acceptance.
    
    Start technical inspection with:
    
    ```sh
    ffprobe -v error -show_format -show_streams -of json INPUT
    ```
    
    For shipped helper workflows, run the helper from the skill root with explicit output paths and limits; treat its JSON result as a report to verify, not as acceptance by itself. When using `extract-review-frames`, pass timestamps as separate values (`--timestamps 0 5 9`), not one comma-separated value.
    
    ## Route to the Focused Reference
    
    ### Evidence-driven media work
    
    - Read `references/media-intake-and-manifest.md` before handling supplied/generated media, sensitive material, multiple sources, or a defined delivery contract.
    - Read `references/video-inspection-and-visual-evidence.md` when extracting or reviewing frames/clips, choosing samples, or making visual claims.
    - Read `references/editorial-video-editing.md` for transcript-assisted selection, sequencing, pacing, transitions, overlays, and reviewable editorial decisions.
    - Read `references/audio-and-podcast-editing.md` for podcast cuts, signal cleanup, silence/noise analysis, loudness measurement, and listening gates.
    - Read `references/ffmpeg-edit-decision-lists.md` before creating, validating, or turning an EDL into a command plan.
    - Read `references/media-verification-and-acceptance.md` before declaring an output complete or compatible.
    - Read `references/media-failure-modes.md` when evidence is contradictory, a cut drifts, a filter is missing, review samples are sparse, or a workflow repeatedly fails.
    - Read `references/media-research-source-index.md` when supporting claims, refreshing version-sensitive guidance, or recording a technical experiment.
    - Read `references/editorial-workflow-example.md` when proving that intake, evidence, EDL, rendering, and acceptance artifacts compose end to end on a synthetic fixture.
    - Read `references/synthetic-media-fixtures.md` when a change needs bounded real-media fixtures for cuts, cadence, concat, audio, subtitles, or visual-boundary sampling.
    
    ### Core FFmpeg work
    
    - Read `references/core-model-and-command-anatomy.md` for containers, streams, codecs, option scope, mapping, copy/transcode, and timestamps.
    - Read `references/filters-and-transformations.md` for filtergraphs, labels, audio/video processing, and incremental graph debugging.
    - Read `references/intermediate-workflows.md` for seeking, trimming, concat, metadata, subtitles, batching, pipes, and streaming.
    - Read `references/advanced-operations-and-safety.md` for hardware acceleration, synchronization, reproducibility, network safety, and failure boundaries.
    - Read `references/command-cookbook.md` only after inspection and capability checks; every recipe is conditional.
    - Read `references/learning-summary.md` for the newcomer-first mental model.
    - Read `references/source-inventory.md` for the original FFmpeg source survey and `references/local-verification.md` only for its explicitly host-specific FFmpeg 8.1.2 observations.
    
    ## Templates
    
    - `templates/media-intake.json` — source identities, probes, contract, privacy, preservation, assumptions
    - `templates/edit-decision-list.json` — reviewable source ranges and treatments
    - `templates/video-inspection-report.md` — technical inspection and bounded evidence ledger
    - `templates/visual-review-packet.md` — attributed frame/clip observations and coverage limits
    - `templates/vision-review-observations.json` — parseable attributed observation block for a prepared packet
    - `templates/podcast-edit-plan.md` — mechanical, signal, and editorial audio decisions
    - `templates/media-acceptance-report.md` — layered verification and criterion verdicts
    - `templates/media-acceptance-contract.json` — machine-readable stream, format, evidence, loudness, and downstream requirements
    - `templates/target-compatibility-manifest.json` — one named target, sourced requirements, technical constraints, and downstream lane
    - `templates/research-experiment-record.md` — reproducible version/command/result record
    
    Run `scripts/editorial-workflow-example` in a new or empty task-local directory when a reproducible synthetic integration proof is required. Its `PASS_WITH_UNVERIFIED_BOUNDARIES` result is deliberately narrower than editorial or destination acceptance.
    
    Run `scripts/generate-media-fixtures` when tests need deterministic non-personal media. Keep its generated binaries and manifest in the task workspace; commit the generator and assertions, not the outputs.
    
    Use `scripts/render-edl` for a non-executing single- or multi-source plan. Default to decoded concat-filter assembly; select concat-demuxer stream copy only with matching probe-derived signatures and verified packet/keyframe boundaries. Unsupported transitions must remain explicit errors.
    
    Use `scripts/audio-inspect` for bounded silence, EBU R128, peak/clipping, and transcript-alignment evidence. Request each measurement explicitly, preserve unavailable filters as `UNAVAILABLE`, and treat every interval or transcript range as a listening-review candidate. Its optional report output refuses overwrite.
    
    Use `scripts/media-verify` with a declared acceptance contract, output FFprobe JSON, and optional evidence JSON. It reports every criterion independently as `PASS`, `FAIL`, `BLOCKED`, `UNVERIFIED`, or `NOT_APPLICABLE`; only a report with no failed or missing required evidence is an overall pass.
    
    Use `scripts/target-compatibility` when acceptance names a real player, editor, host, archive, or service. Keep sourced technical requirements and local probe results separate from evidence produced by that exact consumer; a pass applies only to the named target/version.
    
    Use `scripts/vision-review-handoff` to prepare bounded, privacy-safe frame packets for an authorized human or vision reviewer. Import only attributed reviewed observations with `scripts/import-vision-review`; treat proposed editorial consequences as evidence for review, never automatic decisions.
    
    Copy a template into the task workspace and replace its placeholder/example values. Do not put private paths, media, transcripts, or review evidence in the public skill repository.
    
    ## Non-Negotiable Checks
    
    - Make stream selection explicit whenever multiple inputs/tracks or a complex graph are involved.
    - Treat option order as significant: options generally apply to the next input or output.
    - Do not call silence useless; `silencedetect` reports threshold crossings, not editorial value.
    - Claim loudness, clipping, timing, or keyframe status only from an available measurement method and retain its output.
    - A transcript is evidence only for its text and supplied timing quality; spot-check alignment against media before frame-accurate edits.
    - Stop for review when evidence is sparse, ambiguity could remove meaningful content, an optional tool/filter is absent, privacy/authorization is unclear, or two materially different approaches fail.
    
    ## Completion
    
    Finish only when the requested artifact exists at a new path, required probes and bounded reviews are recorded, the output has been exercised at the relevant downstream boundary, and every acceptance criterion is passed or explicitly blocked. Report untested claims and remaining assumptions instead of filling gaps with plausible output.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related