Install the Bundle:
composer require fmonts/ffmpeg-bundle "^0.7"
bundles.php (Symfony Flex handles this).Configure FFmpeg Binaries (in config/packages/dubture_f_fmpeg.yaml):
dubture_f_fmpeg:
ffmpeg_binary: /usr/bin/ffmpeg
ffprobe_binary: /usr/bin/ffprobe
binary_timeout: 300 # 5 minutes
threads_count: 4
First Use Case: Transcode a Video Inject the service in a controller or command:
use FFMpeg\FFMpeg;
use FFMpeg\Format\Video\X264;
public function transcode(FFMpeg $ffmpeg)
{
$video = $ffmpeg->open('input.mp4');
$video->save(new X264(), 'output.mp4');
}
FFMpeg as a Symfony service (dubture_ffmpeg.ffmpeg).$video = $ffmpeg->open('input.mp4');
$video
->filters()
->resize(new Dimension(1280, 720), ResizeFilter::RESIZEMODE_INSET)
->synchronize();
$video->save(new X264(), 'output.mp4');
FFMpeg\Coordinate\TimeCode for precise trimming:
$video->save(new X264(), 'trimmed.mp4', [
'start' => new TimeCode('00:00:10'),
'end' => new TimeCode('00:00:20'),
]);
// Dispatch a job
ProcessVideoJob::dispatch('input.mp4', 'output.mp4');
// Job handler
public function handle()
{
$ffmpeg = app('dubture_ffmpeg.ffmpeg');
$ffmpeg->open($this->input)->save(new X264(), $this->output);
}
database, redis).$video = $ffmpeg->open('input.mp4');
$frame = $video->frame(FFMpeg\Coordinate\TimeCode::fromSeconds(10));
$frame->save('thumbnail.jpg');
$video = $ffmpeg->open('input.mp4');
$format = $video->getFormat();
$duration = $format->getDuration(); // in seconds
$codec = $format->getVideoCodec();
Environment-Specific Configs:
Use Symfony’s %env% for binaries:
dubture_f_fmpeg:
ffmpeg_binary: "%env(FFMPEG_BINARY)%"
Set via .env:
FFMPEG_BINARY=/usr/local/bin/ffmpeg
Error Handling: Wrap FFmpeg calls in try-catch:
try {
$video->save(...);
} catch (\FFMpeg\Exception\ExecutionException $e) {
Log::error('FFmpeg failed: ' . $e->getMessage());
throw new \RuntimeException('Video processing failed');
}
Testing:
Use FFMpeg\FFProbe to validate inputs:
$ffprobe = $ffmpeg->getFFProbe();
$format = $ffprobe->format('input.mp4');
$this->assertEquals('mp4', $format->getFormat());
Binary Paths:
/usr/bin/ffmpeg) fail across environments.%env% or Docker volumes to mount FFmpeg binaries.FFmpeg Version Mismatch:
php-ffmpeg:^0.13 may require FFmpeg ≥4.0. Older binaries (e.g., 3.x) cause errors.Threading Limits:
threads_count > CPU cores degrades performance.min(4, sysconf('SC_NPROCESSORS_ONLN')) dynamically.Timeouts:
binary_timeout.Symfony 5+ Deprecations:
TreeBuilder usages may need updates.Log FFmpeg Commands:
Enable verbose logging in config/packages/dubture_f_fmpeg.yaml:
dubture_f_fmpeg:
ffmpeg_binary: /usr/bin/ffmpeg -loglevel verbose
Or use Symfony’s monolog:
$ffmpeg->getFFMpeg()->on('progress', function ($progress) {
Log::debug('FFmpeg progress:', ['progress' => $progress]);
});
Check FFmpeg Output: Redirect stderr to a file:
ffmpeg_binary: /usr/bin/ffmpeg -errdetect ignore_err -y 2> /tmp/ffmpeg.log
Custom Filters:
Extend FFMpeg\Filter\FilterInterface and register in the bundle’s config:
dubture_f_fmpeg:
custom_filters:
- App\FFmpeg\CustomFilter
Queue Handlers: Create a base job class for shared FFmpeg logic:
abstract class FFmpegJob implements ShouldQueue
{
protected $ffmpeg;
public function __construct()
{
$this->ffmpeg = app('dubture_ffmpeg.ffmpeg');
}
}
Event Listeners:
Hook into FFmpeg events (e.g., onProgress) via Symfony’s event dispatcher:
$dispatcher = $ffmpeg->getFFMpeg()->getDispatcher();
$dispatcher->addListener('progress', function ($event) { ... });
Reuse FFmpeg Instances:
The bundle provides a singleton service. Avoid recreating $ffmpeg in loops.
Optimize Formats:
Prefer X264 for videos and Lame for audio:
$video->save(new X264(['crf' => 23])); // Higher CRF = smaller file
Parallel Processing:
Use Laravel’s parallel helper for batch jobs:
parallel([
fn() => ProcessVideoJob::dispatch('video1.mp4'),
fn() => ProcessVideoJob::dispatch('video2.mp4'),
]);
Validate Inputs: Sanitize file paths to prevent directory traversal:
$path = str_replace(['../', '..\\'], '', $inputPath);
Resource Limits:
Set binary_timeout and memory_limit in php.ini to prevent DoS:
dubture_f_fmpeg:
binary_timeout: 600 # 10 minutes max
How can I help you explore Laravel packages today?