search
Python star Featured

Python FFMPEG Integration: Edit Videos in Terminal

Master video editing from the command line using Python and FFmpeg. Learn to trim, merge, compress, and manipulate videos programmatically.

person By Gautam Sharma
calendar_today December 31, 2024
schedule 9 min read
Python FFmpeg Video Editing Terminal Command Line

FFmpeg is the industry-standard tool for video processing, and when combined with Python, it becomes a powerful automation engine for video editing tasks. This tutorial shows you how to leverage FFmpeg from Python scripts to edit videos directly from the terminal.

Why FFmpeg with Python?

Combining FFmpeg with Python is perfect for:

  • Automating video processing workflows
  • Batch processing multiple video files
  • Server-side video manipulation without GUI tools
  • Building video processing pipelines
  • Creating custom video editing tools

Prerequisites

First, install FFmpeg on your system:

macOS:

brew install ffmpeg

Ubuntu/Debian:

sudo apt update
sudo apt install ffmpeg

Windows: Download from ffmpeg.org and add to PATH.

Then install the Python wrapper:

pip install ffmpeg-python

For a simpler subprocess approach, FFmpeg is all you need.

Basic FFmpeg Commands from Python

Let’s start with running FFmpeg commands using Python’s subprocess module:

# basic_ffmpeg.py
import subprocess
import sys

def run_ffmpeg_command(command):
    """Execute FFmpeg command and handle output."""
    try:
        result = subprocess.run(
            command,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True,
            check=True
        )
        print("Success!")
        return result
    except subprocess.CalledProcessError as e:
        print(f"Error: {e.stderr}")
        sys.exit(1)

def get_video_info(input_file):
    """Get video information using ffprobe."""
    command = [
        'ffprobe',
        '-v', 'quiet',
        '-print_format', 'json',
        '-show_format',
        '-show_streams',
        input_file
    ]

    result = subprocess.run(
        command,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        text=True
    )

    print(result.stdout)

if __name__ == "__main__":
    get_video_info("input.mp4")

Trimming Videos

Cut a specific portion from a video:

# trim_video.py
import subprocess
import argparse

def trim_video(input_file, output_file, start_time, duration):
    """
    Trim video to specific duration.

    Args:
        input_file: Input video path
        output_file: Output video path
        start_time: Start time (format: HH:MM:SS or seconds)
        duration: Duration in seconds or HH:MM:SS format
    """
    command = [
        'ffmpeg',
        '-i', input_file,
        '-ss', start_time,
        '-t', str(duration),
        '-c:v', 'libx264',
        '-c:a', 'aac',
        '-y',  # Overwrite output file
        output_file
    ]

    print(f"Trimming video from {start_time} for {duration} seconds...")

    try:
        subprocess.run(command, check=True)
        print(f"Video trimmed successfully: {output_file}")
    except subprocess.CalledProcessError as e:
        print(f"Error trimming video: {e}")

def main():
    parser = argparse.ArgumentParser(description='Trim video using FFmpeg')
    parser.add_argument('--input', required=True, help='Input video file')
    parser.add_argument('--output', required=True, help='Output video file')
    parser.add_argument('--start', required=True, help='Start time (HH:MM:SS or seconds)')
    parser.add_argument('--duration', required=True, help='Duration (seconds or HH:MM:SS)')

    args = parser.parse_args()
    trim_video(args.input, args.output, args.start, args.duration)

if __name__ == "__main__":
    main()

Usage:

python trim_video.py --input video.mp4 --output trimmed.mp4 --start 00:00:10 --duration 30

Merging Multiple Videos

Concatenate multiple video files:

# merge_videos.py
import subprocess
import os

def merge_videos(video_list, output_file):
    """
    Merge multiple videos into one.

    Args:
        video_list: List of video file paths
        output_file: Output merged video path
    """
    # Create a temporary file list for FFmpeg
    list_file = 'video_list.txt'

    with open(list_file, 'w') as f:
        for video in video_list:
            # Write in FFmpeg concat format
            f.write(f"file '{video}'\n")

    command = [
        'ffmpeg',
        '-f', 'concat',
        '-safe', '0',
        '-i', list_file,
        '-c', 'copy',
        '-y',
        output_file
    ]

    print(f"Merging {len(video_list)} videos...")

    try:
        subprocess.run(command, check=True)
        print(f"Videos merged successfully: {output_file}")
    except subprocess.CalledProcessError as e:
        print(f"Error merging videos: {e}")
    finally:
        # Clean up temporary file
        if os.path.exists(list_file):
            os.remove(list_file)

