Loading...

PHP FFmpeg Video Streaming

Complete video streaming solution with DASH and HLS support, DRM encryption, and cloud storage integration

Streaming Ready

DASH & HLS with adaptive bitrate

Introduction

PHP FFmpeg Video Streaming is a comprehensive wrapper around PHP-FFMpeg that leverages FFmpeg to create media packages compatible with online streaming protocols like DASH (Dynamic Adaptive Streaming over HTTP) and HLS (HTTP Live Streaming).

Adaptive Streaming

Create DASH and HLS streams with multiple bitrate representations

DRM Support

AES-128 encryption for HLS with key rotation options

Cloud Integration

Direct integration with S3, Google Cloud, Azure and more

Live Streaming

Support for live streaming from webcams and screen capture

Requirements

1. FFMpeg

This library requires a functioning FFmpeg installation, including both FFMpeg and FFProbe binaries.

Windows Users: For binary detection on Windows, ensure FFmpeg and FFProbe paths are added to your system path environment variable.

2. PHP

Compatibility: This library is only compatible with PHP 8.2 or higher.

Installation

Package Installation

Using composer:

composer require aminyazdanpanah/php-ffmpeg-video-streaming

Alternatively, add the dependency to your composer.json file:

{
    "require": {
        "aminyazdanpanah/php-ffmpeg-video-streaming": "^1.2"
    }
}
Want to build an OTT platform? The basic version has everything you need. Online Demo Get Now

Configuration

This package will autodetect FFmpeg and FFprobe binaries. If you want to give binary paths explicitly, you can pass an array as configuration. A Psr\Logger\LoggerInterface can also be passed to log binary executions.

use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use Streaming\FFMpeg;

$config = [
    'ffmpeg.binaries'  => '/usr/bin/ffmpeg',
    'ffprobe.binaries' => '/usr/bin/ffprobe',
    'timeout'          => 3600, // The timeout for the underlying process
    'ffmpeg.threads'   => 12,   // The number of threads that FFmpeg should use
];

$log = new Logger('FFmpeg_Streaming');
$log->pushHandler(new StreamHandler('/var/log/ffmpeg-streaming.log'));

$ffmpeg = FFMpeg::create($config, $log);

Usage

Import the Library

require 'vendor/autoload.php'; // path to the autoload file

Opening a Resource

There are multiple ways to open a media resource:

1. Local File or Supported Resource

$video = $ffmpeg->open('/var/media/video.mp4');

For remote resources:

$video = $ffmpeg->open('https://www.quasarstream.com/?path/to/video.mp4');
Refer to the FFmpeg Protocols Documentation for a list of supported resources (http, ftp, etc.).

2. Cloud Storage

$video = $ffmpeg->openFromCloud($from_google_cloud);
Visit the provided link for examples on accessing files from Amazon S3, Google Cloud Storage, Microsoft Azure Storage, and custom cloud storage solutions.

3. Live Streaming from Webcam or Screen

$capture = $ffmpeg->capture("CAMERA NAME OR SCREEN NAME");
NOTE: Consult the FFmpeg documentation for capturing webcams and desktops.

DASH (Dynamic Adaptive Streaming over HTTP)

Dynamic Adaptive Streaming over HTTP (DASH), also known as MPEG-DASH, allows high-quality streaming over the internet by segmenting content into short HTTP-based file downloads.

Creating DASH Files

The library automatically generates Manifest (MPD) and segment files:

$video->dash()
        ->setAdaption('id=0,streams=v id=1,streams=a')
        ->x264()
        ->autoGenerateRepresentations()
        ->save();

Generating Representations Manually

use Streaming\Representation;

$r_144p  = (new Representation)->setKiloBitrate(95)->setResize(256, 144);
$r_240p  = (new Representation)->setKiloBitrate(150)->setResize(426, 240);
$r_360p  = (new Representation)->setKiloBitrate(276)->setResize(640, 360);
$r_480p  = (new Representation)->setKiloBitrate(750)->setResize(854, 480);
$r_720p  = (new Representation)->setKiloBitrate(2048)->setResize(1280, 720);
$r_1080p = (new Representation)->setKiloBitrate(4096)->setResize(1920, 1080);

