Skip to main content
rule

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.

Install into Cursor · Manually: put the files in place All methods and platforms ↓
.cursor/rules/ffmpeg.mdc
Open on GitHub

What 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 subprocess with ffmpeg, rewrite it against libav*
  • Set up two-pass encoding for this file with a bitrate limit
  • Replace apt-get install ffmpeg in the Dockerfile with a build from a pinned commit

How to install ffmpeg

Manually: put the files in place

.cursor/rules/ffmpeg.mdc

Frontmatter: description, globs, alwaysApply

Step 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

Similar IDE rules

All IDE rules for Backend and APIs →

From the same set69

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor