<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>FFmpeg – IMG.LY Blog</title><description>Posts tagged FFmpeg on the IMG.LY blog.</description><link>https://img.ly/blog/tag/ffmpeg/</link><language>en-us</language><image><url>https://img.ly/apple-touch-icon.png</url><title>FFmpeg – IMG.LY Blog</title><link>https://img.ly/blog/tag/ffmpeg/</link></image><atom:link href="https://img.ly/blog/tag/ffmpeg/rss.xml" rel="self" type="application/rss+xml"/><generator>Astro</generator><lastBuildDate>Tue, 09 Jun 2026 09:48:37 GMT</lastBuildDate><ttl>60</ttl><item><title>A Guide to Batch Video Editing &amp; Server Automation with FFmpeg</title><link>https://img.ly/blog/building-a-production-ready-batch-video-processing-server-with-ffmpeg/</link><guid isPermaLink="true">https://img.ly/blog/building-a-production-ready-batch-video-processing-server-with-ffmpeg/</guid><description>With video production scaling rapidly, manual processing no longer keeps up. This article shows how to build a Docker-based batch processing system using FFmpeg, enabling fast, consistent, and automated video workflows at scale. </description><pubDate>Mon, 10 Nov 2025 10:33:40 GMT</pubDate><content:encoded>&lt;p&gt;The explosion of video content across platforms from social media and streaming services to e-learning and surveillance has made manual video processing increasingly impractical. As organizations manage thousands of clips, the need for automation has become critical to handle transcoding, resizing, thumbnail generation, and other repetitive tasks efficiently. Manual workflows quickly become bottlenecks, consuming engineering time and introducing inconsistencies that slow down production and distribution.&lt;/p&gt;
&lt;p&gt;In this article, you’ll go through a &lt;strong&gt;Docker-based batch video processing system&lt;/strong&gt; powered by FFmpeg, complete with both a REST API and a command-line interface (CLI). This setup enables scalable, reproducible, and automated media operations suitable for local or cloud environments. You can deploy this server on cloud providers – our guides for &lt;a href=&quot;https://img.ly/blog/how-to-run-ffmpeg-on-aws-spot-instances-for-scalable-low-cost-video-processing/&quot;&gt;running FFmpeg on AWS Spot Instances&lt;/a&gt; and &lt;a href=&quot;https://img.ly/blog/ffmpeg-on-google-cloud-platform-guide/&quot;&gt;running FFmpeg on GCP&lt;/a&gt; walk you through setup on each platform. By the end, you’ll understand how to design infrastructure that can process hundreds of videos reliably and in parallel. To follow along, you’ll need a basic understanding of &lt;strong&gt;Python&lt;/strong&gt;, &lt;strong&gt;Docker&lt;/strong&gt;, and common &lt;strong&gt;video formats&lt;/strong&gt; such as MP4 and MOV. For a primer on &lt;a href=&quot;https://img.ly/blog/how-to-run-ffmpeg-inside-a-docker-container/&quot;&gt;running FFmpeg inside a Docker container&lt;/a&gt;, see our Docker guide.&lt;/p&gt;
&lt;p&gt;You can clone &lt;a href=&quot;https://github.com/robinrj6/FFmpeg-batch&quot;&gt;this&lt;/a&gt; github repository and follow the article for a working Docker-based batch video processing system with FFmpeg handling the heavylifting.&lt;/p&gt;
&lt;h2 id=&quot;understanding-video-processing-fundamentals&quot;&gt;Understanding Video Processing Fundamentals&lt;/h2&gt;
&lt;p&gt;FFmpeg is a powerful command-line toolkit capable of reading, transcoding, filtering, and muxing virtually any media format. It’s the foundation of countless commercial encoders, streaming platforms, and post-production tools. With a single command, you can convert between codecs, trim segments, extract audio, or even apply complex filter graphs and when these commands are chained together in scripts or APIs, they form the backbone of scalable media pipelines. For readers unfamiliar with FFmpeg, our &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg/&quot;&gt;Ultimate Guide to FFmpeg&lt;/a&gt; covers the basics and advanced techniques.&lt;/p&gt;
&lt;p&gt;At its core, most video workflows revolve around a few recurring operations: &lt;strong&gt;transcoding&lt;/strong&gt; (changing codecs or bitrates), &lt;strong&gt;compression&lt;/strong&gt; (optimizing size for delivery), &lt;strong&gt;watermarking&lt;/strong&gt; (branding or copyright protection), and &lt;strong&gt;thumbnail extraction&lt;/strong&gt; (visual previews for cataloging). Each operation impacts performance differently depending on your hardware setup. CPU-based processing offers flexibility but can be slow for high-resolution workloads, while GPU acceleration through NVENC or VAAPI dramatically speeds up encoding at the cost of hardware dependencies. Managing memory and I/O efficiently is crucial, as reading and writing large video files can quickly become a bottleneck in batch pipelines.&lt;/p&gt;
&lt;p&gt;Understanding the &lt;strong&gt;file format landscape&lt;/strong&gt; and the &lt;strong&gt;quality–size trade-off&lt;/strong&gt; is key to producing professional results. Modern codecs like &lt;strong&gt;H.264&lt;/strong&gt;, &lt;strong&gt;H.265 (HEVC)&lt;/strong&gt;, and &lt;strong&gt;VP9&lt;/strong&gt; dominate for their balance of efficiency and compatibility, typically wrapped in container formats like &lt;strong&gt;MP4&lt;/strong&gt;, &lt;strong&gt;MOV&lt;/strong&gt;, or &lt;strong&gt;WebM&lt;/strong&gt;. Tools like FFmpeg expose fine-grained control through parameters such as &lt;strong&gt;CRF (Constant Rate Factor)&lt;/strong&gt;, &lt;strong&gt;bitrate targeting&lt;/strong&gt;, and &lt;strong&gt;encoding presets&lt;/strong&gt;, allowing you to tune the ideal compromise between visual fidelity and storage or bandwidth requirements.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/robinrj6/FFmpeg-batch&quot;&gt;&lt;strong&gt;FFmpeg Batch Video Processor&lt;/strong&gt;&lt;/a&gt; builds on these fundamentals to demonstrate how FFmpeg can power fully automated, scalable media workflows. Packaged as a Docker-based, production-ready system, it automates complex operations, such as transcoding, compression, watermarking, and thumbnail generation across multiple videos simultaneously. By combining a FastAPI-powered REST API with a command-line interface (CLI), it provides flexible control over batch processing, progress tracking, and workflow automation. With built-in profiles, concurrent workers, and robust logging, it turns FFmpeg’s raw capabilities into a modern, reproducible video processing pipeline suited for professional production, web delivery, and large-scale content management. For tutorials on cropping and trimming at the client level, see our &lt;a href=&quot;https://img.ly/blog/how-to-crop-and-trim-videos-in-flutter/&quot;&gt;Flutter guide.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now lets dig deeper into the project.&lt;/p&gt;
&lt;h2 id=&quot;architecture-deep-dive&quot;&gt;Architecture Deep Dive&lt;/h2&gt;
&lt;h3 id=&quot;system-components-overview&quot;&gt;System Components Overview&lt;/h3&gt;
&lt;p&gt;The system is modular by design, separating API logic, job management, and media processing into distinct layers for clarity and scalability. Lets look into the different layers of the project now:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;**FastAPI REST interface (api.py)**This file defines the &lt;strong&gt;FastAPI&lt;/strong&gt; application for our batch video processing API powered by &lt;strong&gt;FFmpeg&lt;/strong&gt;. It provides endpoints for managing video processing jobs, profiles, workflows, and file uploads. Key components include:The API supports operations like transcoding, compressing, adding watermarks, and more, using profiles and workflows for predefined configurations. With the help of this API, you can create a dashboard to monitor the jobs and manage them too.
&lt;ol&gt;
&lt;li&gt;Global Instances:The &lt;strong&gt;&lt;code&gt;VideoProcessor&lt;/code&gt;&lt;/strong&gt; executes FFmpeg-based tasks like transcoding and compression, while the &lt;strong&gt;&lt;code&gt;JobQueue&lt;/code&gt;&lt;/strong&gt; manages concurrent job execution through a worker pool. The &lt;strong&gt;&lt;code&gt;ConfigManager&lt;/code&gt;&lt;/strong&gt; maintains processing profiles and workflows, ensuring consistent parameters across all jobs.&lt;/li&gt;
&lt;li&gt;Endpoints:The API provides endpoints for managing jobs, profiles, and workflows, as well as handling uploads and system monitoring. You can create, list, retrieve, cancel, and download jobs; access predefined profiles and workflows; upload videos for processing; check system health and queue stats; and retrieve metadata for uploaded videos.You can go to &lt;a href=&quot;http://localhost:8000/docs#/&quot;&gt;http://localhost:8000/docs#/&lt;/a&gt; when the docker container is running to see a detailed description of every endpoints.&lt;/li&gt;
&lt;li&gt;Startup/Shutdown Events:Initializes and cleans up the job queue.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;**Job queue management (job_queue.py)**This file implements the &lt;strong&gt;job queue system&lt;/strong&gt; for managing and processing video processing tasks asynchronously. It includes:This system makes batch video processing possible, supporting asynchronous execution and state persistence.
&lt;ol&gt;
&lt;li&gt;Job Class:The class represents a video processing task with attributes such as &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;input_file&lt;/code&gt;, &lt;code&gt;output_file&lt;/code&gt;, &lt;code&gt;operation&lt;/code&gt;, &lt;code&gt;parameters&lt;/code&gt;, &lt;code&gt;status&lt;/code&gt;, &lt;code&gt;progress&lt;/code&gt;, timestamps, and error details. It supports operations like thumbnail generation, GIF creation, and audio extraction, automatically generating output file paths. A &lt;code&gt;to_dict&lt;/code&gt; method is included for easy serialization of task data.&lt;/li&gt;
&lt;li&gt;JobQueue Class:The class manages a queue of video processing jobs using a pool of asynchronous workers. It allows adding, retrieving, listing, and canceling jobs, filtering by status, and starting or stopping workers as needed. It also tracks key statistics such as total, completed, and failed jobs and can persist the queue state to a file. Workers process jobs asynchronously, updating progress and handling errors throughout execution.&lt;/li&gt;
&lt;li&gt;Worker System:This component processes jobs using a &lt;code&gt;processor&lt;/code&gt; object that defines the specific operations to perform. It runs these operations within a thread pool to prevent blocking the event loop, while continuously updating job progress and handling both successful completions and failures.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;**Video processing engine (video_processor.py)**This file defines a &lt;code&gt;VideoProcessor&lt;/code&gt; class that provides a set of methods for video processing using FFmpeg. Key features include:The class uses subprocesses to interact with FFmpeg and supports progress callbacks for real-time updates.
&lt;ol&gt;
&lt;li&gt;Video Metadata Extraction:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;get_video_info&lt;/code&gt;: Extracts video details like duration, size, resolution, codec, and frame rate.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Video Operations:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;transcode&lt;/code&gt;: Converts videos to different formats/codecs.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;compress&lt;/code&gt;: Reduces video size with optional scaling or target size.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;add_watermark&lt;/code&gt;: Adds a watermark to a video at specified positions.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;generate_thumbnail&lt;/code&gt;: Creates a thumbnail image from a video.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;extract_audio&lt;/code&gt;: Extracts audio from a video in various formats.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;create_gif&lt;/code&gt;: Converts a video segment into a GIF.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;concatenate_videos&lt;/code&gt;: Merges multiple videos into one.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;trim_video&lt;/code&gt;: Trims a video to a specific duration or time range.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;FFmpeg Execution:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;_execute_ffmpeg&lt;/code&gt;: Handles FFmpeg command execution with progress tracking and error handling.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;**Configuration system (config_manager.py)**This file defines a &lt;code&gt;ConfigManager&lt;/code&gt; class that manages video processing profiles and workflows stored in a YAML configuration file. Key functionalities include:This class is essential for managing reusable configurations for video processing tasks.
&lt;ol&gt;
&lt;li&gt;Configuration Management:Loads profiles and workflows from a YAML file (config/profiles.yaml) and saves any updates or new definitions back to the same file, ensuring configurations remain persistent and easily editable.&lt;/li&gt;
&lt;li&gt;Profiles:The module allows retrieving specific profiles by name and listing all available profiles with their details. It validates each profile to ensure required fields like &lt;code&gt;operation&lt;/code&gt; and &lt;code&gt;parameters&lt;/code&gt; are defined, and also supports creating custom profiles dynamically for flexible and reusable video processing configurations.&lt;/li&gt;
&lt;li&gt;Workflows:The module can retrieve a specific workflow by name and list all available workflows along with their descriptions and the number of jobs they contain, providing a clear overview of predefined processing pipelines.&lt;/li&gt;
&lt;li&gt;Error Handling and Logging:Logs warnings for missing profiles/workflows and errors during file operations.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;design-patterns&quot;&gt;Design Patterns&lt;/h3&gt;
&lt;p&gt;The &lt;strong&gt;FFmpeg-batch&lt;/strong&gt; system applies several proven software design patterns to ensure maintainability and scalability. It follows a &lt;strong&gt;producer–consumer model&lt;/strong&gt; through its job queue: the FastAPI layer acts as the producer by submitting jobs, while asynchronous worker processes act as consumers, executing FFmpeg tasks concurrently. The system also supports &lt;strong&gt;profile-based processing templates&lt;/strong&gt;, where predefined configurations, such as codec settings, bitrates, and resolutions allow teams to standardize repetitive tasks. On top of that, &lt;strong&gt;workflow orchestration&lt;/strong&gt; ties multiple operations together, enabling complex pipelines (for example, transcoding followed by thumbnail extraction) to run automatically in sequence.&lt;/p&gt;
&lt;h3 id=&quot;scalability-considerations&quot;&gt;Scalability Considerations&lt;/h3&gt;
&lt;p&gt;From a scalability perspective, the project uses &lt;strong&gt;worker pools&lt;/strong&gt; to parallelize workloads efficiently, ensuring multiple videos can be processed simultaneously without overloading the system. Effective &lt;strong&gt;resource management&lt;/strong&gt; including queue throttling, memory optimization, and controlled CPU utilization keeps performance consistent even under heavy load. This modular and scalable design makes it straightforward to extend the system horizontally, whether deploying additional Docker containers, scaling Kubernetes pods, or integrating cloud-based distributed processing.&lt;/p&gt;
&lt;h2 id=&quot;setting-up-the-development-environment&quot;&gt;Setting Up the Development Environment&lt;/h2&gt;
&lt;p&gt;Before building or running the &lt;strong&gt;FFmpeg-batch&lt;/strong&gt; system, it’s essential to configure a proper development environment that mirrors production conditions. The setup ensures smooth testing, consistent builds, and predictable behavior across machines.&lt;/p&gt;
&lt;h3 id=&quot;prerequisites-installation&quot;&gt;&lt;strong&gt;Prerequisites Installation&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;You’ll need to have &lt;strong&gt;Docker&lt;/strong&gt;, &lt;strong&gt;Python 3.11&lt;/strong&gt;, and &lt;strong&gt;FFmpeg&lt;/strong&gt; installed. Docker handles containerization and makes it easy to run the application and its dependencies consistently. Python 3.11 provides the runtime for FastAPI and background processing components, while FFmpeg performs the actual video operations. Once installed, you can verify your setup using commands like &lt;code&gt;docker --version&lt;/code&gt;, &lt;code&gt;python3 --version&lt;/code&gt;, and &lt;code&gt;ffmpeg -version&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;project-structure-walkthrough&quot;&gt;&lt;strong&gt;Project Structure Walkthrough&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;The project is organized into clear modules for scalability and maintainability. Core components include &lt;code&gt;api.py&lt;/code&gt; for the REST interface, &lt;code&gt;job_queue.py&lt;/code&gt; for task management, &lt;code&gt;video_processor.py&lt;/code&gt; for media operations, and &lt;code&gt;config_manager.py&lt;/code&gt; for loading and saving profiles and workflows. Additional directories like &lt;code&gt;data/input/&lt;/code&gt; and &lt;code&gt;data/output/&lt;/code&gt; are used for managing uploaded and processed files.&lt;/p&gt;
&lt;h3 id=&quot;configuration-setup&quot;&gt;&lt;strong&gt;Configuration Setup&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;The system uses &lt;strong&gt;environment variables&lt;/strong&gt; (&lt;strong&gt;.env&lt;/strong&gt;) to manage runtime settings like the maximum number of workers or file storage paths and a &lt;strong&gt;profiles.yaml&lt;/strong&gt; file to define reusable encoding profiles and workflows. By editing these configurations, you can customize how the system behaves, whether you’re running quick local tests or deploying to a cloud environment.&lt;/p&gt;
&lt;h3 id=&quot;docker-compose-deep-dive&quot;&gt;&lt;strong&gt;Docker Compose Deep Dive&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;strong&gt;Docker Compose&lt;/strong&gt; configuration defines a single service, &lt;code&gt;video-processor&lt;/code&gt;, which builds from the local &lt;code&gt;Dockerfile&lt;/code&gt; and runs the FFmpeg batch processing API. It maps port &lt;strong&gt;8000&lt;/strong&gt; from the container to the host, allowing you to access the FastAPI interface externally. Several volumes are mounted including &lt;code&gt;./data/input&lt;/code&gt;, &lt;code&gt;./data/output&lt;/code&gt;, and &lt;code&gt;./data/logs&lt;/code&gt; to persist uploaded files, processed results, and runtime logs outside the container lifecycle. The &lt;code&gt;./config&lt;/code&gt; directory is also mounted to provide access to YAML configuration files for profiles and workflows. Environment variables such as &lt;code&gt;MAX_WORKERS&lt;/code&gt;, &lt;code&gt;API_HOST&lt;/code&gt;, and &lt;code&gt;API_PORT&lt;/code&gt; define runtime parameters, while &lt;code&gt;PYTHONUNBUFFERED=1&lt;/code&gt; ensures immediate log output. Finally, the &lt;code&gt;restart: unless-stopped&lt;/code&gt; policy guarantees the service automatically restarts if it crashes or the system reboots, making the setup robust for both local development and long-running batch processing tasks.&lt;/p&gt;
&lt;h3 id=&quot;development-vs-production-setup&quot;&gt;&lt;strong&gt;Development vs. Production Setup&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;In &lt;strong&gt;development mode&lt;/strong&gt;, you can use live-reload for FastAPI and local volumes for quick iteration and debugging. In &lt;strong&gt;production mode&lt;/strong&gt;, Docker Compose can be extended to include load-balanced worker containers, persistent storage, and monitoring tools. This setup enables horizontal scaling and high availability while maintaining the same base configuration used during development ensuring consistent results from your laptop to the cloud.&lt;/p&gt;
&lt;h2 id=&quot;configuration-management--profiles&quot;&gt;Configuration Management &amp;#x26; Profiles&lt;/h2&gt;
&lt;p&gt;A core strength of the &lt;strong&gt;FFmpeg-batch&lt;/strong&gt; system lies in its flexible configuration layer, which defines reusable processing templates and multi-step workflows through a YAML-based profile system. This allows developers and media teams to standardize encoding, compression, and transformation parameters while maintaining full control over quality, size, and performance trade-offs.&lt;/p&gt;
&lt;h3 id=&quot;profile-system-configprofilesyaml&quot;&gt;&lt;strong&gt;Profile System (&lt;code&gt;config/profiles.yaml&lt;/code&gt;)&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Profiles define how a video should be processed, specifying the &lt;strong&gt;operation type&lt;/strong&gt; (e.g., &lt;code&gt;transcode&lt;/code&gt;, &lt;code&gt;compress&lt;/code&gt;, &lt;code&gt;extract_audio&lt;/code&gt;) and its &lt;strong&gt;parameter set&lt;/strong&gt; (e.g., codec, preset, CRF, or scaling). Each profile acts as a self-contained template that can be reused across multiple workflows or customized for specific business needs.&lt;/p&gt;
&lt;p&gt;Common profiles include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;web_optimized&lt;/code&gt;&lt;/strong&gt; — transcodes videos to H.264 with a medium preset and CRF 23 for web streaming.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;social_media&lt;/code&gt;&lt;/strong&gt; — compresses videos to 720p targeting 50 MB, ideal for platforms like Instagram or TikTok.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;mobile_optimized&lt;/code&gt;&lt;/strong&gt; — scales videos down to 480p and limits file size for efficient mobile delivery.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;audio_mp3&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;audio_aac&lt;/code&gt;&lt;/strong&gt; — extract audio tracks at defined bitrates and formats for podcasts or companion files.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;thumbnail&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;preview_gif&lt;/code&gt;&lt;/strong&gt; — generate visual previews or short animated snippets to enhance content catalogs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Creating &lt;strong&gt;custom profiles&lt;/strong&gt; is straightforward: teams can define their own processing templates to meet business-specific requirements such as watermarking, caption embedding, or broadcast-compliant exports. The YAML format also supports &lt;strong&gt;profile inheritance&lt;/strong&gt;, enabling developers to extend base configurations (like &lt;code&gt;web_optimized&lt;/code&gt;) with minor overrides, ensuring consistency while reducing redundancy.&lt;/p&gt;
&lt;h3 id=&quot;workflow-orchestration&quot;&gt;&lt;strong&gt;Workflow Orchestration&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Beyond individual operations, the system supports &lt;strong&gt;multi-step workflows&lt;/strong&gt; that chain multiple profiles into a single automated pipeline. For example, the &lt;code&gt;social_media_package&lt;/code&gt; workflow combines compression, thumbnail generation, and GIF preview creation, all triggered as one batch job. Similarly, the &lt;code&gt;archive_package&lt;/code&gt; workflow transcodes video at high quality, extracts MP3 audio, and generates a thumbnail for long-term storage. You can create your own workflows with your own custom profiles that fits your usecases and use them for your applications with this project.&lt;/p&gt;
&lt;p&gt;Workflows also enable &lt;strong&gt;parallel execution&lt;/strong&gt;, allowing independent operations (such as audio extraction and GIF creation) to run simultaneously for faster turnaround. More advanced pipelines can incorporate &lt;strong&gt;conditional logic&lt;/strong&gt;, such as adapting encoding settings based on input resolution or target platform requirements. Additionally, &lt;strong&gt;batch operations&lt;/strong&gt; can be applied to entire directories, processing hundreds of videos in a consistent, repeatable manner.&lt;/p&gt;
&lt;p&gt;Together, the profile and workflow system transforms FFmpeg-batch from a simple video processor into a scalable, declarative media automation framework, one that adapts to varied production environments and evolving content demands.&lt;/p&gt;
&lt;h2 id=&quot;command-line-interface&quot;&gt;Command Line Interface&lt;/h2&gt;
&lt;p&gt;The &lt;strong&gt;Command Line Interface (CLI)&lt;/strong&gt; in the &lt;strong&gt;FFmpeg-batch&lt;/strong&gt; system offers a fast, intuitive way to trigger and monitor batch processing jobs directly from the terminal. Built on top of the FastAPI backend, the CLI (&lt;code&gt;cli.py&lt;/code&gt;) simplifies automation for developers, video engineers, and content teams who prefer command-line tools or need to integrate FFmpeg-batch into larger production pipelines.&lt;/p&gt;
&lt;h3 id=&quot;cli-design-clipy&quot;&gt;&lt;strong&gt;CLI Design (&lt;code&gt;cli.py&lt;/code&gt;)&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;The CLI is designed around clear, &lt;strong&gt;operation-based command grouping&lt;/strong&gt;. Users can run commands like &lt;code&gt;process&lt;/code&gt;, &lt;code&gt;upload&lt;/code&gt;, &lt;code&gt;list&lt;/code&gt;, or &lt;code&gt;download&lt;/code&gt; to manage the entire lifecycle of a batch job, from input submission to result retrieval. Each command maps closely to the API endpoints, ensuring consistency between programmatic and manual use.&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;interactive mode&lt;/strong&gt;, the CLI provides &lt;strong&gt;progress bars and real-time feedback&lt;/strong&gt; for active jobs, displaying percentage completion, estimated time remaining, and current processing status. This helps operators monitor workloads without constantly querying the API.&lt;/p&gt;
&lt;p&gt;The CLI also supports &lt;strong&gt;batch operations&lt;/strong&gt;, allowing users to process entire directories with a single command (e.g., &lt;code&gt;cli.py process-folder ./input_videos --profile web_optimized&lt;/code&gt;). It automatically applies the chosen profile to all files and stores results in the designated output directory. In CLI, you can run the below docker-compose command to execute it in a docker container:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Using a profile&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;docker-compose&lt;/span&gt;&lt;span&gt; exec&lt;/span&gt;&lt;span&gt; video-processor&lt;/span&gt;&lt;span&gt; python&lt;/span&gt;&lt;span&gt; cli.py&lt;/span&gt;&lt;span&gt; profile&lt;/span&gt;&lt;span&gt; /data/input/video.mp4&lt;/span&gt;&lt;span&gt; web_optimized&lt;/span&gt;&lt;span&gt; --output&lt;/span&gt;&lt;span&gt; /data/output/video.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, the CLI integrates seamlessly with &lt;strong&gt;shell scripting and automation workflows,&lt;/strong&gt; perfect for CI/CD pipelines, cron jobs, or custom scripts that need to trigger video processing as part of larger media workflows. You can also create custom operations like the one given below which transcodes with custom parameters:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Transcode with custom parameters&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;python&lt;/span&gt;&lt;span&gt; cli.py&lt;/span&gt;&lt;span&gt; create&lt;/span&gt;&lt;span&gt; /data/input/video.mp4&lt;/span&gt;&lt;span&gt; transcode&lt;/span&gt;&lt;span&gt; \\\\&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  --params&lt;/span&gt;&lt;span&gt; &apos;{&quot;codec&quot;:&quot;libx264&quot;,&quot;preset&quot;:&quot;slow&quot;,&quot;crf&quot;:18}&apos;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The project demonstrates how even complex, large-scale video operations can be automated using open-source tools like &lt;strong&gt;FFmpeg&lt;/strong&gt;, &lt;strong&gt;FastAPI&lt;/strong&gt;, and &lt;strong&gt;Docker&lt;/strong&gt;. By abstracting individual processing tasks into profiles and workflows, it enables reproducible, scalable, and platform-agnostic video processing pipelines from content creators automating their daily exports to enterprises handling thousands of assets.&lt;/p&gt;
&lt;p&gt;Whether you deploy it locally or in the cloud, this architecture can serve as the foundation for more advanced systems like adding GPU acceleration, distributed job queues, or integration with cloud storage. The full source code is available on &lt;a href=&quot;https://github.com/robinrj6/FFmpeg-batch?utm_source=chatgpt.com&quot;&gt;GitHub&lt;/a&gt;, so you can clone, modify, and extend it for your specific production needs.&lt;/p&gt;</content:encoded><dc:creator>Robin</dc:creator><media:content url="https://blog.img.ly/2025/11/ffmpeg-batch-editing-processing-videos-how-to--1-.jpg" medium="image"/><category>FFmpeg</category><category>Server-side Video</category></item><item><title>How to run FFmpeg inside a Docker container</title><link>https://img.ly/blog/how-to-run-ffmpeg-inside-a-docker-container/</link><guid isPermaLink="true">https://img.ly/blog/how-to-run-ffmpeg-inside-a-docker-container/</guid><description>Running FFmpeg in Docker isn’t just about installation; it’s about scalable media pipelines, automation, and integration with production apps. Learn how to set up the foundation for efficient video processing at scale.</description><pubDate>Wed, 29 Oct 2025 12:22:12 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://ffmpeg.org/&quot;&gt;FFmpeg&lt;/a&gt; is a powerful multimedia framework used for video and audio processing. From transcoding and editing to streaming, its versatility makes it a go-to tool for developers and media professionals. However, installing FFmpeg directly on your system can sometimes lead to dependency conflicts or version mismatches.&lt;/p&gt;
&lt;p&gt;This is where &lt;a href=&quot;https://www.docker.com/&quot;&gt;&lt;strong&gt;Docker&lt;/strong&gt;&lt;/a&gt; comes in. If you’ve never used Docker before, think of it as a platform that lets you package applications and all their dependencies into something called a &lt;a href=&quot;https://www.docker.com/resources/what-container/&quot;&gt;&lt;em&gt;container&lt;/em&gt;&lt;/a&gt;. A container is like a lightweight, portable box that holds everything your application needs to run operating system libraries, binaries, and configurations. Unlike virtual machines, containers don’t include a full guest operating system, which makes them much more efficient and faster to start up. If you’re unfamiliar with FFmpeg itself, our &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg/&quot;&gt;Ultimate Guide to FFmpeg&lt;/a&gt; provides a comprehensive introduction.&lt;/p&gt;
&lt;p&gt;The main takeaway is this: Docker ensures that your application runs the same way everywhere, whether on your laptop, a colleague’s computer, or a cloud server. That means no more “it works on my machine” problems. With FFmpeg inside a Docker container, you get a clean, isolated environment for media processing that you can easily replicate or share with others.&lt;/p&gt;
&lt;p&gt;In this guide, we’ll walk through two approaches for using FFmpeg with Docker:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Running FFmpeg with a pre-built Docker image.&lt;/li&gt;
&lt;li&gt;Building your own Dockerfile for more customization.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Before we start, we need to setup Docker on our machine. Lets do that now&lt;/p&gt;
&lt;h2 id=&quot;installing-docker&quot;&gt;Installing Docker&lt;/h2&gt;
&lt;p&gt;Before working with FFmpeg in Docker, you need to have Docker installed on your system. The &lt;a href=&quot;https://docs.docker.com/get-started/&quot;&gt;installation process&lt;/a&gt; varies depending on your operating system, but Docker provides excellent step-by-step instructions.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Check system requirements.&lt;/strong&gt; Docker Desktop works on macOS, Windows, and Linux distributions. Make sure your system meets the &lt;a href=&quot;https://docs.docker.com/get-docker/&quot;&gt;minimum requirements&lt;/a&gt;.
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Windows / macOS&lt;/strong&gt;: Download Docker Desktop from the &lt;a href=&quot;https://www.docker.com/products/docker-desktop/&quot;&gt;official download page&lt;/a&gt;. Run the installer, and follow the on-screen instructions. Once installed, Docker will run as a background service.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Linux&lt;/strong&gt;: On Linux distributions like Ubuntu, you can install Docker directly via the package manager. For example:&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Test Docker with a hello-world container.&lt;/strong&gt; Run the following command to check that Docker is working correctly:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; run&lt;/span&gt;&lt;span&gt; hello-world&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command pulls a small test image from Docker Hub, runs it, and prints a confirmation message.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Verify the installation.&lt;/strong&gt; After installation, open a terminal (or PowerShell on Windows) and run:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; --version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This should display the installed Docker version.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Install Docker Desktop (recommended for beginners)&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; apt-get&lt;/span&gt;&lt;span&gt; update&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; apt-get&lt;/span&gt;&lt;span&gt; install&lt;/span&gt;&lt;span&gt; docker.io&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alternatively, follow the &lt;a href=&quot;https://docs.docker.com/engine/install/&quot;&gt;Linux installation guide&lt;/a&gt; for your distribution.&lt;/p&gt;
&lt;p&gt;Once you see the “Hello from Docker!” message, you’re ready to start working with FFmpeg inside containers.&lt;/p&gt;
&lt;h2 id=&quot;approach-1-using-a-pre-built-ffmpeg-docker-image&quot;&gt;Approach 1: Using a Pre-Built FFmpeg Docker Image&lt;/h2&gt;
&lt;p&gt;The quickest way to get started with FFmpeg in Docker is to use an image that’s already prepared for you. One of the most widely used options is the image maintained by &lt;strong&gt;jrottenberg&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;pulling-the-image&quot;&gt;Pulling the Image&lt;/h3&gt;
&lt;p&gt;Start by opening your terminal and pulling the official FFmpeg image:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; pull&lt;/span&gt;&lt;span&gt; jrottenberg/ffmpeg&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This downloads the latest FFmpeg image and makes it available on your system. To deploy your containerized FFmpeg pipelines on low‑cost cloud infrastructure, see our guide on &lt;a href=&quot;https://img.ly/blog/how-to-run-ffmpeg-on-aws-spot-instances-for-scalable-low-cost-video-processing/&quot;&gt;running FFmpeg on AWS Spot Instances,&lt;/a&gt; and for deployment on Google Cloud, follow our guide on &lt;a href=&quot;https://img.ly/blog/ffmpeg-on-google-cloud-platform-guide/&quot;&gt;running FFmpeg on GCP.&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;running-a-container&quot;&gt;Running a Container&lt;/h3&gt;
&lt;p&gt;Once the image is available, you can launch a container and access FFmpeg inside it:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; run&lt;/span&gt;&lt;span&gt; -it&lt;/span&gt;&lt;span&gt; jrottenberg/ffmpeg&lt;/span&gt;&lt;span&gt; bash&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command starts a container, opens a bash shell inside it, and gives you access to FFmpeg as if it were installed directly on your machine.&lt;/p&gt;
&lt;h3 id=&quot;using-ffmpeg&quot;&gt;Using FFmpeg&lt;/h3&gt;
&lt;p&gt;Inside the container, you can run FFmpeg commands just like you normally would. For example, to convert a video file from MP4 to AVI:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; input.mp4&lt;/span&gt;&lt;span&gt; output.avi&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If your input files are on your host machine, you’ll want to mount them so the container can access them. A common pattern is:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; run&lt;/span&gt;&lt;span&gt; --rm&lt;/span&gt;&lt;span&gt; -v&lt;/span&gt;&lt;span&gt; $(&lt;/span&gt;&lt;span&gt;pwd&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:/data&lt;/span&gt;&lt;span&gt; jrottenberg/ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; /data/input.mp4&lt;/span&gt;&lt;span&gt; /data/output.avi&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, your current working directory is mounted into the container at &lt;code&gt;/data&lt;/code&gt;, allowing you to process files without copying them into the container.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-rm&lt;/code&gt; : Automatically remove the container when it exits (keeps things clean).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-v $(pwd):/data&lt;/code&gt; : Mounts your current directory (&lt;code&gt;$(pwd)&lt;/code&gt; prints it) into &lt;code&gt;/data&lt;/code&gt; inside the container.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;jrottenberg/ffmpeg&lt;/code&gt; : The ffmpeg image.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ffmpeg -i /data/input.mp4 /data/output.avi&lt;/code&gt; : The FFmpeg command that runs &lt;strong&gt;inside&lt;/strong&gt; the container, reading from and writing to &lt;code&gt;/data&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Want to build an app that crops and trims videos? Our Flutter tutorial shows how to &lt;a href=&quot;https://img.ly/blog/how-to-crop-and-trim-videos-in-flutter/&quot;&gt;crop and trim videos in Flutter&lt;/a&gt; using FFmpeg within a mobile UI.&lt;/p&gt;
&lt;h3 id=&quot;exiting-the-container&quot;&gt;Exiting the Container&lt;/h3&gt;
&lt;p&gt;When you’re done, simply type &lt;code&gt;exit&lt;/code&gt; to leave the container and return to your host terminal.&lt;/p&gt;
&lt;h2 id=&quot;approach-2-building-a-custom-dockerfile-with-ffmpeg&quot;&gt;Approach 2: Building a Custom Dockerfile with FFmpeg&lt;/h2&gt;
&lt;p&gt;Sometimes, you might need more control over your environment—for example, specifying which version of FFmpeg you want to use or installing additional tools. In that case, building your own Docker image is the better choice.&lt;/p&gt;
&lt;h3 id=&quot;creating-a-dockerfile&quot;&gt;Creating a Dockerfile&lt;/h3&gt;
&lt;p&gt;Start by creating a file named &lt;code&gt;Dockerfile&lt;/code&gt; with the following content:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;docker&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; ubuntu:latest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;RUN&lt;/span&gt;&lt;span&gt; apt-get update &amp;#x26;&amp;#x26; apt-get install -y ffmpeg&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This simple Dockerfile uses Ubuntu as the base image and installs FFmpeg on top of it.&lt;/p&gt;
&lt;h3 id=&quot;building-the-image&quot;&gt;Building the Image&lt;/h3&gt;
&lt;p&gt;Navigate to the directory containing your Dockerfile and build the image:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; build&lt;/span&gt;&lt;span&gt; -t&lt;/span&gt;&lt;span&gt; custom-ffmpeg-image&lt;/span&gt;&lt;span&gt; .&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This creates a new image called &lt;code&gt;custom-ffmpeg-image&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;running-your-custom-image&quot;&gt;Running Your Custom Image&lt;/h3&gt;
&lt;p&gt;Now you can start a container from your custom image:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; run&lt;/span&gt;&lt;span&gt; -it&lt;/span&gt;&lt;span&gt; custom-ffmpeg-image&lt;/span&gt;&lt;span&gt; bash&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This gives you the same FFmpeg functionality but within a controlled environment that you designed. Now, you are currently in the terminal of the Ubuntu&lt;strong&gt;GitHub&lt;/strong&gt; system with ffmpeg installed and ready to use. You can either mount your volume to the container or copy the input video to the container to process it with ffmpeg.&lt;/p&gt;
&lt;p&gt;You can use &lt;a href=&quot;https://github.com/imgly/blog-ffmpeg-docker&quot;&gt;this&lt;/a&gt; GitHub repository for the code.&lt;/p&gt;
&lt;h2 id=&quot;beyond-the-basics&quot;&gt;Beyond the Basics&lt;/h2&gt;
&lt;p&gt;Depending on context and requirements, there are a few additional ways people run FFmpeg in Docker that you might find useful. Lets sneak-peak into those approaches now:&lt;/p&gt;
&lt;h3 id=&quot;multi-stage-builds&quot;&gt;&lt;strong&gt;Multi-stage builds&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Instead of installing FFmpeg on top of Ubuntu in a simple Dockerfile, some projects use a &lt;em&gt;multi-stage build&lt;/em&gt; where the first stage compiles FFmpeg from source (with specific options or codecs), and the final stage copies only the compiled binaries into a minimal image (like &lt;code&gt;alpine&lt;/code&gt;). This results in much smaller images, which is valuable for production environments.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;docker&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; alpine:latest &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; build&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;RUN&lt;/span&gt;&lt;span&gt; apk add --no-cache build-base yasm&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;RUN&lt;/span&gt;&lt;span&gt; git clone &amp;#x3C;https://git.ffmpeg.org/ffmpeg.git&gt; ffmpeg &lt;/span&gt;&lt;span&gt;\\&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x26;&amp;#x26; cd ffmpeg &lt;/span&gt;&lt;span&gt;\\&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x26;&amp;#x26; ./configure --enable-gpl --enable-nonfree &lt;/span&gt;&lt;span&gt;\\&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x26;&amp;#x26; make &amp;#x26;&amp;#x26; make install&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; alpine:latest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;COPY&lt;/span&gt;&lt;span&gt; --from=build /usr/local/bin/ffmpeg /usr/local/bin/&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;using-ffmpeg-inside-a-larger-containerized-workflow&quot;&gt;&lt;strong&gt;Using FFmpeg inside a larger containerized workflow&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;In practice, FFmpeg is often not run alone. For example, it might be used as part of a &lt;strong&gt;media processing pipeline&lt;/strong&gt; in Kubernetes or Docker Compose with other services like Nginx, Node.js, or a message queue. In those cases, FFmpeg runs inside a dedicated container, and files are passed to it via mounted volumes or network streams. For a Docker‑based pipeline with REST APIs and worker queues, see our &lt;a href=&quot;https://img.ly/blog/building-a-production-ready-batch-video-processing-server-with-ffmpeg/&quot;&gt;production‑ready batch video processing server&lt;/a&gt; guide.&lt;/p&gt;
&lt;h3 id=&quot;gpu-accelerated-ffmpeg-builds&quot;&gt;G&lt;strong&gt;PU-accelerated FFmpeg builds&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;For users doing heavy video processing, using NVIDIA GPU support is crucial. Docker supports GPU acceleration via &lt;code&gt;-gpus all&lt;/code&gt; and NVIDIA’s base images. There are specialized FFmpeg images (like &lt;code&gt;nvidia/cuda&lt;/code&gt;based builds) that enable hardware-accelerated encoding/decoding (NVENC, NVDEC, CUDA).&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; run&lt;/span&gt;&lt;span&gt; --gpus&lt;/span&gt;&lt;span&gt; all&lt;/span&gt;&lt;span&gt; -it&lt;/span&gt;&lt;span&gt; nvidia/cuda:12.2.0-base&lt;/span&gt;&lt;span&gt; ffmpeg&lt;/span&gt;&lt;span&gt; ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is extremely relevant for modern workloads (e.g., streaming, AI video processing).&lt;/p&gt;
&lt;h2 id=&quot;which-approach-should-you-choose&quot;&gt;Which Approach Should You Choose?&lt;/h2&gt;
&lt;p&gt;If you only need FFmpeg occasionally and want the fastest way to get started, the &lt;strong&gt;pre-built image&lt;/strong&gt; is the way to go. It saves time and effort, and you’ll be up and running within minutes.&lt;/p&gt;
&lt;p&gt;On the other hand, if your workflow requires specific versions of FFmpeg, or if you’d like to bundle other tools into the same container, creating a &lt;strong&gt;custom Dockerfile&lt;/strong&gt; gives you that flexibility.&lt;/p&gt;
&lt;p&gt;While Dockerized FFmpeg is powerful on its own, its real strength becomes clear when integrated into larger developer workflows. For example, &lt;a href=&quot;https://img.ly/products/creative-sdk&quot;&gt;CreativeEditor SDK (CE.SDK)&lt;/a&gt; provides a full-featured, customizable editing environment for images, graphics, and videos. When developers extend CE.SDK with &lt;strong&gt;Dockerized FFmpeg&lt;/strong&gt;, they unlock a scalable way to handle demanding media tasks behind the scenes.&lt;/p&gt;
&lt;h2 id=&quot;conclusion-why-ffmpeg-in-docker-is-the-smart-choice&quot;&gt;Conclusion: Why FFmpeg in Docker is the Smart Choice&lt;/h2&gt;
&lt;p&gt;Running FFmpeg inside Docker combines the power of a world-class media framework with the reliability and portability of containers. Instead of fighting dependency issues on your local system, you get a clean, isolated environment that works consistently across machines, teams, and production servers.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you need &lt;strong&gt;speed and simplicity&lt;/strong&gt;, a pre-built image like &lt;code&gt;jrottenberg/ffmpeg&lt;/code&gt; gets you up and running in minutes.&lt;/li&gt;
&lt;li&gt;If you want &lt;strong&gt;control and customization&lt;/strong&gt;, a custom Dockerfile ensures your build has the exact codecs, libraries, and dependencies you need.&lt;/li&gt;
&lt;li&gt;For advanced scenarios, GPU acceleration, multi-stage builds, or cloud pipelines, Docker ensures FFmpeg scales well into modern production environments.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And beyond standalone usage, frameworks like &lt;a href=&quot;https://img.ly/products/creative-sdk&quot;&gt;**CE.SDK&lt;/a&gt;** demonstrate how FFmpeg’s capabilities can power full-featured creative applications. Whether it’s transcoding video, exporting edits, or automating workflows, running FFmpeg in Docker keeps your setup clean and future-proof.&lt;/p&gt;
&lt;p&gt;By combining Docker’s portability with FFmpeg’s versatility, you’re not just solving installation problems, you’re setting up a foundation for professional, scalable multimedia processing.&lt;/p&gt;</content:encoded><dc:creator>Robin</dc:creator><media:content url="https://blog.img.ly/2025/10/ffmpeg-docker-container-how-to.jpg" medium="image"/><category>FFmpeg</category><category>Server-side Video</category></item><item><title>FFmpeg on GCP: Step-by-Step for Beginners</title><link>https://img.ly/blog/ffmpeg-on-google-cloud-platform-guide/</link><guid isPermaLink="true">https://img.ly/blog/ffmpeg-on-google-cloud-platform-guide/</guid><description>Learn how to run FFmpeg on Google Cloud Platform for fast, scalable video processing. This beginner guide covers setup, storage, VM creation, and media conversion plus how to pair FFmpeg’s backend power with IMG.LY’s CE.SDK for a full creative editing workflow.</description><pubDate>Wed, 08 Oct 2025 06:56:08 GMT</pubDate><content:encoded>&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;When it comes to working with video and audio files, &lt;a href=&quot;https://ffmpeg.org/documentation.html&quot;&gt;&lt;strong&gt;FFmpeg&lt;/strong&gt;&lt;/a&gt; is one of the most powerful and widely used tools available. It’s an open-source command-line utility capable of handling almost any kind of media conversion, compression, and processing task. Whether you need to convert a video from MP4 to WebM, extract audio from a movie, or resize a large video for the web, FFmpeg is often the go-to solution for developers and media professionals alike. For an in‑depth overview of FFmpeg commands and features, read our &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg/&quot;&gt;Ultimate Guide to FFmpeg.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;While you can install and run FFmpeg locally on your computer, there are limitations. Processing large video files on a personal machine can be slow, resource-intensive, and impractical if you need to handle multiple files or larger workloads.&lt;/p&gt;
&lt;p&gt;This is where &lt;a href=&quot;https://cloud.google.com/docs/overview&quot;&gt;&lt;strong&gt;Google Cloud Platform&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;(GCP)&lt;/strong&gt; comes in. By running FFmpeg on GCP, you gain access to scalable cloud infrastructure that can process media files much faster than most local setups. You can choose the right machine size for your task, scale up when handling heavy jobs, and only pay for the resources you use. If you prefer Amazon Web Services, our guide to &lt;a href=&quot;https://img.ly/blog/how-to-run-ffmpeg-on-aws-spot-instances-for-scalable-low-cost-video-processing/&quot;&gt;running FFmpeg on AWS Spot Instances&lt;/a&gt; covers a similar setup.&lt;/p&gt;
&lt;p&gt;By the end of this guide, you’ll know how to set up a Google Cloud project, run FFmpeg on a virtual machine, and process videos efficiently in the cloud. This tutorial is designed with beginners in mind, so even if you’re new to cloud platforms or FFmpeg, you’ll be able to follow along step by step.&lt;/p&gt;
&lt;h2 id=&quot;set-up-a-google-cloud-project&quot;&gt;Set Up a Google Cloud Project&lt;/h2&gt;
&lt;p&gt;Before you can run FFmpeg on Google Cloud, you’ll need to &lt;a href=&quot;https://cloud.google.com/resource-manager/docs/creating-managing-projects&quot;&gt;create a project&lt;/a&gt; where all your resources, such as virtual machines and storage, will live. Think of a Google Cloud project as a workspace that keeps everything organized and secure. Each project has its own settings, billing, and resources, so you can keep experiments separate from production work.&lt;/p&gt;
&lt;h3 id=&quot;1-create-a-new-project&quot;&gt;1. Create a New Project&lt;/h3&gt;
&lt;p&gt;Start by going to the Google Cloud Console. In the top menu, click on the project selector and choose &lt;strong&gt;New Project&lt;/strong&gt;. Give your project a name (for example, &lt;em&gt;ffmpeg-demo&lt;/em&gt;) and, if prompted, select an organization. Once created, this project will serve as the container for all the steps that follow.&lt;/p&gt;
&lt;h3 id=&quot;2-enable-billing&quot;&gt;2. Enable Billing&lt;/h3&gt;
&lt;p&gt;Google Cloud requires billing to be enabled in order to use most services. If you haven’t set this up yet, you’ll be asked to link your project to a billing account. Don’t worry, Google Cloud offers a free tier with credits for new users, which is more than enough to complete this tutorial and experiment with small workloads.&lt;/p&gt;
&lt;h3 id=&quot;3-set-up-google-cloud-storage&quot;&gt;3. Set Up Google Cloud Storage&lt;/h3&gt;
&lt;p&gt;Next, you’ll need a place to store your media files. &lt;a href=&quot;https://cloud.google.com/storage/docs/creating-buckets&quot;&gt;&lt;strong&gt;Google Cloud Storage (GCS)&lt;/strong&gt;&lt;/a&gt; works like a highly reliable online hard drive. You can upload your videos there, process them with FFmpeg on a virtual machine, and then save the results back to the same bucket.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In the Google Cloud Console, navigate to &lt;strong&gt;Cloud&lt;/strong&gt; &lt;strong&gt;Storage&lt;/strong&gt; → &lt;strong&gt;Buckets&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create Bucket&lt;/strong&gt; and choose a unique name (e.g., &lt;em&gt;my-ffmpeg-videos&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;Select the default storage class and location that best matches your region.&lt;/li&gt;
&lt;li&gt;After the bucket is created, click &lt;strong&gt;Upload Files&lt;/strong&gt; and add a small test video, this will be the file you process later in the tutorial.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;At this point, you have a project set up with billing enabled and a storage bucket containing your test video. Need to crop or trim your videos before uploading? Our Flutter guide demonstrates how to &lt;a href=&quot;https://img.ly/blog/how-to-crop-and-trim-videos-in-flutter/&quot;&gt;crop and trim videos in Flutter&lt;/a&gt; using FFmpeg. You’re now ready to create a virtual machine that will run FFmpeg.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: Not all services are enabled when you start working with GCP. If you are prompted to enable a service while working on this setup, please do enable it and keep track on it in &lt;strong&gt;API &amp;#x26; Services.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;start-a-compute-engine-vm&quot;&gt;Start a Compute Engine VM&lt;/h2&gt;
&lt;p&gt;With your project and storage bucket ready, the next step is to create a &lt;strong&gt;Compute Engine Virtual Machine (VM)&lt;/strong&gt;. A VM is essentially a computer in the cloud, you decide how powerful it should be, and you only pay for the time it’s running.&lt;/p&gt;
&lt;h3 id=&quot;1-choose-a-machine-type&quot;&gt;1. Choose a Machine Type&lt;/h3&gt;
&lt;p&gt;Head over to the &lt;a href=&quot;https://console.cloud.google.com/compute&quot;&gt;Compute Engine&lt;/a&gt; section in the Google Cloud Console and click &lt;strong&gt;Create Instance&lt;/strong&gt;. You’ll need to configure a few options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Name:&lt;/strong&gt; Pick something descriptive like &lt;em&gt;ffmpeg-vm&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Region &amp;#x26; Zone:&lt;/strong&gt; Choose a region close to you or your users for faster upload/download speeds.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cloud.google.com/compute/docs/machine-types&quot;&gt;&lt;strong&gt;Machine type&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;For testing and small videos, a smaller instance like &lt;strong&gt;e2-medium&lt;/strong&gt; (2 vCPUs, 4 GB memory) is usually enough.&lt;/li&gt;
&lt;li&gt;For larger video processing jobs, choose something more powerful such as &lt;strong&gt;n2-standard-4&lt;/strong&gt; (4 vCPUs, 16 GB memory) or higher. The more CPU and memory you assign, the faster FFmpeg can handle big files.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 Tip: You can always resize or recreate the VM with a larger machine later if you find it’s too slow.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To automate large numbers of FFmpeg jobs using REST APIs and worker queues, refer to our &lt;a href=&quot;https://img.ly/blog/building-a-production-ready-batch-video-processing-server-with-ffmpeg/&quot;&gt;batch video processing server&lt;/a&gt; guide.&lt;/p&gt;
&lt;h3 id=&quot;2-configure-boot-disk&quot;&gt;2. Configure Boot Disk&lt;/h3&gt;
&lt;p&gt;By default, Google Cloud gives you a Debian or Ubuntu Linux image, which works perfectly for FFmpeg. You don’t need to change anything here unless you prefer another distribution.&lt;/p&gt;
&lt;h3 id=&quot;3-configure-access-scopes&quot;&gt;3. Configure Access Scopes&lt;/h3&gt;
&lt;p&gt;By default, the instances can only read from a bucket, but we need to upload the output back to the bucket, so in &lt;strong&gt;Security&lt;/strong&gt; tab set the &lt;strong&gt;Access Scopes&lt;/strong&gt; to &lt;strong&gt;Set access for each API&lt;/strong&gt; and set &lt;strong&gt;Storage&lt;/strong&gt; to “Read Write”.&lt;/p&gt;
&lt;h3 id=&quot;3-create-and-launch-the-vm&quot;&gt;3. Create and Launch the VM&lt;/h3&gt;
&lt;p&gt;Click &lt;strong&gt;Create&lt;/strong&gt; and wait a moment, Google Cloud will provision your VM. Once it’s ready, you’ll see it listed under &lt;strong&gt;VM Instances&lt;/strong&gt; in the Compute Engine dashboard.&lt;/p&gt;
&lt;h3 id=&quot;4-connect-via-ssh&quot;&gt;4. Connect via SSH&lt;/h3&gt;
&lt;p&gt;To start using the VM, click the &lt;strong&gt;SSH&lt;/strong&gt; button next to your instance in the console. This opens a secure terminal session directly in your browser. You’re now logged into your VM and ready to install software, just as if you were sitting in front of a fresh Linux machine.&lt;/p&gt;
&lt;p&gt;At this stage, you have a working VM connected to your project. Alternatively, you can &lt;a href=&quot;https://img.ly/blog/how-to-run-ffmpeg-inside-a-docker-container/&quot;&gt;run FFmpeg inside a Docker container&lt;/a&gt; for a reproducible environment - our Docker guide shows how. In the next section, you’ll install FFmpeg on this machine and run your first test command.&lt;/p&gt;
&lt;h2 id=&quot;install-ffmpeg&quot;&gt;Install FFmpeg&lt;/h2&gt;
&lt;p&gt;FFmpeg is available in the default Debian/Ubuntu repositories that Google Cloud’s standard Linux images use, so installation is just a couple of terminal commands. For more information on FFmpeg for Linux distros, see &lt;a href=&quot;https://trac.ffmpeg.org/wiki/CompilationGuide/Ubuntu&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;1-install-via-apt-recommended&quot;&gt;1) Install via APT (recommended)&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Update package lists&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; apt&lt;/span&gt;&lt;span&gt; update&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Install FFmpeg and ffprobe&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; apt&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt; install&lt;/span&gt;&lt;span&gt; ffmpeg&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Verify installation&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -hide_banner&lt;/span&gt;&lt;span&gt; -version&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffprobe&lt;/span&gt;&lt;span&gt; -hide_banner&lt;/span&gt;&lt;span&gt; -version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you prefer the very latest FFmpeg, you can also download a prebuilt &lt;strong&gt;Linux static build&lt;/strong&gt; from the official FFmpeg downloads page (no extra system libraries required).&lt;/p&gt;
&lt;h3 id=&quot;2-quick-sanity-checks&quot;&gt;2) Quick sanity checks&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# See available encoders/decoders in your build&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -hide_banner&lt;/span&gt;&lt;span&gt; -encoders&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt; head&lt;/span&gt;&lt;span&gt; -n&lt;/span&gt;&lt;span&gt; 20&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -hide_banner&lt;/span&gt;&lt;span&gt; -decoders&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt; head&lt;/span&gt;&lt;span&gt; -n&lt;/span&gt;&lt;span&gt; 20&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Refer to the &lt;a href=&quot;https://ffmpeg.org/ffmpeg.html&quot;&gt;official command reference&lt;/a&gt; if you want to understand what each option means.&lt;/p&gt;
&lt;h2 id=&quot;test-ffmpeg-with-sample-commands&quot;&gt;Test FFmpeg with sample commands&lt;/h2&gt;
&lt;p&gt;You can test FFmpeg without any input file by generating a short test pattern using the built-in &lt;strong&gt;testsrc&lt;/strong&gt; video source filter:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Generate a 3-second 1280x720 MP4 test video&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -f&lt;/span&gt;&lt;span&gt; lavfi&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; &quot;testsrc=duration=3:size=1280x720:rate=30&quot;&lt;/span&gt;&lt;span&gt; \\&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  -c:v&lt;/span&gt;&lt;span&gt; libx264&lt;/span&gt;&lt;span&gt; -pix_fmt&lt;/span&gt;&lt;span&gt; yuv420p&lt;/span&gt;&lt;span&gt; ~/test.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;testsrc&lt;/code&gt; filter creates a color bars pattern with a timestamp—perfect for verifying your install.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tip: If you encounter “Unknown encoder” for a codec, either switch to another codec available in your build or install a newer build (e.g., the official static builds mentioned above).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You’re now ready to pull a real file from Cloud Storage and process it on your VM in the next section.&lt;/p&gt;
&lt;h2 id=&quot;process-a-video&quot;&gt;Process a Video&lt;/h2&gt;
&lt;p&gt;Now that FFmpeg is installed on your Compute Engine VM, let’s process a real video. The workflow looks like this: &lt;strong&gt;download a file from Google Cloud Storage → process it with FFmpeg → upload the result back to Google Cloud Storage.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;download-video-from-gcs-to-the-vm&quot;&gt;Download Video from GCS to the VM&lt;/h3&gt;
&lt;p&gt;First, make sure the &lt;strong&gt;gcloud CLI&lt;/strong&gt; is installed on your VM (it usually is on Google-provided images). Then, copy the test video from your storage bucket:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Replace with your actual bucket and file names&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;gsutil&lt;/span&gt;&lt;span&gt; cp&lt;/span&gt;&lt;span&gt; gs://my-ffmpeg-videos/test.mp4&lt;/span&gt;&lt;span&gt; ~/test.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command uses &lt;a href=&quot;https://cloud.google.com/storage/docs/gsutil/commands/cp&quot;&gt;&lt;code&gt;gsutil&lt;/code&gt;&lt;/a&gt; to copy the file into your VM’s home directory.&lt;/p&gt;
&lt;h3 id=&quot;run-a-conversion-with-ffmpeg&quot;&gt;Run a Conversion with FFmpeg&lt;/h3&gt;
&lt;p&gt;Let’s do a simple format conversion and resizing example:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Convert MP4 to WebM, resized to 720p&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; ~/test.mp4&lt;/span&gt;&lt;span&gt; -vf&lt;/span&gt;&lt;span&gt; &quot;scale=1280:-2&quot;&lt;/span&gt;&lt;span&gt; \\&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  -c:v&lt;/span&gt;&lt;span&gt; libvpx-vp9&lt;/span&gt;&lt;span&gt; -b:v&lt;/span&gt;&lt;span&gt; 1M&lt;/span&gt;&lt;span&gt; -c:a&lt;/span&gt;&lt;span&gt; libopus&lt;/span&gt;&lt;span&gt; \\&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ~/output.webm&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here’s what this command does:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;i ~/test.mp4&lt;/code&gt; → input file.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;vf &quot;scale=1280:-2&quot;&lt;/code&gt; → resizes video to 1280px wide while keeping aspect ratio.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;c:v libvpx-vp9 -b:v 1M&lt;/code&gt; → encodes video with VP9 codec at ~1 Mbps.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;c:a libopus&lt;/code&gt; → encodes audio with the Opus codec.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;~/output.webm&lt;/code&gt; → output file.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When it finishes, you’ll have a new WebM file in your VM’s home directory.&lt;/p&gt;
&lt;h3 id=&quot;upload-the-result-back-to-gcs&quot;&gt;Upload the Result Back to GCS&lt;/h3&gt;
&lt;p&gt;Finally, copy the processed file back into your storage bucket:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Replace with your bucket name&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;gsutil&lt;/span&gt;&lt;span&gt; cp&lt;/span&gt;&lt;span&gt; ~/output.webm&lt;/span&gt;&lt;span&gt; gs://my-ffmpeg-videos/&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can now view or download the processed video directly from your GCS bucket.&lt;/p&gt;
&lt;p&gt;For a ready to use example of the above architecture, you can refer to &lt;a href=&quot;https://github.com/imgly/blog-ffmpeg-gcp&quot;&gt;this&lt;/a&gt; github repository.&lt;/p&gt;
&lt;h2 id=&quot;other-options-on-gcp&quot;&gt;Other Options on GCP&lt;/h2&gt;
&lt;p&gt;While running FFmpeg on a Compute Engine VM is the most straightforward way to get started, Google Cloud offers several other options that might better suit your needs depending on scale and workflow.&lt;/p&gt;
&lt;h3 id=&quot;cloud-run-for-small-jobs&quot;&gt;Cloud Run for Small Jobs&lt;/h3&gt;
&lt;p&gt;If you want a serverless option, &lt;strong&gt;Cloud Run&lt;/strong&gt; lets you package FFmpeg into a container and run it on demand. This is a great fit for lightweight jobs or API-style services, where you don’t need to manage servers at all. You simply pay for the CPU and memory used during each request, making it cost-efficient for occasional media processing.&lt;/p&gt;
&lt;h3 id=&quot;transcoder-api-for-a-managed-service&quot;&gt;Transcoder API for a Managed Service&lt;/h3&gt;
&lt;p&gt;For teams who want to skip infrastructure entirely, Google offers the &lt;strong&gt;Transcoder API&lt;/strong&gt;. It’s a fully managed video processing service where you just provide the input file and the desired output format, and Google handles the rest. It’s especially useful for production pipelines with predictable formats, but it’s less flexible than rolling your own FFmpeg setup.&lt;/p&gt;
&lt;h3 id=&quot;kubernetes-for-large-scale-workloads&quot;&gt;Kubernetes for Large-Scale Workloads&lt;/h3&gt;
&lt;p&gt;If you’re dealing with a high volume of media jobs, consider deploying FFmpeg in a &lt;strong&gt;Google Kubernetes Engine (GKE)&lt;/strong&gt; cluster. This allows you to scale horizontally, running multiple FFmpeg pods in parallel to process many videos at once. While it requires more setup, it provides the foundation for a production-grade, auto-scaling media processing system.&lt;/p&gt;
&lt;h3 id=&quot;beyond-ffmpeg-a-ui-for-creative-editing&quot;&gt;Beyond FFmpeg: A UI for Creative Editing&lt;/h3&gt;
&lt;p&gt;FFmpeg is excellent for batch conversions and automation, but what if you want to offer &lt;strong&gt;end-users a creative editing experience directly in the browser&lt;/strong&gt;? This is where solutions like &lt;strong&gt;CE.SDK from&lt;/strong&gt; &lt;a href=&quot;http://IMG.LY&quot;&gt;&lt;strong&gt;IMG.LY&lt;/strong&gt;&lt;/a&gt; come in. CE.SDK is a fully customizable editing SDK that you can integrate into your app, enabling Canva-grade photo, video, and design editing features on top of your processing pipeline. In fact, combining FFmpeg’s backend power on GCP with CE.SDK’s front-end UI gives you the best of both worlds: automated conversions plus user-driven creativity.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;By now, you’ve seen how straightforward it is to run &lt;strong&gt;FFmpeg on Google Cloud Platform&lt;/strong&gt;. With just a few steps—creating a project, setting up a Compute Engine VM, installing FFmpeg, and connecting it to Cloud Storage—you can process videos in the cloud as easily as on your local machine. The benefit is that your workflow is now backed by scalable infrastructure: you can choose a lightweight VM for simple conversions or a powerful instance when you need to process large files quickly.&lt;/p&gt;
&lt;p&gt;This tutorial has given you a solid foundation, but it’s only the beginning. From here, you can explore &lt;strong&gt;automation&lt;/strong&gt; by scripting your FFmpeg jobs and scheduling them with Cloud Functions or Cloud Scheduler. For &lt;strong&gt;scaling&lt;/strong&gt;, you might package FFmpeg into containers and deploy it on Kubernetes to process thousands of files in parallel. And if you want to save time managing infrastructure altogether, managed services like the &lt;strong&gt;Transcoder API&lt;/strong&gt; let Google handle the heavy lifting for you.&lt;/p&gt;
&lt;p&gt;Whether you stick with Compute Engine for flexibility or move toward serverless and managed services for efficiency, running FFmpeg on GCP gives you the power to build media pipelines that grow with your needs.&lt;/p&gt;</content:encoded><dc:creator>Robin</dc:creator><media:content url="https://blog.img.ly/2025/10/ffmpeg-GCP-tutorial-1.jpg" medium="image"/><category>FFmpeg</category><category>Server-side Video</category></item><item><title>How to Run FFmpeg on AWS Spot Instances for Scalable, Low-Cost Video Processing</title><link>https://img.ly/blog/how-to-run-ffmpeg-on-aws-spot-instances-for-scalable-low-cost-video-processing/</link><guid isPermaLink="true">https://img.ly/blog/how-to-run-ffmpeg-on-aws-spot-instances-for-scalable-low-cost-video-processing/</guid><description>Learn how to run FFmpeg on AWS Spot Instances for scalable, cost-effective video processing. This step-by-step guide covers S3 setup, installing FFmpeg, handling Spot interruptions, and building resilient cloud workflows.</description><pubDate>Mon, 06 Oct 2025 08:25:43 GMT</pubDate><content:encoded>&lt;p&gt;In this guide, we’ll explore how to harness &lt;a href=&quot;https://docs.aws.amazon.com/&quot;&gt;Amazon Web Services (AWS)&lt;/a&gt; for video processing with FFmpeg. You’ll learn why the cloud is so helpful for handling resource-heavy video tasks, how to set up your AWS Spot Instance and S3 bucket, and how to run FFmpeg jobs at scale. Along the way, we’ll get acquainted with &lt;a href=&quot;https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-spot-instances.html&quot;&gt;AWS Spot Instances&lt;/a&gt;, a cost-saving compute option that can make large-scale video processing far more affordable. If you’re new to the tool itself, start with our &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg/&quot;&gt;Ultimate Guide to FFmpeg.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;By the end of this article, you’ll know how to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Set up AWS with S3 storage and a Spot instance.&lt;/li&gt;
&lt;li&gt;Install and test FFmpeg in the cloud.&lt;/li&gt;
&lt;li&gt;Run video processing jobs on a Spot Instance while keeping costs low.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Whether you’re new to cloud computing or just looking for a practical way to scale your FFmpeg workflows, this guide will walk you through the essentials step by step.&lt;/p&gt;
&lt;p&gt;Lets dive into by setting up the AWS environment.&lt;/p&gt;
&lt;h2 id=&quot;set-up-your-aws-environment&quot;&gt;&lt;strong&gt;Set Up Your AWS Environment&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Before you can run FFmpeg in the cloud, you need to prepare your AWS environment. This involves creating an account, setting up storage, and launching an instance where you’ll install and run FFmpeg. Let’s break it down step by step.&lt;/p&gt;
&lt;h3 id=&quot;create-an-aws-account-and-s3-bucket&quot;&gt;Create an AWS Account and S3 Bucket&lt;/h3&gt;
&lt;p&gt;If you don’t already have one, sign up for an &lt;a href=&quot;https://aws.amazon.com/&quot;&gt;AWS account&lt;/a&gt;. Once logged into the AWS Management Console, the first thing to set up is an &lt;a href=&quot;https://docs.aws.amazon.com/AmazonS3/latest/userguide/GetStartedWithS3.html&quot;&gt;&lt;strong&gt;Amazon S3 bucket&lt;/strong&gt;&lt;/a&gt;. S3 (Simple Storage Service) is AWS’s object storage system, and it’s ideal for hosting both your input and output video files. For example, you can upload raw videos to S3, process them with FFmpeg, and then store the converted files back in the same bucket. When creating your bucket, be sure to choose a region close to where you expect to run your compute resources, as this reduces latency and transfer costs.&lt;/p&gt;
&lt;h3 id=&quot;launch-a-spot-instance&quot;&gt;Launch a Spot Instance&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Spot Instances&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-spot-instances.html&quot;&gt;AWS Spot Instances&lt;/a&gt; are virtual machines that let you access unused cloud capacity at significantly reduced prices, sometimes up to 90% cheaper than regular On-Demand Instances. The catch is that they can be &lt;strong&gt;interrupted&lt;/strong&gt; by AWS with little notice if the capacity is needed elsewhere. For workloads like video processing, which can be restarted or distributed across multiple nodes, Spot Instances are a perfect fit. They allow developers to take advantage of lower costs without sacrificing performance, provided they design workflows with fault tolerance in mind. This balance of affordability and scalability makes Spot Instances a popular choice for running FFmpeg jobs in the cloud.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Setup a Spot Instance&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;From the AWS console, go to the EC2 dashboard and launch a new instance. Choose &lt;strong&gt;Ubuntu Server&lt;/strong&gt; or &lt;strong&gt;Amazon Linux&lt;/strong&gt; as the operating system, since both have excellent community support and are well-documented. When selecting an instance type, start with something lightweight like &lt;code&gt;t2.micro&lt;/code&gt; (eligible for the free tier), but keep in mind you may need more powerful instances (with higher CPU or GPU) for larger video workloads. Open the &lt;strong&gt;Advanced Details&lt;/strong&gt;, under &lt;strong&gt;Purchasing option&lt;/strong&gt;, check the box labeled &lt;strong&gt;Spot Instances&lt;/strong&gt; to tell AWS you want to use discounted spare capacity instead of On-Demand pricing. You can also set a maximum price per instance hour if you want to control costs more tightly, but in most cases leaving it at the default ensures you’ll simply pay the current Spot market price, which automatically adjusts with supply and demand and is never higher than the standard On-Demand rate.&lt;/p&gt;
&lt;p&gt;You can also choose between a &lt;strong&gt;persistent&lt;/strong&gt; or &lt;strong&gt;one-time&lt;/strong&gt; request type in the &lt;strong&gt;Customize Spot Instance options&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Once the instance is created, you can confirm that its lifecycle is &lt;strong&gt;spot&lt;/strong&gt; under the instance details section. This indicates that a Spot Instance has been successfully created.&lt;/p&gt;
&lt;h3 id=&quot;connect-to-your-instance-via-ssh&quot;&gt;Connect to Your Instance via SSH&lt;/h3&gt;
&lt;p&gt;Once your Spot instance is running, you can connect to it remotely using &lt;strong&gt;SSH (Secure Shell)&lt;/strong&gt;. During instance setup, AWS will prompt you to create or download a &lt;strong&gt;key pair&lt;/strong&gt; (&lt;code&gt;.pem&lt;/code&gt; file). Keep this file safe, it’s your private key for secure access. On your local machine, open a terminal at the location where you saved the key pair file and run:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ssh&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; &quot;your-key.pem&quot;&lt;/span&gt;&lt;span&gt; &amp;#x3C;&lt;/span&gt;&lt;span&gt;usernam&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;your-instance-public-dn&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Replace &lt;code&gt;your-key.pem&lt;/code&gt; with the path to your private key file and &lt;code&gt;&amp;#x3C;your-instance-public-dns&gt;&lt;/code&gt; with the public DNS address of your Spot instance (visible in the console). You can get the exact SSH command for your instance by clicking the &lt;strong&gt;Connect&lt;/strong&gt; button in the EC2 dashboard and selecting the &lt;strong&gt;SSH client&lt;/strong&gt; option. Once connected, you’ll be inside the environment where you can install FFmpeg and start running commands. For a smooth operation, make yourself familiar with basic shell commands for the operating system you opted for in the instance. Examples for various shell commands on a linux system can be found &lt;a href=&quot;https://www.geeksforgeeks.org/linux-unix/basic-shell-commands-in-linux/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;install-ffmpeg-on-the-spot-instance&quot;&gt;Install FFmpeg on the Spot Instance&lt;/h2&gt;
&lt;p&gt;With your spot instance up and running, the next step is to install &lt;strong&gt;FFmpeg&lt;/strong&gt; so you can start processing videos. Installation is straightforward, and once complete, you’ll test it with a simple command to confirm everything works.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update Your Package Lists&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Before installing any software, it’s a good practice to make sure your system’s package list is up to date. Run:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; apt&lt;/span&gt;&lt;span&gt; update&lt;/span&gt;&lt;span&gt; &amp;#x26;&amp;#x26; &lt;/span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; apt&lt;/span&gt;&lt;span&gt; upgrade&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command refreshes the list of available software and ensures your instance has the latest security patches. &lt;em&gt;(If you’re using Amazon Linux instead of Ubuntu, replace this with &lt;code&gt;sudo yum update -y&lt;/code&gt;.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Install FFmpeg&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;On Ubuntu, FFmpeg is available directly through the official package repository:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; apt&lt;/span&gt;&lt;span&gt; install&lt;/span&gt;&lt;span&gt; ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For Amazon Linux, the process may require enabling extra repositories first:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; amazon-linux-extras&lt;/span&gt;&lt;span&gt; enable&lt;/span&gt;&lt;span&gt; epel&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; yum&lt;/span&gt;&lt;span&gt; install&lt;/span&gt;&lt;span&gt; ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will download and install FFmpeg along with its dependencies. To avoid dependency issues, consider &lt;a href=&quot;https://img.ly/blog/how-to-run-ffmpeg-inside-a-docker-container/&quot;&gt;running FFmpeg inside a Docker container.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Verify the Installation&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;After installation, check that FFmpeg is available by running:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see output showing the installed FFmpeg version and configuration details.&lt;/p&gt;
&lt;h2 id=&quot;run-ffmpeg-on-a-spot-instance&quot;&gt;Run FFmpeg on a Spot Instance&lt;/h2&gt;
&lt;p&gt;Now that you’ve installed FFmpeg on a regular Spot instance, it’s time to make evrything work together. In this section, upload a video to Amazon S3, and then run FFmpeg on the spot instance to process the video and save the output.&lt;/p&gt;
&lt;p&gt;While it’s possible to upload videos directly to the instance and process them there, this approach isn’t reliable since the instance can be terminated at any time when AWS reclaims capacity. The better and safer practice is to use the &lt;strong&gt;S3 bucket&lt;/strong&gt;, which ensures your files are stored safely and don’t need to be re-uploaded each time. This is especially important when working with large video files or multiple inputs, as S3 provides a more reliable and scalable storage solution. Prefer Google Cloud? See our tutorial on &lt;a href=&quot;https://img.ly/blog/ffmpeg-on-google-cloud-platform-guide/&quot;&gt;running FFmpeg on GCP&lt;/a&gt; for similar steps.&lt;/p&gt;
&lt;h3 id=&quot;upload-a-sample-video-to-s3&quot;&gt;&lt;strong&gt;Upload a Sample Video to S3&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Before running FFmpeg, you’ll need a video file available in your S3 bucket. From your local machine terminal (not the instance terminal), use the AWS CLI to upload a sample file:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;aws&lt;/span&gt;&lt;span&gt; s3&lt;/span&gt;&lt;span&gt; cp&lt;/span&gt;&lt;span&gt; sample.mp4&lt;/span&gt;&lt;span&gt; s3://your-bucket-name/input/sample.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This stores &lt;code&gt;sample.mp4&lt;/code&gt; inside the &lt;code&gt;input&lt;/code&gt; folder of your bucket. Organizing files into &lt;code&gt;input&lt;/code&gt; and &lt;code&gt;output&lt;/code&gt; directories is a good practice for video workflows. You can do the same using the S3 dashboard in AWS. If your workflow involves cropping or trimming videos before upload, check out our Flutter guide on &lt;a href=&quot;https://img.ly/blog/how-to-crop-and-trim-videos-in-flutter/&quot;&gt;crop and trim videos in Flutter.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you are using AWS CLI for the first time in you machine, then setup your credentials using &lt;code&gt;aws configure&lt;/code&gt; command. See &lt;a href=&quot;https://docs.aws.amazon.com/cli/latest/reference/configure/&quot;&gt;here&lt;/a&gt; for more help.&lt;/p&gt;
&lt;h3 id=&quot;run-ffmpeg-on-the-spot-instance&quot;&gt;&lt;strong&gt;Run FFmpeg on the Spot Instance&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Once your Spot Instance is running, connect to it via SSH (the same way you did earlier). Before we get the video from the S3 bucket, you should set an IAM Role for the instance to access the S3 bucket. For that, go to &lt;strong&gt;IAM&lt;/strong&gt;&gt;&lt;strong&gt;Roles&lt;/strong&gt;&gt;&lt;strong&gt;Create Role,&lt;/strong&gt; select EC2 from use case dropdown menu. On the next page, search for &lt;code&gt;AmazonS3FullAccess&lt;/code&gt; and select it, give a role name and finaly create it. Now the instances can access the files from the bucket.&lt;/p&gt;
&lt;p&gt;Since the instance is a new system, you will need to install &lt;code&gt;awscli&lt;/code&gt; from &lt;a href=&quot;https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html&quot;&gt;here&lt;/a&gt;. You will also have to setup credentials using &lt;code&gt;aws configure&lt;/code&gt; command as mentioned above. Then, copy the input file from S3 down to your instance:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;aws&lt;/span&gt;&lt;span&gt; s3&lt;/span&gt;&lt;span&gt; cp&lt;/span&gt;&lt;span&gt; s3://your-bucket-name/input/sample.mp4&lt;/span&gt;&lt;span&gt; sample.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, run FFmpeg to process the video. For example, converting MP4 to WebM:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; sample.mp4&lt;/span&gt;&lt;span&gt; output.webm&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After processing, upload the result back to S3:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;aws&lt;/span&gt;&lt;span&gt; s3&lt;/span&gt;&lt;span&gt; cp&lt;/span&gt;&lt;span&gt; output.webm&lt;/span&gt;&lt;span&gt; s3://your-bucket-name/output/output.webm&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can now access your processed file directly in S3 and even configure it for delivery via Amazon CloudFront if needed.&lt;/p&gt;
&lt;p&gt;By running FFmpeg jobs on Spot Instances, you can save up to &lt;strong&gt;90% of costs compared to On-Demand pricing&lt;/strong&gt;, making this setup ideal for batch video processing.&lt;/p&gt;
&lt;p&gt;You can refer to &lt;a href=&quot;https://github.com/imgly/blog-ffmpeg-aws-spot-instances&quot;&gt;this&lt;/a&gt; github repository for examples.&lt;/p&gt;
&lt;h2 id=&quot;spot-instances-vs-aws-lambda-for-ffmpeg&quot;&gt;Spot Instances vs AWS Lambda for FFmpeg&lt;/h2&gt;
&lt;p&gt;When running FFmpeg on AWS, you have more than one option for compute. Two of the most common choices are &lt;strong&gt;AWS Lambda&lt;/strong&gt; and &lt;strong&gt;EC2 Spot Instances&lt;/strong&gt;. Each has its strengths and trade-offs, and the best fit depends on the type of video workload you’re running.&lt;/p&gt;
&lt;h3 id=&quot;aws-lambda-for-lightweight-tasks&quot;&gt;AWS Lambda for Lightweight Tasks&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.aws.amazon.com/lambda/latest/dg/welcome.html&quot;&gt;AWS Lambda&lt;/a&gt; is Amazon’s &lt;em&gt;serverless compute service&lt;/em&gt;—you don’t need to manage servers, and your code runs only when triggered. It’s easy to set up and integrates seamlessly with other AWS services such as S3 and API Gateway. For example, you can automatically run an FFmpeg job as soon as a new video is uploaded to your S3 bucket.&lt;/p&gt;
&lt;p&gt;However, Lambda comes with limitations: functions can only run for a maximum of &lt;strong&gt;15 minutes&lt;/strong&gt;, and they have limited temporary storage (512MB by default, extendable up to 10GB). This makes Lambda a great choice for lightweight tasks like generating thumbnails, clipping short videos, or converting smaller files, but not for large-scale processing.&lt;/p&gt;
&lt;h3 id=&quot;ec2-spot-instances-for-heavy-workloads&quot;&gt;EC2 Spot Instances for Heavy Workloads&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;EC2 Spot Instances&lt;/strong&gt;, by contrast, are designed for &lt;strong&gt;long and heavy workloads&lt;/strong&gt;. Spot Instances give you access to unused EC2 capacity at discounts of up to &lt;strong&gt;90% compared to On-Demand pricing&lt;/strong&gt;. Since they are full-fledged EC2 machines, Spot Instances don’t impose runtime limits or storage caps.&lt;/p&gt;
&lt;p&gt;This makes them ideal for &lt;strong&gt;large video files, long-running transcodes, and GPU-accelerated workloads&lt;/strong&gt; such as 4K/8K encoding or complex filter pipelines. The trade-off is that Spot Instances can be &lt;strong&gt;interrupted by AWS&lt;/strong&gt; with two minutes of notification when EC2 needs the capacity back, so jobs need to be either restartable or split into smaller tasks.&lt;/p&gt;
&lt;h3 id=&quot;rule-of-thumb&quot;&gt;Rule of Thumb&lt;/h3&gt;
&lt;p&gt;A simple guideline to choose between the two is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Small, quick jobs → use AWS Lambda&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Large, long jobs but restartable → use EC2 Spot Instances&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By matching the right AWS service to your workload, you get the best balance of cost, performance, and reliability for running FFmpeg in the cloud.&lt;/p&gt;
&lt;h2 id=&quot;handling-spot-interruptions&quot;&gt;Handling Spot Interruptions&lt;/h2&gt;
&lt;h3 id=&quot;two-minute-warning&quot;&gt;Two-Minute Warning&lt;/h3&gt;
&lt;p&gt;One important aspect of using &lt;strong&gt;Spot Instances&lt;/strong&gt; is that they can be interrupted at any time if AWS needs the capacity back. When this happens, AWS provides a &lt;strong&gt;two-minute warning&lt;/strong&gt; before the instance is terminated. For video processing workflows, this means you need to plan for interruptions so that work isn’t lost midway.&lt;/p&gt;
&lt;h3 id=&quot;break-videos-into-smaller-chunks&quot;&gt;Break Videos into Smaller Chunks&lt;/h3&gt;
&lt;p&gt;A common strategy is to &lt;strong&gt;break long videos into smaller chunks&lt;/strong&gt; before processing. For example, instead of transcoding a two-hour video in one run, you can split it into 10-minute segments, process each segment separately, and then stitch them back together later. This way, if an interruption occurs, only the current segment is affected rather than the entire job. FFmpeg supports segmenting input files using options like &lt;code&gt;-ss&lt;/code&gt; (start time) and &lt;code&gt;-t&lt;/code&gt; (duration), which makes this approach straightforward.&lt;/p&gt;
&lt;h3 id=&quot;store-partial-results-in-s3&quot;&gt;Store Partial Results in S3&lt;/h3&gt;
&lt;p&gt;Another best practice is to &lt;strong&gt;store partial results in Amazon S3&lt;/strong&gt; as soon as they are completed. For instance, after processing each video chunk, upload the output back to your S3 bucket immediately. This ensures that even if the Spot Instance is terminated, your progress is safe, and you can resume processing from the last completed chunk instead of starting over. Using S3 as a central storage layer makes your workflow fault-tolerant and resilient against interruptions.&lt;/p&gt;
&lt;h3 id=&quot;reliable-and-cost-effective-workflows&quot;&gt;Reliable and Cost-Effective Workflows&lt;/h3&gt;
&lt;p&gt;By designing your FFmpeg workflows with these strategies, you can take full advantage of the &lt;strong&gt;cost savings of Spot Instances&lt;/strong&gt; without risking data loss. This makes Spot instances a reliable option even for long-running or large-scale video processing jobs, provided you build in restart and recovery mechanisms.&lt;/p&gt;
&lt;h2 id=&quot;tips--next-steps&quot;&gt;Tips &amp;#x26; Next Steps&lt;/h2&gt;
&lt;p&gt;Once you’ve set up FFmpeg on AWS and experimented with Spot Instances, there are a few ways to refine your workflow and explore more advanced options. These next steps will help you get the most out of your cloud-based video processing setup.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Try Different Instance Types&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;AWS offers many EC2 instance families, each optimized for different workloads. For general transcoding and compression tasks, &lt;strong&gt;compute-optimized instances&lt;/strong&gt; like the &lt;code&gt;c5&lt;/code&gt; family are often a good fit, providing strong CPU performance at a reasonable cost. If you’re working with high-resolution videos or need hardware acceleration, &lt;strong&gt;GPU-based instances&lt;/strong&gt; such as the &lt;code&gt;g4dn&lt;/code&gt; family can dramatically speed up encoding with FFmpeg’s GPU-enabled libraries. Experimenting with instance types lets you balance performance and cost for your specific use case.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Orchestrate Jobs with SQS or Step Functions&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;As your workflows grow, running jobs manually can become inefficient. AWS provides orchestration tools to help automate FFmpeg jobs. &lt;strong&gt;Amazon SQS (Simple Queue Service)&lt;/strong&gt; can be used to queue video processing tasks, allowing multiple Spot Instances to work on jobs in parallel. For a full Docker‑based pipeline with REST APIs and worker queues, see our &lt;a href=&quot;https://img.ly/blog/building-a-production-ready-batch-video-processing-server-with-ffmpeg/&quot;&gt;production‑ready batch video processing server&lt;/a&gt; guide. For more complex workflows, &lt;strong&gt;AWS Step Functions&lt;/strong&gt; provide a way to chain together multiple tasks such as fetching input files, running FFmpeg, handling errors, and saving outputs into a reliable, serverless state machine. These services ensure your video pipelines are scalable and fault-tolerant without requiring you to build custom orchestration logic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Consider CE.SDK as an Alternative&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you’re building custom editing workflows on AWS, consider integrating a frontend editor like our &lt;a href=&quot;https://img.ly/products/creative-sdk&quot;&gt;CreativeEditor SDK.&lt;/a&gt; While FFmpeg and AWS handle the heavy lifting of transcoding and storage, CE.SDK provides a &lt;strong&gt;powerful in-browser editing interface&lt;/strong&gt; for trimming, adding overlays, applying filters, or creating design templates. This combination allows you to deliver both &lt;strong&gt;scalable backend video processing&lt;/strong&gt; and &lt;strong&gt;interactive, user-facing editing tools&lt;/strong&gt;, similar to what platforms like Canva provide.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Running &lt;strong&gt;FFmpeg on AWS Spot Instances&lt;/strong&gt; gives you a powerful and cost-effective way to handle large-scale video transcoding. By leveraging Spot pricing, you can cut costs by up to 90% compared to On-Demand instances, making it possible to process even long, high-resolution videos without breaking the budget. With strategies like chunking videos and storing partial outputs in S3, interruptions become manageable, allowing you to balance affordability with reliability.&lt;/p&gt;
&lt;p&gt;For &lt;strong&gt;smaller, lightweight, or event-driven workflows&lt;/strong&gt;, &lt;strong&gt;AWS Lambda&lt;/strong&gt; is a perfect complement. Its serverless nature makes it easy to trigger FFmpeg jobs automatically, for example, generating thumbnails or clipping short clips as soon as a file is uploaded to S3. Although Lambda has strict runtime and storage limits, it shines when simplicity and automation are priorities.&lt;/p&gt;
&lt;p&gt;As a &lt;strong&gt;next step&lt;/strong&gt;, you can begin experimenting with automation and orchestration using services like &lt;strong&gt;SQS, Step Functions, or even AWS Batch&lt;/strong&gt; to build resilient pipelines that combine the strengths of both Spot Instances and Lambda. For teams looking for a fully managed alternative, CE.SDK is also worth exploring.&lt;/p&gt;
&lt;p&gt;By now, you’ve seen how to install FFmpeg, run it on spot instance, and handle serverless workflows with Lambda. With these tools and strategies, you’re ready to design &lt;strong&gt;flexible, scalable, and affordable video processing pipelines&lt;/strong&gt; in the AWS cloud.&lt;/p&gt;
&lt;p&gt;Join 3,000+ creative professionals who get early access to new features and updates—&lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i?ref=img.ly&quot;&gt;subscribe&lt;/a&gt;.&lt;/p&gt;</content:encoded><dc:creator>Robin</dc:creator><media:content url="https://blog.img.ly/2025/10/ffmpeg-awe.jpg" medium="image"/><category>FFmpeg</category><category>Video Editing</category><category>Server-side Video</category></item><item><title>FFmpeg - The Ultimate Guide</title><link>https://img.ly/blog/ultimate-guide-to-ffmpeg/</link><guid isPermaLink="true">https://img.ly/blog/ultimate-guide-to-ffmpeg/</guid><description>This guide covers the ins and outs of FFmpeg starting with fundamental concepts and moving to media transcoding and video and audio processing providing practical examples along the way.</description><pubDate>Mon, 21 Nov 2022 12:16:19 GMT</pubDate><content:encoded>&lt;p&gt;In this guide, we’ll go through the hot topics of FFmpeg. But before that, we’ll cover some base ground to help you understand basic media concepts and FFmpeg. Feel free to skip the parts that are already trivial for you!&lt;/p&gt;
&lt;h2 id=&quot;introduction-to-ffmpeg&quot;&gt;Introduction to FFmpeg&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://ffmpeg.org/about.html&quot;&gt;FFmpeg.org&lt;/a&gt;’s definition is the following: “FFmpeg is the leading multimedia framework, able to decode, encode, transcode, mux, demux, stream, filter and play pretty much anything that humans and machines have created. It supports the most obscure ancient formats up to the cutting edge. No matter if they were designed by some standards committee, the community or a corporation.”&lt;/p&gt;
&lt;p&gt;I think of FFmpeg as the go-to application for audio/video manipulation in an automated or scripted manner.&lt;/p&gt;
&lt;p&gt;When you need to implement a service that manipulates video, or just have 300 media files that need to be converted into a different format, FFmpeg is your - nerdy - friend.&lt;/p&gt;
&lt;p&gt;FFmpeg can do large chunks of the basic functionalities of a modern Non-linear (NLE) video editors, e.g., Davinci Resolve Studio or Premiere Pro. But, it does not have a graphical interface in that sense as those behemoths do, and unarguably it is way less friendly.&lt;/p&gt;
&lt;p&gt;In a general NLE, you might do things like these:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Click to import a file&lt;/li&gt;
&lt;li&gt;Drop it into the timeline&lt;/li&gt;
&lt;li&gt;Trim and Cut&lt;/li&gt;
&lt;li&gt;Add an overlay image&lt;/li&gt;
&lt;li&gt;Crop that overlay&lt;/li&gt;
&lt;li&gt;Add vignette&lt;/li&gt;
&lt;li&gt;Add some color changing effects, e.g. change the hue&lt;/li&gt;
&lt;li&gt;Add an extra audio track to the mix&lt;/li&gt;
&lt;li&gt;Change the volume&lt;/li&gt;
&lt;li&gt;Add some effects, e.g.: echo&lt;/li&gt;
&lt;li&gt;Export into various formats&lt;/li&gt;
&lt;li&gt;Export into a deployable video format&lt;/li&gt;
&lt;li&gt;Export the master audio in wav&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Learn how to &lt;a href=&quot;https://img.ly/blog/how-to-crop-and-trim-videos-in-flutter/#get-started&quot;&gt;crop and trim videos in Flutter&lt;/a&gt;. Or, to achieve the exact same thing, you could also execute this command:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt;  \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    -ss&lt;/span&gt;&lt;span&gt; 20&lt;/span&gt;&lt;span&gt; -t&lt;/span&gt;&lt;span&gt; 60&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    -i&lt;/span&gt;&lt;span&gt; train.jpg&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    -ss&lt;/span&gt;&lt;span&gt; 4&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; voice_recording.wav&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    -filter_complex&lt;/span&gt;&lt;span&gt; &quot;[0:v]hue=h=80:s=1[main] ; [1:v]crop=w=382:h=304:x=289:y=227[train] ; [main][train]overlay=x=200:y=200,vignette=PI/4[video] ; [2:a]volume=1.5,aecho=0.8:0.9:100:0.3[speech] ; [0:a][speech]amix=duration=shortest,asplit[audio1][audio2]&quot;&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    -map&lt;/span&gt;&lt;span&gt; &apos;[video]&apos;&lt;/span&gt;&lt;span&gt; -map&lt;/span&gt;&lt;span&gt; &apos;[audio1]&apos;&lt;/span&gt;&lt;span&gt; -metadata&lt;/span&gt;&lt;span&gt; title=&quot;Editor&apos;s cut&quot;&lt;/span&gt;&lt;span&gt; bbb_edited.mp4&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    -map&lt;/span&gt;&lt;span&gt; &apos;[audio2]&apos;&lt;/span&gt;&lt;span&gt; bbb_edited_audio_only.wav&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yes, it isn’t friendly at all, but it is very, very powerful once you become friends with FFmpeg.&lt;/p&gt;
&lt;p&gt;Check out this comparison of the original and the edited one:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1520px) 1520px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1520&quot; height=&quot;488&quot; src=&quot;https://img.ly/_astro/img-1-edit-before-after_Z1QXYCT.webp&quot; srcset=&quot;/_astro/img-1-edit-before-after_ZGfT24.webp 640w, /_astro/img-1-edit-before-after_27k274.webp 750w, /_astro/img-1-edit-before-after_oiRf3.webp 828w, /_astro/img-1-edit-before-after_XVrkH.webp 1080w, /_astro/img-1-edit-before-after_1CaUoV.webp 1280w, /_astro/img-1-edit-before-after_Z1QXYCT.webp 1520w&quot;&gt;&lt;/p&gt;
&lt;p&gt;If you want to try this command out, get the &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg//#example-material&quot;&gt;example&lt;/a&gt; files and see it for yourself!&lt;/p&gt;
&lt;h3 id=&quot;installing-ffmpeg&quot;&gt;Installing FFmpeg&lt;/h3&gt;
&lt;p&gt;FFmpeg is available for most common and even uncommon platforms and architectures. You can be on Linux, Mac OS X or Microsoft Windows, and you’ll be able to run or link to FFmpeg.&lt;/p&gt;
&lt;p&gt;Installing FFmpeg is easy on most platforms! There is no installer, usually just a compressed archive you need to get for your platform and architecture.&lt;/p&gt;
&lt;p&gt;In the case of Linux, most distributions include a pre-built FFmpeg in their software repositories. Therefore, you can install FFmpeg from those even more quickly.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/download.html#build-windows&quot;&gt;Download for Microsoft Windows&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/download.html#build-mac&quot;&gt;Download for Mac&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/download.html#build-linux&quot;&gt;Download for Linux&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;ffmpeg-history&quot;&gt;FFmpeg history&lt;/h3&gt;
&lt;p&gt;The project was started in 2000 by the awesome &lt;a href=&quot;https://bellard.org&quot;&gt;Fabrice Bellard&lt;/a&gt;. The name is a concatenation of “FF” meaning “fast-forward” and MPEG, the name of a video standards group. It has been very well, active and alive since then, &lt;a href=&quot;https://ffmpeg.org/releases/&quot;&gt;releasing&lt;/a&gt; a new release about every three months.&lt;/p&gt;
&lt;h3 id=&quot;ffmpeg-supported-codecs-and-formats&quot;&gt;FFmpeg supported codecs and formats&lt;/h3&gt;
&lt;p&gt;The default FFmpeg shipped with my Ubuntu Linux distribution supports about 460 codecs and 370 formats.&lt;/p&gt;
&lt;p&gt;See it for yourself:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -codecs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -formats&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;compilation-of-ffmpeg&quot;&gt;Compilation of FFmpeg&lt;/h3&gt;
&lt;p&gt;Keep in mind that the supported codecs and formats (and filters, demuxers, muxers, input and output methods, etc.) are highly dependent on the so-called compilation flags.&lt;/p&gt;
&lt;p&gt;This means that the above number only represents the fact that it supports at least this many codecs and formats. Still, there are even more that the package builders excluded for various reasons, e.g.: licensing, architecture, size considerations, etc.&lt;/p&gt;
&lt;p&gt;Since FFmpeg is &lt;a href=&quot;https://ffmpeg.org/download.html#repositories&quot;&gt;open source&lt;/a&gt;, you can &lt;a href=&quot;https://trac.ffmpeg.org/wiki/CompilationGuide&quot;&gt;compile FFmpeg&lt;/a&gt; for yourself at any time.&lt;/p&gt;
&lt;p&gt;Suppose for example, that you care about your layer’s size (therefore the bootstrap speed) in AWS Lambda. In this case, you can compile an FFmpeg binary that only contains the mp3 encoder for example, and nothing else. For a full tutorial on &lt;a href=&quot;https://img.ly/blog/how-to-run-ffmpeg-on-aws-spot-instances-for-scalable-low-cost-video-processing/&quot;&gt;running FFmpeg on AWS Spot Instances&lt;/a&gt;, see our cloud guide. Prefer Google Cloud? Our guide on &lt;a href=&quot;https://img.ly/blog/ffmpeg-on-google-cloud-platform-guide/&quot;&gt;running FFmpeg on Google Cloud Platform&lt;/a&gt; shows you how.&lt;/p&gt;
&lt;p&gt;Also, you might not want to run into licensing issues and leave out stuff that would cause problems for your use case. Therefore you choose to leave out particular codecs/formats. I highly recommend checking out the “—enable-gpl”, “—enable-nonfree” and “—enable-version3” &lt;a href=&quot;https://github.com/FFmpeg/FFmpeg/blob/master/configure&quot;&gt;compilation flags&lt;/a&gt; in this case, as well as &lt;a href=&quot;https://ffmpeg.org/legal.html&quot;&gt;this&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Or you might want to have a standalone FFmpeg binary in your project (e.g.: embedded, or some cloud instance), that does not depend on any operating system libraries. Then you want to make a so-called static build, that compiles in all the libraries into a single binary file, and does not depend on your OS’ libraries and the runtime loading of other FFmpeg libraries. Search around for “—enable-static” in this case.&lt;/p&gt;
&lt;p&gt;Finally, you can find pre-built static FFmpeg builds &lt;a href=&quot;https://johnvansickle.com/ffmpeg/&quot;&gt;right here&lt;/a&gt; too. Alternatively, you can &lt;a href=&quot;https://img.ly/blog/how-to-run-ffmpeg-inside-a-docker-container/&quot;&gt;package FFmpeg in a Docker container&lt;/a&gt; for consistent environments - our Docker guide covers this approach.&lt;/p&gt;
&lt;h3 id=&quot;ffmpegs-strengths&quot;&gt;FFmpeg’s strengths&lt;/h3&gt;
&lt;p&gt;FFmpeg reads and writes most video and audio formats that matter for most of us. It is a very capable and high-performance tool for converting and manipulating these formats.&lt;/p&gt;
&lt;p&gt;But FFmpeg can do even more! For examples of these operations integrated into an automated pipeline, read our article on a &lt;a href=&quot;https://img.ly/blog/building-a-production-ready-batch-video-processing-server-with-ffmpeg/&quot;&gt;batch video processing server.&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;filtering&quot;&gt;Filtering&lt;/h3&gt;
&lt;p&gt;FFmpeg has vast amounts of filters for audio and video. Therefore, video manipulation is also a key feature of FFmpeg.&lt;/p&gt;
&lt;h3 id=&quot;hardware-acceleration&quot;&gt;Hardware acceleration&lt;/h3&gt;
&lt;p&gt;It does support many kinds of hardware accelerations! Video encoding is a very resource-intensive operation, and you might come across quite a few hardware devices or features that might speed up your process!&lt;/p&gt;
&lt;p&gt;Most notably, if you have an NVIDIA card, you can increase your H.264 or H.265 encoding and decoding throughput by multipliers compared to your CPU. But other things, such as VDPAU, VAAPI, or OpenCL, can be leveraged to boost your pipeline’s throughput.&lt;/p&gt;
&lt;p&gt;Learn more about the supported hardware acceleration methods &lt;a href=&quot;https://trac.ffmpeg.org/wiki/HWAccelIntro&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;versatile-inputoutput-methods&quot;&gt;Versatile input/output methods&lt;/h3&gt;
&lt;p&gt;FFmpeg is also very capable when it comes to accessing input and output data.&lt;/p&gt;
&lt;p&gt;Just to name a few: it can use your webcam, record from your microphone, grab your screen, or capture from your Blackmagic DeckLink. But FFmpeg can download directly from a web address, open all kinds of streams, read from a pipe, a socket, and of course, from files.&lt;/p&gt;
&lt;p&gt;The same holds true for outputting the data. It can write to your webcam, play audio on your microphone… Just kidding:) It can output to files, streams, pipes, sockets and so on.&lt;/p&gt;
&lt;h3 id=&quot;running-example-commands&quot;&gt;Running example commands&lt;/h3&gt;
&lt;p&gt;This article is full of FFmpeg commands that are working examples. The reason for that is that you could test these out for yourself! But the command line interfaces of different operating systems are slightly different, so the commands in this article are meant to be executed in a Linux bash shell.&lt;/p&gt;
&lt;p&gt;To adopt these command lines to Microsoft Windows, you might need to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Change (cd) into the directory where you extracted the ffmpeg.exe. Alternatively, add that directory to the &lt;a href=&quot;https://duckduckgo.com/?t=ffab&amp;#x26;q=add+binary+to+path+windows&quot;&gt;path&lt;/a&gt; to make it callable from anywhere.&lt;/li&gt;
&lt;li&gt;You might need to replace “ffmpeg” to “ffmpeg.exe”&lt;/li&gt;
&lt;li&gt;You will need to replace ”&lt;strong&gt;\&lt;/strong&gt;“-s (backslashes) at the end of the lines with ”&lt;strong&gt;^&lt;/strong&gt;“-s (hats)&lt;/li&gt;
&lt;li&gt;You’ll need to replace the &lt;code&gt;fontfile&lt;/code&gt; argument’s value to something like this: &lt;code&gt;fontfile=/Windows/Fonts/arial.ttf&lt;/code&gt; to get commands with the drawtext filter working.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;MacOS users will need steps #1 and #4.&lt;/p&gt;
&lt;h2 id=&quot;introduction-to-media-concepts&quot;&gt;Introduction to media concepts&lt;/h2&gt;
&lt;p&gt;Now let’s have a quick overview of media concepts. These concepts will be vital for us if we want to understand the latter sections of this article and FFmpeg’s workings. To keep this section brief, it is a higher-level, simplified explanation of these concepts.&lt;/p&gt;
&lt;h3 id=&quot;audio&quot;&gt;Audio&lt;/h3&gt;
&lt;p&gt;We’ll briefly cover the following terms:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Sampling rate&lt;/li&gt;
&lt;li&gt;Bitrate&lt;/li&gt;
&lt;li&gt;Channels&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;sampling-rate&quot;&gt;Sampling Rate&lt;/h3&gt;
&lt;p&gt;The sampling rate is the factor that shows how many times we measure/scan/sample the input data stream.&lt;/p&gt;
&lt;p&gt;The image below shows the measurement windows (quantization) as gray bars.&lt;/p&gt;
&lt;p&gt;Why does this matter? Because it is a balancing act. If we measure the signal less often, we’ll lose more details (bad). Also, by having fewer samples, we’ll have less data in the end. Therefore the file size will be smaller (good).&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1600px) 1600px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1600&quot; height=&quot;1200&quot; src=&quot;https://img.ly/_astro/img-2-sampling-rate_16IiDT.webp&quot; srcset=&quot;/_astro/img-2-sampling-rate_ZEjEs8.webp 640w, /_astro/img-2-sampling-rate_2lFo7Q.webp 750w, /_astro/img-2-sampling-rate_ZptRzw.webp 828w, /_astro/img-2-sampling-rate_2dctkz.webp 1080w, /_astro/img-2-sampling-rate_ZlnVMi.webp 1280w, /_astro/img-2-sampling-rate_16IiDT.webp 1600w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Here are some ballpark values:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;8 kHz (GSM - Low quality)&lt;/li&gt;
&lt;li&gt;44.1 kHz (CD - High quality)&lt;/li&gt;
&lt;li&gt;48 kHz (Very high quality)&lt;/li&gt;
&lt;li&gt;88.2 kHz (Insane - usually for production only)&lt;/li&gt;
&lt;li&gt;96 kHz (Insane - usually for production only)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are no definite “right answers” here. The question is what is “good enough” for your use case? GSM focuses on speech, and not even quality but understandability and the least possible amount of data. Therefore, they found that 8 kHz is enough (there are quite a few more tricks), for their purposes.&lt;/p&gt;
&lt;p&gt;The “CD quality” aimed for high quality. Therefore they chose 44.1 kHz, that number has some history in it, but the main reason for aiming above 40 kHz lies in physics and how the human ear works.&lt;/p&gt;
&lt;p&gt;There were two very smart guys whose &lt;a href=&quot;https://en.wikipedia.org/wiki/Nyquist%E2%80%93Shannon_sampling_theorem&quot;&gt;theorem&lt;/a&gt; basically says that if you want a quite good signal representation, you have to sample it at twice the speed as its original frequency. Human hearing generally &lt;a href=&quot;https://en.wikipedia.org/wiki/Hearing_range&quot;&gt;works&lt;/a&gt; up until about 20 kHz, so if you want “good quality”, you should aim for at least 40 kHz. And 40 kHz + some headroom + some more physics + historical reasons = 44.1 kHz! :)&lt;/p&gt;
&lt;p&gt;As for the higher rates, those are only used when very high-quality audio editing is needed.&lt;/p&gt;
&lt;h3 id=&quot;bitrate&quot;&gt;Bitrate&lt;/h3&gt;
&lt;p&gt;Bitrate represents the amount of data per second that results from our transcoding/quantization process. If it is 1411 kbit/s, that means that for every second of audio data, about 1411 kbit of output data will be produced.&lt;/p&gt;
&lt;p&gt;Therefore, you can say that 1 minute of audio with 1411 kbit/sec will require:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;(1411 kbit / 8) kbyte * 60 second = 10582 kbyte = 10.33 mbyte&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Now, it is only easy like that with raw audio data and with a few simple codecs, e.g. PCM in WAVs.&lt;/p&gt;
&lt;p&gt;Codecs compressing hard might throw your numbers around a little, as input data might be compressible with different rates. Variable bitrate is usually happening to save space. The encoder might output a lower bitrate if the data is “simple” and does not require high precision.&lt;/p&gt;
&lt;p&gt;Here are some ballpark values:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;13 kbits/s (GSM quality)&lt;/li&gt;
&lt;li&gt;320 kbit/s (High-quality MP3)&lt;/li&gt;
&lt;li&gt;1411 kbit/s (16bit WAV, CD quality, PCM)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;channels&quot;&gt;Channels&lt;/h3&gt;
&lt;p&gt;Inside of most audio formats, you can have more audio channels. This means multiple, separated audio streams can be in the same file.&lt;/p&gt;
&lt;p&gt;Many times, multiple channels have their own name:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you have a single microphone, you will most probably record it into a single channel called Mono.&lt;/li&gt;
&lt;li&gt;General music from the FM radio or streaming services usually has two channels in a so-called “Stereo” configuration.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With stereo, there could be several methods how the audio “image” can be made richer by leveraging audio &lt;a href=&quot;https://en.wikipedia.org/wiki/Panning%5F(audio)&quot;&gt;panning&lt;/a&gt;, time and phase-shifting and much more. There is a special recording technique too, called &lt;a href=&quot;https://en.wikipedia.org/wiki/Binaural_recording&quot;&gt;Binaural recording&lt;/a&gt;, which is super awesome. Wear headphones for &lt;a href=&quot;https://www.youtube.com/watch?v=aQH-jwE_kfo&quot;&gt;this&lt;/a&gt;, and don’t be scared:)&lt;/p&gt;
&lt;p&gt;For example, here are &lt;a href=&quot;https://peach.blender.org/&quot;&gt;Big Buck Bunny&lt;/a&gt;’s audio waveforms in &lt;a href=&quot;https://www.audacityteam.org/&quot;&gt;Audacity&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1756px) 1756px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1756&quot; height=&quot;307&quot; src=&quot;https://img.ly/_astro/img-3-waveforms_QyR5N.webp&quot; srcset=&quot;/_astro/img-3-waveforms_d7t0d.webp 640w, /_astro/img-3-waveforms_20zbLg.webp 750w, /_astro/img-3-waveforms_2hIR4S.webp 828w, /_astro/img-3-waveforms_1Awtx2.webp 1080w, /_astro/img-3-waveforms_Z31aji.webp 1280w, /_astro/img-3-waveforms_UYkTp.webp 1668w, /_astro/img-3-waveforms_QyR5N.webp 1756w&quot;&gt;&lt;/p&gt;
&lt;p&gt;You can see that there are two lines of waveforms and also that they are pretty similar. That is normal, as you usually hear the same thing with your two ears, but the matter is in the subtle differences between the two. That’s where directionality, richness, and all kinds of other effects lie.&lt;/p&gt;
&lt;p&gt;But why stop at two? The list continues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2.1, as it is often called, means three channels: 2 for stereo and one for the LFE (“low-frequency effects” a.k.a.: “bass”).&lt;/li&gt;
&lt;li&gt;5.1 is similar, with five directional channels (2 front, 1 center, 2 rear) and the LFE.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So channels are just separate “recordings” or “streams” of audio signals.&lt;/p&gt;
&lt;h3 id=&quot;image-properties&quot;&gt;Image properties&lt;/h3&gt;
&lt;p&gt;For images, there are quite a few parameters, but we’ll check out only these:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Resolution&lt;/li&gt;
&lt;li&gt;Bit-depth&lt;/li&gt;
&lt;li&gt;Transparency&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;resolution&quot;&gt;Resolution&lt;/h3&gt;
&lt;p&gt;An image consists of pixels, single points that have a single color. The resolution of an image determines how many columns and rows of pixels are in an image. In other words: an image has a width and a height.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1520px) 1520px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1520&quot; height=&quot;572&quot; src=&quot;https://img.ly/_astro/img-4-resolution-1_Z28Fb9g.webp&quot; srcset=&quot;/_astro/img-4-resolution-1_2oaDvQ.webp 640w, /_astro/img-4-resolution-1_Z1a5LF7.webp 750w, /_astro/img-4-resolution-1_2cdshh.webp 828w, /_astro/img-4-resolution-1_Z2vj565.webp 1080w, /_astro/img-4-resolution-1_Zv4bSK.webp 1280w, /_astro/img-4-resolution-1_Z28Fb9g.webp 1520w&quot;&gt;&lt;/p&gt;
&lt;p&gt;This image shows the first 10 pixels in the first row.&lt;/p&gt;
&lt;p&gt;Here are some ballpark values for resolution:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“HD” or “Full HD” or “1K” or “1080p” means 1920x1080 pixels.&lt;/li&gt;
&lt;li&gt;“4K” could mean a few values, but it should be about 3840x2160 pixels.&lt;/li&gt;
&lt;li&gt;A regular 16mp photo you make of your cat is about 4608x3456 pixels.&lt;/li&gt;
&lt;li&gt;General social media image posts are about 1080x1080 pixels.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;bit-depth&quot;&gt;Bit-depth&lt;/h3&gt;
&lt;p&gt;Bit-depth represents the number of bits used for storing a single pixel’s color value. This is the same balancing game, and you need to decide between quality or file size.&lt;/p&gt;
&lt;p&gt;General ballpark values for bit-depth:&lt;/p&gt;






























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Bits&lt;/th&gt;&lt;th&gt;Colors&lt;/th&gt;&lt;th&gt;Notes&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;Black &amp;#x26; White&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;256&lt;/td&gt;&lt;td&gt;B/W or Limited color palette&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;24&lt;/td&gt;&lt;td&gt;16.7m&lt;/td&gt;&lt;td&gt;3x&lt;strong&gt;8 bit&lt;/strong&gt; for R-G-B “True color”&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;30&lt;/td&gt;&lt;td&gt;1073m&lt;/td&gt;&lt;td&gt;3x&lt;strong&gt;10 bit&lt;/strong&gt; for R-G-B “Deep color”&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;These last two sometimes are referred to as “8 bit” or “10 bit” respectively, especially when talking about videos. That means 8/10 bits per single color channel.&lt;/p&gt;
&lt;h3 id=&quot;transparency&quot;&gt;Transparency&lt;/h3&gt;
&lt;p&gt;Some image formats support an additional channel together with the red, green, and blue components: the alpha channel. The alpha channel determines how transparent a single pixel is, and it can have different bit-depths, it is usually either 1, 8 or 16 bits.&lt;/p&gt;
&lt;p&gt;If the alpha channel is 1 bit, then the format can encode a pixel to be either transparent or non-transparent. If it is 8 or more bits, then the format can encode 256 or more steps of transparency.&lt;/p&gt;
&lt;h3 id=&quot;video-properties&quot;&gt;Video properties&lt;/h3&gt;
&lt;p&gt;Video data is built by single images shown right after each other. This brings in most attributes of images and a few more!&lt;/p&gt;
&lt;p&gt;So a video has a &lt;code&gt;resolution&lt;/code&gt; that is its width and height.&lt;/p&gt;
&lt;p&gt;Then the first obvious parameter of a video is the &lt;code&gt;framerate&lt;/code&gt;, which defines how many images are shown in a second. Common values for this are 24, 25, 30, or 60.&lt;/p&gt;
&lt;p&gt;A video file also has a &lt;code&gt;codec&lt;/code&gt; assigned to it, which is the format describing how all those images were compressed into this video file. There are many more attributes of videos, but this is a good start.&lt;/p&gt;
&lt;h3 id=&quot;video-codecs&quot;&gt;Video codecs&lt;/h3&gt;
&lt;p&gt;Compression is a super important thing when it comes to video because you have thousands of images to keep together. If you aren’t doing it in a smart way, then the resulting video will be very, very large.&lt;/p&gt;
&lt;p&gt;Just imagine a 2-minute video, with 30 fps. That means it will have 60 s * 2 * 30 fps = 3600 frames! I have just taken a screenshot of an HD video, which was 730 kbyte in JPEG format. Now 3600 frame * 730 kbyte equals 2.5 gigabytes!&lt;/p&gt;
&lt;p&gt;Can you imagine that? I hope not, and that’s because compression brings that way, way down, to the level of tens of megabytes. These days a video of that size is quite high quality and about 2 hours long. Also, don’t forget, that JPEG is already compressed, a single frame would be 6 mbyte when uncompressed. Now that 2-minute video would be 21 gigabytes if we’d store it uncompressed.&lt;/p&gt;
&lt;p&gt;Standard codecs such as H.264 and H.265 are doing very clever and complex operations to achieve high compression ratios with good quality.&lt;/p&gt;
&lt;p&gt;Just think about that, most frames in a video are quite similar, only containing small differences. So if we could only store that little difference between frames, we’d won a huge bonus! And that’s just one of the many tricks codecs do.&lt;/p&gt;
&lt;p&gt;Codec designers are also exploiting the weaknesses and features of the human eye. Such as the fact that we are more sensitive to light intensity changes than color changes (say hello to &lt;a href=&quot;https://en.wikipedia.org/wiki/YUV&quot;&gt;YUV&lt;/a&gt;). And they can get away with lower quality details for parts &lt;a href=&quot;https://en.wikipedia.org/wiki/Motion_blur#Biology&quot;&gt;that are moving fast&lt;/a&gt;, and so on.&lt;/p&gt;
&lt;p&gt;Because why lose precious bits for things that you can’t even notice?!&lt;/p&gt;
&lt;p&gt;There are many codecs out there, with different goals in mind, although the majority focus on keeping the file size low.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;H.264, H.265: These are the most common ones, with the widest support in browsers, phones, players, etc. It focuses on small file sizes with good quality. (At the cost of resource intensiveness.)&lt;/li&gt;
&lt;li&gt;Apple ProRes, DNxHD: These are common formats for production. They focus on quality and ease of processing and not on file size.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;audio-codecs&quot;&gt;Audio codecs&lt;/h3&gt;
&lt;p&gt;The goal of audio codecs is the same as what we saw with the video codecs. It is just harder to demonstrate it as audio does not consist of single image frames but audio frames/packets. So an analog audio signal is of an almost infinite, or at least very high quality if you think of it.&lt;/p&gt;
&lt;p&gt;At the lowest level, the speed and amplitude resolution is very high. We could say “atomic”, as we need to measure and store the speed and direction of atoms. So if you want to store that exactly, that will require a super high-quality measurement, which will also result in a very high bitrate data stream.&lt;/p&gt;
&lt;p&gt;Thankfully, the sound is at least not propagating with light speed so we can save quite a lot just by that fact. (There’s no need for an extreme sampling rate.) Then our hearing is very limited if we take the previous paragraph as a scale, so we win there again. We don’t need most of that high precision that is there.&lt;/p&gt;
&lt;p&gt;But still, if we take our hearing capability and want to store raw audio data with about 44.1 kHz of sample rate with about 1 Mbit/sec bitrate, we’d still get quite a lot of data. Check the calculations in the &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg//#bitrate&quot;&gt;audio bitrate&lt;/a&gt; section above.&lt;/p&gt;
&lt;p&gt;So raw audio can be compressed further, which is what many popular codecs do. They also exploit the human senses, but this time the human ear. We started with the basics that the human ear has a limit on the frequencies it can detect. Therefore, we can save a lot by cutting out the range of frequencies outside our hearing range. Unless you are a bat, you are fine between 20-20khz! :)&lt;/p&gt;
&lt;p&gt;But there are other tricks, for example, &lt;a href=&quot;https://en.wikipedia.org/wiki/Auditory_masking&quot;&gt;auditory masking&lt;/a&gt;. That means that the presence of one frequency can affect your capability to detect a different frequency. From the codec’s viewpoint, it can skip encoding a few frequencies if it is smart enough to know which ones you’ll not notice. I’m sure there are a lot more tricks, let me know if you know about a few more interesting ones!&lt;/p&gt;
&lt;p&gt;Here is a list of common codecs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MP3, AAC, OGG: These are common lossy audio formats.&lt;/li&gt;
&lt;li&gt;PCM (e.g. in a WAV container), FLAC: These are lossless formats.&lt;/li&gt;
&lt;li&gt;MIDI: It is a funny format. It is like a music sheet that might sound different on different players or settings. It is usually not made from real audio data, but from recording a digital keyboard or as an output from an audio composing software.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;containers&quot;&gt;Containers&lt;/h3&gt;
&lt;p&gt;Now we got through the fundamental building blocks, the image, the video, the video codecs, and the audio codecs, and we reached the top of this iceberg: the containers.&lt;/p&gt;
&lt;p&gt;A container is a format specification, that combines all these streams into a single file format. It defines how to put all these data together, how to attach metadata (e.g. author, description, etc), how to synchronize these streams, and sometimes a container even contains indexes to aid seeking.&lt;/p&gt;
&lt;p&gt;So, for example, a MOV container can contain an H.264 video stream and an AAC audio stream together.&lt;/p&gt;
&lt;p&gt;Common containers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MOV&lt;/li&gt;
&lt;li&gt;MP4&lt;/li&gt;
&lt;li&gt;MKV&lt;/li&gt;
&lt;li&gt;WebM&lt;/li&gt;
&lt;li&gt;WAV (audio only)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;example-material&quot;&gt;Example Material&lt;/h2&gt;
&lt;p&gt;I will use these example materials as inputs in the following parts of this article. If you’d like to follow along, save these files for yourself!&lt;/p&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Name&lt;/th&gt;&lt;th&gt;Resource&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Big Buck Bunny&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_1080p_60fps_normal.mp4&quot;&gt;http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_1080p_60fps_normal.mp4&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Train&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/ffmpeg-examples/train.jpg&quot;&gt;train.jpg&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Smiley&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/ffmpeg-examples/smiley.png&quot;&gt;smiley.png&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Voice recording&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/ffmpeg-examples/voice_recording.wav&quot;&gt;voice_recording.wav&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Big Buck Bunny’s audio&lt;/td&gt;&lt;td&gt;ffmpeg -i bbb_sunflower_1080p_60fps_normal.mp4 -map 0:1 bbb_audio.wav&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;n&lt;/p&gt;
&lt;p&gt;And we will make our own audio file by extracting the audio from the Big Buck Bunny movie! We’ll use this file as an example, so after downloading the video file, please execute this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; -map&lt;/span&gt;&lt;span&gt; 0:1&lt;/span&gt;&lt;span&gt; bbb_audio.wav&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By the middle of this article, you’ll understand this command, but for now, just make sure to have the WAV file next to your video file to test out the commands later in the article.&lt;/p&gt;
&lt;p&gt;We’ll use these files in the following parts of this article. Therefore make sure to get them!&lt;/p&gt;
&lt;h2 id=&quot;ffplay-and-ffprobe&quot;&gt;FFplay and FFprobe&lt;/h2&gt;
&lt;p&gt;FFmpeg is the name of the main binary and the project itself, but it is shipped together with two other binaries, ffplay and ffprobe.&lt;/p&gt;
&lt;p&gt;Let’s check them out quickly, right in the command line!&lt;/p&gt;
&lt;h3 id=&quot;ffplay&quot;&gt;FFplay&lt;/h3&gt;
&lt;p&gt;FFplay is a basic video player, that can be used for playing media. It’s not a friendly video player, but it is a good testing ground for various things.&lt;/p&gt;
&lt;p&gt;To execute it, just simply supply a media file:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffplay&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you want to test this exact command, you’ll need to get the &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg//#example-material&quot;&gt;example&lt;/a&gt; files.&lt;/p&gt;
&lt;p&gt;For example, it can be used to preview filters (we’ll discuss those &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg//#filtering&quot;&gt;later&lt;/a&gt;), but let’s see an example:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffplay&lt;/span&gt;&lt;span&gt; -vf&lt;/span&gt;&lt;span&gt; &quot;drawtext=text=&apos;HELLO THERE&apos;:y=h-text_h-10:x=(w/2-text_w/2):fontsize=200:f&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1962px) 1962px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1962&quot; height=&quot;1145&quot; src=&quot;https://img.ly/_astro/img-5-big-bunny_Z1KSyDO.webp&quot; srcset=&quot;/_astro/img-5-big-bunny_Z2tEJ77.webp 640w, /_astro/img-5-big-bunny_Z2gSLvG.webp 750w, /_astro/img-5-big-bunny_1Aamch.webp 828w, /_astro/img-5-big-bunny_Z14XPyG.webp 1080w, /_astro/img-5-big-bunny_1Yyeb4.webp 1280w, /_astro/img-5-big-bunny_1AAWwo.webp 1668w, /_astro/img-5-big-bunny_Z1KSyDO.webp 1962w&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;ffprobe&quot;&gt;FFprobe&lt;/h3&gt;
&lt;p&gt;FFprobe, as its name implies, is a tool for getting information about media files.&lt;/p&gt;
&lt;p&gt;This command:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffprobe&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Will return us some general information about the video file:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Input #0, mov,mp4,m4a,3gp,3g2,mj2, from &apos;bbb_sunflower_1080p_60fps_normal.mp4&apos;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Metadata:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[...]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    title           : Big Buck Bunny, Sunflower version&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    artist          : Blender Foundation 2008, Janus Bager Kristensen 2013&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[...]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Stream #0:0[0x1](und): Video: h264 [...]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[...]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Stream #0:1[0x2](und): Audio: mp3 [...]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[...]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Stream #0:2[0x3](und): Audio: ac3 [...]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I have abbreviated it heavily, as we’ll check this out later.&lt;/p&gt;
&lt;p&gt;But FFprobe is way more powerful than just this!&lt;/p&gt;
&lt;p&gt;With the following command, we can get the same listing in JSON format, which is machine-readable!&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffprobe&lt;/span&gt;&lt;span&gt; -v&lt;/span&gt;&lt;span&gt; error&lt;/span&gt;&lt;span&gt; -hide_banner&lt;/span&gt;&lt;span&gt; -print_format&lt;/span&gt;&lt;span&gt; json&lt;/span&gt;&lt;span&gt; -show_streams&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The explanation of this command is the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“&lt;strong&gt;-v error -hide_banner&lt;/strong&gt;”: This part hides extra output, such as headers and the default build information.&lt;/li&gt;
&lt;li&gt;“&lt;strong&gt;-print_format json&lt;/strong&gt;”: Obviously, this causes ffprobe to output a JSON.&lt;/li&gt;
&lt;li&gt;“&lt;strong&gt;-show_streams&lt;/strong&gt;” is the main switch that requests the stream information.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;streams&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &quot;index&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &quot;codec_name&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;h264&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &quot;codec_long_name&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &quot;width&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1920&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &quot;height&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1080&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &quot;bit_rate&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;4001453&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &quot;duration&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;634.533333&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &quot;############################&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;[~50 lines removed]&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &quot;index&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &quot;codec_name&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;mp3&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &quot;channels&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &quot;bit_rate&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;160000&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &quot;############################&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;[~40 lines removed]&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &quot;index&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &quot;codec_name&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;ac3&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &quot;channels&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;6&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &quot;############################&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;[~20 lines removed]&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this output, you can see three streams of data in this video file. The first (index: 0) is a video stream, that is an HD video with an H.264 codec. Then we have two audio streams, the first (index: 1) is a simple mp3 stream with stereo audio, and the second (index: 2) is an ac3 stream with 6 channels, most likely in an 5.1 configuration.&lt;/p&gt;
&lt;p&gt;I have removed quite a lot of output for brevity, but you can get way more information out of these streams, e.g. fps for the video stream and so on.&lt;/p&gt;
&lt;p&gt;Other than &lt;strong&gt;-show_streams&lt;/strong&gt;, there are 3 more: &lt;strong&gt;-show_format&lt;/strong&gt;, &lt;strong&gt;-show_packets&lt;/strong&gt; and &lt;strong&gt;-show_frames&lt;/strong&gt;. Unless you are really deep in the rabbit hole, you’ll not need the last two, but &lt;strong&gt;-show_format&lt;/strong&gt; could be useful:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffprobe&lt;/span&gt;&lt;span&gt; -v&lt;/span&gt;&lt;span&gt; error&lt;/span&gt;&lt;span&gt; -hide_banner&lt;/span&gt;&lt;span&gt; -print_format&lt;/span&gt;&lt;span&gt; json&lt;/span&gt;&lt;span&gt; -show_format&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;format&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;filename&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;bbb_sunflower_1080p_60fps_normal.mp4&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;nb_streams&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;nb_programs&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;format_name&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;mov,mp4,m4a,3gp,3g2,mj2&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;format_long_name&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;QuickTime / MOV&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;start_time&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;0.000000&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;duration&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;634.533333&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;size&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;355856562&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;bit_rate&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;4486529&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;probe_score&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;tags&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &quot;major_brand&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;isom&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &quot;minor_version&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;1&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &quot;compatible_brands&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;isomavc1&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &quot;creation_time&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;2013-12-16T17:59:32.000000Z&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &quot;title&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;Big Buck Bunny, Sunflower version&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &quot;artist&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;Blender Foundation 2008, Janus Bager Kristensen 2013&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &quot;comment&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;Creative Commons Attribution 3.0 - http://bbb3d.renderfarming.net&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &quot;genre&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;Animation&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &quot;composer&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;Sacha Goedegebure&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is an overview of “what is this file”. As we see, it is a MOV file (format_name), with three streams (nb_streams), and it is 634 seconds long. Also, there are some tags where we can see the title, the artist, and other information.&lt;/p&gt;
&lt;h2 id=&quot;ffmpeg-concepts&quot;&gt;FFmpeg concepts&lt;/h2&gt;
&lt;p&gt;Here is a quick intro to how FFmpeg actually works!&lt;/p&gt;
&lt;p&gt;For those who are just joining in: please get the &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg//#example-material&quot;&gt;example assets&lt;/a&gt; if you want to test out the commands shown in this chapter!&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1592px) 1592px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1592&quot; height=&quot;948&quot; src=&quot;https://img.ly/_astro/img-6-input-output_1VpFon.webp&quot; srcset=&quot;/_astro/img-6-input-output_1ca7gh.webp 640w, /_astro/img-6-input-output_KPHvd.webp 750w, /_astro/img-6-input-output_1qXtyE.webp 828w, /_astro/img-6-input-output_8zrzA.webp 1080w, /_astro/img-6-input-output_Wreqk.webp 1280w, /_astro/img-6-input-output_1VpFon.webp 1592w&quot;&gt;&lt;/p&gt;
&lt;p&gt;FFmpeg opens the file, decodes it into memory, then encodes the in-memory packets back and puts them into some container: some output file. The term “codec” is a mix of the words “&lt;strong&gt;cod&lt;/strong&gt;er &amp;#x26; &lt;strong&gt;e&lt;/strong&gt;n&lt;strong&gt;c&lt;/strong&gt;oder”. Those are the magic parts before and after the “decoded frames”.&lt;/p&gt;
&lt;p&gt;The decoded frames are uncompressed images in-memory, e.g. the most basic pixel format for video frames is called “rgb24”. This just stores red, green, and blue values right after each other in 3x8 bits, or 3x1 byte, which could hold 16m colors.&lt;/p&gt;
&lt;p&gt;The importance of this is that other than &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg//#editing-without-reencoding&quot;&gt;a few exceptions&lt;/a&gt;, you can only manipulate or encode the decoded frames. So when we get to different audio/video filters or transcoding, you’ll need the decoded frames for all that. But don’t worry, FFmpeg does this automatically for you.&lt;/p&gt;
&lt;h3 id=&quot;inputs&quot;&gt;Inputs&lt;/h3&gt;
&lt;p&gt;So you see and probably guessed, that FFmpeg must access the input data somehow. FFmpeg knows how to handle most media files, as the awesome people who develop FFmpeg and the related libraries made encoders and decoders for most formats available!&lt;/p&gt;
&lt;p&gt;Don’t think that it is a trivial thing. Many formats are reverse engineered, a hard task requiring brilliant people.&lt;/p&gt;
&lt;p&gt;So although we often refer to input files, the input could come from many sources, such as the network, a hardware device and so on. We’ll learn more about that &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg//#inputs&quot;&gt;later&lt;/a&gt; on in this article.&lt;/p&gt;
&lt;p&gt;Many media files are containers for different streams, meaning that a single file might contain multiple streams of content.&lt;/p&gt;
&lt;p&gt;For example, a .mov file might contain one or more streams:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;video tracks&lt;/li&gt;
&lt;li&gt;audio tracks (e.g. for the different languages or audio formats such as stereo or 5.1)&lt;/li&gt;
&lt;li&gt;subtitle tracks&lt;/li&gt;
&lt;li&gt;thumbnails&lt;/li&gt;
&lt;li&gt;…&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All these are streams of data from the viewpoint of FFmpeg. Input files and their streams are numerically differentiated with a 0-based index. So, for example, 1:0 means the first(0) stream of the second(1) input file. We’ll &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg//#mapping&quot;&gt;learn more&lt;/a&gt; about that later too!&lt;/p&gt;
&lt;p&gt;Important to note that FFmpeg can open any number of input files simultaneously, and the filtering and mapping will decide what it will do with those. Again more on that later!&lt;/p&gt;
&lt;h3 id=&quot;streams&quot;&gt;Streams&lt;/h3&gt;
&lt;p&gt;As we have seen in the previous section, streams are the fundamental building blocks of containers. So every input file must have at least one stream. And that’s what you can list by the simple &lt;code&gt;ffmpeg -i&lt;/code&gt; command for example.&lt;/p&gt;
&lt;p&gt;A stream might contain an audio format such as MP3, or a video format such as an H.264 stream.&lt;/p&gt;
&lt;p&gt;Also, a stream, depending on the codec, might contain multiple “things”. For example, an mp3 or a WAV stream might include various audio channels.&lt;/p&gt;
&lt;p&gt;So the building block hierarchy, in this case is: File → Stream → Channels.&lt;/p&gt;
&lt;h3 id=&quot;outputs&quot;&gt;Outputs&lt;/h3&gt;
&lt;p&gt;Of course, an output could be a local file, but it doesn’t need to be. It could be a socket, a stream and so on. In the same way as with inputs, you could have multiple outputs, and the mapping determines what goes into which output file.&lt;/p&gt;
&lt;p&gt;The output also must have some format or container. Most of the time FFmpeg can and will guess that for us, mostly from the extension, but we can specify it too.&lt;/p&gt;
&lt;h3 id=&quot;mapping&quot;&gt;Mapping&lt;/h3&gt;
&lt;p&gt;Mapping refers to the act of connecting input file streams with output file streams. So if you give 3 input files and 4 output files to FFmpeg, you must also define what should go to where.&lt;/p&gt;
&lt;p&gt;If you give a single input and a single output, then FFmpeg will guess it for you without specifying any mapping, but make sure you know how exactly that happens, to avoid surprises. More on all that later!&lt;/p&gt;
&lt;h3 id=&quot;filtering-1&quot;&gt;Filtering&lt;/h3&gt;
&lt;p&gt;Filtering stands for the feature of FFmpeg to modify the decoded frames (audio or video). Other applications might call them effects, but i’m sure there is a reason why FFmpeg calls them filters.&lt;/p&gt;
&lt;p&gt;There are two kinds of filtering supported by FFmpeg, simple and complex. In this article we’ll only discuss the complex filters, as it is a superset of the simple filters, and this way, we avoid confusion and redundant content.&lt;/p&gt;
&lt;p&gt;Simple filters are a single chain of filters between a single input and output. Complex filters can have more chains of filters, with any number of inputs and outputs.&lt;/p&gt;
&lt;p&gt;The following figure extends the previous overview image with the filtering module:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1686px) 1686px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1686&quot; height=&quot;1520&quot; src=&quot;https://img.ly/_astro/img-7-encode-decode_Z2fYIgb.webp&quot; srcset=&quot;/_astro/img-7-encode-decode_13jrWu.webp 640w, /_astro/img-7-encode-decode_1P8TzA.webp 750w, /_astro/img-7-encode-decode_ZMUCC2.webp 828w, /_astro/img-7-encode-decode_10OkPy.webp 1080w, /_astro/img-7-encode-decode_kekdC.webp 1280w, /_astro/img-7-encode-decode_KN3vN.webp 1668w, /_astro/img-7-encode-decode_Z2fYIgb.webp 1686w&quot;&gt;&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;complex filter graph&lt;/code&gt; is built from &lt;code&gt;filter chains&lt;/code&gt;, which are built from &lt;code&gt;filters&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So a single &lt;strong&gt;filter&lt;/strong&gt; does a single thing, for example, changes the &lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#volume&quot;&gt;volume&lt;/a&gt;. This filter is quite trivial, it has a single input, changes the volume, and it has a single output.&lt;/p&gt;
&lt;p&gt;For video, we could check out the &lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#scale&quot;&gt;scale&lt;/a&gt; filter, which is also quite straightforward: it has a single input, scales the incoming frames, and it has a single output too.&lt;/p&gt;
&lt;p&gt;You can &lt;strong&gt;chain&lt;/strong&gt; these filters, meaning that you connect the output of one to the input of the next one! So you can have a &lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#volume&quot;&gt;volume&lt;/a&gt; filter after an &lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#aecho&quot;&gt;echo&lt;/a&gt; filter, for example, and this way, you’ll add echo, and then you change the volume.&lt;/p&gt;
&lt;p&gt;This way, your chain will have a single input, and it will do several things with it and will output something at the end.&lt;/p&gt;
&lt;p&gt;Now, the “&lt;strong&gt;complex&lt;/strong&gt;” comes in when you have multiple chains of these filters!&lt;/p&gt;
&lt;p&gt;But before we go there, you should also know that some single filters might have multiple inputs or outputs!&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#overlay&quot;&gt;overlay&lt;/a&gt; filter puts 2 video streams above each other and will output a single video stream.&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#split&quot;&gt;split&lt;/a&gt; filter splits a single video stream into 2+ video streams (by copying).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So let’s discuss a complex example from a bird’s eye view! I have two video files, I want to put them above each other, and I want the output in two files/sizes, 720p and 1080p.&lt;/p&gt;
&lt;p&gt;Now, that’s where complex filtering will be faithful to its name: to achieve this, you’ll need several filter chains!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Chain 1: &lt;code&gt;[input1.mp4] [input2.mp4]&lt;/code&gt; → &lt;strong&gt;overlay&lt;/strong&gt; → &lt;strong&gt;split&lt;/strong&gt; → &lt;code&gt;[overlaid1] [overlaid2]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Chain 2: &lt;code&gt;[overlaid1]&lt;/code&gt; → &lt;strong&gt;scale&lt;/strong&gt; → &lt;code&gt;[720p_output]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Chain 3: &lt;code&gt;[overlaid2]&lt;/code&gt; → &lt;strong&gt;scale&lt;/strong&gt; → &lt;code&gt;[1080p_output]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you see, you can connect chains, and you can connect chains to output files. There is a rule that you can only consume a chain once, and that’s why we used split instead of the same input for chains 2 and 3.&lt;/p&gt;
&lt;p&gt;The takeaway is this: with complex filter graphs (and mapping), you can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;build individual chains of filters&lt;/li&gt;
&lt;li&gt;connect input files to filter chains&lt;/li&gt;
&lt;li&gt;connect filter chains to filter chains&lt;/li&gt;
&lt;li&gt;connect filter chains to output files&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;ffmpegs-command-line-system&quot;&gt;FFmpeg’s command line system&lt;/h2&gt;
&lt;p&gt;For those who are just joining in: please get the &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg//#example-material&quot;&gt;example assets&lt;/a&gt; if you want to test out the commands shown in this chapter!&lt;/p&gt;
&lt;h3 id=&quot;ffmpeg-cli&quot;&gt;FFmpeg CLI&lt;/h3&gt;
&lt;p&gt;Finally, we arrived at FFmpeg, and trust me, we’ll execute it quite a lot of times! Let’s see how FFmpeg’s command line options are organized, as that is the first tricky part we need to understand!&lt;/p&gt;
&lt;p&gt;FFmpeg mostly thinks about input and output files and their options together with global options. You specify input files with the “-i” flag followed by a file name. For the output file, specify it as-is without any preceding CLI (command line interface) flag.&lt;/p&gt;
&lt;h3 id=&quot;specifying-an-input-file&quot;&gt;Specifying an input file&lt;/h3&gt;
&lt;p&gt;Let’s specify just an input file:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The following image helps to understand the output:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1623px) 1623px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1623&quot; height=&quot;907&quot; src=&quot;https://img.ly/_astro/img-8-output_12XQP9.webp&quot; srcset=&quot;/_astro/img-8-output_Z1rgNnu.webp 640w, /_astro/img-8-output_Z2duH6y.webp 750w, /_astro/img-8-output_Z29hKMP.webp 828w, /_astro/img-8-output_ZaxUwk.webp 1080w, /_astro/img-8-output_Z1MW61C.webp 1280w, /_astro/img-8-output_12XQP9.webp 1623w&quot;&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;First, you get the “banner”, where you see the build information and lib versions. If you watch closely, you’ll see the compilation flags, starting with &lt;strong&gt;—&lt;/strong&gt;, e.g. —enable-shared.&lt;/li&gt;
&lt;li&gt;Then you get the same output as we have seen with ffprobe earlier.&lt;/li&gt;
&lt;li&gt;And then you get a complaint that there is no output file(s) specified. That’s fine for now.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can remove the banner here with “-hide_banner”, but for brevity’s sake I’ll not include that anymore in the commands here, and I will leave it out from the outputs too.&lt;/p&gt;
&lt;p&gt;Now, let’s get brave, and specify an output file!&lt;/p&gt;
&lt;h3 id=&quot;specifying-an-output&quot;&gt;Specifying an output&lt;/h3&gt;
&lt;p&gt;As I’ve said earlier, the output file is understood by FFmpeg as it is just a filename. But more specifically, it is after the input(s) specifications, and it is not a value of any other switches.&lt;/p&gt;
&lt;p&gt;Don’t be confused for now, but yes, FFmpeg can have as many inputs and outputs as you’d like. We’ll cover that in more detail soon!&lt;/p&gt;
&lt;p&gt;This command line specifies a single output file:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; audio_only.wav&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Before taking a look at the output, let me congratulate you! You have just converted a video file into an audio file, by keeping just the audio content!&lt;/p&gt;
&lt;p&gt;This is how you transcode! Of course, you’ll want to specify more parameters later on.&lt;/p&gt;
&lt;p&gt;So, here is the output:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1174px) 1174px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1174&quot; height=&quot;533&quot; src=&quot;https://img.ly/_astro/img-9-output_yvlsv.webp&quot; srcset=&quot;/_astro/img-9-output_ZXLK5E.webp 640w, /_astro/img-9-output_Z1dxq3z.webp 750w, /_astro/img-9-output_Z1fKoIz.webp 828w, /_astro/img-9-output_256iWk.webp 1080w, /_astro/img-9-output_yvlsv.webp 1174w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Let’s analyze it!&lt;/p&gt;
&lt;p&gt;(1) First, we have our input metadata printing, which we saw many times already.&lt;/p&gt;
&lt;p&gt;(2) Then we have something called “stream mapping”. We forced FFmpeg into a decision situation, as we specified an input file with 1 video and 2 audio streams. We said we wanted an audio output (guessed from the .wav extension). But we didn’t specify which audio stream we wanted, so let’s see what FFmpeg decided:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“&lt;strong&gt;Stream #0:2&lt;/strong&gt;” means “The first input file’s third stream” or “input file index 0’s stream with index 2.” This is the input.&lt;/li&gt;
&lt;li&gt;”&lt;strong&gt;-&gt; #0:0&lt;/strong&gt;” means the first output file’s first stream. This is the output.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg.html#Automatic-stream-selection&quot;&gt;Here&lt;/a&gt; you can learn more about how FFmpeg decide this.&lt;/li&gt;
&lt;li&gt;Later on, we’ll manually override the mapping.&lt;/li&gt;
&lt;li&gt;Summary: FFmpeg decided to convert the third stream in the input file (the ac3 5.1 audio) into the first stream of the output file.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;(3) Then we have our output metadata information. This reveals what FFmpeg will output. It usually copies most of the metadata, and here you also see the container/format information too.&lt;/p&gt;
&lt;p&gt;(4) And then we see the output summary. For example, the transcoding was 181x faster than the playback speed. Nice!&lt;/p&gt;
&lt;h3 id=&quot;understanding-the-command-line-order&quot;&gt;Understanding the command line order&lt;/h3&gt;
&lt;p&gt;Before going further, let’s understand FFmpeg’s command line arguments from a bird’s eye view!&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;https://ffmpeg.org/ffmpeg.html#Synopsis&quot;&gt;manual&lt;/a&gt;, you’ll see this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; [global_options] {[input_file_options] -i input_url} ... {[output_file_options] output_url} ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(Parts in […] are meant to be optional, and parts in {…} are meant to be specified 1 or more times.)&lt;/p&gt;
&lt;p&gt;This is the general outline of how to specify inputs, outputs, input options, output options, and global options. The order matters, but it is easy to remember: global options, inputs and outputs. Also, i/o options come BEFORE the i/o specification.&lt;/p&gt;
&lt;p&gt;Let’s put these into pseudo command line options, to understand it better:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# One inputs, one output, nothing fancy&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; input1.mp4&lt;/span&gt;&lt;span&gt; output1.wav&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Two inputs, one output&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; input1.mp4&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; input2.mp4&lt;/span&gt;&lt;span&gt; output1.wav&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Two inputs, two outputs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; input1.mp4&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; input2.mp4&lt;/span&gt;&lt;span&gt; output1.wav&lt;/span&gt;&lt;span&gt; output2.mp3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# One input, one output, with options&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; [input1 &lt;/span&gt;&lt;span&gt;options]&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; input1.mp4&lt;/span&gt;&lt;span&gt; [output2 &lt;/span&gt;&lt;span&gt;options]&lt;/span&gt;&lt;span&gt; output1.wav&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Two inputs, two outputs with options&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; [input1 &lt;/span&gt;&lt;span&gt;options]&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; input1.mp4&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;       [input2 &lt;/span&gt;&lt;span&gt;options]&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; input2.mp4&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;       [output1 &lt;/span&gt;&lt;span&gt;options]&lt;/span&gt;&lt;span&gt; output1.wav&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;       [output2 &lt;/span&gt;&lt;span&gt;options]&lt;/span&gt;&lt;span&gt; output2.mp3&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As for the global options, these are the ones you might care about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;-hide_banner&lt;/strong&gt;: To skip printing the banner.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-y&lt;/strong&gt;: To overwrite the output even if it exists.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, you can run this as many times as you want:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt; -hide_banner&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; audio_only.wav&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And it will overwrite the output and be less verbose than earlier.&lt;/p&gt;
&lt;p&gt;Without explaining the options themselves, let’s just see some real-world examples with options:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1047px) 1047px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1047&quot; height=&quot;345&quot; src=&quot;https://img.ly/_astro/img-10-cmd-order_1VUsz0.webp&quot; srcset=&quot;/_astro/img-10-cmd-order_Z24PuYm.webp 640w, /_astro/img-10-cmd-order_2rcG5A.webp 750w, /_astro/img-10-cmd-order_tI8X3.webp 828w, /_astro/img-10-cmd-order_1VUsz0.webp 1047w&quot;&gt;&lt;/p&gt;
&lt;p&gt;And here it is with two inputs and two outputs:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1296px) 1296px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1296&quot; height=&quot;381&quot; src=&quot;https://img.ly/_astro/img-11-cmd-order_1tE6MK.webp&quot; srcset=&quot;/_astro/img-11-cmd-order_Z13ghw4.webp 640w, /_astro/img-11-cmd-order_Z130DqX.webp 750w, /_astro/img-11-cmd-order_Z1YzaGd.webp 828w, /_astro/img-11-cmd-order_4yOs1.webp 1080w, /_astro/img-11-cmd-order_1CBEPs.webp 1280w, /_astro/img-11-cmd-order_1tE6MK.webp 1296w&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;mapping-files&quot;&gt;Mapping files&lt;/h3&gt;
&lt;p&gt;We saw above that this command:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; audio_only.wav&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;… will result in an audio file that contains one of the audio streams from the input video chosen by FFmpeg. This &lt;a href=&quot;https://ffmpeg.org/ffmpeg.html#Automatic-stream-selection&quot;&gt;automatic stream selection&lt;/a&gt; is usually handy when it is trivial. For example, when you have one stream as input and one output file, you don’t need to specify any mapping manually.&lt;/p&gt;
&lt;p&gt;But in cases where it is not so trivial, you are usually better off manually specifying what you really want to do.&lt;/p&gt;
&lt;p&gt;The following image summarises what our current situation is:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1770px) 1770px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1770&quot; height=&quot;918&quot; src=&quot;https://img.ly/_astro/img-12-mapping_Z1pRjg7.webp&quot; srcset=&quot;/_astro/img-12-mapping_1bbT7B.webp 640w, /_astro/img-12-mapping_2dl19K.webp 750w, /_astro/img-12-mapping_1WXOdo.webp 828w, /_astro/img-12-mapping_5YBOe.webp 1080w, /_astro/img-12-mapping_fvTdb.webp 1280w, /_astro/img-12-mapping_Z2oS17K.webp 1668w, /_astro/img-12-mapping_Z1pRjg7.webp 1770w&quot;&gt;&lt;/p&gt;
&lt;p&gt;The video stream was not matched, as the output format was an audio file (.wav). But then FFmpeg chose Stream #2, because it has more channels.&lt;/p&gt;
&lt;p&gt;So what if we’d like to get the stereo track instead? That is where mapping comes in! The mapping is a parameter of the OUTPUT file. Therefore the mapping arguments should come right before our output file definition!&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; -map&lt;/span&gt;&lt;span&gt; 0:1&lt;/span&gt;&lt;span&gt; stereo_audio_only.wav&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The argument &lt;strong&gt;-map 0:1&lt;/strong&gt; means, that in the &lt;code&gt;output&lt;/code&gt; (since we specify it as an output option) we’d like to have &lt;code&gt;Input #0&lt;/code&gt;’s (the first input file) &lt;code&gt;Stream #1&lt;/code&gt;!&lt;/p&gt;
&lt;p&gt;Let’s see the relevant parts from the output!&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Input #0, mov,mp4,m4a,3gp,3g2,mj2, from &apos;bbb_sunflower_1080p_60fps_normal.mp4&apos;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[...]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Stream mapping:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Stream #0:1 -&gt; #0:0 (mp3 (mp3float) -&gt; pcm_s16le (native))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[...]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Output #0, wav, to &apos;stereo_audio_only.wav&apos;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Metadata:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[...]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    Stream #0:0(und): [...] stereo [...]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The “Stream #0:1 -&gt; #0:0” part means that we have successfully overridden the mapping, to get the mp3 stream (0:1) into our output! Also, the output metadata reveals that we’ll get a stereo result instead of the 5.1 earlier.&lt;/p&gt;
&lt;h3 id=&quot;multiple-outputs&quot;&gt;Multiple outputs&lt;/h3&gt;
&lt;p&gt;You can have multiple outputs from a single input, let’s see when that might be useful!&lt;/p&gt;
&lt;p&gt;Let’s say, we want to extract BOTH audio streams into two separate WAV files! It is super easy:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; -map&lt;/span&gt;&lt;span&gt; 0:1&lt;/span&gt;&lt;span&gt; stereo_audio_only.wav&lt;/span&gt;&lt;span&gt; -map&lt;/span&gt;&lt;span&gt; 0:2&lt;/span&gt;&lt;span&gt; ac3_audio_only.wav&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;See? I have just specified two output files with two mapping specifications! Also, I have sneaked in the “-y” to have it overwrite our previous file!&lt;/p&gt;
&lt;p&gt;Let’s check out the relevant parts of the output!&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Input #0, mov,mp4,m4a,3gp,3g2,mj2, from &apos;bbb_sunflower_1080p_60fps_normal.mp4&apos;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[...]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Stream mapping:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Stream #0:1 -&gt; #0:0 (mp3 (mp3float) -&gt; pcm_s16le (native))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Stream #0:2 -&gt; #1:0 (ac3 (native) -&gt; pcm_s16le (native))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[...]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Output #0, wav, to &apos;stereo_audio_only.wav&apos;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    Stream #0:0(und): [...] stereo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[...]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Output #1, wav, to &apos;ac3_audio_only.wav&apos;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    Stream #1:0(und): Audio: [...] 5.1(side)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the mapping reveals two lines, as we have two outputs! And indeed, you’ll get two .wav files as the output, one is stereo, and one is 5.1!&lt;/p&gt;
&lt;p&gt;There might be several other reasons why you’d want to get multiple outputs. Let’s briefly check out a few!&lt;/p&gt;
&lt;p&gt;Different formats:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; stereo_audio_only.wav&lt;/span&gt;&lt;span&gt;  stereo_audio_only.mp3&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Wow, did you catch that? We just created a WAV and an mp3 in a single command line! I’ve reverted to the automatic stream selection for brevity’s sake.&lt;/p&gt;
&lt;p&gt;A bit closer to real-life needs, you might want different output qualities:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt;  -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt;  \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-map &lt;/span&gt;&lt;span&gt;0:1&lt;/span&gt;&lt;span&gt; -b:a&lt;/span&gt;&lt;span&gt; 320k&lt;/span&gt;&lt;span&gt; stereo_audio_only_high_quality.mp3&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-map &lt;/span&gt;&lt;span&gt;0:1&lt;/span&gt;&lt;span&gt; -b:a&lt;/span&gt;&lt;span&gt; 64k&lt;/span&gt;&lt;span&gt;  stereo_audio_only_low_quality.mp3&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here &lt;strong&gt;-b:a 320k&lt;/strong&gt; means “&lt;strong&gt;b&lt;/strong&gt;itrate of &lt;strong&gt;a&lt;/strong&gt;udio should be around &lt;strong&gt;320 kbit/sec&lt;/strong&gt;”. So I have requested FFmpeg to make two mp3s for me, from the stereo stream of the input.&lt;/p&gt;
&lt;p&gt;Checking on the files, this is what we got:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; 25Mb stereo_audio_only_high_quality.mp3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;4,9Mb stereo_audio_only_low_quality.mp3&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One more common reason for having multiple outputs or using mapping is when we introduce filters into our pipeline, but that will be discussed later!&lt;/p&gt;
&lt;p&gt;Now you understand the foundations of how to communicate your basic requirements to FFmpeg via its command line! Great job! Now we can dive even deepert.&lt;/p&gt;
&lt;h2 id=&quot;hands-on-with-ffmpeg&quot;&gt;Hands-on with FFmpeg&lt;/h2&gt;
&lt;p&gt;In this section, we will discover and even try out some common features of FFmpeg!&lt;/p&gt;
&lt;p&gt;For those who are just joining in: please get the &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg//#example-material&quot;&gt;example assets&lt;/a&gt; if you want to test out the commands shown in this chapter!&lt;/p&gt;
&lt;h3 id=&quot;inputs-1&quot;&gt;Inputs&lt;/h3&gt;
&lt;p&gt;Let’s see the common ways FFmpeg is fed with different data!&lt;/p&gt;
&lt;h3 id=&quot;file&quot;&gt;File&lt;/h3&gt;
&lt;p&gt;Of course, you have already seen that if you have a local file on your filesystem, FFmpeg is happy to read it!&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; -map&lt;/span&gt;&lt;span&gt; 0:1&lt;/span&gt;&lt;span&gt; stereo_audio_only.wav&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command which is exactly the same as one of our previous ones just reads a local file. Really, that’s it.&lt;/p&gt;
&lt;h3 id=&quot;network&quot;&gt;Network&lt;/h3&gt;
&lt;p&gt;Did you know, that FFmpeg can open a file directly on the network?!&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -t&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; bbb_first_5_seconds.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The command above opens the file directly from the network and saves the first 5 seconds into a local file!&lt;/p&gt;
&lt;p&gt;I wanted to spare bandwidth for these awesome guys over renderfarming.net, so I added the duration flag: &lt;strong&gt;-t 5&lt;/strong&gt;. FFmpeg doesn’t even download the full video for this operation. Isn’t that wonderful?!&lt;/p&gt;
&lt;h3 id=&quot;webcam&quot;&gt;Webcam&lt;/h3&gt;
&lt;p&gt;FFmpeg can also open your webcam!&lt;/p&gt;
&lt;p&gt;This is an example command for Linux:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -f&lt;/span&gt;&lt;span&gt; v4l2&lt;/span&gt;&lt;span&gt; -framerate&lt;/span&gt;&lt;span&gt; 25&lt;/span&gt;&lt;span&gt; -video_size&lt;/span&gt;&lt;span&gt; 640x480&lt;/span&gt;&lt;span&gt; -t&lt;/span&gt;&lt;span&gt; 10&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; /dev/video0&lt;/span&gt;&lt;span&gt; 10seconds_of_webcam.webm&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This would record 10 seconds of your webcam!&lt;/p&gt;
&lt;p&gt;Accessing the webcam happens differently on different platforms. Also specifying parameters is different for each platform, so for this reason, if you’d like to access your webcam with FFmpeg, please refer to the documentation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://trac.ffmpeg.org/wiki/Capture/Webcam#Linux&quot;&gt;Linux&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://trac.ffmpeg.org/wiki/Capture/Webcam#Windows&quot;&gt;Windows&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://trac.ffmpeg.org/wiki/Capture/Webcam#OSX&quot;&gt;OS X&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;microphone&quot;&gt;Microphone&lt;/h3&gt;
&lt;p&gt;Let’s record some audio directly from your microphone!&lt;/p&gt;
&lt;p&gt;List microphones:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;arecord&lt;/span&gt;&lt;span&gt; -l&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Start 10 seconds of recording:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -f&lt;/span&gt;&lt;span&gt; alsa&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; hw:0,0&lt;/span&gt;&lt;span&gt; -t&lt;/span&gt;&lt;span&gt; 10&lt;/span&gt;&lt;span&gt; out.wav&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command was meant to work on Linux, but you can check out how to do that on &lt;a href=&quot;https://trac.ffmpeg.org/wiki/Capture/Desktop#Windows&quot;&gt;Microsoft Windows&lt;/a&gt; or &lt;a href=&quot;https://trac.ffmpeg.org/wiki/Capture/Desktop#macOS&quot;&gt;macOS&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;pipe&quot;&gt;Pipe&lt;/h3&gt;
&lt;p&gt;Finally, FFmpeg can read from a pipe, and also output to a pipe.&lt;/p&gt;
&lt;p&gt;On Linux, you could do something like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;cat&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt; ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; -&lt;/span&gt;&lt;span&gt; -f&lt;/span&gt;&lt;span&gt; wav&lt;/span&gt;&lt;span&gt; pipe:1&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt; pv&lt;/span&gt;&lt;span&gt; &gt;&lt;/span&gt;&lt;span&gt; output.wav&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Alternative, without pv:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;cat&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt; ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; -&lt;/span&gt;&lt;span&gt; -f&lt;/span&gt;&lt;span&gt; wav&lt;/span&gt;&lt;span&gt; pipe:1&lt;/span&gt;&lt;span&gt; &gt;&lt;/span&gt;&lt;span&gt; output.wav&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command would use the &lt;strong&gt;cat&lt;/strong&gt; program to simply read in the video file and output it to its standard output. Then this output is piped INTO FFmpeg, through its standard input. The combination “&lt;strong&gt;-i -&lt;/strong&gt;” means “read from standard input”. By the way, standard input would be your keyboard otherwise, if we wouldn’t use any redirection here.&lt;/p&gt;
&lt;p&gt;Then we specify the required output format for FFmpeg, with “&lt;strong&gt;-f wav&lt;/strong&gt;”. This is needed because now we’ll have no output file name, and FFmpeg will not be able to guess the format. Then we specify “&lt;strong&gt;pipe:1&lt;/strong&gt;” as an output, meaning we’d like FFmpeg to output to its standard output.&lt;/p&gt;
&lt;p&gt;From then, we pipe the data into a program called “&lt;strong&gt;pv&lt;/strong&gt;”, it is just a metering tool, that dumps information on the throughput (from its stdin to its stdout). Finally, we redirect pv’s output into a WAV file.&lt;/p&gt;
&lt;p&gt;You might ask why we’d want to do that, why we talk about this. Piping can be useful if you build a complex pipeline from different programs or if you want to spare reading and writing to a local file.&lt;/p&gt;
&lt;p&gt;For example, the node package &lt;a href=&quot;https://www.npmjs.com/package/fluent-ffmpeg&quot;&gt;fluent-ffmpeg&lt;/a&gt; can leverage this functionality by supplying input and output streams. For example, you can read from an S3 bucket and write to one directly.&lt;/p&gt;
&lt;p&gt;But be warned, hell is awaiting you on that road. No kidding. You need to research the limitations of this technique. For example, many formats can not be streamed in this manner, as they need random access to the output data to write the indices at the beginning of the file after processing.&lt;/p&gt;
&lt;h3 id=&quot;outputs-1&quot;&gt;Outputs&lt;/h3&gt;
&lt;p&gt;FFmpeg can output into many protocols, from local file storage and ftp to message queue protocols all the way to streaming protocols.&lt;/p&gt;
&lt;p&gt;For more information, check out the documentation &lt;a href=&quot;https://ffmpeg.org/ffmpeg-protocols.html#Protocols&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;transcoding-audio-with-ffmpeg&quot;&gt;Transcoding audio with FFmpeg&lt;/h2&gt;
&lt;p&gt;In this chapter, we’ll be going to see how to transcode into audio with FFmpeg!&lt;/p&gt;
&lt;p&gt;The general formula is:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; {input&lt;/span&gt;&lt;span&gt; audio&lt;/span&gt;&lt;span&gt; or&lt;/span&gt;&lt;span&gt; video&lt;/span&gt;&lt;span&gt; file&lt;/span&gt;&lt;span&gt; with&lt;/span&gt;&lt;span&gt; audio}&lt;/span&gt;&lt;span&gt; [output &lt;/span&gt;&lt;span&gt;options]&lt;/span&gt;&lt;span&gt; output_audio.ext&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;choosing-a-format&quot;&gt;Choosing a format&lt;/h3&gt;
&lt;p&gt;FFmpeg is quite smart, and by the extension, it can determine which codec to use. If you specify “audio.wav” or “audio.mp3” for example, FFmpeg will use the appropriate codec to do the encoding.&lt;/p&gt;
&lt;p&gt;It is perfectly guessing most of the time. But if you want to specify the format manually, then the “-f” flag is your friend.&lt;/p&gt;
&lt;p&gt;For this, you might want to consult the list of formats:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -formats&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, these three commands will do exactly the same, but the last two requires the &lt;strong&gt;-f&lt;/strong&gt; flag.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Output codec is determined from the extension&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_audio.wav&lt;/span&gt;&lt;span&gt; bbb_audio.mp3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# No extension in the filename&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_audio.wav&lt;/span&gt;&lt;span&gt; -f&lt;/span&gt;&lt;span&gt; mp3&lt;/span&gt;&lt;span&gt; bbb_audio&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Piped output therefore no filename, so no extension to use for guessing&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_audio.wav&lt;/span&gt;&lt;span&gt; -f&lt;/span&gt;&lt;span&gt; mp3&lt;/span&gt;&lt;span&gt; pipe:1&lt;/span&gt;&lt;span&gt; &gt;&lt;/span&gt;&lt;span&gt; bbb_audio&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;setting-the-bitrate&quot;&gt;Setting the bitrate&lt;/h3&gt;
&lt;p&gt;In most cases. you want to specify the target bitrate you expect from your codec to output. If you are unsure what bitrate is, please read this article’s &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg//#bitrate&quot;&gt;audio bitrate&lt;/a&gt; section.&lt;/p&gt;
&lt;p&gt;To specify the audio bitrate, use the “&lt;strong&gt;-b:a&lt;/strong&gt;” option with a corresponding value, e.g.:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;-b:a 320k&lt;/strong&gt;: For the mp3 codec this is considered high quality.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-b:a 128k&lt;/strong&gt;: Lower quality.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-b:a 64k&lt;/strong&gt;: Low quality.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_audio.wav&lt;/span&gt;&lt;span&gt; -b:a&lt;/span&gt;&lt;span&gt; 320k&lt;/span&gt;&lt;span&gt; bbb_audio_320k.mp3&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;setting-the-sample-rate&quot;&gt;Setting the sample rate&lt;/h3&gt;
&lt;p&gt;You may want to specify the sample rate to ensure quality or low output file size. Half the sample rate could mean half the output file size. If you are unsure what the sample rate is, please read the “&lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg//#sampling-rate&quot;&gt;audio sample rate&lt;/a&gt;” section of this article.&lt;/p&gt;
&lt;p&gt;To specify the audio sample rate, use the “&lt;strong&gt;-ar&lt;/strong&gt;” option with a corresponding value, e.g.:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;-ar 48000&lt;/strong&gt;: For high quality.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-ar 44100&lt;/strong&gt;: For CD quality (still high).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-ar 22500&lt;/strong&gt;: A bit of a compromise, not recommended for music, but for speech, it might be enough.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-ar 8000&lt;/strong&gt;: Low quality, e.g. if you only want “understandable” speech.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_audio.wav&lt;/span&gt;&lt;span&gt; -ar&lt;/span&gt;&lt;span&gt; 44100&lt;/span&gt;&lt;span&gt; bbb_audio_44100khz.mp3&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;setting-the-channel-count&quot;&gt;Setting the channel count&lt;/h3&gt;
&lt;p&gt;Setting the channel count can be useful, for example, if you have a stereo recording of a single person’s speech. In that case, you might be content with just a mono output half the size of the original recording.&lt;/p&gt;
&lt;p&gt;If you are unsure what an audio channel is, please read the “&lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg//#channels&quot;&gt;audio channels&lt;/a&gt;” section of this article.&lt;/p&gt;
&lt;p&gt;To specify the channel count use the “&lt;strong&gt;-ac&lt;/strong&gt;” option with a corresponding value, e.g.:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;-ac 1&lt;/strong&gt;: For mono&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-ac 2&lt;/strong&gt;: For stereo&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-ac 6&lt;/strong&gt;: For 5.1&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_audio.wav&lt;/span&gt;&lt;span&gt; -ac&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt; bbb_audio_mono.mp3&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;complete-command-line-for-converting-audio-with-ffmpeg&quot;&gt;Complete command line for converting audio with FFmpeg&lt;/h3&gt;
&lt;p&gt;This is how you produce a high-quality output:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Convert wav to mp3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_audio.wav&lt;/span&gt;&lt;span&gt; -ac&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;span&gt; -ar&lt;/span&gt;&lt;span&gt; 44100&lt;/span&gt;&lt;span&gt; -b:a&lt;/span&gt;&lt;span&gt; 320k&lt;/span&gt;&lt;span&gt; bbb_audio_hqfull.mp3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Convert wav to m4a (aac)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_audio.wav&lt;/span&gt;&lt;span&gt; -ac&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;span&gt; -ar&lt;/span&gt;&lt;span&gt; 44100&lt;/span&gt;&lt;span&gt; -b:a&lt;/span&gt;&lt;span&gt; 320k&lt;/span&gt;&lt;span&gt; bbb_audio_hqfull.m4a&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Convert wav to ogg (vorbis)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_audio.wav&lt;/span&gt;&lt;span&gt; -ac&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;span&gt; -ar&lt;/span&gt;&lt;span&gt; 44100&lt;/span&gt;&lt;span&gt; -b:a&lt;/span&gt;&lt;span&gt; 320k&lt;/span&gt;&lt;span&gt; bbb_audio_hqfull.ogg&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check out &lt;a href=&quot;https://trac.ffmpeg.org/wiki/Encode/HighQualityAudio&quot;&gt;this&lt;/a&gt; documentation about good quality audio transcoding too!.&lt;/p&gt;
&lt;h3 id=&quot;lossless-formats&quot;&gt;Lossless formats&lt;/h3&gt;
&lt;p&gt;If you want to convert audio into a lossless format, here are a few choices for you:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Convert to flac (Free Lossless Audio Codec)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_audio.wav&lt;/span&gt;&lt;span&gt; -compression_level&lt;/span&gt;&lt;span&gt; 12&lt;/span&gt;&lt;span&gt; bbb_audio_lossless_12.flac&lt;/span&gt;&lt;span&gt; # Best compression, slowest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_audio.wav&lt;/span&gt;&lt;span&gt; -compression_level&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;span&gt; bbb_audio_lossless_5.flac&lt;/span&gt;&lt;span&gt;   # Default&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_audio.wav&lt;/span&gt;&lt;span&gt; -compression_level&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt; bbb_audio_lossless_0.flac&lt;/span&gt;&lt;span&gt;   # Least compression, fastest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Convert to wav&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;cp&lt;/span&gt;&lt;span&gt; bbb_audio.wav&lt;/span&gt;&lt;span&gt; bbb_audio_lossless.wav&lt;/span&gt;&lt;span&gt; # Just kidding:)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Convert to wav&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; any_audio.ext&lt;/span&gt;&lt;span&gt; bbb_audio_lossless.wav&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s good if you know that flac results in a smaller file than WAV, as WAV doesn’t actually compress by default:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;117M bbb_audio.wav&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;52M  bbb_audio_lossless_0.flac&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;45M  bbb_audio_lossless_5.flac&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;43M  bbb_audio_lossless_12.flac&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;WAV is generally thought of as a lossless format, but keep in mind that the WAV container can contain lossy content too, but by default FFmpeg uses the pcm_s16le format, which is the 16 bit PCM, that could be understood as lossless.&lt;/p&gt;
&lt;p&gt;Learn more &lt;a href=&quot;https://en.wikipedia.org/wiki/WAV#Comparison_of_coding_schemes&quot;&gt;here&lt;/a&gt; and &lt;a href=&quot;https://trac.ffmpeg.org/wiki/audio%20types&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;transcoding-video-with-ffmpeg&quot;&gt;Transcoding video with FFmpeg&lt;/h2&gt;
&lt;p&gt;In this chapter, we’ll be going to see how to transcode a video file into the two most common formats!&lt;/p&gt;
&lt;h3 id=&quot;converting-to-h264&quot;&gt;Converting to H.264&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Advanced_Video_Coding&quot;&gt;H264&lt;/a&gt; is one of the most popular video codecs. Most devices, browsers and video players understand how to play it. It is efficient in storing video content, but as with most advanced video codecs, it is a resource intensive-process to encode and decode.&lt;/p&gt;
&lt;p&gt;A complete command line for a high-quality H.264 transcoding with high-quality AAC audio is the following:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-c:v &lt;/span&gt;&lt;span&gt;libx264&lt;/span&gt;&lt;span&gt; -preset&lt;/span&gt;&lt;span&gt; slow&lt;/span&gt;&lt;span&gt; -crf&lt;/span&gt;&lt;span&gt; 22&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-profile:v &lt;/span&gt;&lt;span&gt;main&lt;/span&gt;&lt;span&gt; -g&lt;/span&gt;&lt;span&gt; 250&lt;/span&gt;&lt;span&gt; -pix_fmt&lt;/span&gt;&lt;span&gt; yuv420p&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-map &lt;/span&gt;&lt;span&gt;0:0&lt;/span&gt;&lt;span&gt; -map&lt;/span&gt;&lt;span&gt; 0:1&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-acodec &lt;/span&gt;&lt;span&gt;aac&lt;/span&gt;&lt;span&gt; -ar&lt;/span&gt;&lt;span&gt; 44100&lt;/span&gt;&lt;span&gt; -b:a&lt;/span&gt;&lt;span&gt; 320k&lt;/span&gt;&lt;span&gt; bbb_transcoded_h264_HQ.mov&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make sure to understand this command and to customize it to match your needs.&lt;/p&gt;
&lt;p&gt;To help you do that, let’s dissect this command!&lt;/p&gt;
&lt;p&gt;Global options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;-y&lt;/strong&gt;: Overwrite the output.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Input options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;-i bbb_sunflower_1080p_60fps_normal.mp4&lt;/strong&gt;: The input file.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Output options:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;-c:v libx264&lt;/strong&gt;: Set the codec to libx264.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;-preset slow&lt;/strong&gt;: libx264 has a lot of variables that you can be tune, and most of them balance the coding speed and the resulting file size. To make your life easier, there are &lt;a href=&quot;https://trac.ffmpeg.org/wiki/Encode/H.264#Preset&quot;&gt;presets&lt;/a&gt; by which you can easily declare what you need: small size or speed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;-crf 22&lt;/strong&gt;: This is the constant rate factor, the main option for setting image quality. It is a number between 0-51, where 0 is lossless, and 51 is the worst quality. Generally, you want something between 17 and 28. This is the option to tune the balance between image quality and file size. Check my comparison video &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg//#comparing-crf-values-with-h264-and-h265&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;-profile:v main -g 250 -pix_fmt yuv420p&lt;/strong&gt;: These are advanced options, guaranteeing you a quite backward compatible result. (See &lt;a href=&quot;https://ffmpeg.org/ffmpeg-codecs.html#Options-26&quot;&gt;this&lt;/a&gt;, &lt;a href=&quot;https://trac.ffmpeg.org/wiki/Encode/H.264#Profile&quot;&gt;this&lt;/a&gt;, and &lt;a href=&quot;https://ffmpeg.org/ffmpeg.html#Advanced-Video-options&quot;&gt;this&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;-map 0:0 -map 0:1&lt;/strong&gt;: You might not need this: these options are selecting the correct video and audio streams. &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg//#ffprobe&quot;&gt;In our case&lt;/a&gt;, we have two audio streams, and we need the stereo one to avoid some issues with our aac stream.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;-acodec aac&lt;/strong&gt;: Select the AAC (Advanced Audio Coding) &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg//#audio-codecs&quot;&gt;codec&lt;/a&gt; for the audio in the output. We need to be more specific than just &lt;strong&gt;-f&lt;/strong&gt; for the format. We need to specify the audio codec here manually.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;-ar 44100&lt;/strong&gt;: Set the audio &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg//#sampling-rate&quot;&gt;sampling rate&lt;/a&gt; (learn more about that in previous chapters of this article).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;-b:a 320k&lt;/strong&gt;: Set the audio &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg//#bitrate&quot;&gt;bitrate&lt;/a&gt; (learn more about that in previous chapters of this article).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30seconds_of_bb.mkv&lt;/strong&gt;: The output file name. All the options since the last -i (or the last output file) considered to be a modifier for this output.&lt;/p&gt;
&lt;p&gt;Let’s see the output:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Input #0, mov,mp4,m4a,3gp,3g2,mj2, from &apos;bbb_sunflower_1080p_60fps_normal.mp4&apos;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[...]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Stream mapping:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Stream #0:0 -&gt; #0:0 (h264 (native) -&gt; h264 (libx264))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Stream #0:1 -&gt; #0:1 (mp3 (mp3float) -&gt; aac (native))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[...]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Output #0, mov, to &apos;bbb_transcoded_h264_HQ.mov&apos;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    Stream #0:0(und): Video: h264 (libx264) (avc1 / 0x31637661), yuv420p(progressive), 1920x1080 [SAR 1:1 DAR 16:9], q=-1--1, 60 fps, 15360 tbn, 60 tbc (default)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, 5.1(side), fltp, 320 kb/s (default)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[...]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;frame=38074 fps= 35 q=-1.0 Lsize=  324855kB time=00:10:34.51 bitrate=4194.1kbits/s dup=2 drop=0 speed=0.58x&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From this, we understand that FFmpeg chose the mp3 stream from the input file because we told it to do so. (Remember, it has two audio streams in it, a stereo mp3 and a 5.1 ac3.) We also see that my machine could transcode with 35fps (0.58 times the playback speed), and our settings resulted in an average video bitrate of 4200 kbit/s.&lt;/p&gt;
&lt;p&gt;The video bitrate is an interesting question in this mode. With the CRF option, we specify the “constant visual quality” we want. To reach a constant visual quality, the encoder works hard to guess how much it can compress certain parts of every frame, and the result of that guess defines the final average video bitrate.&lt;/p&gt;
&lt;p&gt;If you want even better results with H.264, and you can afford a bit more processing time and a bit more complicated process, check out the &lt;a href=&quot;https://trac.ffmpeg.org/wiki/Encode/H.264#twopass&quot;&gt;2-pass encoding&lt;/a&gt; instead of the constant rate factor method introduced above.&lt;/p&gt;
&lt;p&gt;To learn more about these two different rate control methods, read the awesome &lt;a href=&quot;https://slhck.info/video/2017/03/01/rate-control.html&quot;&gt;Understanding Rate Control Modes&lt;/a&gt; article. And to learn more about the intricacies of H.264 encoding, check out the &lt;a href=&quot;https://trac.ffmpeg.org/wiki/Encode/H.264&quot;&gt;H264 encoding guide&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Finally, &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg//#comparing-crf-values-with-h264-and-h265&quot;&gt;later on&lt;/a&gt;, I will show you a comparison video that shows how different CRF values perform!&lt;/p&gt;
&lt;h3 id=&quot;converting-to-h265&quot;&gt;Converting to H.265&lt;/h3&gt;
&lt;p&gt;H.265 is the successor of H.264, according to the &lt;a href=&quot;https://trac.ffmpeg.org/wiki/Encode/H.265&quot;&gt;official FFmpeg manual&lt;/a&gt;, it offers 25-50% bitrate savings while retaining the same visual quality.&lt;/p&gt;
&lt;p&gt;A complete command line for a high-quality H.265 transcoding with high-quality AAC audio is the following:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-c:v &lt;/span&gt;&lt;span&gt;libx265&lt;/span&gt;&lt;span&gt; -preset&lt;/span&gt;&lt;span&gt; slow&lt;/span&gt;&lt;span&gt; -crf&lt;/span&gt;&lt;span&gt; 27&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-profile:v &lt;/span&gt;&lt;span&gt;main&lt;/span&gt;&lt;span&gt; -g&lt;/span&gt;&lt;span&gt; 250&lt;/span&gt;&lt;span&gt; -pix_fmt&lt;/span&gt;&lt;span&gt; yuv420p&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-map &lt;/span&gt;&lt;span&gt;0:0&lt;/span&gt;&lt;span&gt; -map&lt;/span&gt;&lt;span&gt; 0:1&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-acodec &lt;/span&gt;&lt;span&gt;aac&lt;/span&gt;&lt;span&gt; -ar&lt;/span&gt;&lt;span&gt; 44100&lt;/span&gt;&lt;span&gt; -b:a&lt;/span&gt;&lt;span&gt; 320k&lt;/span&gt;&lt;span&gt; bbb_transcoded_h265_HQ.mov&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the result is:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;encoded 38074 frames in 3384.84s (11.25 fps), 1720.32 kb/s, Avg QP:35.29&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;H.265 also has multiple rate control algorithms, I used the CRF method here. If you want to use a different rate control algorithm, then you may check out the &lt;a href=&quot;https://trac.ffmpeg.org/wiki/Encode/H.265&quot;&gt;H.265 encoding guide&lt;/a&gt;. Also, check out the next section, where I’ll reveal how different CRF values perform!&lt;/p&gt;
&lt;p&gt;This command is almost the same as what we used in the &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg//#converting-to-h264&quot;&gt;H.264 example&lt;/a&gt; above, so please refer to that section to understand the arguments.&lt;/p&gt;
&lt;p&gt;If we compare H.264 and H.265 with our commands above, taking into account this 10-minute long video on my system, these are the results:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;H.264 is 3 times faster (35 fps vs 11 fps)&lt;/li&gt;
&lt;li&gt;H.264 produces a 2 times larger file (318 mb vs 156 mb)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;comparing-crf-values-with-h264-and-h265&quot;&gt;Comparing CRF values with H.264 and H.265&lt;/h3&gt;
&lt;p&gt;I have created a video for your convenience, that shows the different crf values in action. The selected frame had some movement on it with the leaves in the bunny’s hand. Movement is important with video codecs, as usually that’s where quality losses are first visible.&lt;/p&gt;
&lt;p&gt;This video shows how the different CRF values perform, from 0-51 with the H.264 and H.265 formats!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/vid-1-comparison-264-265.mov&quot;&gt;H.264 &amp;#x26; H.265 CRF comparison video&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;(Can you guess which program I was using to make this?:))&lt;/p&gt;
&lt;h2 id=&quot;basic-editing-with-ffmpeg&quot;&gt;Basic editing with FFmpeg&lt;/h2&gt;
&lt;p&gt;In this section, we’ll achieve basic editing tasks by using FFmpeg only!&lt;/p&gt;
&lt;p&gt;We’ll just get a basic mp4 with default settings in these examples to keep things simple. But to encode the result in a proper, high quality way, please check the earlier sections where we learned how to encode into H.264 and H.265!&lt;/p&gt;
&lt;h3 id=&quot;trimming-from-the-beginning-of-the-clip&quot;&gt;Trimming from the beginning of the clip&lt;/h3&gt;
&lt;p&gt;It is possible to specify an in-point for a media file. By doing that, you essentially cut off the specified amount from the beginning of the input file. Therefore, FFmpeg will skip the first part of the file and only transcode the remainder!&lt;/p&gt;
&lt;p&gt;For this, you need the “&lt;strong&gt;-ss&lt;/strong&gt;” flag! The value can be specified in seconds (5 or 5.2) or as a timestamp (HOURS:MM:SS.MILLISECONDS).&lt;/p&gt;
&lt;p&gt;To get the outro only, we could seek all the way to the end of the video! (It is 00:10:34.53 or 635 seconds long!)&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Get&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# 635 - 4 = 631&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt; -ss&lt;/span&gt;&lt;span&gt; 631&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; last_4_seconds.mp4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# 00:10:34.53 - 4 = 00:10:30.53&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt; -ss&lt;/span&gt;&lt;span&gt; 00:10:30.53&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; last_4_seconds.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Seeking can be a bit tricky, so you may want to learn more about seeking &lt;a href=&quot;https://trac.ffmpeg.org/wiki/Seeking&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;trimming-from-the-end-of-the-clip&quot;&gt;Trimming from the end of the clip&lt;/h3&gt;
&lt;p&gt;You can also set an out-point for an input file, therefore shortening it. There are two options for this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;-t&lt;/strong&gt;: This sets the duration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-to&lt;/strong&gt;: This sets the timestamp where the input video should stop.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These two are mutually exclusive, and also they do the same if no -ss is specified. The value can be specified in seconds (5 or 5.2) or as a timestamp (HOURS:MM:SS.MILLISECONDS).&lt;/p&gt;
&lt;p&gt;Let’s experiment with them!&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# &quot;Get 30 seconds of the input.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt; -t&lt;/span&gt;&lt;span&gt; 30&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; first_30_seconds.mp4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt; -t&lt;/span&gt;&lt;span&gt; 00:00:30.0&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; first_30_seconds.mp4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# &quot;Get everything until the content&apos;s 30th second.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt; -to&lt;/span&gt;&lt;span&gt; 30&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; first_30_seconds.mp4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt; -to&lt;/span&gt;&lt;span&gt; 00:00:30.0&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; first_30_seconds.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All four above commands result in exactly the same video. (For nerds: even the md5sum is the same.)&lt;/p&gt;
&lt;p&gt;But let’s see how they perform when we introduce seeking!&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# &quot;Seek to the 10th second and get me 30 seconds of the input.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt; -ss&lt;/span&gt;&lt;span&gt; 10&lt;/span&gt;&lt;span&gt; -t&lt;/span&gt;&lt;span&gt; 30&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; part_between_10_and_40.mp4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# &quot;Seek to the 10th second and get the content until the 30th second.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt; -ss&lt;/span&gt;&lt;span&gt; 10&lt;/span&gt;&lt;span&gt; -to&lt;/span&gt;&lt;span&gt; 30&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; part_between_10_and_30.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first command will result in a 30 second long video, while the second command will be 20 seconds long only!&lt;/p&gt;
&lt;p&gt;The figure below shows the difference:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 2000px) 2000px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;2000&quot; height=&quot;521&quot; src=&quot;https://img.ly/_astro/img-13-trimming_k6wLr.webp&quot; srcset=&quot;/_astro/img-13-trimming_Z151890.webp 640w, /_astro/img-13-trimming_2j0cm0.webp 750w, /_astro/img-13-trimming_DVKAT.webp 828w, /_astro/img-13-trimming_Z1NCTy9.webp 1080w, /_astro/img-13-trimming_Z1Fq4uO.webp 1280w, /_astro/img-13-trimming_Z7BfOo.webp 1668w, /_astro/img-13-trimming_k6wLr.webp 2000w&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;editing-without-reencoding&quot;&gt;Editing without reencoding&lt;/h3&gt;
&lt;p&gt;FFmpeg can do something I’m not aware of in any other popular NLE: it can edit videos without reencoding them!&lt;/p&gt;
&lt;p&gt;The usual &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg//#ffmpeg-concepts&quot;&gt;workflow&lt;/a&gt; is to decode the data frames (a/v) into memory, modify them as much as we like and then encode them into a new video file. The problem with this is that unless you work with raw or lossless codecs, you’ll lose some quality in the process. Another issue with this approach is that it is computationally intensive.&lt;/p&gt;
&lt;p&gt;For certain operations, you can configure FFmpeg, to keep the data frames intact, and this way, you can avoid decoding and encoding them! This is incredibly faster than regular transcoding, usually hundreds of times faster.&lt;/p&gt;
&lt;p&gt;The “certain operations” are those that don’t need to modify the data frames themselves. For example, you can cut and trim this way. Also, you can manipulate streams while keeping others, like you can replace the audio track without touching the video frames.&lt;/p&gt;
&lt;p&gt;All this is a bit of magic, and there are caveats you need to prepare for, but it is good if you know about this, as it is often handy!&lt;/p&gt;
&lt;p&gt;The trick lies in two options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;-c:v copy&lt;/strong&gt;: The “copy” video codec&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-c:a copy&lt;/strong&gt;: The “copy” audio codec&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let’s see a few examples!&lt;/p&gt;
&lt;h4 id=&quot;remove-audio-while-keeping-the-video-without-reencoding&quot;&gt;Remove audio while keeping the video without reencoding&lt;/h4&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; -c:v&lt;/span&gt;&lt;span&gt; copy&lt;/span&gt;&lt;span&gt; -an&lt;/span&gt;&lt;span&gt; copied_video_only.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we used the “&lt;strong&gt;-an&lt;/strong&gt;” option, which removes all audio streams. I remembered it as “&lt;strong&gt;a&lt;/strong&gt;udio &lt;strong&gt;n&lt;/strong&gt;o”, but that is just my mnemonic:)&lt;/p&gt;
&lt;p&gt;Let’s see how fast it was:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;frame=38072 fps=20950 q=-1.0 Lsize=  310340kB time=00:10:34.51 bitrate=4006.7kbits/s speed= 349x&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So It processed the whole 10 minutes of video in 2 seconds, 349x faster than playback, with 20950 fps!&lt;/p&gt;
&lt;h4 id=&quot;remove-video-while-keeping-the-audio-without-reencoding&quot;&gt;Remove video while keeping the audio without reencoding&lt;/h4&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; -c:a&lt;/span&gt;&lt;span&gt; copy&lt;/span&gt;&lt;span&gt; -vn&lt;/span&gt;&lt;span&gt; copied_audio_only.wav&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we used the “&lt;strong&gt;-vn&lt;/strong&gt;” option, which removes all video streams. I remembered it as “&lt;strong&gt;v&lt;/strong&gt;ideo &lt;strong&gt;n&lt;/strong&gt;o”.&lt;/p&gt;
&lt;p&gt;Let’s see how fast it was:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;size=   24772kB time=00:10:34.14 bitrate= 320.0kbits/s speed= 776x&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;776x faster than playback, finished in about a second, not bad!&lt;/p&gt;
&lt;h4 id=&quot;cut-and-trim-without-reencoding&quot;&gt;Cut and trim without reencoding&lt;/h4&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -ss&lt;/span&gt;&lt;span&gt; 10&lt;/span&gt;&lt;span&gt; -t&lt;/span&gt;&lt;span&gt; 10&lt;/span&gt;&lt;span&gt;  -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; -c:a&lt;/span&gt;&lt;span&gt; copy&lt;/span&gt;&lt;span&gt; -c:v&lt;/span&gt;&lt;span&gt; copy&lt;/span&gt;&lt;span&gt; part_from_10_to_20_copied.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There could be precision issues with seeking while you do this, so you may want to learn more about seeking and copying &lt;a href=&quot;https://trac.ffmpeg.org/wiki/Seeking&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&quot;replace-audio-on-video-file-without-reencoding&quot;&gt;Replace audio on video file without reencoding&lt;/h4&gt;
&lt;p&gt;We have removed audio and video already, but what if we want to swap them?&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-i &lt;/span&gt;&lt;span&gt;bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-i &lt;/span&gt;&lt;span&gt;voice_recording.wav&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-map &lt;/span&gt;&lt;span&gt;&quot;0:v&quot;&lt;/span&gt;&lt;span&gt; -map&lt;/span&gt;&lt;span&gt; &quot;1:a&quot;&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-c:v &lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt; -c:a&lt;/span&gt;&lt;span&gt; copy&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;bbb_with_replaced_audio.mov&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is quite a lot going on in here, so let’s explain the parts!&lt;/p&gt;
&lt;p&gt;First, we have two inputs (&lt;strong&gt;-i&lt;/strong&gt;), meaning we are better off manually specifying the &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg//#mapping&quot;&gt;mapping&lt;/a&gt;. The command would work without the “&lt;strong&gt;-map&lt;/strong&gt;” options, but it would ignore our second input.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;-map &quot;0:v&quot; -map &quot;1:a&quot;&lt;/code&gt; means that please use the first file’s (first) video stream and the second file’s (first) audio stream.&lt;/p&gt;
&lt;p&gt;With &lt;code&gt;-c:v copy -c:a copy&lt;/code&gt;, we require FFmpeg to copy the already encoded data packets without touching them. Therefore FFmpeg’s work is mostly really just copying bytes, no decoding, no encoding.&lt;/p&gt;
&lt;p&gt;Not surprisingly, that’s what we see in the stream mapping too:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Stream mapping:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Stream #0:0 -&gt; #0:0 (copy)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Stream #1:0 -&gt; #0:1 (copy)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Press [q] to stop, [?] for help&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;frame=38072 fps=9750 q=-1.0 Lsize=  320645kB time=00:10:34.51 bitrate=4139.7kbits/s speed= 162x&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And since it is just copying, it was crazy fast, 162x of the playback speed, or almost 10k frames per second!&lt;/p&gt;
&lt;p&gt;But!&lt;/p&gt;
&lt;p&gt;Execute the exact same command, but with “bbb_with_replaced_audio.&lt;strong&gt;mp4&lt;/strong&gt;” (.mp4 container instead of .mov) as an output file! You’ll get this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Could not find tag for codec pcm_s16le in stream #1, codec not currently supported in container&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The message is quite clear. You can not have a pcm_s16le (raw WAV, say that 10 times:)) stream in an MP4 container. I’m not sure if it is FFmpeg’s or the container’s lack of support, but we need to solve this. If you run into this situation, you might consider two solutions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Change the container: I’ve just tried MOV, and it worked.&lt;/li&gt;
&lt;li&gt;Encode the audio: We still copy the video data, and encoding audio isn’t that painful.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I just showed you option #1, so let’s see option #2:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-i &lt;/span&gt;&lt;span&gt;bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-i &lt;/span&gt;&lt;span&gt;voice_recording.wav&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-map &lt;/span&gt;&lt;span&gt;&quot;0:v&quot;&lt;/span&gt;&lt;span&gt; -map&lt;/span&gt;&lt;span&gt; &quot;1:a&quot;&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-c:v &lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-c:a &lt;/span&gt;&lt;span&gt;aac&lt;/span&gt;&lt;span&gt; -b:a&lt;/span&gt;&lt;span&gt; 320k&lt;/span&gt;&lt;span&gt; -ar&lt;/span&gt;&lt;span&gt; 44100&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;bbb_with_replaced_audio_aac.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This copies the video frames and encodes our WAV into a supported codec to be held in the mp4 container. You can refer back to the &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg//#how-to-transcode-audio-with-ffmpeg&quot;&gt;audio encoding&lt;/a&gt; section if you want to learn more about that.&lt;/p&gt;
&lt;p&gt;Here is the output:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Stream mapping:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Stream #0:0 -&gt; #0:0 (copy)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Stream #1:0 -&gt; #0:1 (pcm_s16le (native) -&gt; aac (native))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Press [q] to stop, [?] for help&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;frame=38072 fps=2176 q=-1.0 Lsize=  313058kB time=00:10:34.51 bitrate=4041.8kbits/s speed=36.3x&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;“Only” 36x faster than playback, 2176 fps, still not that bad!&lt;/p&gt;
&lt;h2 id=&quot;filtering-overview&quot;&gt;Filtering overview&lt;/h2&gt;
&lt;p&gt;FFmpeg supports many audio and video &lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html&quot;&gt;filters&lt;/a&gt;. Currently, there are 116 audio and 286 video filters, but there are a bit more if we count the hardware accelerated ones too.&lt;/p&gt;
&lt;p&gt;So how do we leverage them?&lt;/p&gt;
&lt;p&gt;There are two ways to define filters, but I’m going to explain the complex filter, as the difference is not much, but it is more versatile. So there is a global option for FFmpeg, called: &lt;strong&gt;&lt;code&gt;-filter_complex&lt;/code&gt;&lt;/strong&gt;. With quite a weird syntax, you can specify all your filters and their parameters right after this option.&lt;/p&gt;
&lt;p&gt;You can imagine the process with the following image:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1352px) 1352px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1352&quot; height=&quot;512&quot; src=&quot;https://img.ly/_astro/img-14-complex_filter_intro_G5hEn.webp&quot; srcset=&quot;/_astro/img-14-complex_filter_intro_117lQc.webp 640w, /_astro/img-14-complex_filter_intro_Yucac.webp 750w, /_astro/img-14-complex_filter_intro_Z1VS6KM.webp 828w, /_astro/img-14-complex_filter_intro_CDlGA.webp 1080w, /_astro/img-14-complex_filter_intro_1ezHqr.webp 1280w, /_astro/img-14-complex_filter_intro_G5hEn.webp 1352w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Basically, your filter graph can access all the inputs (-i a.mp4 -i b.mp4 -i c.mp4), and it can produce as many outputs as you like (-map might be needed).&lt;/p&gt;
&lt;h3 id=&quot;basic-syntax&quot;&gt;Basic syntax&lt;/h3&gt;
&lt;p&gt;Let’s take a look at a simple, basic example:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt;  -t&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-i &lt;/span&gt;&lt;span&gt;bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-filter_complex &lt;/span&gt;&lt;span&gt;&quot;drawtext=text=&apos;HELLO THERE&apos;:y=20:x=30:fontsize=200:fontfile=/usr/share/fonts/truetype/freefont/FreeSerif.ttf&quot;&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;filter_complex1.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Although &lt;code&gt;-filter_complex&lt;/code&gt; is a global option, I like to put it after the inputs and before the outputs as it is a bit easier to overlook the whole command that way. Thankfully the command line parser of FFmpeg is smart enough, and it works.&lt;/p&gt;
&lt;p&gt;The command above produces a 5-second-long video, where the text “HELLO THERE” is overlaid on the intro of Big Buck Bunny.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1397px) 1397px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1397&quot; height=&quot;912&quot; src=&quot;https://img.ly/_astro/img-15-hello-there_TGV9p.webp&quot; srcset=&quot;/_astro/img-15-hello-there_ZcTUvf.webp 640w, /_astro/img-15-hello-there_Zjr7vJ.webp 750w, /_astro/img-15-hello-there_Z2dDYkq.webp 828w, /_astro/img-15-hello-there_ZRoNox.webp 1080w, /_astro/img-15-hello-there_4xGVW.webp 1280w, /_astro/img-15-hello-there_TGV9p.webp 1397w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Let’s understand the weird format for specifying filters!&lt;/p&gt;
&lt;p&gt;We’ll go bottom-up, and we’ll build it from there. So the most basic format is this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;FILTER_NAME=ARGUMENT1=VALUE1:ARGUMENT2=VALUE2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;drawtext=text=&apos;HELLO THERE&apos;:y=20:x=30&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first thing before the first equal (=) sign is the filter’s name, which is the &lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#drawtext-1&quot;&gt;drawtext&lt;/a&gt; filter in this case. Then we have our first argument, “text” and its value “‘HELLO THERE’”. Right after that, separated with a colon (:) comes the next argument, “y” with a value of “20”.&lt;/p&gt;
&lt;p&gt;You can guess what each of the text, y, x, fontsize and fontfile arguments do, as it is quite self-explaining. But especially for the first time, you’ll heavily rely on the &lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html&quot;&gt;filtering documentation&lt;/a&gt; to understand every filter and every argument.&lt;/p&gt;
&lt;p&gt;Also, several characters are reserved, such as: &lt;code&gt;, : =&lt;/code&gt; and a few others depending on your environment, so sooner or later you need to learn about &lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#toc-Notes-on-filtergraph-escaping&quot;&gt;escaping&lt;/a&gt; too.&lt;/p&gt;
&lt;p&gt;To recap, our pipeline looks like this now:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 2384px) 2384px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;2384&quot; height=&quot;594&quot; src=&quot;https://img.ly/_astro/img-16-complex_filter_multi_EsmbN.webp&quot; srcset=&quot;/_astro/img-16-complex_filter_multi_2gMSNr.webp 640w, /_astro/img-16-complex_filter_multi_Z171xGF.webp 750w, /_astro/img-16-complex_filter_multi_Z1K5OSl.webp 828w, /_astro/img-16-complex_filter_multi_1VImWl.webp 1080w, /_astro/img-16-complex_filter_multi_1vVNzi.webp 1280w, /_astro/img-16-complex_filter_multi_Z1czkU1.webp 1668w, /_astro/img-16-complex_filter_multi_aGdU2.webp 2048w, /_astro/img-16-complex_filter_multi_EsmbN.webp 2384w&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;multiple-filters-in-a-chain&quot;&gt;Multiple filters in a chain&lt;/h3&gt;
&lt;p&gt;This previous command is a single filter chain that consists of a single filter only, but you could have more filters put right after each other! It means that the output of one filter will be the input for the next! The way to do this is by separating them with a comma!&lt;/p&gt;
&lt;p&gt;Let’s draw two boxes with the &lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#drawbox&quot;&gt;drawbox&lt;/a&gt; filter!&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt;  -t&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-i &lt;/span&gt;&lt;span&gt;bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-filter_complex &lt;/span&gt;&lt;span&gt;&quot;  drawbox=x=10:y=10:w=100:h=100:color=red  ,  drawbox=x=200:y=200:w=100:h=100:color=blue  &quot;&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;filter_complex2.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;See? The output of the first filter is passed to the output of the second filter!&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1596px) 1596px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1596&quot; height=&quot;1044&quot; src=&quot;https://img.ly/_astro/img-17-filter-bick-buck_2mfaYg.webp&quot; srcset=&quot;/_astro/img-17-filter-bick-buck_Z1lJ69B.webp 640w, /_astro/img-17-filter-bick-buck_Z1SUwKi.webp 750w, /_astro/img-17-filter-bick-buck_fycbY.webp 828w, /_astro/img-17-filter-bick-buck_ZC3JU5.webp 1080w, /_astro/img-17-filter-bick-buck_ZQcKyY.webp 1280w, /_astro/img-17-filter-bick-buck_2mfaYg.webp 1596w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Let’s visualize our pipeline again:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 2384px) 2384px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;2384&quot; height=&quot;594&quot; src=&quot;https://img.ly/_astro/img-18-complex_filter_multi_2_2bafu7.webp&quot; srcset=&quot;/_astro/img-18-complex_filter_multi_2_Z1qWCdN.webp 640w, /_astro/img-18-complex_filter_multi_2_XFBaH.webp 750w, /_astro/img-18-complex_filter_multi_2_1E6QOl.webp 828w, /_astro/img-18-complex_filter_multi_2_1UA8rw.webp 1080w, /_astro/img-18-complex_filter_multi_2_Z1aU3Xr.webp 1280w, /_astro/img-18-complex_filter_multi_2_Z2qfGxK.webp 1668w, /_astro/img-18-complex_filter_multi_2_pgEz4.webp 2048w, /_astro/img-18-complex_filter_multi_2_2bafu7.webp 2384w&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;input-and-output-pads&quot;&gt;Input and output pads&lt;/h3&gt;
&lt;p&gt;Now, we have skipped something this far, because for simple uses FFmpeg is smart enough to do it for us. And this is the specification of a chain’s input and output pads!&lt;/p&gt;
&lt;p&gt;Let’s draw just a single rectangle for now:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt;  -t&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; -filter_complex&lt;/span&gt;&lt;span&gt; &quot;drawbox=x=10:y=10:w=100:h=100:color=red&quot;&lt;/span&gt;&lt;span&gt; filter_complex3.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;FFmpeg sees that the input for our filter chain is a single video file, and the output is a single output video file. Therefore, it safely assumes that we want that single input as the input of our single filter chain. And that single output should be the single output of our single output chain.&lt;/p&gt;
&lt;p&gt;That’s really nice, as, in simple situations like this, we don’t need to assign and map inputs and outputs manually! But when we get more inputs, filter chains, or outputs, it is no longer possible. Therefore, we need to understand how to assign inputs and outputs!&lt;/p&gt;
&lt;p&gt;First of all, let’s compare the following two command lines. They result in exactly the same result, but the second one represents what FFmpeg does internally (roughly):&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt;  -t&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; -filter_complex&lt;/span&gt;&lt;span&gt; &quot;drawbox=x=10:y=10:w=100:h=100:color=red&quot;&lt;/span&gt;&lt;span&gt; filter_complex3.mp4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt;  -t&lt;/span&gt;&lt;span&gt; 5&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; -filter_complex&lt;/span&gt;&lt;span&gt; &quot;[0:v]drawbox=x=10:y=10:w=100:h=100:color=red[out_link_0]&quot;&lt;/span&gt;&lt;span&gt; -map&lt;/span&gt;&lt;span&gt; &quot;[out_link_0]&quot;&lt;/span&gt;&lt;span&gt; filter_complex3.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Do you see the difference? Before our filter chain, an “input pad” is defined: &lt;code&gt;[0:v]&lt;/code&gt;. The expected format between the square brackets is documented in the &lt;a href=&quot;https://ffmpeg.org/ffmpeg.html#Stream-selection&quot;&gt;stream selection&lt;/a&gt; section of the official documentation, and this article already &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg//#mapping&quot;&gt;covered&lt;/a&gt; it.&lt;/p&gt;
&lt;p&gt;But, a quick summary:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;0:v&lt;/strong&gt;: This means the first video stream of the first input file.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0:v:0&lt;/strong&gt;: Means exactly the same thing but in a long form.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0:0&lt;/strong&gt;: Means the first stream of the first input file (not recommended, as it could be anything in theory. It could be a subtitle stream, a thumbnail, a video or an audio stream…)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0:a&lt;/strong&gt;: This means the first audio stream of the first input file.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0:a:0&lt;/strong&gt;: Means exactly the same thing but in a long form.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0:a:1&lt;/strong&gt;: Means the second (index #1) audio stream of the first input file.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So we can specify which input file should be connected to which input of the filter graph!&lt;/p&gt;
&lt;p&gt;Also, something similar is going on at the end! Do you see, the &lt;code&gt;[out_link_0]&lt;/code&gt; output pad definition at the end of our filter chain?&lt;/p&gt;
&lt;p&gt;The naming here is easier, as basically you can specify any arbitrary name in here. It roughly means, “please store the output data under this name”.&lt;/p&gt;
&lt;p&gt;And when you specify your output file, you can or need to map it by selecting one of your filter graph outputs! Therefore, we must add the -map “[out_link_0]” option before our output file.&lt;/p&gt;
&lt;p&gt;This map option means this: “Please save the data stream with this name into the following output file.”&lt;/p&gt;
&lt;p&gt;This is how you can visualize this input/output mapping:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 2590px) 2590px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;2590&quot; height=&quot;724&quot; src=&quot;https://img.ly/_astro/img-19-complex_filter_multi_3_cb9GV.webp&quot; srcset=&quot;/_astro/img-19-complex_filter_multi_3_Z2sAwc5.webp 640w, /_astro/img-19-complex_filter_multi_3_ZfzIQf.webp 750w, /_astro/img-19-complex_filter_multi_3_Z1SuENk.webp 828w, /_astro/img-19-complex_filter_multi_3_1NtTKc.webp 1080w, /_astro/img-19-complex_filter_multi_3_1F6bbb.webp 1280w, /_astro/img-19-complex_filter_multi_3_Z1dAxRT.webp 1668w, /_astro/img-19-complex_filter_multi_3_RAhvH.webp 2048w, /_astro/img-19-complex_filter_multi_3_ZCLv9A.webp 2560w, /_astro/img-19-complex_filter_multi_3_cb9GV.webp 2590w&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;multiple-chains&quot;&gt;Multiple chains&lt;/h3&gt;
&lt;p&gt;Coming from the previous sections, you are now ready to see and understand an even more complicated configuration, which has multiple input files, output files, and filter chains!&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt;  \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-i &lt;/span&gt;&lt;span&gt;train.jpg&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-t &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-filter_complex &lt;/span&gt;&lt;span&gt;&quot;[0:v]drawbox=x=10:y=10:w=100:h=100:color=red[train_box] ; [1:v]drawbox=x=10:y=10:w=100:h=100:color=red[bbb_box]&quot;&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-map &lt;/span&gt;&lt;span&gt;&quot;[train_box]&quot;&lt;/span&gt;&lt;span&gt; filter_complex4_train.jpg&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-map &lt;/span&gt;&lt;span&gt;&quot;[bbb_box]&quot;&lt;/span&gt;&lt;span&gt; filter_complex4_bbb.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s see the output (two files next to each other):&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1470px) 1470px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1470&quot; height=&quot;656&quot; src=&quot;https://img.ly/_astro/img-20-filters_output_3_1wupPS.webp&quot; srcset=&quot;/_astro/img-20-filters_output_3_ZQfV9n.webp 640w, /_astro/img-20-filters_output_3_Zswwdy.webp 750w, /_astro/img-20-filters_output_3_1gyM4x.webp 828w, /_astro/img-20-filters_output_3_CwvA.webp 1080w, /_astro/img-20-filters_output_3_Z12PHKj.webp 1280w, /_astro/img-20-filters_output_3_1wupPS.webp 1470w&quot;&gt;&lt;/p&gt;
&lt;p&gt;We had two inputs, and we got two output files, an image, and a video, with a red rectangle on them, with a single command!&lt;/p&gt;
&lt;p&gt;Are you still here? I hope! Let’s understand what happened in that crazy command! We have two input files:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;-i train.jpg&lt;/strong&gt;: A simple image file&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-t 5 -i bbb_sunflower_1080p_60fps_normal.mp4&lt;/strong&gt;: Our video file, but to make it quick, just the first five seconds of it&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then the first thing to note is that we have two filter chains! They are separated with a ”&lt;strong&gt;;&lt;/strong&gt;”.&lt;/p&gt;
&lt;p&gt;Our first filter graph is this: &lt;code&gt;[0:v]...[train_box]&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This requests the first input file as an input&lt;/li&gt;
&lt;li&gt;Draws a red box&lt;/li&gt;
&lt;li&gt;Saves the output into the “train_box” output pad&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Our second filter graph is this: &lt;code&gt;[1:v]...[bbb_box]&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This requests the second input file as an input&lt;/li&gt;
&lt;li&gt;Draws a red box&lt;/li&gt;
&lt;li&gt;Saves the output into the “bbb_box” output pad&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And finally, we got two outputs, each mapping to one of the outputs of the filter graph:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;-map “[train_box]” filter_complex4_train.jpg&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-map “[bbb_box]” filter_complex4_bbb.mp4&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here is the same thing visually:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 2898px) 2898px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;2898&quot; height=&quot;988&quot; src=&quot;https://img.ly/_astro/img-21-complex_filter_multi_4_4Rlkm.webp&quot; srcset=&quot;/_astro/img-21-complex_filter_multi_4_1MDtiy.webp 640w, /_astro/img-21-complex_filter_multi_4_279DG.webp 750w, /_astro/img-21-complex_filter_multi_4_WpIQ9.webp 828w, /_astro/img-21-complex_filter_multi_4_Cj59z.webp 1080w, /_astro/img-21-complex_filter_multi_4_MmBkH.webp 1280w, /_astro/img-21-complex_filter_multi_4_2odyoG.webp 1668w, /_astro/img-21-complex_filter_multi_4_26HLAj.webp 2048w, /_astro/img-21-complex_filter_multi_4_Z2rPCuv.webp 2560w, /_astro/img-21-complex_filter_multi_4_4Rlkm.webp 2898w&quot;&gt;&lt;/p&gt;
&lt;p&gt;If you are thinking about making it even more complex and making filter graphs that combine multiple inputs into one for example, you are on the right track! It is possible, and we will get to that!&lt;/p&gt;
&lt;p&gt;This was the introduction to the filtering system and its syntax.&lt;/p&gt;
&lt;h2 id=&quot;editing-video&quot;&gt;Editing video&lt;/h2&gt;
&lt;p&gt;Now let’s get to know a few filters and make some interesting stuff!&lt;/p&gt;
&lt;h3 id=&quot;resizing-or-scaling&quot;&gt;Resizing or scaling&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#scale&quot;&gt;scale&lt;/a&gt; filter is a simple one, yet it is quite powerful!&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt;  \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-t &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-filter_complex &lt;/span&gt;&lt;span&gt;&quot;scale=width=600:height=-1:force_original_aspect_ratio=decrease&quot;&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;filter_complex5_scaled1.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The arguments speak for themselves, but a few things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Specifying -1 to either width or height means rescaling while keeping the aspect ratio.&lt;/li&gt;
&lt;li&gt;“force_original_aspect_ratio” can be &lt;code&gt;increase&lt;/code&gt;, &lt;code&gt;decrease&lt;/code&gt;. Meaning it will increase or decrease the image to fit the specified bounding box while keeping the aspect ratio.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;adding-text&quot;&gt;Adding text&lt;/h3&gt;
&lt;p&gt;We have already covered this a little, so let’s dive deeper!&lt;/p&gt;
&lt;p&gt;This is what we used earlier:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt;  \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-t &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-filter_complex &lt;/span&gt;&lt;span&gt;&quot;drawtext=text=&apos;HELLO THERE&apos;:y=20:x=30:fontsize=200:fontfile=/usr/share/fonts/truetype/freefont/FreeSerif.ttf&quot;&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;filter_complex1.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now let’s discover how to align the text!&lt;/p&gt;
&lt;p&gt;Many filters, including drawtext, support variables in some of its argument’s values. If you scroll down in the &lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#drawtext-1&quot;&gt;documentation of drawtext&lt;/a&gt;, you’ll find this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“The parameters for x and y are expressions containing the following constants and functions: ”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And after this part, you’ll see many variables which you can include in your x and y variables!&lt;/p&gt;
&lt;p&gt;Let’s see:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Align the text to the center&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt;  \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-t &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-filter_complex &lt;/span&gt;&lt;span&gt;&quot;drawtext=text=&apos;HELLO THERE&apos;:y=h/2-text_h/2:x=w/2-text_w/2:fontsize=200:fontfile=/usr/share/fonts/truetype/freefont/FreeSerif.ttf&quot;&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;filter_complex6_center.mp4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# y=h/2-text_h/2 means: y position = (image height / 2) - (text height / 2)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Align the text to the right:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt;  \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-t &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-filter_complex &lt;/span&gt;&lt;span&gt;&quot;drawtext=text=&apos;HELLO THERE&apos;:y=30:x=w-text_w-20:fontsize=200:fontfile=/usr/share/fonts/truetype/freefont/FreeSerif.ttf&quot;&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;filter_complex6_right.mp4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# x=w-text_w-20 means: x position = image width - text width - 20pixel padding&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Align the text to the bottom:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt;  \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-t &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-filter_complex &lt;/span&gt;&lt;span&gt;&quot;drawtext=text=&apos;HELLO THERE&apos;:y=h-text_h-20:x=30:fontsize=200:fontfile=/usr/share/fonts/truetype/freefont/FreeSerif.ttf&quot;&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;filter_complex6_bottom.mp4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# y=h-text_h-20 means: y position = image height - text height - 20pixel padding&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And this is what we’ll get in the end:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1802px) 1802px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1802&quot; height=&quot;1043&quot; src=&quot;https://img.ly/_astro/img-22-filters_output_4_Zc05ub.webp&quot; srcset=&quot;/_astro/img-22-filters_output_4_ZlgkEK.webp 640w, /_astro/img-22-filters_output_4_Z1sYxVz.webp 750w, /_astro/img-22-filters_output_4_ZqN21H.webp 828w, /_astro/img-22-filters_output_4_Z2bweND.webp 1080w, /_astro/img-22-filters_output_4_Z1cq8p8.webp 1280w, /_astro/img-22-filters_output_4_Z27qRVh.webp 1668w, /_astro/img-22-filters_output_4_Zc05ub.webp 1802w&quot;&gt;&lt;/p&gt;
&lt;p&gt;I need to mention one good trick that might not be obvious at first. So the &lt;code&gt;text_h&lt;/code&gt; variable is a tricky one, because different text will be of different height! E.g.: ”____” and “WWW” will result in a different height.&lt;/p&gt;
&lt;p&gt;For this reason, you do not always want to use text_h or even just a constant y=value expression but rather, you need to align text by its &lt;a href=&quot;https://en.wikipedia.org/wiki/Baseline%5F(typography)&quot;&gt;&lt;strong&gt;baseline&lt;/strong&gt;&lt;/a&gt;. So just remember to use the “&lt;strong&gt;ascent&lt;/strong&gt;” variable whenever you need to align text vertically!&lt;/p&gt;
&lt;p&gt;Check out these two examples! Each has two drawtext filters printing ”_” and “_H”:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# This one uses y=200 for both, still the text isn&apos;t aligned properly!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt;  \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-t &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-filter_complex &lt;/span&gt;&lt;span&gt;&quot;drawtext=text=&apos;_&apos;:y=200:x=30:fontsize=200:fontfile=/usr/share/fonts/truetype/freefont/FreeSerif.ttf,drawtext=text=&apos;_H&apos;:y=200:x=500:fontsize=200:fontfile=/usr/share/fonts/truetype/freefont/FreeSerif.ttf&quot;&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;filter_complex7_bad_text.mp4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# This one uses y=200-ascent for both and the text is aligned as expected!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt;  \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-t &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-filter_complex &lt;/span&gt;&lt;span&gt;&quot;drawtext=text=&apos;_&apos;:y=200-ascent:x=30:fontsize=200:fontfile=/usr/share/fonts/truetype/freefont/FreeSerif.ttf,drawtext=text=&apos;_H&apos;:y=200-ascent:x=500:fontsize=200:fontfile=/usr/share/fonts/truetype/freefont/FreeSerif.ttf&quot;&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;filter_complex7_good_text.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now let’s compare the difference:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1771px) 1771px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1771&quot; height=&quot;525&quot; src=&quot;https://img.ly/_astro/img-23-filters_output_4_textalign_2lsx6V.webp&quot; srcset=&quot;/_astro/img-23-filters_output_4_textalign_2qB7V4.webp 640w, /_astro/img-23-filters_output_4_textalign_xgGX5.webp 750w, /_astro/img-23-filters_output_4_textalign_Z1SnAfT.webp 828w, /_astro/img-23-filters_output_4_textalign_1It9OQ.webp 1080w, /_astro/img-23-filters_output_4_textalign_L5FXv.webp 1280w, /_astro/img-23-filters_output_4_textalign_JOoiO.webp 1668w, /_astro/img-23-filters_output_4_textalign_2lsx6V.webp 1771w&quot;&gt;&lt;/p&gt;
&lt;p&gt;See? This is the difference between aligning the “top left” or the “baseline” of the text!&lt;/p&gt;
&lt;h3 id=&quot;adding-an-overlay&quot;&gt;Adding an overlay&lt;/h3&gt;
&lt;p&gt;Overlaying is a very interesting thing to do with FFmpeg. Let’s jump right in!&lt;/p&gt;
&lt;h4 id=&quot;basic&quot;&gt;Basic&lt;/h4&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt;  \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-t &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt;  \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-i &lt;/span&gt;&lt;span&gt;smiley.png&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-filter_complex &lt;/span&gt;&lt;span&gt;&quot;overlay&quot;&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;filter_complex8_overlay1.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Easy as that!&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1497px) 1497px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1497&quot; height=&quot;980&quot; src=&quot;https://img.ly/_astro/img-24-overlay_1_Z1nBChF.webp&quot; srcset=&quot;/_astro/img-24-overlay_1_ZncyFD.webp 640w, /_astro/img-24-overlay_1_IaJii.webp 750w, /_astro/img-24-overlay_1_ASKXl.webp 828w, /_astro/img-24-overlay_1_my3ED.webp 1080w, /_astro/img-24-overlay_1_Z1N7Swu.webp 1280w, /_astro/img-24-overlay_1_Z1nBChF.webp 1497w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Of course, the &lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#overlay&quot;&gt;overlay&lt;/a&gt; filter has a ton of options, but I wanted to demonstrate the easiest possible command line. We don’t even need to mess with input/output pads, as FFmpeg automatically understands the situation: two inputs for the overlay filter and its single output into a single output.&lt;/p&gt;
&lt;p&gt;But just to exercise, we could have executed it like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt;  \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-t &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt;  \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-i &lt;/span&gt;&lt;span&gt;smiley.png&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-filter_complex &lt;/span&gt;&lt;span&gt;&quot;[0:v][1:v]overlay[output]&quot;&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-map &lt;/span&gt;&lt;span&gt;&quot;[output]&quot;&lt;/span&gt;&lt;span&gt; filter_complex8_overlay2.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And this would result in the same output! Check it out, now I have specified the two inputs for the overlay: &lt;code&gt;[0:v][1:v]&lt;/code&gt;!&lt;/p&gt;
&lt;h4 id=&quot;aligned&quot;&gt;Aligned&lt;/h4&gt;
&lt;p&gt;Let’s align the smiley into the center!&lt;/p&gt;
&lt;p&gt;As we have seen with the drawtext, the overlay filter’s arguments also support a few dynamic variables. We’ll use those to achieve what we want!&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt;  \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-t &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt;  \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-i &lt;/span&gt;&lt;span&gt;smiley.png&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-filter_complex &lt;/span&gt;&lt;span&gt;&quot;overlay=x=main_w/2-overlay_w/2:y=main_h/2-overlay_h/2&quot;&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;filter_complex8_overlay3.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 869px) 869px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;869&quot; height=&quot;627&quot; src=&quot;https://img.ly/_astro/img-25-overlay_2_UYX62.webp&quot; srcset=&quot;/_astro/img-25-overlay_2_1gM7AF.webp 640w, /_astro/img-25-overlay_2_1EXof0.webp 750w, /_astro/img-25-overlay_2_1JUWjv.webp 828w, /_astro/img-25-overlay_2_UYX62.webp 869w&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;preprocessing-the-input-for-overlay&quot;&gt;Preprocessing the input for overlay&lt;/h4&gt;
&lt;p&gt;Let’s get a bit creative!&lt;/p&gt;
&lt;p&gt;I want to make it &lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#scale&quot;&gt;smaller&lt;/a&gt;, and I also want to &lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#scale&quot;&gt;blur&lt;/a&gt; it!&lt;/p&gt;
&lt;p&gt;Now pause for a minute, and think about it, how you’d do that?!&lt;/p&gt;
&lt;p&gt;…&lt;/p&gt;
&lt;p&gt;Ready?&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt;  \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-t &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt;  \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-i &lt;/span&gt;&lt;span&gt;smiley.png&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-filter_complex &lt;/span&gt;&lt;span&gt;&quot;[1:v]scale=w=200:h=-1,gblur=sigma=3[smiley] ; [0:v][smiley]overlay=x=100:y=100&quot;&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;filter_complex8_overlay4.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 996px) 996px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;996&quot; height=&quot;703&quot; src=&quot;https://img.ly/_astro/img-26-overlay_3_6Y6rG.webp&quot; srcset=&quot;/_astro/img-26-overlay_3_ZvuUJA.webp 640w, /_astro/img-26-overlay_3_jhI7.webp 750w, /_astro/img-26-overlay_3_Z2rI2GV.webp 828w, /_astro/img-26-overlay_3_6Y6rG.webp 996w&quot;&gt;&lt;/p&gt;
&lt;p&gt;For this we needed to have two filter graphs!&lt;/p&gt;
&lt;p&gt;The first one is this: &lt;code&gt;[1:v]scale=w=200:h=-1,gblur=sigma=3[smiley]&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Scales the input image (the smiley).&lt;/li&gt;
&lt;li&gt;Then the scaled output is also blurred.&lt;/li&gt;
&lt;li&gt;Then the output is saved into the output pad named “smiley”.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then, we have our second filter graph: &lt;code&gt;[0:v][smiley]overlay=x=100:y=100&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This takes as input the first input file (the video).&lt;/li&gt;
&lt;li&gt;This also takes as input the output pad named “smiley”. (We are connecting two chains this time!)&lt;/li&gt;
&lt;li&gt;Then the overlay filter does its overlaying thing, and we trust FFmpeg to pair the unnamed output with the single output file we specified.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;reusing-content&quot;&gt;Reusing content&lt;/h4&gt;
&lt;p&gt;Let’s do one more, a really complicated one!&lt;/p&gt;
&lt;p&gt;Let’s have the outro overlaid over the intro!&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-t &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-t &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt; -ss&lt;/span&gt;&lt;span&gt; 00:09:40&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt;  \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-filter_complex &lt;/span&gt;&lt;span&gt;&quot; [1:v]scale=w=1920/2:h=-1[outro]; [0:v][outro]overlay&quot;&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;filter_complex8_overlay5.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1170px) 1170px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1170&quot; height=&quot;794&quot; src=&quot;https://img.ly/_astro/img-27-overlay_4_ZFl9Ib.webp&quot; srcset=&quot;/_astro/img-27-overlay_4_Z1UFLMT.webp 640w, /_astro/img-27-overlay_4_mGyKD.webp 750w, /_astro/img-27-overlay_4_ZiLCyz.webp 828w, /_astro/img-27-overlay_4_eB1jO.webp 1080w, /_astro/img-27-overlay_4_ZFl9Ib.webp 1170w&quot;&gt;&lt;/p&gt;
&lt;p&gt;We could have achieved it in several ways, e.g. we could use the &lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#trim&quot;&gt;trim&lt;/a&gt; filter, but to keep it easy, we just open the same file twice and &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg//#cutting-off-from-the-beginning-of-the-clip&quot;&gt;seek/trim&lt;/a&gt; them.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;-t 5 -i bbb_sunflower_1080p_60fps_normal.mp4&lt;/strong&gt;: Open the video, and keep the first five seconds of it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-t 5 -ss 00:09:40 -i bbb_sunflower_1080p_60fps_normal.mp4&lt;/strong&gt;: Open the same video again, but seek to the end and keep five seconds from there.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then we have two filter graphs again, one scales down the outro, and the second is just an overlay.&lt;/p&gt;
&lt;p&gt;Are you excited?:) I hope these made-up examples opened up your eye for the possibilities, and I hope you’ll create very creative stuff with this knowledge!&lt;/p&gt;
&lt;h3 id=&quot;chroma-keying-green-screen-blue-screen&quot;&gt;Chroma keying, green screen, blue screen&lt;/h3&gt;
&lt;p&gt;In this section, we’ll use chroma keying to remove the background from Big Buck Bunny’s intro, and then we will put the transparent logo over the original video, as if it would be some kind of a logo overlay!&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-ss &lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt; -t&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-ss &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; bbb_sunflower_1080p_60fps_normal.mp4&lt;/span&gt;&lt;span&gt;  \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-filter_complex &lt;/span&gt;&lt;span&gt;&quot; [0:v]chromakey=color=0xfdfdfd:similarity=0.1:blend=0.2 , scale=w=-1:h=300 , loop=loop=-1:start=0:size=120[intro] ; [1:v][intro]overlay=x=-40:y=-40&quot;&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-t &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt; filter_complex9.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So just to recap, Big Buck Bunny’s first few seconds are like this:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 854px) 854px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;854&quot; height=&quot;614&quot; src=&quot;https://img.ly/_astro/img-28-chroma_basic_ZCF03.webp&quot; srcset=&quot;/_astro/img-28-chroma_basic_ZMx3FJ.webp 640w, /_astro/img-28-chroma_basic_ZusNr3.webp 750w, /_astro/img-28-chroma_basic_1kYLiL.webp 828w, /_astro/img-28-chroma_basic_ZCF03.webp 854w&quot;&gt;&lt;/p&gt;
&lt;p&gt;And this is the result:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1114px) 1114px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1114&quot; height=&quot;764&quot; src=&quot;https://img.ly/_astro/img-29-chroma1_Z2g6XrH.webp&quot; srcset=&quot;/_astro/img-29-chroma1_Z1RBLwH.webp 640w, /_astro/img-29-chroma1_fjr5z.webp 750w, /_astro/img-29-chroma1_2nsk0h.webp 828w, /_astro/img-29-chroma1_14SEPM.webp 1080w, /_astro/img-29-chroma1_Z2g6XrH.webp 1114w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Also, the butterfly moves its wings repeatedly!&lt;/p&gt;
&lt;p&gt;Let’s examine the command!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;-ss 0.5 -t 2 -i bbb_sunflower_1080p_60fps_normal.mp4&lt;/strong&gt;: We read in the intro from 0.5 to 2.5 seconds.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-ss 10 -i bbb_sunflower_1080p_60fps_normal.mp4&lt;/strong&gt;: We read in the video, starting from the 10th second.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then we have two filter graphs, the first being this:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;[0:v]chromakey=color=0xfdfdfd:similarity=0.1:blend=0.2 , scale=w=-1:h=300 , loop=loop=-1:start=0:size=120[intro]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;As we see, we have three filters in here!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#chromakey&quot;&gt;&lt;strong&gt;chromakey&lt;/strong&gt;&lt;/a&gt;: This one takes a color and a few parameters as input, and outputs transparent frames. The specified color + the blended areas will be the transparent sections. In our case we replaced the white-ish (#fdfdfd) background color with transparency.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#scale&quot;&gt;&lt;strong&gt;scale&lt;/strong&gt;&lt;/a&gt;: We resize the full 1080p image into something around 300px high.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#loop&quot;&gt;&lt;strong&gt;loop&lt;/strong&gt;&lt;/a&gt;: With the loop filter, we repeat all the 2 seconds worth of 120 frames (60*2) over and over again, to have the butterfly move its wings continuously.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And then, finally we have the second filter graph:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;[1:v][intro]overlay=x=-40:y=-40&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Nothing fancy, just an overlay of the original video and our chrome keyed intro.&lt;/p&gt;
&lt;h3 id=&quot;what-else&quot;&gt;What else?&lt;/h3&gt;
&lt;p&gt;You might want to check out a few more &lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#toc-Video-Filters&quot;&gt;filters&lt;/a&gt;, that I didn’t cover here.&lt;/p&gt;
&lt;p&gt;Here are just a few interesting ones:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#colorcorrect&quot;&gt;colorcorrect&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#colorchannelmixer&quot;&gt;colorchannelmixer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#colorize&quot;&gt;colorize&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#fps&quot;&gt;fps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#trim&quot;&gt;trim&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#crop&quot;&gt;crop&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#delogo&quot;&gt;delogo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#derain&quot;&gt;derain&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#deshake&quot;&gt;deshake&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#erosion&quot;&gt;erosion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#edgedetect&quot;&gt;edgedetect&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#hflip&quot;&gt;hflip&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#vflip&quot;&gt;vflip&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#hstack&quot;&gt;hstack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#vstack&quot;&gt;vstack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#xstack&quot;&gt;xstack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#lumakey&quot;&gt;lumakey&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#reverse&quot;&gt;reverse&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#rotate&quot;&gt;rotate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#scroll&quot;&gt;scroll&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#pad&quot;&gt;pad&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#vignette&quot;&gt;vignette&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#zoompan&quot;&gt;zoompan&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;audio-manipulation&quot;&gt;Audio manipulation&lt;/h2&gt;
&lt;p&gt;In this chapter, we’ll be going to check out some &lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#toc-Audio-Filters&quot;&gt;audio manipulation techniques&lt;/a&gt; with FFmpeg!&lt;/p&gt;
&lt;p&gt;First of all, let’s see our &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg//#example-material&quot;&gt;example&lt;/a&gt; file:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1920px) 1920px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1920&quot; height=&quot;400&quot; src=&quot;https://img.ly/_astro/img-30-voice_recording_1UlWX5.webp&quot; srcset=&quot;/_astro/img-30-voice_recording_1ecLD4.webp 640w, /_astro/img-30-voice_recording_cilwF.webp 750w, /_astro/img-30-voice_recording_1XAAfW.webp 828w, /_astro/img-30-voice_recording_17KzOf.webp 1080w, /_astro/img-30-voice_recording_st2hO.webp 1280w, /_astro/img-30-voice_recording_2jkO3S.webp 1668w, /_astro/img-30-voice_recording_1UlWX5.webp 1920w&quot;&gt;&lt;/p&gt;
&lt;p&gt;It is a voice recording, and it is intentionally… well, quite bad.&lt;/p&gt;
&lt;p&gt;From the waveform, it is obvious that there are very different volume ranges in it. This is an example recording where each sentence was read in different strengths: “normal”, “whisper” or “powerful”, this is why you see repeating patterns of amplitude ranges on the image.&lt;/p&gt;
&lt;p&gt;It isn’t visible, but it has some noise too, and of course, it is not normalized or enhanced in any way. Yet.&lt;/p&gt;
&lt;p&gt;Please note that there are different scenarios, requirements, and ways to enhance audio. This is a simplified method to show the outline of the process in this article. I’m not an audio engineer, although I have some experience in the area. So if you know it better, feel free to fine-tune it for yourself even more, or contact me and recommend improvements!&lt;/p&gt;
&lt;p&gt;I’m showing an example here with a very rough input, one that you’d just reject in real life as it would be useless due to its quality. But it is an excellent example to show the different steps of the enhancing process and to see what can be done to it!&lt;/p&gt;
&lt;p&gt;The following steps are built upon each other, and we’ll reach the complete command at the &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg//#putting-it-all-together&quot;&gt;end&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;Don’t forget that these settings are specific to this voice recording. Sadly this can not be generalized too much.&lt;/p&gt;
&lt;h3 id=&quot;gate&quot;&gt;Gate&lt;/h3&gt;
&lt;p&gt;Let’s start with the &lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#agate&quot;&gt;gate&lt;/a&gt; filter!&lt;/p&gt;
&lt;p&gt;A gate is like a switch that opens only if the signal is stronger than the threshold. So if the signal level is lower than the threshold, it cuts to complete silence. Although you might soften or delay this cut with the &lt;em&gt;knee&lt;/em&gt;, &lt;em&gt;attack&lt;/em&gt;, and &lt;em&gt;release&lt;/em&gt; arguments.&lt;/p&gt;
&lt;p&gt;We’ll use this filter as a basic noise reduction method now! This helps us remove the noise between words and sentences by cutting it to silence. It doesn’t remove noise in any other way, e.g. it doesn’t touch the static on the voice itself.&lt;/p&gt;
&lt;p&gt;Check this out!&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-i &lt;/span&gt;&lt;span&gt;voice_recording.wav&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-filter_complex &lt;/span&gt;&lt;span&gt;&quot;agate=threshold=0.01:attack=80:release=840:makeup=1:ratio=3:knee=8&quot;&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;gate.wav&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s hear it: &lt;a href=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/ffmpeg-audio/audio-5-gate.wav&quot;&gt;gate.wav&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And let’s see it:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 655px) 655px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;655&quot; height=&quot;599&quot; src=&quot;https://img.ly/_astro/img-31-a_compression_result_ZdNo4g.webp&quot; srcset=&quot;/_astro/img-31-a_compression_result_1rLY1L.webp 640w, /_astro/img-31-a_compression_result_ZdNo4g.webp 655w&quot;&gt;&lt;/p&gt;
&lt;p&gt;As you can see, the “silent” parts were attenuated heavily, while the above-the-threshold parts remained similar. Those parts were still affected by the knee, attack, and release arguments determining how hard (knee) and quick (attack/release) the cut is.&lt;/p&gt;
&lt;p&gt;I’ve left a quite high release timeout here to avoid sudden dips in the amplitude.&lt;/p&gt;
&lt;p&gt;This is where we are right now:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1920px) 1920px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1920&quot; height=&quot;400&quot; src=&quot;https://img.ly/_astro/img-32-gate_2cOAMH.webp&quot; srcset=&quot;/_astro/img-32-gate_ZqA69z.webp 640w, /_astro/img-32-gate_Z2a2c7C.webp 750w, /_astro/img-32-gate_Z2gGRxK.webp 828w, /_astro/img-32-gate_Z1iqObf.webp 1080w, /_astro/img-32-gate_Z1UP9EM.webp 1280w, /_astro/img-32-gate_Z1j6JuW.webp 1668w, /_astro/img-32-gate_2cOAMH.webp 1920w&quot;&gt;&lt;/p&gt;
&lt;p&gt;The silent parts are more silent than before, but still, the amplitude range or the dynamic range is quite high. You must change your volume levels to hear everything and void blowing your speakers/brain out.&lt;/p&gt;
&lt;h3 id=&quot;equalization&quot;&gt;Equalization&lt;/h3&gt;
&lt;p&gt;Before fixing that, let’s do a bit more housekeeping. Let’s do some equalization and frequency filtering!&lt;/p&gt;
&lt;p&gt;We’ll use these filters:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#highpass&quot;&gt;highpass&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#lowpass&quot;&gt;lowpass&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#anequalizer&quot;&gt;anequalizer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-i &lt;/span&gt;&lt;span&gt;gate.wav&lt;/span&gt;&lt;span&gt;  \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-filter_complex &lt;/span&gt;&lt;span&gt;&quot;highpass=f=100:width_type=q:width=0.5 , lowpass=f=10000 , anequalizer=c0 f=250 w=100 g=2 t=1|c0 f=700 w=500 g=-5 t=1|c0 f=2000 w=1000 g=2 t=1&quot;&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;gate_eq.wav&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s hear it: &lt;a href=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/ffmpeg-audio/audio-5-gate.wav&quot;&gt;gate_eq.wav&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This command gradually attenuates frequencies below 100hz, as there are not much valuable content in there, but it can really lower the clarity of the speech.&lt;/p&gt;
&lt;p&gt;Then we do the same, but for frequencies above 10 kHz. This is mostly needed because we have a lot of high-frequency noise, so this is a workaround for those. Also, a male voice is generally deeper than a woman’s, so you might want to pay attention to how low you can put the bar.&lt;/p&gt;
&lt;p&gt;Then comes anequalizer, which has a crazy an exceptional way of setting its arguments:&lt;/p&gt;
&lt;p&gt;This: &lt;code&gt;anequalizer=c0 f=250 w=100 g=2 t=1|c0 f=700 w=500 g=-5 t=1|c0 f=2000 w=1000 g=2 t=1&lt;/code&gt; means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;at 250hz with a width of 100hz boost by 2 db, with Chebyshev type 1 filter on channel 0.&lt;/li&gt;
&lt;li&gt;at 700hz with a width of 500hz attenuate by 5 db, with Chebyshev type 1 filter on channel 0.&lt;/li&gt;
&lt;li&gt;at 2000hz with a width of 1000hz attenuate by 2 db, with Chebyshev type 1 filter on channel 0.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I agree. You might have used a friendlier equalizer in your life than this one:)&lt;/p&gt;
&lt;p&gt;Those values are based on experimentation and common recommendations for voice. Feel free to tune it for your own needs!&lt;/p&gt;
&lt;p&gt;Let’s compare the frequency plots before and after:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1463px) 1463px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1463&quot; height=&quot;386&quot; src=&quot;https://img.ly/_astro/img-33-a_eq_Z2gUDKL.webp&quot; srcset=&quot;/_astro/img-33-a_eq_Z1V1zyn.webp 640w, /_astro/img-33-a_eq_1bHq3R.webp 750w, /_astro/img-33-a_eq_Z15FmFs.webp 828w, /_astro/img-33-a_eq_Z1dDKHz.webp 1080w, /_astro/img-33-a_eq_rII8h.webp 1280w, /_astro/img-33-a_eq_Z2gUDKL.webp 1463w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Tip: To see the frequency plot in Audacity, open a file, select all, and choose Analyze → Plot spectrum!&lt;/p&gt;
&lt;h3 id=&quot;compression&quot;&gt;Compression&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#acompressor&quot;&gt;compressor&lt;/a&gt; filter applies &lt;a href=&quot;https://en.wikipedia.org/wiki/Dynamic_range_compression&quot;&gt;dynamic range compression&lt;/a&gt; on the incoming audio data. To simplify this, the compressor varies the attenuation based on the incoming signal level. Basically, when you watch a badly mastered movie, this is what you are doing. When it is way too loud in some action scene, you reach for the remote control or mouse to lower the volume, but in the next moment, you will not hear what your heroes are saying, so you increase it back again.&lt;/p&gt;
&lt;p&gt;Dynamic range compression roughly does the same. You may set it up in a way so that it would attenuate louder parts, therefore keeping the overall volume range relatively small.&lt;/p&gt;
&lt;p&gt;It often happens that performers on the stage use a high dynamic range. Many performers will shout at one moment and then whisper in the next to increase drama or keep the attention. If you want to avoid manually adjusting the volume in real-time (while blowing off your speakers and pulling your hair out), then a compressor will save you in these situations!&lt;/p&gt;
&lt;p&gt;This is why our example audio consists of different speaking strengths, so that we could see the dramatic effect of this filter.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-i &lt;/span&gt;&lt;span&gt;gate_eq.wav&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-filter_complex &lt;/span&gt;&lt;span&gt;&quot;acompressor=level_in=6:threshold=0.025:ratio=20:makeup=6&quot;&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;gate_eq_comp.wav&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s hear it: &lt;a href=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/ffmpeg-audio/audio-1-gate-eq-comp.wav&quot;&gt;gate_eq_comp.wav&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And let’s compare the result of this with the original waveform!&lt;/p&gt;
&lt;p&gt;Original:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1920px) 1920px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1920&quot; height=&quot;400&quot; src=&quot;https://img.ly/_astro/img-34-voice_recording_Z29Di0o.webp&quot; srcset=&quot;/_astro/img-34-voice_recording_Z20VCWo.webp 640w, /_astro/img-34-voice_recording_22l4K9.webp 750w, /_astro/img-34-voice_recording_Z1gxOkv.webp 828w, /_astro/img-34-voice_recording_27WsEH.webp 1080w, /_astro/img-34-voice_recording_1sEU8h.webp 1280w, /_astro/img-34-voice_recording_Z1KEqTA.webp 1668w, /_astro/img-34-voice_recording_Z29Di0o.webp 1920w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Result:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1920px) 1920px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1920&quot; height=&quot;400&quot; src=&quot;https://img.ly/_astro/img-35-gate_eq_comp_Z2mmEkT.webp&quot; srcset=&quot;/_astro/img-35-gate_eq_comp_1tg7KR.webp 640w, /_astro/img-35-gate_eq_comp_2vyYBl.webp 750w, /_astro/img-35-gate_eq_comp_1xRkJE.webp 828w, /_astro/img-35-gate_eq_comp_tpybk.webp 1080w, /_astro/img-35-gate_eq_comp_1DreDv.webp 1280w, /_astro/img-35-gate_eq_comp_xv3Vx.webp 1668w, /_astro/img-35-gate_eq_comp_Z2mmEkT.webp 1920w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Quite dramatic, isn’t it?:)&lt;/p&gt;
&lt;p&gt;Let’s analyze this: &lt;code&gt;acompressor=level_in=6:threshold=0.025:ratio=20:makeup=6&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;First, &lt;code&gt;level_in=6&lt;/code&gt; sets the input gain. It is 1 by default, but since our example, audio is extremely silent at places, we boost up the whole thing before processing.&lt;/p&gt;
&lt;p&gt;Then &lt;code&gt;threshold=0.025&lt;/code&gt; defines that everything above 0.025 should be attenuated.&lt;/p&gt;
&lt;p&gt;Based on the image below, I’ve decided to cut at this point, as this is above most of the whispering, which cuts hard pops and “s”-es even in the “whisper zone”.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1916px) 1916px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1916&quot; height=&quot;455&quot; src=&quot;https://img.ly/_astro/img-36-eq_1D0n3V.webp&quot; srcset=&quot;/_astro/img-36-eq_25pidd.webp 640w, /_astro/img-36-eq_Z2b9iqi.webp 750w, /_astro/img-36-eq_1vTL83.webp 828w, /_astro/img-36-eq_WykcW.webp 1080w, /_astro/img-36-eq_UxPb5.webp 1280w, /_astro/img-36-eq_Z284TW8.webp 1668w, /_astro/img-36-eq_1D0n3V.webp 1916w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Then &lt;code&gt;ratio=20&lt;/code&gt; means 1:20 in attenuation ratio, which means that if the level rises 20 dB above the threshold, it will be only 1 dB above the line after the attenuation. Basically, this is a very strong compression ratio, it is almost a &lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#alimiter&quot;&gt;limiter&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This far, we boosted the signal, then turned down everything that was above our “whisper line” with a quite strong ratio, and now, everything is basically at the whisper level, even the parts that are shouting.&lt;/p&gt;
&lt;p&gt;Finally, with the &lt;code&gt;makeup=6&lt;/code&gt; we just bring back everything to the level where the “normal” parts were before.&lt;/p&gt;
&lt;p&gt;Let’s take a look back now, to understand why we used the gate and did the equalization before the compressor.&lt;/p&gt;
&lt;p&gt;Generally, you want to remove unneeded parts and frequencies before compression, as the compressor will likely increase those too! So by removing most of the noise in the gaps, we avoided &lt;code&gt;level_in=6&lt;/code&gt; to increase them too! And the same goes for the high- and lowpass filtering.&lt;/p&gt;
&lt;h3 id=&quot;changing-the-volume&quot;&gt;Changing the volume&lt;/h3&gt;
&lt;p&gt;Now, if we want to make the result a bit louder, we could increase the previous step’s &lt;code&gt;makeup&lt;/code&gt; argument, or leverage the volume &lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#volume&quot;&gt;filter&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;While we are at it, let’s cut the first 4 seconds too with &lt;code&gt;-ss 4&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-ss &lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt; -i&lt;/span&gt;&lt;span&gt; gate_eq_comp.wav&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-filter_complex &lt;/span&gt;&lt;span&gt;&quot;volume=1.1&quot;&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;gate_eq_volume_comp.wav&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s hear it: &lt;a href=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/ffmpeg-audio/audio-2-gate_eq_volume_comp.wav&quot;&gt;gate_eq_volume_comp.wav&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;lets-make-audio-gate-again&quot;&gt;Let’s make audio gate again&lt;/h3&gt;
&lt;p&gt;Excuse me for that title:)&lt;/p&gt;
&lt;p&gt;So as I’ve described earlier, compression can amplify the noises, so you might want to run the result through a gate again:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-i &lt;/span&gt;&lt;span&gt;gate_eq_volume_comp.wav&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-filter_complex &lt;/span&gt;&lt;span&gt;&quot;agate=threshold=0.1:attack=50:release=50:ratio=1.5:knee=4&quot;&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;gate_eq_volume_comp_gate.wav&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s hear it: &lt;a href=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/ffmpeg-audio/audio-3-gate_eq_volume_comp_gate.wav&quot;&gt;gate_eq_volume_comp_gate.wav&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In this case, I’ve used a softer gate, with &lt;code&gt;ratio=1.5&lt;/code&gt;. Because of this, I could use shorter attack and release delays too, as the attenuation is not that strong, it isn’t causing hard dips in the audio.&lt;/p&gt;
&lt;h3 id=&quot;putting-it-all-together&quot;&gt;Putting it all together&lt;/h3&gt;
&lt;p&gt;Just a single command could have achieved all the steps above:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-i &lt;/span&gt;&lt;span&gt;voice_recording.wav&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-filter_complex &lt;/span&gt;&lt;span&gt;&quot;agate=threshold=0.01:attack=80:release=840:makeup=1:ratio=3:knee=8 , highpass=f=100:width_type=q:width=0.5 , lowpass=f=10000 , anequalizer=c0 f=250 w=100 g=2 t=1|c0 f=700 w=500 g=-5 t=1|c0 f=2000 w=1000 g=2 t=1 , acompressor=level_in=6:threshold=0.025:ratio=20:makeup=6 , volume=1.1 , agate=threshold=0.1:attack=50:release=50:ratio=1.5:knee=4&quot;&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;gate_eq_volume_comp_gate_together.wav&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I just copy-pasted all the filters right after each other with a comma between them.&lt;/p&gt;
&lt;p&gt;Isn’t it beautiful? Yeah, it isn’t, but it is very practical:)&lt;/p&gt;
&lt;p&gt;For the last time, check out the difference:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Original: &lt;a href=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/ffmpeg-audio/audio-4-voice_recording.wav&quot;&gt;voice_recording.wav&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Final: &lt;a href=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/ffmpeg-audio/audio-3-gate_eq_volume_comp_gate.wav&quot;&gt;gate_eq_volume_comp_gate.wav&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It has less noise, more clear voice, and a small volume range. Therefore it is easy on your ears!&lt;/p&gt;
&lt;h3 id=&quot;what-else-1&quot;&gt;What else?&lt;/h3&gt;
&lt;p&gt;You might want to check out a few more &lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#toc-Audio-Filters&quot;&gt;filters&lt;/a&gt; that I didn’t cover here.&lt;/p&gt;
&lt;p&gt;Here are just a few interesting ones:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#adeclick&quot;&gt;adeclick&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#adeclip&quot;&gt;adeclip&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#aecho&quot;&gt;aecho&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#deesser&quot;&gt;deesser&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html#alimiter&quot;&gt;alimiter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;documentation&quot;&gt;Documentation&lt;/h2&gt;
&lt;p&gt;For your convenience, let me list the most important documentations that might be important for you! Most of these were already linked many times in this article.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg.html&quot;&gt;FFmpeg main documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://trac.ffmpeg.org/wiki&quot;&gt;FFmpeg WIKI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://trac.ffmpeg.org/wiki/CompilationGuide&quot;&gt;FFmpeg compilation guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-filters.html&quot;&gt;FFmpeg filters documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/ffmpeg-formats.html&quot;&gt;FFmpeg formats documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://trac.ffmpeg.org/wiki/Encode/H.264&quot;&gt;H.264 Video Encoding Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://trac.ffmpeg.org/wiki/Encode/H.265&quot;&gt;H.265 Video Encoding Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you got this far from top to bottom, then you are a true hero! I hope you enjoyed this, and I also hope that it inspired you to create something awesome with FFmpeg! Please consider &lt;a href=&quot;https://ffmpeg.org/donations.html&quot;&gt;donating&lt;/a&gt; to FFmpeg – they are fantastic.&lt;/p&gt;
&lt;p&gt;If you’re looking to take your creative projects to the next level, check out our products - &lt;a href=&quot;https://img.ly/products/creative-sdk&quot;&gt;Creative Editor SDK&lt;/a&gt;, &lt;a href=&quot;https://img.ly/products/video-sdk&quot;&gt;Video Editor SDK&lt;/a&gt;, and &lt;a href=&quot;https://img.ly/products/photo-sdk&quot;&gt;Photo Editor SDK&lt;/a&gt;. These versatile tools empower you to bring your vision to life, whether you’re editing images, crafting stunning videos, or unleashing your artistic talents.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thanks for reading! Let us know what you think on&lt;/strong&gt; &lt;a href=&quot;https://twitter.com/imgly&quot;&gt;&lt;strong&gt;Twitter&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;! To stay in the loop, subscribe to our&lt;/strong&gt; &lt;a href=&quot;https://img.us13.list-manage.com/subscribe?u=dc9f652839dbb620d14d6d28d&amp;#x26;id=04a306e4b2&quot;&gt;&lt;strong&gt;Newsletter&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Csaba</dc:creator><media:content url="https://blog.img.ly/2022/11/FFmpeg_ultimate_guide.png" medium="image"/><category>FFmpeg</category><category>Video App</category><category>Audio</category><category>Tech</category><category>Tutorial</category></item><item><title>How to Crop and Trim Videos in Flutter</title><link>https://img.ly/blog/how-to-crop-and-trim-videos-in-flutter/</link><guid isPermaLink="true">https://img.ly/blog/how-to-crop-and-trim-videos-in-flutter/</guid><description>Create your own video app in Flutter with the free-to-use and open source solution FFmpeg.</description><pubDate>Tue, 27 Sep 2022 08:45:16 GMT</pubDate><content:encoded>&lt;p&gt;If you are looking for a package that crops and trims videos in Flutter, you must have already come across the &lt;a href=&quot;https://pub.dev/packages/video_trimmer&quot;&gt;video_trimmer&lt;/a&gt; Flutter package. This package can trim videos but does not provide video cropping (at least not out-of-the-box). In fact, none of the packages on pub.dev, as of today, allow cropping a video in Flutter. If you have dug deeper, you might have come across FFmpeg — a powerful video editing command line tool, that is not the easiest to get started with. See the &lt;a href=&quot;https://img.ly/blog/ultimate-guide-to-ffmpeg/&quot;&gt;Ultimate Guide to FFmpeg&lt;/a&gt; for help.In this article we will use the FFmpeg library to crop and trim a video in a Flutter project.&lt;/p&gt;
&lt;p&gt;Here is a list of packages that we will be using; &lt;a href=&quot;https://pub.dev/packages/ffmpeg_kit_flutter&quot;&gt;ffmpeg_kit_flutter&lt;/a&gt; package for cropping and trimming videos, the &lt;a href=&quot;https://pub.dev/packages/path_provider&quot;&gt;path_provider&lt;/a&gt; package to get the path to the application or external directory where the video files will be stored, and the &lt;a href=&quot;https://pub.dev/packages/video_player&quot;&gt;video_player&lt;/a&gt; package to play the video preview.&lt;/p&gt;
&lt;p&gt;This article will not cover building any UI for cropping and trimming. But, it will discuss in brief how to crop the video using the video_trimmer library as well. If you want to package this workflow for production, check out our guide on &lt;a href=&quot;https://img.ly/blog/how-to-run-ffmpeg-inside-a-docker-container/&quot;&gt;running FFmpeg inside a Docker container&lt;/a&gt;. Also, you can try out the code used in this article from this &lt;a href=&quot;https://github.com/numerative/flutter_crop_and_trim_video&quot;&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;get-started&quot;&gt;Get Started&lt;/h2&gt;
&lt;p&gt;The end result of this tutorial will be a simple app that would look like the following screenshot.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Create a Flutter app to trim and crop videos easily.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 323px) 323px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;323&quot; height=&quot;700&quot; src=&quot;https://img.ly/_astro/resized_Z1EVmRl.webp&quot; srcset=&quot;/_astro/resized_Z1EVmRl.webp 323w&quot;&gt;&lt;/p&gt;
&lt;p&gt;When you tap the &lt;strong&gt;Save Video&lt;/strong&gt;, the preview will refresh with the cropped and trimmed video replacing the original video. Follow the instructions in this article, and you will be able to develop a similar Flutter app.&lt;/p&gt;
&lt;p&gt;First, let us start by adding dependencies to a new Flutter project.&lt;/p&gt;
&lt;h3 id=&quot;add-dependencies&quot;&gt;Add Dependencies&lt;/h3&gt;
&lt;p&gt;In your project’s &lt;code&gt;pubspec.yaml&lt;/code&gt; file add the following 3 dependencies.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;dependencies&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ffmpeg_kit_flutter&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;^4.5.1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  path_provider&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;^2.0.11&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  video_player&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;^2.4.6&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, execute &lt;code&gt;flutter pub get&lt;/code&gt; command from the project folder root.&lt;/p&gt;
&lt;h3 id=&quot;set-minimum-sdk-version-and-platform-version&quot;&gt;Set Minimum SDK Version and Platform Version&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;ffmpeg_kit_flutter&lt;/code&gt; plugin runs on Android SDK API level 24+ and iOS SDK 12.1+. Therefore, modify the module level &lt;code&gt;build.gradle&lt;/code&gt; file to declare the &lt;code&gt;minSdkVersion&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;groovy&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;android {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  defaultConfig {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ..&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    minSdkVersion &lt;/span&gt;&lt;span&gt;24&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ..&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, modify the &lt;code&gt;Podfile&lt;/code&gt; to declare the minimum global platform.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Uncomment this line to define a global platform for your project&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;platform &lt;/span&gt;&lt;span&gt;:ios&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;12.1&apos;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;add-a-video-asset&quot;&gt;Add a Video Asset&lt;/h3&gt;
&lt;p&gt;For simplicity, we will be using a video asset instead of implementing a file picker. Choose a video file you would like to work with or &lt;a href=&quot;https://github.com/numerative/flutter_crop_and_trim_video/raw/main/assets/file1.mp4&quot;&gt;download&lt;/a&gt; this sample video file. Next, copy the video file to a new directory named &lt;code&gt;assets&lt;/code&gt; at the root of your project folder.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Copy your video of choice to your assets folder at the root of your project folder.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 318px) 318px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;318&quot; height=&quot;240&quot; src=&quot;https://img.ly/_astro/file1-mp4-placement_ZVFQb9.webp&quot; srcset=&quot;/_astro/file1-mp4-placement_ZVFQb9.webp 318w&quot;&gt;&lt;/p&gt;
&lt;p&gt;And then reference the video file from the &lt;code&gt;pubspec.yaml&lt;/code&gt; file.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;flutter&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  assets&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    - &lt;/span&gt;&lt;span&gt;assets/file1.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Execute &lt;code&gt;flutter pub get&lt;/code&gt; again.&lt;/p&gt;
&lt;p&gt;Having added the required dependencies, let us move on to coding.&lt;/p&gt;
&lt;h3 id=&quot;implement-video-crop-and-trim&quot;&gt;Implement Video Crop and Trim&lt;/h3&gt;
&lt;p&gt;Replace the code in your &lt;code&gt;main.dart&lt;/code&gt; file with the following code in the code block. This will be our starter code.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;dart&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &apos;package:flutter/material.dart&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; main&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  runApp&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; MyApp&lt;/span&gt;&lt;span&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; MyApp&lt;/span&gt;&lt;span&gt; extends&lt;/span&gt;&lt;span&gt; StatelessWidget&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; MyApp&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;span&gt;Key&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; key}) &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; super&lt;/span&gt;&lt;span&gt;(key&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; key);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // This widget is the root of your application.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  @override&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Widget&lt;/span&gt;&lt;span&gt; build&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;BuildContext&lt;/span&gt;&lt;span&gt; context) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; MaterialApp&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      title&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &apos;Crop and Trim Demo&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      home&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; const&lt;/span&gt;&lt;span&gt; MyHomePage&lt;/span&gt;&lt;span&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; MyHomePage&lt;/span&gt;&lt;span&gt; extends&lt;/span&gt;&lt;span&gt; StatefulWidget&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; MyHomePage&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;span&gt;Key&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; key}) &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; super&lt;/span&gt;&lt;span&gt;(key&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; key);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  @override&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  State&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;MyHomePage&lt;/span&gt;&lt;span&gt;&gt; &lt;/span&gt;&lt;span&gt;createState&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; _MyHomePageState&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; _MyHomePageState&lt;/span&gt;&lt;span&gt; extends&lt;/span&gt;&lt;span&gt; State&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;MyHomePage&lt;/span&gt;&lt;span&gt;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  @override&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Widget&lt;/span&gt;&lt;span&gt; build&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;BuildContext&lt;/span&gt;&lt;span&gt; context) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; Scaffold&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      appBar&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; AppBar&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        title&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Text&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Flutter Crop and Trim&quot;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      ),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      body&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Column&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        children&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      ),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;initialize-the-input-player&quot;&gt;Initialize the Input Player&lt;/h3&gt;
&lt;p&gt;Add the following code to the &lt;code&gt;_MyHomePageState&lt;/code&gt; class.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;dart&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &apos;dart:io&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &apos;package:flutter/services.dart&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &apos;package:path_provider/path_provider.dart&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &apos;package:video_player/video_player.dart&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; _MyHomePageState&lt;/span&gt;&lt;span&gt; extends&lt;/span&gt;&lt;span&gt; State&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;MyHomePage&lt;/span&gt;&lt;span&gt;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  late&lt;/span&gt;&lt;span&gt; String&lt;/span&gt;&lt;span&gt; inputPath;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  VideoPlayerController&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; controller;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  String&lt;/span&gt;&lt;span&gt; outputPath &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &quot;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  @override&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  void&lt;/span&gt;&lt;span&gt; initState&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    super&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;initState&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    copyVideoToApplicationDirectory&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;span&gt;((path) &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      inputPath &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; path;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      controller &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; VideoPlayerController&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;File&lt;/span&gt;&lt;span&gt;(inputPath));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      await&lt;/span&gt;&lt;span&gt; controller&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;initialize&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      await&lt;/span&gt;&lt;span&gt; controller&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;play&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      setState&lt;/span&gt;&lt;span&gt;(() {});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      outputPath &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; getOutputPath&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ///Copy input file to ApplicationStorage Directory&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ///returns path to copied video&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Future&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&gt; &lt;/span&gt;&lt;span&gt;copyVideoToApplicationDirectory&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; filename &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &quot;file1.mp4&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    var&lt;/span&gt;&lt;span&gt; bytes &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; rootBundle.&lt;/span&gt;&lt;span&gt;load&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;assets/file1.mp4&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    String&lt;/span&gt;&lt;span&gt; dir &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; getApplicationDocumentsDirectory&lt;/span&gt;&lt;span&gt;()).path;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    writeToFile&lt;/span&gt;&lt;span&gt;(bytes, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;dir&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;filename&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; &apos;&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;dir&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;filename&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ///Write to Path.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Future&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt;&gt; &lt;/span&gt;&lt;span&gt;writeToFile&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ByteData&lt;/span&gt;&lt;span&gt; data, &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt; path) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    final&lt;/span&gt;&lt;span&gt; buffer &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; data.buffer;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; File&lt;/span&gt;&lt;span&gt;(path).&lt;/span&gt;&lt;span&gt;writeAsBytes&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        buffer.&lt;/span&gt;&lt;span&gt;asUint8List&lt;/span&gt;&lt;span&gt;(data.offsetInBytes, data.lengthInBytes));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;copyVideoToApplicationDirectory&lt;/code&gt; method is copying the video from Assets to the Application directory on the phone’s file system. The resulting path is then stored in the &lt;code&gt;inputPath&lt;/code&gt; variable which is then supplied to the FFmpeg command. For large‑scale workloads, you can move this FFmpeg command to &lt;a href=&quot;https://img.ly/blog/how-to-run-ffmpeg-on-aws-spot-instances-for-scalable-low-cost-video-processing/&quot;&gt;AWS Spot Instances&lt;/a&gt;; our AWS guide shows how to configure a cloud environment for scalable video processing.&lt;/p&gt;
&lt;p&gt;Next, add the &lt;code&gt;VideoPlayer&lt;/code&gt; widget to the &lt;code&gt;Column&lt;/code&gt; widget.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;dart&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Column&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  children&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    (controller &lt;/span&gt;&lt;span&gt;!=&lt;/span&gt;&lt;span&gt; null&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        ?&lt;/span&gt;&lt;span&gt; AspectRatio&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        aspectRatio&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; controller&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;.value.aspectRatio,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        child&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; VideoPlayer&lt;/span&gt;&lt;span&gt;(controller&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        :&lt;/span&gt;&lt;span&gt; const&lt;/span&gt;&lt;span&gt; SizedBox&lt;/span&gt;&lt;span&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run the app and the video should start playing.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;You can now play your video.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 323px) 323px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;323&quot; height=&quot;700&quot; src=&quot;https://img.ly/_astro/resized_screen_2sxrDY.webp&quot; srcset=&quot;/_astro/resized_screen_2sxrDY.webp 323w&quot;&gt;&lt;/p&gt;
&lt;p&gt;In the next step, we will add the FFmpeg command, but before that add the following 2 more methods to the &lt;code&gt;_MyHomePageState&lt;/code&gt; class.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;dart&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &apos;package:ffmpeg_kit_flutter/ffmpeg_kit.dart&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &apos;package:ffmpeg_kit_flutter/log.dart&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &apos;package:ffmpeg_kit_flutter/return_code.dart&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; _MyHomePageState&lt;/span&gt;&lt;span&gt; extends&lt;/span&gt;&lt;span&gt; State&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;MyHomePage&lt;/span&gt;&lt;span&gt;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  /// Output path with a file name where the result will be stored.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Future&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&gt; &lt;/span&gt;&lt;span&gt;getOutputPath&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    final&lt;/span&gt;&lt;span&gt; appDirectory &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Platform&lt;/span&gt;&lt;span&gt;.isAndroid&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        ?&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; getExternalStorageDirectory&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        :&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; getApplicationDocumentsDirectory&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    final&lt;/span&gt;&lt;span&gt; externalPath &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &apos;&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;appDirectory&lt;/span&gt;&lt;span&gt;?.&lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;/out_file.mp4&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; externalPath;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ///Executes the FFMPEG &lt;/span&gt;&lt;span&gt;[command]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ///Note: Green bar on the right is a Flutter issue. &amp;#x3C;https://github.com/flutter/engine/pull/24888&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ///Should get fixed in a 3.1.0+ stable release &amp;#x3C;https://github.com/flutter/engine/pull/24888#issuecomment-1212374010&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Future&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt;&gt; &lt;/span&gt;&lt;span&gt;ffmpegExecute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt; command) &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    final&lt;/span&gt;&lt;span&gt; session &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; FFmpegKit&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;execute&lt;/span&gt;&lt;span&gt;(command);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    final&lt;/span&gt;&lt;span&gt; returnCode &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; session.&lt;/span&gt;&lt;span&gt;getReturnCode&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;ReturnCode&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;isSuccess&lt;/span&gt;&lt;span&gt;(returnCode)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Success&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      //Replace the preview video&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      await&lt;/span&gt;&lt;span&gt; controller&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;pause&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      await&lt;/span&gt;&lt;span&gt; controller&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;dispose&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      controller &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; VideoPlayerController&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;File&lt;/span&gt;&lt;span&gt;(outputPath));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      await&lt;/span&gt;&lt;span&gt; controller&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;initialize&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      await&lt;/span&gt;&lt;span&gt; controller&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;play&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      setState&lt;/span&gt;&lt;span&gt;(() {});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;ReturnCode&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;isCancel&lt;/span&gt;&lt;span&gt;(returnCode)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Cancel&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Error&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      final&lt;/span&gt;&lt;span&gt; failStackTrace &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; session.&lt;/span&gt;&lt;span&gt;getFailStackTrace&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      print&lt;/span&gt;&lt;span&gt;(failStackTrace);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      List&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;Log&lt;/span&gt;&lt;span&gt;&gt; logs &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; session.&lt;/span&gt;&lt;span&gt;getLogs&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; element &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; logs) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        print&lt;/span&gt;&lt;span&gt;(element.&lt;/span&gt;&lt;span&gt;getMessage&lt;/span&gt;&lt;span&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;getOutputPath&lt;/code&gt; method provides the path where the resulting video will be saved. The &lt;code&gt;outputPath&lt;/code&gt; will be passed to the FFmpeg command whereas the &lt;code&gt;ffmpegExecute&lt;/code&gt; method is where the FFmpeg magic takes place.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;ffmpegExecute&lt;/code&gt; method expects a valid FFmpeg &lt;code&gt;String&lt;/code&gt; command. The &lt;code&gt;String&lt;/code&gt; command is then passed to the &lt;code&gt;FFmpegKit.execute&lt;/code&gt; method which returns an instance of &lt;code&gt;FFMpegSession&lt;/code&gt; . It will tell us whether our command was executed successfully or not. If it was not executed successfully, we can extract error logs from it.&lt;/p&gt;
&lt;h3 id=&quot;ffmpeg-execute-command&quot;&gt;FFmpeg Execute Command&lt;/h3&gt;
&lt;p&gt;Add a &lt;code&gt;TextButton&lt;/code&gt; from where the FFmpeg command shall be sent.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;dart&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;TextButton&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  child&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Text&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;Save Video&apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  onPressed&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    //TODO: Call FFMPEG Execute&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, call the following &lt;code&gt;ffmpegExecute&lt;/code&gt; method from the &lt;code&gt;onPressed&lt;/code&gt; property.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;dart&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ffmpegExecute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;-ss 0:00:15 -to 0:00:45 -y -i &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;inputPath&lt;/span&gt;&lt;span&gt; -filter:v &quot;crop=320:150&quot; -c:a copy &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;outputPath&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run the app and tap the &lt;strong&gt;Save Video&lt;/strong&gt; button. You will notice that the player is now playing the new cropped and trimmed video.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;You can now play your cropped and trimmed video!&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 323px) 323px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;323&quot; height=&quot;700&quot; src=&quot;https://img.ly/_astro/flutter_video_app_crop_trim_screen_ZP5gb4.webp&quot; srcset=&quot;/_astro/flutter_video_app_crop_trim_screen_ZP5gb4.webp 323w&quot;&gt;&lt;/p&gt;
&lt;p&gt;If you were able to successfully follow the instructions up till here, it is time to dig a little deeper into the FFmpeg command that we just ran earlier.&lt;/p&gt;
&lt;h3 id=&quot;understanding-the-ffmpeg-command&quot;&gt;Understanding the FFmpeg Command&lt;/h3&gt;
&lt;p&gt;Because command FFmpeg is a command line tool, it expects string-only commands. For this reason, we do not have any dart Classes, Methods, or Parameters to work with that we usually get when working with a dart plugin. Let us dissect the above command line command that is helping us crop and trim a video.&lt;/p&gt;





































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Command&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;-ss 0:00:15&lt;/td&gt;&lt;td&gt;Seeks to position on the input video. The trim starts from this position.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;-to 0:00:45&lt;/td&gt;&lt;td&gt;Stops reading at the position in the input video. The trim stops at this position. The total length of the video is 0:02:05.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;-y&lt;/td&gt;&lt;td&gt;Overwrite output files. Helpful when the Save Video button is tapped more than once.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;-i $inputPath&lt;/td&gt;&lt;td&gt;Input file location. This is the file on which the crop and trim are applied.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;-filter:v&lt;/td&gt;&lt;td&gt;Apply a filter to the video stream.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;”crop=320:150”&lt;/td&gt;&lt;td&gt;Apply a crop filter from the center of the video that is 320 pixels wide and 150 pixels tall. The original dimensions of the video were 320 x 240.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;-c:a copy $outputPath&lt;/td&gt;&lt;td&gt;Specifies the codec with which the output file must be encoded. Here, copy is a special value to indicate the stream is not to be re-encoded. The a after the colon is a stream specifier for the audio stream.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;That will be either the application directory or the external directory of the application, depending on whether the app is running on iOS or Android.&lt;/p&gt;
&lt;p&gt;You can check out the app’s code and play with it by downloading it from this &lt;a href=&quot;https://github.com/numerative/flutter_crop_and_trim_video&quot;&gt;GitHub Repository&lt;/a&gt;. This project allows you to tweak the duration of the trim and dimensions of the crop, so it has more code, but at the heart of it, it still uses the above FFmpeg command.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Download this app code from the GitHub Repository.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 378px) 378px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;378&quot; height=&quot;700&quot; src=&quot;https://img.ly/_astro/flutter_video_app_crop_trim_info_Z2pzU1G.webp&quot; srcset=&quot;/_astro/flutter_video_app_crop_trim_info_Z2pzU1G.webp 378w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;cropping-using-the-video_trimmer-plugin&quot;&gt;Cropping Using the video_trimmer Plugin&lt;/h2&gt;
&lt;p&gt;As a bonus, here is a tip on how to use the video_trimmer plugin for cropping videos.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://pub.dev/packages/video_trimmer&quot;&gt;video_trimmer&lt;/a&gt; plugin also uses FFmpeg at its core and allows us to pass on an FFmpeg command while saving the video. For this reason, it is an easy task for us to apply the crop to a video using the video_trimmer plugin.&lt;/p&gt;
&lt;p&gt;To do this, pass the following command to the &lt;code&gt;saveTrimmedVideo&lt;/code&gt; method’s &lt;code&gt;ffmpegCommand&lt;/code&gt; parameter as shown in the code block below. The video_trimmer package expects the &lt;code&gt;customVideoFormat&lt;/code&gt; parameter argument when the &lt;code&gt;ffmpegCommand&lt;/code&gt; parameter is used.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;await &lt;/span&gt;&lt;span&gt;_trimmer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    .&lt;/span&gt;&lt;span&gt;saveTrimmedVideo&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        startValue:&lt;/span&gt;&lt;span&gt; _startValue&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        endValue:&lt;/span&gt;&lt;span&gt; _endValue&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        ffmpegCommand:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            &apos;-filter:v &quot;crop=320:150&quot;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        customVideoFormat:&lt;/span&gt;&lt;span&gt; &apos;.mp4&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    .&lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;span&gt;((value) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  setState&lt;/span&gt;&lt;span&gt;(() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    _value&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; value;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;limitations-of-this-approach&quot;&gt;Limitations of this Approach&lt;/h2&gt;
&lt;p&gt;The crop that we are applying is from the center of the frame. The present command would also need starting coordinates to crop a non-center frame.&lt;/p&gt;
&lt;p&gt;The project will need a refined UI for cropping and trimming videos to offer a complete app experience to users. The current approach is miles away from creating that experience.&lt;/p&gt;
&lt;p&gt;But perhaps the most significant limitation is FFmpeg’s &lt;strong&gt;licensing&lt;/strong&gt;. FFmpeg is available with both LGPL and GPL licenses, so you must ensure your project is compatible with those licenses. For many commercial projects, this is a non-starter. Alternatively, you can &lt;a href=&quot;https://img.ly/blog/ffmpeg-on-google-cloud-platform-guide/&quot;&gt;run FFmpeg on Google Cloud Platform&lt;/a&gt;; our GCP tutorial walks you through setup.&lt;/p&gt;
&lt;h2 id=&quot;commercial-alternative&quot;&gt;Commercial Alternative&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://img.ly/docs/vesdk/flutter/getting-started/integration/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;VideoEditor SDK (VE.SDK)&lt;/a&gt; from &lt;a href=&quot;https://img.ly&quot;&gt;IMG.LY&lt;/a&gt; provides powerful video editing features, including cropping and trimming videos in a Flutter project. You will receive staples of video editing, including straightening videos, filters, brightness, color adjustments, and more. Follow our guide to learn how to integrate IMG.LY’s &lt;a href=&quot;https://img.ly/blog/a-modern-video-editor-sdk-for-your-flutter-app/&quot;&gt;video editor for Flutter&lt;/a&gt; into your app.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;While still a complex topic, video manipulation is more attainable to implement on Flutter than on native Android. Nevertheless, FFmpeg is the only open-source, free-to-use option to edit videos on Flutter right now. To automate cropping and trimming across many files or build a transcoding server, see our article on &lt;a href=&quot;https://img.ly/blog/building-a-production-ready-batch-video-processing-server-with-ffmpeg/&quot;&gt;building a production‑ready batch video processing server.&lt;/a&gt;&lt;/p&gt;</content:encoded><dc:creator>Michael H.</dc:creator><media:content url="https://blog.img.ly/2022/09/trim_videos_with_flutter_tutorial.png" medium="image"/><category>How-To</category><category>Flutter</category><category>Video Editor</category><category>App Development</category><category>FFmpeg</category><category>Tutorial</category></item></channel></rss>