phpffmpegapilaravel

How to Use FFmpeg with PHP (No Installation Required)

·Javid Jamae·6 min read
How to Use FFmpeg with PHP (No Installation Required)

You need to process video in your PHP application. Resize uploads, convert formats, extract thumbnails, or generate social clips from user content. You search "php ffmpeg" and find tutorials telling you to install FFmpeg on your server and call it with exec().

That works on your local machine. It falls apart the moment you deploy.

TL;DR: You can process video from PHP without installing FFmpeg. Send an HTTP request to a cloud API, get results back. Works anywhere PHP runs: shared hosting, Laravel Forge, serverless, Docker.

The exec() Approach (And Why It Breaks)

The standard PHP approach to FFmpeg looks like this:

$input = escapeshellarg("/path/to/input.mp4");
$output = escapeshellarg("/path/to/output.mp4");

exec("ffmpeg -i {$input} -c:v libx264 -crf 23 {$output} 2>&1", $out, $code);

if ($code !== 0) {
    throw new RuntimeException("FFmpeg failed: " . implode("\n", $out));
}

On your dev machine, this is fine. In production:

  • Shared hosting almost never has FFmpeg installed, and you can't add it
  • exec() is disabled on many hosts for security reasons
  • Laravel Forge and similar platforms require manual FFmpeg setup per server
  • Serverless environments (Laravel Vapor, Vercel) have no persistent filesystem for FFmpeg binaries
  • You're responsible for codec updates, security patches, and disk space for temp files

The php-ffmpeg Library

PHP-FFmpeg wraps the binary in an object-oriented API:

$ffmpeg = FFMpeg\FFMpeg::create();
$video = $ffmpeg->open("input.mp4");

$video->filters()->resize(new FFMpeg\Coordinate\Dimension(1280, 720));
$video->save(new FFMpeg\Format\Video\X264(), "output.mp4");

Cleaner syntax, but it still requires FFmpeg installed on the machine. It's a wrapper, not a replacement. The library's GitHub repo has hundreds of open issues and updates have slowed considerably.

Processing Video via API (No FFmpeg Needed)

FFmpeg Micro is a cloud API that runs FFmpeg for you. One HTTP request from PHP, and you can transcode, resize, compress, or watermark video. No binary, no server config.

With PHP's built-in cURL:

$apiKey = getenv("FFMPEG_MICRO_API_KEY");

$ch = curl_init("https://api.ffmpeg-micro.com/v1/transcodes");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer " . $apiKey,
        "Content-Type: application/json",
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "inputs" => [["url" => "https://example.com/video.mp4"]],
        "outputFormat" => "mp4",
        "preset" => ["quality" => "high", "resolution" => "1080p"],
    ]),
    CURLOPT_RETURNTRANSFER => true,
]);

$response = json_decode(curl_exec($ch), true);
curl_close($ch);

echo "Job ID: " . $response["id"];

If you're using Laravel or any framework with Guzzle:

use GuzzleHttp\Client;

$client = new Client([
    "base_uri" => "https://api.ffmpeg-micro.com",
    "headers" => ["Authorization" => "Bearer " . $apiKey],
]);

$response = $client->post("/v1/transcodes", [
    "json" => [
        "inputs" => [["url" => "https://example.com/video.mp4"]],
        "outputFormat" => "mp4",
        "preset" => ["quality" => "high", "resolution" => "1080p"],
    ],
]);

$job = json_decode($response->getBody(), true);
echo "Job ID: " . $job["id"];

Both approaches do the same thing. Pick whichever matches your project.

Checking Job Status and Downloading Results

Transcoding takes seconds to minutes depending on file size. Poll for status:

$jobId = $response["id"];

do {
    sleep(2);
    $ch = curl_init("https://api.ffmpeg-micro.com/v1/transcodes/{$jobId}");
    curl_setopt_array($ch, [
        CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $apiKey],
        CURLOPT_RETURNTRANSFER => true,
    ]);
    $status = json_decode(curl_exec($ch), true);
    curl_close($ch);
} while ($status["status"] === "queued" || $status["status"] === "processing");

if ($status["status"] === "completed") {
    $ch = curl_init("https://api.ffmpeg-micro.com/v1/transcodes/{$jobId}/download");
    curl_setopt_array($ch, [
        CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $apiKey],
        CURLOPT_RETURNTRANSFER => true,
    ]);
    $download = json_decode(curl_exec($ch), true);
    curl_close($ch);

    echo "Download: " . $download["url"];
}

