/PHP-FFmpeg-video-streaming

📼 Package media content for online streaming(DASH and HLS) using FFmpeg

Primary LanguagePHPMIT LicenseMIT

📼 PHP FFMPEG Video Streaming

Build Status Build status Scrutinizer Code Quality Total Downloads Latest Version on Packagist Software License

Overview

This package provides integration with PHP-FFmpeg and packages media content for online streaming such as DASH and HLS. You can also use DRM for HLS packaging. There are options to open a file from clouds and also save files to clouds.

  • This package uses FFMpeg, so before you get started, take the time to read the FFMpeg documentation.
  • Full Documentation is available describing all features and components.
  • For DRM and encryption, I recommend to try Shaka PHP, which is a great tool for this use case.

Contents

Requirements

  1. This version of the package is only compatible with PHP 7.2 or higher.

  2. To use this package, you need to install the FFMpeg. You will need both FFMpeg and FFProbe binaries to use it.

Installation

Install the package via composer:

composer require aminyazdanpanah/php-ffmpeg-video-streaming

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

"require": {
    "aminyazdanpanah/php-ffmpeg-video-streaming": "^1.1"
}

Quickstart

First of all, you need to include the package in Your Code:

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

Note: If you are using such a framework(e.g. Laravel) that auto include the autoload in your code, then you can skip this step.

Configuration

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

$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
];
    
$ffmpeg = Streaming\FFMpeg::create($config);

Opening a File

There are several ways to open a file:

1. From a Local Path

You can pass a local path of video to the open method:

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

2. From a Cloud

You can open a file by passing a URL to the fromURL method:

$video = $ffmpeg->fromURL('https://www.aminyazdanpanah.com/my_sweetie.mp4');

A path to save the file, the method of request, and request options can also be passed to the method.

$api = 'https://www.aminyazdanpanah.com/api/v1.0';
$save_to = '/var/www/media/videos/my_sweetie.mp4';
$method = 'POST';
$current_percentage = 0;
$options = [
    'auth' => ['username', 'password', 'digest'],
    'form_params' => [
        'user'      => 'USER_ID',
        'method'    => 'download',
        'file'      => ['dir3', 'videos', 'my_sweetie.mp4']
    ],
    'headers' => [
        'User-Agent'        => 'Mozilla/5.0 (compatible; AminYazdanpanahBot/1.0; +http://aminyazdanpanah.com/bots)',
        'Accept'            => 'application/json',
        'Authorization'     => 'Bearer ACCESS_TOKEN'
    ],
    'progress' => function(
        $downloadTotal,
        $downloadedBytes,
        $uploadTotal,
        $uploadedBytes
    ) use (&$current_percentage) {
        $percentage = ($downloadTotal > 200) ? intval(($downloadedBytes / $downloadTotal) * 100) : 0;
        if ($current_percentage !== $percentage) {
            // You can update a field in your database
            // You can also create a socket connection and show the progress to users
            echo "$percentage% is downloaded\n";
            $current_percentage = $percentage;
        }
    },
];

$video = $ffmpeg->fromURL($api, $save_to, $method, $options);

NOTE: This package uses Guzzle to send and receive files. Learn more.

3. From Amazon S3

Amazon S3 or Amazon Simple Storage Service is a service offered by Amazon Web Services (AWS) that provides object storage through a web service interface. Learn more

  • For getting credentials, you need to have an AWS account or you can create one.

For downloading a file from Amazon S3, you need to pass an associative array of options, the name of your bucket, and the key of your bucket to the fromS3 method:

$config = [
    'version'     => 'latest',
    'region'      => 'us-west-1',
    'credentials' => [
        'key'    => 'my-access-key-id',
        'secret' => 'my-secret-access-key',
    ]
];
$bucket = 'my-bucket-name';
$key = '/videos/my_sweetie.mp4';

$video = $ffmpeg->fromS3($config, $bucket, $key);

A path can also be passed to save the file on your local machine.

4. From Google Cloud Storage

Google Cloud Storage is a RESTful online file storage web service for storing and accessing data on Google Cloud Platform infrastructure. The service combines the performance and scalability of Google's cloud with advanced security and sharing capabilities. It is an Infrastructure as a Service (IaaS), comparable to Amazon S3 online storage service. Contrary to Google Drive and according to different service specifications, Google Cloud Storage appears to be more suitable for enterprises. Learn more

  • For creating credentials, read the Cloud Storage Authentication found here or you can create it directly (Select the "Service account key" option).

For downloading a file from Google Cloud Storage, you need to pass an associative array of config, the name of your bucket, and the name of your file in the bucket to the fromGCS method:

$config = [
    'keyFilePath' => '/path/to/credentials.json' // Alternativaely, you can authenticate by setting the environment variable. See https://cloud.google.com/docs/authentication/production#auth-cloud-implicit-php
];
$bucket = 'my_bucket';
$name = 'my_sweetie.mp4';

$video = $ffmpeg->fromGCS($config, $bucket, $name);

A path can also be passed to save the file on your local machine.

DASH

