What is PHP FFmpeg Video Streaming?

This library is a wrapper around PHP-FFMpeg and leverages FFmpeg to create media packages compatible with online streaming protocols like DASH (Dynamic Adaptive Streaming over HTTP) and HLS (HTTP Live Streaming). It also offers functionalities for DRM (Digital Rights Management) for HLS packaging and managing files stored in cloud storage.

GitHub Repository: https://github.com/quasarstream/php-ffmpeg-video-streaming

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

Usage

Import the Library:

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

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')); // path to log file

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

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');

Refer to the FFmpeg Protocols Documentation for a list of supported resources (http, ftp, etc.).

$video = $ffmpeg->open('https://www.quasarstream.com/?"PATH TO A VIDEO FILE" or "PATH TO A LIVE HTTP STREAM"');
2. Cloud Storage:

You can open a file from a cloud by passing an instance of the Cloud object to the `input` method.

$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:

You can pass the name of a supported, connected capture device(i.e. the name of a webcam, camera, screen and etc) to the input method to stream a live media over the network from your connected device.

$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. Clients can choose the segment with the highest bitrate suitable for their network conditions, resulting in a seamless playback experience. Learn more

Creating DASH Files:

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

$video->dash()
    ->setAdaption('id=0,streams=v id=1,streams=a') // Set the adaption.
    ->x264() // Format of the video. Alternatives: x264() and vp9()
    ->autoGenerateRepresentations() // Auto generate representations
    ->save(); // It can be passed a path to the method or it can be null
Generating Representations Manually:

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

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);
$r_2k    = (new Representation)->setKiloBitrate(6144)->setResize(2560, 1440);
$r_4k    = (new Representation)->setKiloBitrate(17408)->setResize(3840, 2160);

$video->dash()
    ->setSegDuration(30) // Default value is 10
    ->setAdaption('id=0,streams=v id=1,streams=a')
    ->x264()
    ->addRepresentations([$r_144p, $r_240p, $r_360p, $r_480p, $r_720p, $r_1080p, $r_2k, $r_4k])
    ->save('/var/media/dash-stream.mpd');
Generating HLS Playlists:

To automatically generate HLS playlists for DASH and HLS, use the auto_generate_representations method:

$video->dash()
    ->generateHlsPlaylist()
    ->setAdaption('id=0,streams=v id=1,streams=a')
    ->x264()
    ->autoGenerateRepresentations()
    ->save();
DASH from Camera/Screen:

For live streaming from a camera or screen, create Representation objects and add them to the dash instance:

use Streaming\Representation;

$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.

NOTE:

See DASH options and DASH documentation for more information.

HLS:

HTTP Live Streaming (also known as HLS) is an adaptive bitrate streaming protocol. It uses HTTP-based file downloads and M3U playlists. Learn more

Creating HLS Files:

To create HLS files, use the hls instance and specify the output filename:

$video->hls()
    ->x264()
    ->autoGenerateRepresentations([720, 360]) // You can limit the numbers of representatons
    ->save();
Generating Representations Manually:

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

use Streaming\Representation;

$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') // Add a base URL
    ->setHlsTime(5) // Set Hls Time. Default value is 10
    ->setHlsAllowCache(false) // Default value is true
    ->x264()
    ->addRepresentations([$r_360p, $r_480p, $r_720p])
    ->save();
Fragmented MP4:

For fragmented MP4 format in HLS, use the fragmented_mp4 method:

$video->hls()
    ->fragmentedMP4()
    ->x264()
    ->autoGenerateRepresentations([720, 360]) // You can limit the numbers of representatons
    ->save();
HLS Live Streaming:

For live streaming from a camera or screen, use the hls instance and specify the output filename:

use Streaming\Representation;

$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.mpd');

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.

NOTE:

See HLS options and HLS documentation for more information.

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. This key file must be accessible from your website.
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. For example, with a value of 10, a new key will be created every 10 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, 10)
    ->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, which offers secure key exchange and playback protection across devices. Other options include Microsoft's PlayReady and Google’s Widevine.

Protecting Your Keys:

It's crucial to secure your encryption keys on your website. Here are some methods:

  • Token-based access control: Use a token (via GET/POST methods or authorization headers) to verify if a user is authorized to access the key.
  • Session or cookie-based access control: Implement session or cookie management to restrict access to authorized users.
Subtitles

You can add subtitles to a HLS stream using 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 also extend FFMpeg\Format\ProgressableInterface to get realtime information about the transcoding.