$video->dash()
    ->setSegDuration(30)
    ->setAdaption('id=0,streams=v id=1,streams=a')
    ->x264()
    ->addRepresentations([$r_144p, $r_240p, $r_360p, $r_480p, $r_720p, $r_1080p])
    ->save('/var/media/dash-stream.mpd');

DASH from Camera/Screen

$r_240p = (new Representation)->setKiloBitrate(150)->setResize(426, 240);
$r_360p = (new Representation)->setKiloBitrate(276)->setResize(640, 360);

$capture->dash()
    ->setAdaption('id=0,streams=v id=1,streams=a')
    ->x264()
    ->addRepresentations([$r_240p, $r_360p])
    ->save('/var/media/dash-stream.mpd');
Note: The autoGenerateRepresentations() method cannot be used for camera-based media.

HLS (HTTP Live Streaming)

HTTP Live Streaming (HLS) is an adaptive bitrate streaming protocol that uses HTTP-based file downloads and M3U playlists.

Creating HLS Files

To create HLS files, use the <code class="inline-code">hls</code> instance and specify the output filename:

$video->hls()
    ->x264()
    ->autoGenerateRepresentations([720, 360])
    ->save();

Manual Representations

To specify the exact kilobit rate and size for each stream, manually create Representation objects and add them to the dash or hls instance:

$r_360p = (new Representation)->setKiloBitrate(276)->setResize(640, 360);
$r_480p = (new Representation)->setKiloBitrate(750)->setResize(854, 480);
$r_720p = (new Representation)->setKiloBitrate(2048)->setResize(1280, 720);

$video->hls()
    ->setHlsBaseUrl('https://bucket.s3-us-west-1.amazonaws.com/videos')
    ->setHlsTime(5)
    ->setHlsAllowCache(false)
    ->x264()
    ->addRepresentations([$r_360p, $r_480p, $r_720p])
    ->save();

Fragmented MP4

For fragmented MP4 format in HLS, use the <code class="inline-code">fragmentedMP4</code> method:

$video->hls()
    ->fragmentedMP4()
    ->x264()
    ->autoGenerateRepresentations([720, 360])
    ->save();

HLS Live Streaming

For live streaming from a camera or screen, use the <code class="inline-code">hls</code> instance and specify the output filename:

$r_480p = (new Representation)->setKiloBitrate(750)->setResize(854, 480);

$capture->hls()
    ->setHlsListSize(10) // Delete this line if you want to save video after live streaming
    ->setFlags([\Streaming\HLSFlag::DELETE_SEGMENTS]) // Delete this line if you want to save video after live streaming
    ->x264()
    ->addRepresentation($r_480p)
    ->save('/var/media/hls-stream.m3u8');
Note: The autoGenerateRepresentations() method cannot be used for camera-based media. HEVC and VP9 formats are not supported for HLS packaging unless using fragmented MP4.

Encryption (DRM)

HLS encryption uses the Advanced Encryption Standard (AES) in Cipher Block Chaining (CBC) mode. This means each block is encrypted using the previous block's ciphertext. Learn more

Generating Encryption Keys

  • Path: Specify a path to save a random key file on your local machine
  • URL: Provide a URL (or path) on your website where the key file can be accessed

Single Key Encryption

The following code generates a single key for all segments:

//A path you want to save a random key to your local machine
$save_to = '/home/public_html/PATH_TO_KEY_DIRECTORY/key';

//A URL (or a path) to access the key on your website
$url = 'https://www.quasarstream.com/?PATH_TO_KEY_DIRECTORY/key';
// or $url = '/PATH_TO_KEY_DIRECTORY/random_key.key';

$video->hls()
    ->encryption($save_to, $url)
    ->x264()
    ->autoGenerateRepresentations([1080, 480, 240])
    ->save('/var/media/hls-stream.m3u8');

Key Rotation

You can optionally rotate encryption keys periodically by passing a "key rotation period" to the encryption method. This generates a new key after a specified number of segments:

