ffmpeg — Cursor rule: how to handle FFmpeg in your project
A Cursor rule that tells the agent how to work with FFmpeg: libav* API calls, two-pass encoding, and pinned builds in CI.
.cursor/rules/ffmpeg.mdcWhat ffmpeg does
The rule covers how FFmpeg gets embedded into an application. The main demand: talk to the library through the libav* APIs (libavcodec, libavformat, libavfilter) instead of shelling out to ffmpeg from code. The command-line tool stays for quick tests, scripting and debugging. The reason is practical — calling the CLI from production code gives you weak error handling, resource leaks and a shell injection hole.
For encoding, the rule asks for two-pass mode wherever bitrate is constrained: streaming output, fixed file size targets. Keyframe intervals are set explicitly with -g and -keyint_min, otherwise seeking suffers. Web delivery means multiple renditions for HLS or DASH on modern codecs such as AV1 and HEVC. For audio in archival work the rule leans on lossless formats — FLAC or WAV with sample rate and format stated outright, so nothing degrades in conversion.
Builds are pinned to a specific Git commit in CI rather than a system ffmpeg from the package manager. After updates, FATE runs as part of the pipeline. If your setup installs FFmpeg with apt-get install, that is worth a second look: the version can drift between builds. The rule file lives at .cursor/rules/ffmpeg.mdc and applies across the project.
What it helps with
- choose between CLI calls and the
libav*API in application code - set up two-pass encoding under a bitrate cap
- pick a keyframe interval so seeking works in a stream
- produce several quality renditions for HLS or DASH
- pin the FFmpeg version in CI so builds stay consistent
Example prompts for the agent
- Check my transcoding code: if it uses
subprocesswithffmpeg, rewrite it againstlibav* - Set up two-pass encoding for this file with a bitrate limit
- Replace
apt-get install ffmpegin the Dockerfile with a build from a pinned commit
How to install ffmpeg
Manually: put the files in place
.cursor/rules/ffmpeg.mdcFrontmatter: description, globs, alwaysApply
Manually: put the files in place
.claude/rules/ffmpeg.mdThe paths frontmatter field limits the files: move globs from the source into paths
AGENTS.md
Codex reads AGENTS.md: paste the rule text there
Manually: put the files in place
.github/instructions/ffmpeg.instructions.mdThe applyTo field sets which files it applies to: move globs from the source into applyTo
Manually: put the files in place
.devin/rules/ffmpeg.mdManually: put the files in place
.clinerules/ffmpeg.mdStep by step, with commands for each platform: Cursor rules: .mdc files, four modes and what replaced .cursorrules
Questions about ffmpeg
How do I install the ffmpeg rule in Cursor?
Create .cursor/rules/ffmpeg.mdc in your project root and paste the rule content into it. Cursor picks it up automatically for files matching **/*.
What does the ffmpeg rule do?
It gives the agent a set of conventions for FFmpeg work: libav* calls instead of the CLI, two-pass encoding, explicit keyframe intervals, lossless audio for archives, and pinned builds in CI.
Why does the rule forbid calling ffmpeg from application code?
Shelling out through subprocess gives weak error handling, leaks resources and opens a shell injection risk. The CLI is meant for tests, scripts and debugging.
Source text
ffmpeg.mdc · CC0-1.0
---
description: This guide defines best practices for integrating and utilizing FFmpeg within our applications, emphasizing API-first development, secure and reproducible builds, and optimal multimedia encoding techniques for quality and performance.
globs: **/*
---
# FFmpeg Best Practices
FFmpeg is the bedrock of our multimedia processing. Adhering to these guidelines ensures our implementations are secure, performant, and maintainable.
## 1. API-First for Application Integration
**Always interact with FFmpeg via its `libav*` APIs (e.g., `libavcodec`, `libavformat`, `libavfilter`) for application-level logic.** The `ffmpeg` command-line tool is for quick tests, scripting, or debugging, *never* for direct invocation within production application code.
❌ **BAD: Shelling out to `ffmpeg` CLI from application code**
```python
import subprocess
# Insecure, slow, hard to manage errors, resource leaks
def transcode_video_bad(input_path, output_path):
cmd = f"ffmpeg -i {input_path} -c:v libx264 -crf 23 {output_path}"
subprocess.run(cmd, shell=True, check=True)
```
✅ **GOOD: Using `libav*` APIs (conceptual C/C++)**
```c
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
// ... other libav* headers
// Proper API usage for transcoding (simplified for brevity)
int transcode_video_good(const char* input_path, const char* output_path) {
AVFormatContext *input_ctx = NULL, *output_ctx = NULL;
AVCodecContext *decoder_ctx = NULL, *encoder_ctx = NULL;
// ... Initialize FFmpeg libraries
// ... Open input file (avformat_open_input)
// ... Find stream info (avformat_find_stream_info)
// ... Find best video/audio stream (av_find_best_stream)
// ... Allocate and open decoder context (avcodec_alloc_context3, avcodec_open2)
// ... Allocate and open encoder context (avcodec_alloc_context3, avcodec_open2)
// ... Open output file (avio_open, avformat_write_header)
// ... Main loop: read packet, decode, filter, encode, write packet
// ... Write trailer, cleanup (avformat_close_input, avio_closep, etc.)
return 0; // Or error code
}
```
**Rationale:** Direct API calls offer fine-grained control, robust error handling, better resource management, and eliminate shell injection vulnerabilities.
## 2. Encoding for Quality & Performance
Optimize video encoding for target platforms and quality requirements.
### 2.1 Two-Pass Encoding for Bitrate Control
For any bitrate-constrained output (e.g., streaming, fixed file size targets), **always use two-pass encoding**. This delivers superior quality at a given bitrate compared to single-pass.
❌ **BAD: Single-pass encoding for bitrate-critical scenarios**
```bash
# Inconsistent quality, less efficient bitrate distribution
ffmpeg -i input.mp4 -c:v libx264 -b:v 1M output.mp4
```
✅ **GOOD: Two-pass encoding (using libx264 as an example)**
```bash
# Pass 1: Analyze video, no output
ffmpeg -i input.mp4 -c:v libx264 -b:v 1M -pass 1 -an -f null /dev/null && \
# Pass 2: Encode with optimized settings from pass 1
ffmpeg -i input.mp4 -c:v libx264 -b:v 1M -pass 2 -c:a aac -b:a 128k output.mp4
```
**API Equivalent (conceptual):** Set `AVCodecContext::flags |= AV_CODEC_FLAG_PASS1` for the first pass and `AV_CODEC_FLAG_PASS2` for the second, managing log files between passes.
### 2.2 Keyframe Interval Optimization
Set explicit keyframe intervals (`-g` and `-keyint_min`) to balance seekability and compression efficiency, especially for streaming or interactive playback.
❌ **BAD: Relying on default keyframe intervals for streaming**
```bash
# Suboptimal seeking performance, larger file size for given seekability
ffmpeg -i input.mp4 -c:v libx264 -crf 23 output.mp4
```
✅ **GOOD: Explicit keyframe intervals (e.g., 2-second interval for 30fps)**
```bash
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -g 60 -keyint_min 60 output.mp4
```
**Rationale:** A consistent keyframe interval improves seeking and stream segmenting for ABR.
### 2.3 Adaptive Bitrate Streaming (ABR)
For web delivery, **always generate multiple renditions for ABR streaming (HLS/DASH)** using modern codecs like AV1 (`libaom-av1`, `libsvtav1`) or HEVC (`libx265`).
```bash
# Example for HLS ABR (simplified for multiple resolutions/bitrates)
# This requires multiple ffmpeg runs or a complex filtergraph
ffmpeg -i input.mp4 \
-map 0:v:0 -map 0:a:0 \
-c:v libx264 -preset medium -g 60 -keyint_min 60 -sc_threshold 0 -b:v 2000k -maxrate 2140k -bufsize 3000k -vf "scale=-2:1080" -c:a aac -b:a 128k \
-hls_time 10 -hls_playlist_type vod -hls_segment_filename "stream_1080p_%03d.ts" stream_1080p.m3u8 \
-map 0:v:0 -map 0:a:0 \
-c:v libx264 -preset medium -g 60 -keyint_min 60 -sc_threshold 0 -b:v 1000k -maxrate 1070k -bufsize 1500k -vf "scale=-2:720" -c:a aac -b:a 96k \
-hls_time 10 -hls_playlist_type vod -hls_segment_filename "stream_720p_%03d.ts" stream_720p.m3u8
# ... and so on for other resolutions, then generate master playlist
```
**Rationale:** ABR provides the best user experience by adapting to network conditions.
## 3. Audio Integrity
For audio-critical workflows (e.g., forensic, archival), **prioritize lossless formats and metadata preservation.**
✅ **GOOD: Lossless audio conversion for forensic/archival**
```bash
# Convert to FLAC, preserving original sample rate and metadata
ffmpeg -i input.mp3 -c:a flac -sample_fmt s32 -ar 48000 output.flac
```
**Rationale:** Lossless formats like FLAC or WAV (PCM) ensure no data degradation. Explicitly setting sample format and rate prevents accidental downsampling.
## 4. Reproducibility & Security
Integrate FFmpeg builds into CI pipelines and manage dependencies rigorously.
### 4.1 Pinning FFmpeg Versions
**Always pin a specific Git commit hash for FFmpeg builds in CI/CD** to ensure reproducibility. Periodically update to the latest master for security patches and new features.
❌ **BAD: Relying on system-installed `ffmpeg` or floating versions**
```dockerfile
# Unpredictable behavior across environments/builds
RUN apt-get update && apt-get install -y ffmpeg
```
✅ **GOOD: Building FFmpeg from a pinned Git commit**
```dockerfile
ARG FFMPEG_COMMIT="a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0" # Example commit hash
RUN git clone https://git.ffmpeg.org/ffmpeg.git /usr/src/ffmpeg && \
cd /usr/src/ffmpeg && \
git checkout ${FFMPEG_COMMIT} && \
./configure --enable-shared --disable-static --enable-gpl --enable-libx264 --enable-libx265 && \
make -j$(nproc) && make install && ldconfig
```
**Rationale:** Pinning commits guarantees that your build environment uses the exact FFmpeg version, preventing unexpected regressions.
### 4.2 FATE Test Suite Integration
**Run the FFmpeg FATE test suite (or a subset) as part of your CI pipeline** after any FFmpeg-related changes or updates.
**Rationale:** FATE is the upstream project's verification suite, catching regressions and ensuring correctness with untrusted media data.
## 5. Configuration Management
### 5.1 Using Preset Files
Store common or complex FFmpeg configurations in preset files (`.ffpreset` or `.avpreset`) for consistency and maintainability.
❌ **BAD: Long, repetitive command-line options**
```bash
ffmpeg -i input.mp4 -c:v libx264 -preset veryslow -crf 18 -profile:v high -level 4.1 -pix_fmt yuv420p -bf 2 -g 250 -keyint_min 25 -sc_threshold 40 -qcomp 0.6 -aq-mode 2 -aq-strength 1.0 -c:a aac -b:a 192k output.mp4
```
✅ **GOOD: Utilizing preset files**
```bash
# In your custom_x264_preset.ffpreset:
# preset=veryslow
# crf=18
# profile=high
# level=4.1
# pix_fmt=yuv420p
# bf=2
# g=250
# keyint_min=25
# sc_threshold=40
# qcomp=0.6
# aq-mode=2
# aq-strength=1.0
# Then use:
ffmpeg -i input.mp4 -c:v libx264 -fpre custom_x264_preset -c:a aac -b:a 192k output.mp4
```
**Rationale:** Presets centralize complex option sets, making commands cleaner and configurations reusable.
### 5.2 AVOption Naming Conventions
Adhere to `AVOption` naming conventions (e.g., `-c:v`, `-b:a`) for clarity and consistency when specifying stream-specific options.
```bash
# Explicitly target video codec and audio bitrate
ffmpeg -i input.mp4 -c:v libx264 -b:a 128k output.mp4
```
**Rationale:** Clear stream specifiers prevent ambiguity and improve readability.
© sanjeed5 · CC0-1.0 · sanjeed5/awesome-cursor-rules-mdc