The following code demonstrates how to monitor 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 can 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 From a Terminal:

Output Options:

1. Local Path:

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

Here's an example using DASH:

$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, the library will save the files to the same directory as the input file by default.

Here's an example using HLS:

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

$hls->save();

Important Note:

When opening a file from cloud storage without specifying a local path, you must provide an output path using save to save it to your local machine.

2. Cloud Storage (VOD Only):

The library allows you to save packaged files directly to cloud storage providers. This functionality is currently limited to Video On-Demand (VOD) and doesn't support live streaming.

To achieve this, you'll need to create a CloudManager object and configure your cloud storage credentials. Here's an example using Amazon S3:

$dash->save(null, [$to_aws_cloud]);

For configuration details and examples with other cloud providers like Google Cloud Storage, Microsoft Azure Storage, refer to the provided link.

Additionally, you can save a local copy of the files while uploading to cloud storage:

$hls->save('/var/media/hls.m3u8', [$to_google_cloud, $to_custom_cloud]);
3. Uploading to Server (Live Streaming)

This method allows you to upload the segmented files and update manifest files directly to an HTTP server or other supported protocols like FTP. This functionality is suitable for live streaming scenarios.

// 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');

Important Note:

For HLS, you need to upload the master playlist file to the server manually before using this method.

Extracting Metadata from Videos:

You can get information from multimedia streams and the video file using the following code.

$hls = $hls->save();

$metadata = $hls->metadata();

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

var_dump($metadata->getVideoStreams()); // FFMpeg\FFProbe\DataMapping\StreamCollection object
var_dump($metadata->getFormat()); // FFMpeg\FFProbe\DataMapping\Format object
echo gmdate("H:i:s", intval($metadata->getFormat()->get('duration'))); // get the duration of the video (i.e. "01:34:26")

print_r($metadata->export()); // print and save metadata

Stream Conversion

This section demonstrates how to convert streams using our library. You'll provide a manifest URL to the input method.

Supported Conversions:
1. HLS To DASH:
// Replace with the actual HLS manifest URL
$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');
2. 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');
3. Stream (DASH or HLS) 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');

Other Advanced Features

You can easily use other advanced features in the PHP-FFMpeg library. In fact, when you open a file with the open method(or openFromCloud), it holds the Media object that belongs to the PHP-FFMpeg.

$ffmpeg = Streaming\FFMpeg::create()
$video = $ffmpeg->fromURL("https://www.quasarstream.com/?video.mp4", "/var/wwww/media/my/new/video.mp4");

Extracting image

You can extract a frame at any timecode using the FFMpeg\Media\Video::frame method.

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

NOTE:

You can use the image as a video's poster.

Gif

A gif is an animated image extracted from a sequence of the video. You can save gif files using the FFMpeg\Media\Gif::save method.

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

This method has a third optional boolean parameter, which is the duration of the animation. If you don't set it, you will get a fixed gif image.

NOTE:

You can use the gif as a video's thumbnail.

Asynchronous Task Execution

Packaging process might take a while and it is recommended to run it in the background(or in a cloud e.g. AWS). There are some libraries that you can use for this use case.

Symphony(The Console Component): You can use this library to create command-line commands. Your console commands can be used for any recurring task, such as cronjobs, imports, or other batch jobs. Learn more.

Laravel(Queues): If you are using Laravel for development, Laravel Queues is a wonderful tool for this use case. It allows you to create a job and dispatch it. Learn more

NOTE:

You can also create a script to create packaged video files and create your own crontab file to run the script.

Open-Source Players for Playback

Here are some recommended open-source players for playing packaged videos:




Important Notes:

  • Manifest URL: Always pass the URL of the master playlist (manifest) to the players.
  • Cloud Storage: If saving your stream content to a cloud (e.g., Amazon S3), ensure the content link is public.
  • iOS DASH Support: iOS does not have native support for DASH. While there are libraries like Viblast and MPEGDASH-iOS-Player, their compatibility may vary.

💗 If you find this project helpful, please give it a ⭐️ Star on GitHub.💗

Build Your Own OTT Platform

Need a solution that's perfectly aligned with your unique requirements? Our custom features can handle high volume and complexity. Contact Us to discuss your needs.

Basic

  • Video On-Demand (HLS and DASH)
  • Video Quality Settings
  • Real-Time Progress Monitoring
  • Dark and light theme
Pro

  • Live Streaming
  • Add Subtitles and Audios
  • Monetization
  • Robust DRM Systems