The download endpoint returns a signed URL valid for 10 minutes. Save the file locally or redirect your user to it.

Advanced: Custom FFmpeg Options

Presets cover most cases. For full control, pass raw FFmpeg options:

$response = $client->post("/v1/transcodes", [
    "json" => [
        "inputs" => [["url" => "https://example.com/video.mp4"]],
        "outputFormat" => "webm",
        "options" => [
            ["option" => "-c:v", "argument" => "libvpx-vp9"],
            ["option" => "-crf", "argument" => "30"],
            ["option" => "-b:v", "argument" => "0"],
            ["option" => "-c:a", "argument" => "libopus"],
        ],
    ],
]);

Same FFmpeg flags you'd use on the command line, passed as structured JSON. This converts MP4 to WebM using VP9 and Opus codecs.

Uploading Local Files

If your video isn't hosted at a public URL, upload it first. Three steps:

// Step 1: Get a presigned upload URL
$presign = $client->post("/v1/upload/presigned-url", [
    "json" => [
        "filename" => "my-video.mp4",
        "contentType" => "video/mp4",
        "fileSize" => filesize("/path/to/my-video.mp4"),
    ],
]);
$presignData = json_decode($presign->getBody(), true);

// Step 2: Upload directly to cloud storage
$uploadClient = new Client();
$uploadClient->put($presignData["result"]["uploadUrl"], [
    "headers" => ["Content-Type" => "video/mp4"],
    "body" => fopen("/path/to/my-video.mp4", "r"),
]);

// Step 3: Confirm the upload
$confirm = $client->post("/v1/upload/confirm", [
    "json" => [
        "filename" => $presignData["result"]["filename"],
        "fileSize" => filesize("/path/to/my-video.mp4"),
    ],
]);
$confirmData = json_decode($confirm->getBody(), true);

// Use the GCS URL in your transcode request
$gcsUrl = $confirmData["result"]["gcsUrl"];

After confirmation, pass the returned GCS URL as the input for your transcode job.

Common Pitfalls

  • **Using file_get_contents for API calls.** It doesn't support custom headers cleanly. Use cURL or Guzzle.
  • **Forgetting json_encode on the request body.** The API expects JSON, not form-encoded data. Set the Content-Type header and encode your payload.
  • Polling too aggressively. A 2-second interval is plenty. Don't hit the status endpoint every 100ms.
  • **Ignoring the failed status.** Always check for failure in your polling loop. An infinite loop on a failed job wastes resources and never resolves.
  • Hardcoding API keys. Use environment variables. In Laravel: config("services.ffmpeg_micro.key"). In vanilla PHP: getenv("FFMPEG_MICRO_API_KEY").

Frequently Asked Questions

Does this work on shared hosting?

Yes. The API approach only needs PHP's cURL extension, which virtually every host has enabled. No FFmpeg binary, no exec(), no special server permissions.

What PHP version do I need?

PHP 7.4 or later. The code examples use modern array syntax and type handling. PHP 8+ works fine and gives you named arguments for cleaner Guzzle calls.

How much does FFmpeg Micro cost?

FFmpeg Micro bills per minute of video processed. The free tier gives you enough minutes to build and test your integration. A 5-minute video costs a few cents to transcode. Check the pricing page for current rates.

Can I process video synchronously in a web request?

You can for short clips, but you shouldn't for anything substantial. Queue the transcode job, return a 202 to your user, and poll from a background worker. Laravel's job system or a simple cron works well for this.

What video formats are supported?

MP4, WebM, and MOV for video output. The API also supports image output formats (JPEG, PNG, WebP) for extracting thumbnails. See the thumbnail guide for details on frame extraction.

*Last verified: May 2026. All API examples tested against FFmpeg Micro v1.*

About Javid Jamae

Founder & CEO at FFmpeg Micro

Javid is a software engineer, author, and entrepreneur with over 25 years of professional software development experience across enterprise, startup, and consulting environments. He founded FFmpeg Micro to make video processing accessible to developers through a simple, automation-first REST API.

Software EngineeringVideo ProcessingFFmpegCloud ArchitectureAPI DesignAutomation

Ready to process videos at scale?

Start using FFmpeg Micro's simple API today. No infrastructure required.

Get Started Free