Dynamic Adaptive Streaming over HTTP (DASH), also known as MPEG-DASH, is an adaptive bitrate streaming technique that enables high quality streaming of media content over the Internet delivered from conventional HTTP web servers.

Similar to Apple's HTTP Live Streaming (HLS) solution, MPEG-DASH works by breaking the content into a sequence of small HTTP-based file segments, each segment containing a short interval of playback time of content that is potentially many hours in duration, such as a movie or the live broadcast of a sports event. The content is made available at a variety of different bit rates, i.e., alternative segments encoded at different bit rates covering aligned short intervals of playback time. While the content is being played back by an MPEG-DASH client, the client uses a bit rate adaptation (ABR) algorithm to automatically select the segment with the highest bit rate possible that can be downloaded in time for playback without causing stalls or re-buffering events in the playback. The current MPEG-DASH reference client dash.js offers both buffer-based (BOLA) and hybrid (DYNAMIC) bit rate adaptation algorithms. Thus, an MPEG-DASH client can seamlessly adapt to changing network conditions and provide high quality playback with fewer stalls or re-buffering events. Learn more

Create DASH Files:

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

You can also create multi-representations video files using the Representation object:

use Streaming\Representation;

$rep_1 = (new Representation())->setKiloBitrate(800)->setResize(1080 , 720);
$rep_2 = (new Representation())->setKiloBitrate(300)->setResize(640 , 360);

$video->DASH()
    ->HEVC()
    ->addRepresentation($rep_1) // Add a representation
    ->addRepresentation($rep_2) 
    ->setAdaption('id=0,streams=v id=1,streams=a') // Set a adaption.
    ->save('/var/www/media/videos/dash/test.mpd');

See DASH options for more information.

HLS

HTTP Live Streaming (also known as HLS) is an HTTP-based adaptive bitrate streaming communications protocol implemented by Apple Inc. as part of its QuickTime, Safari, OS X, and iOS software. Client implementations are also available in Microsoft Edge, Firefox and some versions of Google Chrome. Support is widespread in streaming media servers.

HLS resembles MPEG-DASH in that it works by breaking the overall stream into a sequence of small HTTP-based file downloads, each download loading one short chunk of an overall potentially unbounded transport stream. A list of available streams, encoded at different bit rates, is sent to the client using an extended M3U playlist. Learn more

Create HLS files based on original video(auto generate qualities).

$video->HLS()
    ->X264()
    ->autoGenerateRepresentations([720, 360]) // You can limit the numbers of representatons
    ->save();

Create multi-qualities video files using the Representation object(set bit-rate and size manually):

use Streaming\Representation;

$rep_1 = (new Representation())->setKiloBitrate(1000)->setResize(1080 , 720);
$rep_2 = (new Representation())->setKiloBitrate(500)->setResize(640 , 360);
$rep_3 = (new Representation())->setKiloBitrate(200)->setResize(480 , 270);

$video->HLS()
    ->X264()
    ->setHlsBaseUrl('https://bucket.s3-us-west-1.amazonaws.com/videos') // Add a base URL
    ->addRepresentation($rep_1)
    ->addRepresentation($rep_2)
    ->addRepresentation($rep_3)
    ->setHlsTime(5) // Set Hls Time. Default value is 10 
    ->setHlsAllowCache(false) // Default value is true 
    ->save();

NOTE: You cannot use HEVC and VP9 formats for HLS packaging.

Encrypted HLS

The encryption process requires some kind of secret (key) together with an encryption algorithm. HLS uses AES in cipher block chaining (CBC) mode. This means each block is encrypted using the ciphertext of the preceding block. Learn more

You need to pass both URL to the key and path to save a random key to the generateRandomKeyInfo method:

//A path you want to save a random key on your server
$save_to = '/var/www/my_website_project/keys/enc.key';

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

$video->HLS()
    ->X264()
    ->setTsSubDirectory('ts_files')// put all ts files in a subdirectory
    ->generateRandomKeyInfo($url, $save_to)
    ->autoGenerateRepresentations([1080, 480, 240])
    ->save('/var/www/media/videos/hls/test.m3u8');

NOTE: It is very important to protect your key on your website using a token or a session/cookie(It is highly recommended).

See HLS options for more information.

Transcoding

A format can also extend FFMpeg\Format\ProgressableInterface to get realtime information about the transcoding.

Transcoding progress can be monitored in realtime, see Format documentation in PHP-FFMpeg documentation for more information.

$format = new Streaming\Format\HEVC();
$current_percentage = 0;

$format->on('progress', function ($video, $format, $percentage) use (&$current_percentage) {
    if ($current_percentage !== intval($percentage)) {
        // You can update a field in your database
        // You can also create a socket connection and show the progress to users
        echo "$percentage% is transcoded\n";
        $current_percentage = intval($percentage);
    }
});

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

HLS Transcoding:

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

$format->on('progress', function ($video, $format, $percentage) use (&$current_percentage) {
    if ($current_percentage !== intval($percentage)) {
        echo "$percentage% is transcoded\n";
        $current_percentage = intval($percentage);
    }
});

