Complete video streaming solution with DASH and HLS support, DRM encryption, and cloud storage integration
DASH & HLS with adaptive bitrate
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).
Create DASH and HLS streams with multiple bitrate representations
AES-128 encryption for HLS with key rotation options
Direct integration with S3, Google Cloud, Azure and more
Support for live streaming from webcams and screen capture
This library requires a functioning FFmpeg installation, including both FFMpeg and FFProbe binaries.
Compatibility: This library is only compatible with PHP 8.2 or higher.
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"
}
}
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);
require 'vendor/autoload.php'; // path to the autoload file
There are multiple ways to open a media resource:
$video = $ffmpeg->open('/var/media/video.mp4');
For remote resources:
$video = $ffmpeg->open('https://www.quasarstream.com/?path/to/video.mp4');
$video = $ffmpeg->openFromCloud($from_google_cloud);
$capture = $ffmpeg->capture("CAMERA NAME OR SCREEN NAME");
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.
The library automatically generates Manifest (MPD) and segment files:
$video->dash()
->setAdaption('id=0,streams=v id=1,streams=a')
->x264()
->autoGenerateRepresentations()
->save();
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');
$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');
autoGenerateRepresentations()
method cannot be used for camera-based media.
HTTP Live Streaming (HLS) is an adaptive bitrate streaming protocol that uses HTTP-based file downloads and M3U playlists.
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();
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();
For fragmented MP4 format in HLS, use the <code class="inline-code">fragmentedMP4</code> method:
$video->hls()
->fragmentedMP4()
->x264()
->autoGenerateRepresentations([720, 360])
->save();
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');
autoGenerateRepresentations()
method cannot be used for camera-based media. HEVC and VP9 formats are not supported for HLS packaging unless using fragmented MP4.
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:
//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');
It's crucial to secure your encryption keys on your website:
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');
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();
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();
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]);
An illustration of the end-to-end process.
Cloud
Local
Cloud
Local
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');
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
Convert between different streaming formats by providing a manifest URL to the input method:
$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 use other advanced features from the PHP-FFMpeg library. The open method holds the Media object from PHP-FFMpeg:
Extract a frame at any timecode:
$frame = $video->frame(FFMpeg\Coordinate\TimeCode::fromSeconds(42));
$frame->save('/var/media/poster.jpg');
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');
For production environments, consider running packaging processes in the background:
Recommended open-source players for playing packaged videos:
Platform | Player | Supported Formats |
---|---|---|
Android | AndroidX Media | DASH & HLS |
iOS | Native Player | HLS |
Windows/Linux/macOS | FFmpeg (ffplay), VLC media player | DASH & HLS |
If you find this project useful, please support us: