Stream videos to HTML5 video container using HTTP & PHP

Sometimes we need to feed videos dynamically from the server-side. If you’re feeding the video to a HTML5 <video> element, you may find that the video progress controls freezes and users cannot move it in any ways. (Thought this situation only happens in some browsers like Chrome and Firefox, the user experience hurts a lot.)

With a bit of investigation, I found out that Chrome requested the video with an HTTP range request which, the server-side handle it incorrectly and Chrome falls back to progressive downloading the video. With the little PHP script I wrote below, the server-side can now handle the HTTP range requests normally and the progress controls no longer freezes! (There’s one more benefit: fast forward and backward works much smoother in large video files.)

<?php
// Clears the cache and prevent unwanted output
ob_clean();
@ini_set('error_reporting', E_ALL & ~ E_NOTICE);
@apache_setenv('no-gzip', 1);
@ini_set('zlib.output_compression', 'Off');

$file = "/path/to/your/media/file"; // The media file's location
$mime = "application/octet-stream"; // The MIME type of the file, this should be replaced with your own.
$size = filesize($file); // The size of the file

// Send the content type header
header('Content-type: ' . $mime);

// Check if it's a HTTP range request
if(isset($_SERVER['HTTP_RANGE'])){
    // Parse the range header to get the byte offset
    $ranges = array_map(
        'intval', // Parse the parts into integer
        explode(
            '-', // The range separator
            substr($_SERVER['HTTP_RANGE'], 6) // Skip the `bytes=` part of the header
        )
    );

    // If the last range param is empty, it means the EOF (End of File)
    if(!$ranges[1]){
        $ranges[1] = $size - 1;
    }

    // Send the appropriate headers
    header('HTTP/1.1 206 Partial Content');
    header('Accept-Ranges: bytes');
    header('Content-Length: ' . ($ranges[1] - $ranges[0])); // The size of the range

    // Send the ranges we offered
    header(
        sprintf(
            'Content-Range: bytes %d-%d/%d', // The header format
            $ranges[0], // The start range
            $ranges[1], // The end range
            $size // Total size of the file
        )
    );

    // It's time to output the file
    $f = fopen($file, 'rb'); // Open the file in binary mode
    $chunkSize = 8192; // The size of each chunk to output

    // Seek to the requested start range
    fseek($f, $ranges[0]);

    // Start outputting the data
    while(true){
        // Check if we have outputted all the data requested
        if(ftell($f) >= $ranges[1]){
            break;
        }

        // Output the data
        echo fread($f, $chunkSize);

        // Flush the buffer immediately
        @ob_flush();
        flush();
    }
}
else {
    // It's not a range request, output the file anyway
    header('Content-Length: ' . $size);

    // Read the file
    @readfile($file);

    // and flush the buffer
    @ob_flush();
    flush();
}

Create real-time updates using Server-sent Events and libSSE

When I first learn node.js, I learned how to use socket.io, a node.js module for real-time communication between the client and server. However, when I’m going back coding PHP for a while, I started to figure out only few PHP libraries offered this functions and the techniques they uses are old (AJAX Streaming, Forever <iframe>, etc.). One day I found a new technique called Server-sent Events and it’s pretty easy to implant. I have an idea of creating an interface for Server-sent Events in order to speed up development and make it easier to use. libSSE is the library I’ve create just for this and it’s event-based which means data is sent only when an event happens. It also has some utility classes for communicate with other PHP scripts.

Here’s the library: https://github.com/licson0729/libSSE-php

It’s pretty easy to use. Here’s an example.

<?php
require_once('./src/libsse.php');//include the library

//create the event handler
//every event must inherit the base event class SSEEvent
class YourEventHandler extends SSEEvent {
    public function update(){
        //Here's the place to send data
        return time();
    }
    public function check(){
        //Here's the place to check when the data needs update
        return time() % 20 === 0;
    }
}

$sse = new SSE();//create a libSSE instance
//register your event handler
$sse->addEventListener(
    'event_name', //The name of the event
    new YourEventHandler() //The event handler defined above
);
$sse->start();//start the event loop

On the client-side you need to set up an EventSource object to listen event from server.

var sse = new EventSource('path/to/your/sse/script.php');
sse.addEventListener('event_name',function(e){
    var data = e.data;
    //handle your data here
},false);

Remember that Server-sent Events is still a new standard and not all browsers supports it. However, polyfills are available for them. Hope you like it and find it useful.

Encrypt files in PHP

Have you ever think of encrypting your secret files yourself? If you dunno how to do it yourself, here’s the rescue! In this article, I’ll teach you how to encrypt/decrypt files entirely in PHP!

First, please make sure Mcrypt is with your PHP bundle. If you don’t have this extension, please install it first as we need this to encrypt/decrypt files.

Then, it’s time to code! The code is pretty short as it uses the Mcrypt stream filter which also brings a huge performance increase when handling large files.

<?php
// The password
$passphrase = 'My secret';

// Turn a human readable passphrase
// into a reproducible iv/key pair

$iv = substr(md5("\x1B\x3C\x58".$passphrase, true), 0, 8);
$key = substr(md5("\x2D\xFC\xD8".$passphrase, true) .
md5("\x2D\xFC\xD9".$passphrase, true), 0, 24);
$opts = array('iv' => $iv, 'key' => $key, 'mode' => 'stream');

// Open the file
$fp = fopen('secret-file.enc', 'wb');

// Add the Mcrypt stream filter
// We use Triple DES here, but you
// can use other encryption algorithm here
stream_filter_append($fp, 'mcrypt.tripledes', STREAM_FILTER_WRITE, $opts);

// Wrote some contents to the file
fwrite($fp, 'Secret secret secret data');

// Close the file
fclose($fp);

This is the code that encrypt contents written into the files. Isn’t this pretty short? But, how can I make the contents readable again? Here’s the way to decrypt the files:

<?php
// The password
$passphrase = 'My secret';

// Turn a human readable passphrase
// into a reproducible iv/key pair

$iv = substr(md5("\x1B\x3C\x58".$passphrase, true), 0, 8);
$key = substr(md5("\x2D\xFC\xD8".$passphrase, true) .
md5("\x2D\xFC\xD9".$passphrase, true), 0, 24);
$opts = array('iv' => $iv, 'key' => $key, 'mode' => 'stream');

// Open the file
$fp = fopen('secret-file.enc', 'rb');

// Add the Mcrypt stream filter
// We use Triple DES here, but you
// can use other encryption algorithm here
stream_filter_append($fp, 'mdecrypt.tripledes', STREAM_FILTER_READ, $opts);

// Read the file contents
fpassthru($content);

Now we have a nice and easy solution to keep things secret in PHP. (Just to make sure, you need to change $passphrase into your very own password and keep it secret.)