//A path you want to save a random key to your local machine
    $save_to = '/home/public_html/PATH_TO_KEY_DIRECTORY/key';

//A URL (or a path) to access the key on your website
$url = 'https://www.quasarstream.com/?PATH_TO_KEY_DIRECTORY/key';

$video->hls()
    ->encryption($save_to, $url, 10) // New key every 10 segments
    ->x264()
    ->autoGenerateRepresentations([720, 240])
    ->save('/var/media/hls-stream.m3u8');
Limitations and Alternatives: While FFmpeg supports AES encryption for HLS, it's not a complete Digital Rights Management (DRM) solution. For a full DRM solution, consider FairPlay Streaming, Microsoft's PlayReady, or Google's Widevine.

Protecting Your Keys

It's crucial to secure your encryption keys on your website:

  • Token-based access control: Use tokens via GET/POST methods or authorization headers
  • Session or cookie-based access control: Implement session management to restrict access

Subtitles

You can add subtitles to HLS streams using the subtitle method:

use Streaming\HLSSubtitle;

$persian = new HLSSubtitle('/var/subtitles/subtitles_fa.vtt', 'فارسی', 'fa');
$persian->default();
$english = new HLSSubtitle('/var/subtitles/subtitles_en.vtt', 'english', 'en');
$german  = new HLSSubtitle('/var/subtitles/subtitles_de.vtt', 'Deutsch', 'de');
$chinese = new HLSSubtitle('/var/subtitles/subtitles_zh.vtt', '中文', 'zh');
$spanish = new HLSSubtitle('/var/subtitles/subtitles_es.vtt', 'Español', 'es');

$video->hls()
    ->subtitles([$persian, $english, $german, $chinese, $spanish])
    ->x264()
    ->autoGenerateRepresentations([1080, 720])
    ->save('/var/media/hls-stream.m3u8');
Note: All m3u8 files will be generated using rules based on RFC 8216. Only WebVTT files are acceptable for now.

Transcoding Monitoring

A format can extend FFMpeg\Format\ProgressableInterface to get realtime information about the transcoding process:

$format = new Streaming\Format\X264();
$start_time = 0;

$percentage_to_time_left = function ($percentage) use (&$start_time) {
    if($start_time === 0){
        $start_time = time();
        return "Calculating...";
    }

    $diff_time = time() - $start_time;
    $seconds_left = 100 * $diff_time / $percentage - $diff_time;

    return gmdate("H:i:s", $seconds_left);
};

$format->on('progress', function ($video, $format, $percentage) use($percentage_to_time_left) {
    // You can update a field in your database or log it to a file
    // You can also create a socket connection and show a progress bar to users
    echo sprintf("\rTranscoding...(%s%%) %s [%s%s]",
        $percentage,
        $percentage_to_time_left($percentage),
        str_repeat('#', $percentage),
        str_repeat('-', (100 - $percentage))
    );
});

$video->dash()
    ->setFormat($format)
    ->autoGenerateRepresentations()
    ->setAdaption('id=0,streams=v id=1,streams=a')
    ->save();

Output Options

1. Local Path

You can define the output location by passing a local path. If the directory doesn't exist, the library will automatically create it:

$dash = $video->dash()
    ->x264()
    ->autoGenerateRepresentations()
    ->setAdaption('id=0,streams=v id=1,streams=a');

$dash->save('/var/media/dash/test.mpd');

If you omit the save parameter, files will be saved to the same directory as the input file:

$hls = $video->hls()
    ->x264()
    ->autoGenerateRepresentations();

$hls->save();
Important: When opening a file from cloud storage without specifying a local path, you must provide an output path using save().

2. Cloud Storage (VOD Only)

Save packaged files directly to cloud storage providers (Video On-Demand only):

$dash->save(null, [$to_aws_cloud]);
$hls->save('/var/media/hls.m3u8', [$to_google_cloud, $to_custom_cloud]);

Video Transcoding Workflow

An illustration of the end-to-end process.

Input Video File
Output Transcoded File

Input Source

