Windows Users:
For binary detection on Windows, ensure FFmpeg and FFProbe paths are added to your system path environment variable.
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
This library requires a functioning FFmpeg installation, including both FFMpeg and FFProbe binaries.
For binary detection on Windows, ensure FFmpeg and FFProbe paths are added to your system path environment variable.
Compatibility: This library is only compatible with PHP 8.2 or higher.
composer require aminyazdanpanah/php-ffmpeg-video-streaming
"require": {
"aminyazdanpanah/php-ffmpeg-video-streaming": "^1.2"
}
Import the Library:
require 'vendor/autoload.php'; // path to the autoload file
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);
There are multiple ways to open a media 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"');
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.
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");
Consult the FFmpeg documentation for capturing webcams and desktops.
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
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
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');
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();
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');
The autoGenerateRepresentations() method cannot be used for camera-based media.
See DASH options and DASH documentation for more information.
HTTP Live Streaming (also known as HLS) is an adaptive bitrate streaming protocol. It uses HTTP-based file downloads and M3U playlists. Learn more
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();
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();
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();
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');
The autoGenerateRepresentations() method cannot be used for camera-based media. HEVC and VP9 formats are not supported for HLS packaging unless using fragmented MP4.
See HLS options and HLS documentation for more information.
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
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');
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');
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.
It's crucial to secure your encryption keys on your website. Here are some methods:
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');
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();
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();
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.
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]);
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');
For HLS, you need to upload the master playlist file to the server manually before using this method.
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
This section demonstrates how to convert streams using our library. You'll provide a manifest URL to the input method.
// 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');
$stream = $ffmpeg->open('https://www.quasarstream.com/?PATH/TO/DASH-MANIFEST.MPD');
$stream->hls()
->x264()
->autoGenerateRepresentations([720, 360])
->save('/var/media/hls-stream.m3u8');
$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');
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");
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');
You can use the image as a video's poster.
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.
You can use the gif as a video's thumbnail.
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
You can also create a script to create packaged video files and create your own crontab file to run the script.
Here are some recommended open-source players for playing packaged videos:
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.