if __name__ == "__main__":
    videos = ['video1.mp4', 'video2.mp4', 'video3.mp4']
    merge_videos(videos, 'merged_output.mp4')

Compressing Videos

Reduce video file size while maintaining quality:

# compress_video.py
import subprocess
import os
import argparse

def get_file_size_mb(file_path):
    """Get file size in MB."""
    return os.path.getsize(file_path) / (1024 * 1024)

def compress_video(input_file, output_file, crf=23):
    """
    Compress video using H.264 codec.

    Args:
        input_file: Input video path
        output_file: Output video path
        crf: Constant Rate Factor (18-28, lower = better quality)
    """
    command = [
        'ffmpeg',
        '-i', input_file,
        '-c:v', 'libx264',
        '-crf', str(crf),
        '-preset', 'medium',
        '-c:a', 'aac',
        '-b:a', '128k',
        '-y',
        output_file
    ]

    original_size = get_file_size_mb(input_file)
    print(f"Compressing video (CRF: {crf})...")
    print(f"Original size: {original_size:.2f} MB")

    try:
        subprocess.run(command, check=True)
        compressed_size = get_file_size_mb(output_file)
        compression_ratio = (1 - compressed_size/original_size) * 100

        print(f"Compressed size: {compressed_size:.2f} MB")
        print(f"Compression: {compression_ratio:.1f}% reduction")
        print(f"Video compressed successfully: {output_file}")
    except subprocess.CalledProcessError as e:
        print(f"Error compressing video: {e}")

def main():
    parser = argparse.ArgumentParser(description='Compress video using FFmpeg')
    parser.add_argument('--input', required=True, help='Input video file')
    parser.add_argument('--output', required=True, help='Output video file')
    parser.add_argument('--crf', type=int, default=23, help='CRF value (18-28)')

    args = parser.parse_args()
    compress_video(args.input, args.output, args.crf)

if __name__ == "__main__":
    main()

Converting Video Formats

Convert between different video formats:

# convert_format.py
import subprocess
import argparse

def convert_video_format(input_file, output_file, codec='libx264'):
    """
    Convert video to different format.

    Args:
        input_file: Input video path
        output_file: Output video path
        codec: Video codec (libx264, libx265, libvpx-vp9)
    """
    command = [
        'ffmpeg',
        '-i', input_file,
        '-c:v', codec,
        '-c:a', 'aac',
        '-strict', 'experimental',
        '-y',
        output_file
    ]

    print(f"Converting {input_file} to {output_file}...")

    try:
        subprocess.run(command, check=True)
        print(f"Video converted successfully: {output_file}")
    except subprocess.CalledProcessError as e:
        print(f"Error converting video: {e}")

def main():
    parser = argparse.ArgumentParser(description='Convert video format')
    parser.add_argument('--input', required=True, help='Input video file')
    parser.add_argument('--output', required=True, help='Output video file')
    parser.add_argument('--codec', default='libx264', help='Video codec')

    args = parser.parse_args()
    convert_video_format(args.input, args.output, args.codec)

if __name__ == "__main__":
    main()

Extracting Audio from Video

Extract audio track to a separate file:

# extract_audio.py
import subprocess
import argparse

def extract_audio(input_file, output_file, format='mp3'):
    """
    Extract audio from video file.

    Args:
        input_file: Input video path
        output_file: Output audio path
        format: Audio format (mp3, aac, wav)
    """
    command = [
        'ffmpeg',
        '-i', input_file,
        '-vn',  # No video
        '-acodec', 'libmp3lame' if format == 'mp3' else format,
        '-q:a', '2',  # Quality (0-9, lower is better)
        '-y',
        output_file
    ]

    print(f"Extracting audio from {input_file}...")

    try:
        subprocess.run(command, check=True)
        print(f"Audio extracted successfully: {output_file}")
    except subprocess.CalledProcessError as e:
        print(f"Error extracting audio: {e}")

def main():
    parser = argparse.ArgumentParser(description='Extract audio from video')
    parser.add_argument('--input', required=True, help='Input video file')
    parser.add_argument('--output', required=True, help='Output audio file')
    parser.add_argument('--format', default='mp3', help='Audio format')

    args = parser.parse_args()
    extract_audio(args.input, args.output, args.format)

if __name__ == "__main__":
    main()

Adding Watermark to Video

Overlay an image or text watermark:

# add_watermark.py
import subprocess
import argparse