$video->HLS()
    ->setFormat($format)
    ->setTsSubDirectory('ts_files')
    ->setHlsBaseUrl('https://bucket.s3-us-west-1.amazonaws.com/videos')
    ->autoGenerateRepresentations([240, 144], [200, 100]) // You can also set the kilo bite rate of each video
    ->save('/var/www/media/videos/dash/test.m3u8');

Saving Files

There are several options to save your packaged files.

1. To a Local Path

You can pass a local path to the save method. If there was no directory in the path, then the package auto makes the directory.

$dash = $video->DASH()
            ->HEVC()
            ->autoGenerateRepresentations()
            ->setAdaption('id=0,streams=v id=1,streams=a');
            
$dash->save('/var/www/media/videos/dash/test.mpd');

It can also be null. The default path to save files is the input path.

$hls = $video->HLS()
            ->X264()
            ->autoGenerateRepresentations();
            
$hls->save();

NOTE: If you opened a file from cloud and did not pass a path to save a file, then you have to pass a local path to the save method.

2. To a Cloud

You can save your files to a cloud using the saveToCloud method.

$api = 'https://www.aminyazdanpanah.com/api/v1.0/video/uploading';
$field_name = 'MY_FILES';
$method = 'POST';
$headers = [
    'User-Agent'        => 'Mozilla/5.0 (compatible; AminYazdanpanahBot/1.0; +http://aminyazdanpanah.com/bots)',
    'Accept'            => 'application/json',
    'Authorization'     => 'Bearer ACCESS_TOKEN'
];
$current_percentage = 0;
$options = [
    'auth' => ['username', 'password', 'digest'],
    'progress' => function(
        $downloadTotal,
        $downloadedBytes,
        $uploadTotal,
        $uploadedBytes
    ) use (&$current_percentage) {
        $percentage = ($uploadTotal > 200) ? intval(($uploadedBytes / $uploadTotal) * 100) : 0;
        if ($current_percentage !== $percentage) {
            // You can update a field in your database
            // You can also create a socket connection and show the progress to users
            echo "$percentage% is uploaded\n";
            $current_percentage = $percentage;
        }
    },
];

$dash->saveToCloud($api, $field_name, null, $method, $headers, $options);

A path can also be passed to save a copy of files on your local machine:

$save_to = '/var/www/media/videos/hls/test.m3u8';
$hls->saveToCloud($api, $field_name, $save_to, $method, $headers, $options);

3. TO Amazon S3

You can save and upload entire packaged video files to Amazon S3. For uploading files, you need to have credentials.

$config = [
    'version' => 'latest',
    'region' => 'us-west-1',
    'credentials' => [
        'key' => 'my-access-key-id',
        'secret' => 'my-secret-access-key',
    ]
];
$dest = 's3://bucket'; 

Upload DASH files to Amazon Simple Storage Service:

$dash->saveToS3($config, $dest);

A path can also be passed to save a copy of files on your local machine.

$hls->saveToS3($config, $dest, '/var/www/media/videos/hls/test.m3u8');

4. TO Google Cloud Storage

You can save and upload entire packaged video files to Google Cloud Storage. For uploading files, you need to have credentials.

$config = [
    'keyFilePath' => '/path/to/credentials.json'
];
$bucket = 'my_bucket';

$dash->saveToGCS($config, $bucket);

A path can also be passed to save a copy of files on your local machine.

$hls->saveToGCS($config, $bucket, '/var/www/media/videos/hls/test.m3u8');

NOTE: You can mix opening and saving options together. For instance, you can open a file on your local machine and save packaged files to a Cloud (or vice versa).

schema

Metadata Extraction

After saving files(wherever you saved them), you can extract the metadata from the video and streams:

$metadata = $hls->save();

echo $metadata['filename']; // path to metadata.json
var_dump($metadata['metadata']); // dump all metadata

NOTE: You can save these metadata to your database.

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 fromURL), it holds the Media object that belongs to the PHP-FFMpeg.

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

Example(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('image.jpg');

Asynchronous Task Execution

Packaging process will may take a while and it is recommended to run it in the background(or in a cloud e.g. Google Cloud). There are some libraries that you can use.

  • 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

  • Google Cloud Tasks: Google Cloud Tasks is a fully managed service that allows you to manage the execution, dispatch and delivery of a large number of distributed tasks. You can asynchronously perform work outside of a user request. Learn more

NOTE: It is not necessary to use these libraries. It is just a suggestion. You can also create a script to create packaged video files and run it in a cron-job.

Several Open Source Players

You can use these libraries to play your streams.

NOTE: You should pass a manifest of stream(e.g. https://www.aminyazdanpanah.com/videos/dash/lesson-1/test.mpd or /videos/hls/lesson-2/test.m3u8 ) to these players.

Contributing and Reporting Bugs

I'd love your help in improving, correcting, adding to the specification. Please file an issue or submit a pull request.

  • Please see Contributing File for more information.
  • If you have any questions or you want to report a bug, please just file an issue
  • If you discover a security vulnerability within this package, please see SECURITY File for more information to help with that.

NOTE: If you have any questions about this package or FFMpeg, please DO NOT send an email to me or submit the contact form in my website. Emails related to these issues will be ignored.

Credits

License

The MIT License (MIT). Please see License File for more information.