-
-
Save ToshY/0b1b818b080bf9528dfbcc375b937a08 to your computer and use it in GitHub Desktop.
<?php | |
/* | |
VOD HLS streaming for BunnyCDN with Token authentication V2 | |
NOTES: | |
> Credits to Dejan from BunnyCDN Support for the function for token authentication V2 | |
> I've created this snippet to show how to use HLS streaming with the directory tokens. | |
This is a workaround which works by appending the token query string for every request to the TS segments (see JS below) | |
> Please add the appropriate CORS headers if enabled in the BunnyCDN panel (for HLS add 'm3u8, ts') | |
> Chromecast with HLS streaming and the usage of directory tokens will not. | |
If you really want to have it work with Chromecast, I suggest using MP4 instead and use the option "Optimize for video delivery" | |
in BunnyCDN's control panel. Especially if you have high bitrate videos or segments (>15 Mbps), the Chromecast (V3) is able to | |
handle MP4 much better than TS (hanging). | |
> Tested with VideoJS 7.5.5 (19-6-2020 working) | |
*/ | |
function sign_bcdn_url($url, $securityKey, $expiration_time = 3600, $user_ip = NULL, $path_allowed = NULL, $countries_allowed = NULL, $countries_blocked = NULL) | |
{ | |
if(!is_null($countries_allowed)) | |
{ | |
$url .= (parse_url($url, PHP_URL_QUERY) == "") ? "?" : "&"; | |
$url .= "token_countries={$countries_allowed}"; | |
} | |
if(!is_null($countries_blocked)) | |
{ | |
$url .= (parse_url($url, PHP_URL_QUERY) == "") ? "?" : "&"; | |
$url .= "token_countries_blocked={$countries_blocked}"; | |
} | |
$url_scheme = parse_url($url, PHP_URL_SCHEME); | |
$url_host = parse_url($url, PHP_URL_HOST); | |
$url_path = parse_url($url, PHP_URL_PATH); | |
$url_query = parse_url($url, PHP_URL_QUERY); | |
$parameters = array(); | |
parse_str($url_query, $parameters); | |
// Check if the path is specified and ovewrite the default | |
$signature_path = $url_path; | |
if(!is_null($path_allowed)) | |
{ | |
$signature_path = $path_allowed; | |
$parameters["token_path"] = $signature_path; | |
} | |
// Expiration time | |
$expires = time() + $expiration_time; | |
// Construct the parameter data | |
ksort($parameters); // Sort alphabetically, very important | |
$parameter_data = ""; | |
$parameter_data_url = ""; | |
if(sizeof($parameters) > 0) | |
{ | |
foreach ($parameters as $key => $value) | |
{ | |
if(strlen($parameter_data) > 0) | |
$parameter_data .= "&"; | |
$parameter_data_url .= "&"; | |
$parameter_data .= "{$key}=" . $value; | |
$parameter_data_url .= "{$key}=" . $value; | |
} | |
} | |
// Generate the toke | |
$hashableBase = $securityKey.$signature_path.$expires.$parameter_data; | |
// If using IP validation | |
if(!is_null($user_ip)) | |
{ | |
$hashableBase .= $user_ip; | |
} | |
// Generate the token | |
$token = hash('sha256', $hashableBase, true); | |
$token = base64_encode($token); | |
$token = strtr($token, '+/', '-_'); | |
$token = str_replace('=', '', $token); | |
//$url_path = str_replace("%2F", "/", urlencode($url_path)); // URL encode everything but slashes for the URL data | |
$query_string = "?token={$token}{$parameter_data_url}&expires={$expires}&token_ver=2"; | |
return [$query_string, "{$url_scheme}://{$url_host}{$url_path}{$query_string}"]; | |
} | |
list($query_string, $secure_url) = sign_bcdn_url("https://mydomain.b-cdn.net/test/bigbuck.m3u8", "a1b2c3d4-e5f6-g7h8-i9j1-k2l3m4n5o6p7", 3600, NULL, "/test/"); | |
?> | |
<html> | |
<head> | |
<link href="https://vjs.zencdn.net/7.5.5/video-js.css" rel="stylesheet" type="text/css"> | |
<link href="https://cdn.jsdelivr.net/npm/@silvermine/[email protected]/dist/css/quality-selector.css" rel="stylesheet" type="text/css"> | |
<link href="https://cdn.jsdelivr.net/npm/@silvermine/[email protected]/dist/silvermine-videojs-chromecast.css" rel="stylesheet" type="text/css"> | |
</head> | |
<body> | |
<video | |
id="MyPlayer" | |
class="video-js vjs-16-9 vjs-big-play-centered" | |
controls | |
preload="auto" | |
poster="" | |
data-title="" | |
data-poster="" | |
> | |
<source src="<?php echo $secure_url; ?>" type="application/x-mpegURL" label="" /> | |
<p class="vjs-no-js"> | |
To view this video please enable JavaScript, and consider upgrading to a | |
web browser that | |
<a href="https://videojs.com/html5-video-support/" target="_blank" | |
>supports HTML5 video</a | |
> | |
</p> | |
</video> | |
</body> | |
<script src="https://code.jquery.com/jquery-3.5.1.js"></script> | |
<script src="https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1"></script> | |
<script src="https://vjs.zencdn.net/7.5.5/video.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/@silvermine/[email protected]/dist/js/silvermine-videojs-quality-selector.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/@silvermine/[email protected]/dist/silvermine-videojs-chromecast.js"></script> | |
<script> | |
// current source | |
csource = $('#MyPlayer source').attr('src') | |
titles = { | |
csource: $('#MyPlayer').attr('data-title') | |
} | |
// options for chromecast | |
options = { | |
controls: true, | |
techOrder: [ 'chromecast', 'html5' ], | |
chromecast: { | |
requestTitleFn: function(source) { | |
return titles[csource.url]; | |
} | |
}, | |
plugins: { | |
chromecast: {} | |
} | |
}; | |
// append the token query string to the TS segments | |
videojs.Hls.xhr.beforeRequest = function(options) { | |
if(options.uri.includes('.ts')) options.uri += '<?php echo $query_string; ?>'; | |
return options; | |
}; | |
var vjplayer = videojs("MyPlayer", options); | |
</script> | |
</html> |
Yes, query_string
contains the token and other parameters which needs to be appended to request for the the .ts
segments (assuming they are in the same folder (/test
) as the .m3u8
file).
So, it's not working with iOS swift :( because iOS cannot append the query string at the runtime.
Ah okay, well good to know. Initially I uploaded this snippet to show that BunnyCDN already has the option to use directory tokens for a while now, but till this day the new token authentication is still not documented. I've received the PHP snippet from the owner through their support, and decided to add this code to show that it's possible to do some basic HLS streaming with token authentication.
Anyway, I had another idea a while back regarding the appending of the query_string
as well, which doesnt require the javascript trick.
It goes something like this:
- Parse the
initial .m3u8
file throughsign_bcdn_url
- Download the
initial .m3u8
and create a copy, e.g.copied.m3u8
(you could also append the expiration unix timestamp to the filename ofcopied.m3u8
so you can create a cronjob to delete them later automatically if they are expired, e.g.copied_1602005952.m3u8
) - In the
copied.m3u8
file, append thequery_string
to every.ts
segment
3.1 If you want to keep thecopied.m3u8
on the server you just downloaded it to, you have to make the URLs absolute:
https://mydomain.b-cdn.net/test/bigbuck0.ts?token=1a2b3c&expires=1602005952&token_ver=2
3.2 If you decide to save the new file to bunny servers (in the same folder as theinitial.m3u8
and.ts
segments), just appending thequery_string
is enough:
bigbuck0.ts?token=1a2b3c&expires=1602005952&token_ver=2
- Save
copied.m3u8
(either on your own or bunny servers) - In the source attribute of the video player, use the link to
copied.m3u8
and append the samequery_string
to it:
https://mydomain.b-cdn.net/test/copied.m3u8?token=1a2b3c&expires=1602005952&token_ver=2
- Play video.
- Create a task/cronjob/whatever that deletes these
copied.m3u8
files after they expire
While this method of creating these temporary m3u8
files work (yes I've tried it myself a while back), it's more of a workaround than an actual solution. It was a while after this that I found out you could simply append something to the URL using videojs.Hls.xhr.beforeRequest
, which made the whole thing a whole lot simpler.
FYI at some point I also dropped the idea of using HLS streaming myself due to the fact that high bitrate ts
segments (>30 Mbps) could not be played using the silvermine's chromecast plugin and the video would just keep endlessly trying to load the segment (both on the Chromecast V3 and Ultra).
Finally I got support from bunny.
I forked your code and edit it.
Thanks for your support.
https://gist.github.com/saturngod/01ca507ee0134c38f85874cc4061b320
Is this the code to play the video stream in bunny cdn?
I tested this code. worked perfectly.
Could you provide the php code to upload the live streaming from camera input?
oh! need to append the query string in ts ?