def add_image_watermark(input_file, watermark_image, output_file, position='bottom-right'):
    """
    Add image watermark to video.

    Args:
        input_file: Input video path
        watermark_image: Watermark image path (PNG with transparency)
        output_file: Output video path
        position: Watermark position (top-left, top-right, bottom-left, bottom-right)
    """
    # Position mapping
    positions = {
        'top-left': '10:10',
        'top-right': 'main_w-overlay_w-10:10',
        'bottom-left': '10:main_h-overlay_h-10',
        'bottom-right': 'main_w-overlay_w-10:main_h-overlay_h-10'
    }

    overlay_position = positions.get(position, positions['bottom-right'])

    command = [
        'ffmpeg',
        '-i', input_file,
        '-i', watermark_image,
        '-filter_complex', f'[1:v]scale=100:-1[wm];[0:v][wm]overlay={overlay_position}',
        '-c:a', 'copy',
        '-y',
        output_file
    ]

    print(f"Adding watermark to video ({position})...")

    try:
        subprocess.run(command, check=True)
        print(f"Watermark added successfully: {output_file}")
    except subprocess.CalledProcessError as e:
        print(f"Error adding watermark: {e}")

def add_text_watermark(input_file, text, output_file):
    """Add text watermark to video."""
    command = [
        'ffmpeg',
        '-i', input_file,
        '-vf', f"drawtext=text='{text}':fontsize=24:fontcolor=white:x=10:y=10:box=1:boxcolor=black@0.5:boxborderw=5",
        '-c:a', 'copy',
        '-y',
        output_file
    ]

    print(f"Adding text watermark: {text}")

    try:
        subprocess.run(command, check=True)
        print(f"Text watermark added successfully: {output_file}")
    except subprocess.CalledProcessError as e:
        print(f"Error adding text watermark: {e}")

def main():
    parser = argparse.ArgumentParser(description='Add watermark to video')
    parser.add_argument('--input', required=True, help='Input video file')
    parser.add_argument('--output', required=True, help='Output video file')
    parser.add_argument('--image', help='Watermark image file')
    parser.add_argument('--text', help='Watermark text')
    parser.add_argument('--position', default='bottom-right', help='Watermark position')

    args = parser.parse_args()

    if args.image:
        add_image_watermark(args.input, args.image, args.output, args.position)
    elif args.text:
        add_text_watermark(args.input, args.text, args.output)
    else:
        print("Error: Provide either --image or --text")

if __name__ == "__main__":
    main()

Creating Thumbnails

Extract frames as thumbnails:

# create_thumbnails.py
import subprocess
import argparse

def create_thumbnail(input_file, output_file, timestamp='00:00:01'):
    """
    Extract frame as thumbnail.

    Args:
        input_file: Input video path
        output_file: Output image path
        timestamp: Time to extract frame (HH:MM:SS)
    """
    command = [
        'ffmpeg',
        '-i', input_file,
        '-ss', timestamp,
        '-vframes', '1',
        '-q:v', '2',
        '-y',
        output_file
    ]

    print(f"Creating thumbnail at {timestamp}...")

    try:
        subprocess.run(command, check=True)
        print(f"Thumbnail created successfully: {output_file}")
    except subprocess.CalledProcessError as e:
        print(f"Error creating thumbnail: {e}")

def create_multiple_thumbnails(input_file, output_pattern, interval=5):
    """
    Create multiple thumbnails at intervals.

    Args:
        input_file: Input video path
        output_pattern: Output pattern (e.g., 'thumb_%03d.jpg')
        interval: Interval in seconds between thumbnails
    """
    command = [
        'ffmpeg',
        '-i', input_file,
        '-vf', f'fps=1/{interval}',
        '-q:v', '2',
        '-y',
        output_pattern
    ]

    print(f"Creating thumbnails every {interval} seconds...")

    try:
        subprocess.run(command, check=True)
        print(f"Thumbnails created successfully: {output_pattern}")
    except subprocess.CalledProcessError as e:
        print(f"Error creating thumbnails: {e}")

def main():
    parser = argparse.ArgumentParser(description='Create video thumbnails')
    parser.add_argument('--input', required=True, help='Input video file')
    parser.add_argument('--output', required=True, help='Output file or pattern')
    parser.add_argument('--timestamp', default='00:00:01', help='Timestamp for single thumbnail')
    parser.add_argument('--interval', type=int, help='Interval for multiple thumbnails')

    args = parser.parse_args()

    if args.interval:
        create_multiple_thumbnails(args.input, args.output, args.interval)
    else:
        create_thumbnail(args.input, args.output, args.timestamp)