Cloud

Local

Transcoding

HLS
DASH

Output

Cloud

Local

Visit the provided link for examples on accessing files from Amazon S3, Google Cloud Storage, Microsoft Azure Storage, and custom cloud storage solutions.

3. Uploading to Server (Live Streaming)

Upload segmented files and update manifest files directly to an HTTP server:

// DASH
$dash->live('http://YOUR-WEBSITE.COM/live-stream/out.mpd');

// HLS
$hls->setMasterPlaylist('/var/media/live-master-manifest.m3u8')
    ->live('http://YOUR-WEBSITE.COM/live-stream/out.m3u8');

Extracting Metadata

Get information from multimedia streams and video files:

$hls = $hls->save();
$metadata = $hls->metadata();

print_r($metadata->get()); // print an array
$metadata->saveAsJson('path/to/meta.json'); // save metadata as JSON

var_dump($metadata->getVideoStreams()); // StreamCollection object
var_dump($metadata->getFormat()); // Format object
echo gmdate("H:i:s", intval($metadata->getFormat()->get('duration'))); // duration

Stream Conversion

Convert between different streaming formats by providing a manifest URL to the input method:

HLS to DASH

$stream = $ffmpeg->open('https://www.quasarstream.com/?PATH/TO/HLS-MANIFEST.M3U8');

$stream->dash()
    ->x264()
    ->addRepresentations([$r_360p, $r_480p])
    ->save('/var/media/dash-stream.mpd');

DASH to HLS

$stream = $ffmpeg->open('https://www.quasarstream.com/?PATH/TO/DASH-MANIFEST.MPD');

$stream->hls()
    ->x264()
    ->autoGenerateRepresentations([720, 360])
    ->save('/var/media/hls-stream.m3u8');

Stream to File

$format = new Streaming\Format\X264();
$format->on('progress', function ($video, $format, $percentage){
    echo sprintf("\rTranscoding...(%s%%) [%s%s]",
        $percentage,
        str_repeat('#', $percentage),
        str_repeat('-', (100 - $percentage))
    );
});

$stream->stream2file()
    ->setFormat($format)
    ->save('/var/media/new-video.mp4');

Advanced Features

You can use other advanced features from the PHP-FFMpeg library. The open method holds the Media object from PHP-FFMpeg:

Extracting Images

Extract a frame at any timecode:

$frame = $video->frame(FFMpeg\Coordinate\TimeCode::fromSeconds(42));
$frame->save('/var/media/poster.jpg');

Creating GIFs

Create animated images from video sequences:

$video->gif(
        FFMpeg\Coordinate\TimeCode::fromSeconds(2),
        new FFMpeg\Coordinate\Dimension(640, 480),
        3 // duration in seconds
    )
        ->save('/var/media/animated_image.gif');

Asynchronous Task Execution

For production environments, consider running packaging processes in the background:

  • Symfony Console Component: Create command-line commands for cronjobs and batch processing
  • Laravel Queues: Dispatch jobs to background queues for processing
  • Custom Scripts: Create scripts with crontab for scheduled processing

Open-Source Players

Recommended open-source players for playing packaged videos:

Web Players

DASH and HLS Support

DASH Only

HLS Only

Mobile and Desktop Players

Platform Player Supported Formats
Android AndroidX Media DASH & HLS
iOS Native Player HLS
Windows/Linux/macOS FFmpeg (ffplay), VLC media player DASH & HLS
Important Notes:
  • Always pass the URL of the master playlist (manifest) to the players
  • If saving stream content to cloud storage, ensure the content link is public
  • iOS does not have native support for DASH. Libraries like Viblast and MPEGDASH-iOS-Player exist but compatibility may vary

Support Us

If you find this project useful, please support us:

  • ❤️ Become a sponsor or Donate on GitHub
  • Star the repository on GitHub
  • 🔗 Fork the repository on GitHub and fix bugs
  • 🔁 Share it with your developer communities
  • 🧩 Contribute by opening issues or pull requests
  • 💬 Give feedback or feature requests via GitHub Discussions or Issues