if __name__ == "__main__":
    main()

Batch Processing Videos

Process multiple videos automatically:

# batch_processor.py
import subprocess
import os
import glob

class VideoProcessor:
    """Batch video processing utility."""

    def __init__(self, input_dir, output_dir):
        self.input_dir = input_dir
        self.output_dir = output_dir

        # Create output directory if it doesn't exist
        os.makedirs(output_dir, exist_ok=True)

    def process_all_videos(self, operation='compress', **kwargs):
        """
        Process all videos in input directory.

        Args:
            operation: Operation to perform (compress, convert, thumbnail)
            **kwargs: Additional arguments for the operation
        """
        video_files = glob.glob(os.path.join(self.input_dir, '*.mp4'))

        if not video_files:
            print(f"No videos found in {self.input_dir}")
            return

        print(f"Found {len(video_files)} videos to process")

        for idx, video_file in enumerate(video_files, 1):
            filename = os.path.basename(video_file)
            output_file = os.path.join(self.output_dir, filename)

            print(f"\n[{idx}/{len(video_files)}] Processing: {filename}")

            if operation == 'compress':
                self.compress_video(video_file, output_file, kwargs.get('crf', 23))
            elif operation == 'convert':
                self.convert_video(video_file, output_file, kwargs.get('format', 'mp4'))
            elif operation == 'thumbnail':
                thumb_file = output_file.replace('.mp4', '.jpg')
                self.create_thumbnail(video_file, thumb_file)

        print(f"\n✓ Batch processing complete! Output in: {self.output_dir}")

    def compress_video(self, input_file, output_file, crf=23):
        """Compress single video."""
        command = [
            'ffmpeg',
            '-i', input_file,
            '-c:v', 'libx264',
            '-crf', str(crf),
            '-preset', 'medium',
            '-c:a', 'aac',
            '-b:a', '128k',
            '-y',
            output_file
        ]
        subprocess.run(command, check=True, capture_output=True)

    def convert_video(self, input_file, output_file, format='mp4'):
        """Convert video format."""
        output_file = output_file.replace('.mp4', f'.{format}')
        command = [
            'ffmpeg',
            '-i', input_file,
            '-c:v', 'libx264',
            '-c:a', 'aac',
            '-y',
            output_file
        ]
        subprocess.run(command, check=True, capture_output=True)

    def create_thumbnail(self, input_file, output_file):
        """Create thumbnail from video."""
        command = [
            'ffmpeg',
            '-i', input_file,
            '-ss', '00:00:01',
            '-vframes', '1',
            '-q:v', '2',
            '-y',
            output_file
        ]
        subprocess.run(command, check=True, capture_output=True)

if __name__ == "__main__":
    processor = VideoProcessor('./input_videos', './output_videos')
    processor.process_all_videos(operation='compress', crf=23)

Best Practices

When working with FFmpeg and Python:

  1. Always validate input files - Check file existence before processing
  2. Handle errors gracefully - Use try-except blocks for subprocess calls
  3. Use appropriate codecs - H.264 for compatibility, H.265 for better compression
  4. Monitor encoding progress - Parse FFmpeg output for progress updates
  5. Clean up temporary files - Remove intermediate files after processing
  6. Test commands individually - Verify FFmpeg commands work before automating
  7. Consider hardware acceleration - Use GPU encoding for faster processing

Conclusion

FFmpeg with Python provides a powerful combination for video editing automation. Whether you need to:

  • Process videos in batch operations
  • Build automated video workflows
  • Create custom video editing tools
  • Handle server-side video processing

This terminal-based approach gives you complete control over video manipulation without requiring heavy GUI applications. Start with these examples and build more complex video processing pipelines tailored to your specific needs.

The flexibility of combining FFmpeg’s powerful video processing capabilities with Python’s scripting makes it an ideal solution for both simple tasks and complex video editing workflows.

Gautam Sharma

About Gautam Sharma

Full-stack developer and tech blogger sharing coding tutorials and best practices

Related Articles

Python

Python ReportLab Tutorial: Edit PDF Files in Terminal

Learn how to use Python's ReportLab library to create and edit PDF files directly from the command line with practical examples.

December 31, 2024
Python

Generate Excel Files from Raw Data with Python

Quick guide to creating Excel files from raw data using Python. Learn to use openpyxl, xlsxwriter, and pandas for Excel generation.

December 31, 2024
Python

Read and Write CSV Files with Python

Simple guide to reading and writing CSV files in Python using csv module and pandas. Quick examples for data processing.

December 31, 2024