Skip to content

Instantly share code, notes, and snippets.

@saturngod
Forked from ToshY/BunnyVOD.php
Last active March 22, 2023 21:08

Revisions

  1. saturngod revised this gist Oct 7, 2020. 1 changed file with 88 additions and 152 deletions.
    240 changes: 88 additions & 152 deletions BunnyVOD.php
    Original file line number Diff line number Diff line change
    @@ -1,155 +1,91 @@
    <?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))

    function sign_bcdn_url($url, $securityKey, $expiration_time = 3600, $user_ip = NULL, $is_directory_token = false, $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)
    {
    $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}"];
    if(strlen($parameter_data) > 0)
    $parameter_data .= "&";

    $parameter_data_url .= "&";

    $parameter_data .= "{$key}=" . $value;
    $parameter_data_url .= "{$key}=" . urlencode($value); // URL encode everything but slashes for the URL data
    }
    }

    // Generate the toke
    $hashableBase = $securityKey.$signature_path.$expires;

    // If using IP validation
    if(!is_null($user_ip))
    {
    $hashableBase .= $user_ip;
    }

    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/videojs-quality-selector@1.2.4/dist/css/quality-selector.css" rel="stylesheet" type="text/css">
    <link href="https://cdn.jsdelivr.net/npm/@silvermine/videojs-chromecast@1.2.1/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/videojs-quality-selector@1.2.4/dist/js/silvermine-videojs-quality-selector.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@silvermine/videojs-chromecast@1.2.1/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>

    $hashableBase .= $parameter_data;

    // Generate the token
    $token = hash('sha256', $hashableBase, true);
    $token = base64_encode($token);
    $token = strtr($token, '+/', '-_');
    $token = str_replace('=', '', $token);

    if($is_directory_token)
    {
    return "{$url_scheme}://{$url_host}/bcdn_token={$token}&expires={$expires}{$parameter_data_url}{$url_path}";
    }
    else
    {
    return "{$url_scheme}://{$url_host}{$url_path}?token={$token}{$parameter_data_url}&expires={$expires}";
    }
    }



    // Single URL signing example
    echo sign_bcdn_url(
    "http://sample-stg.b-cdn.net/smaple_video/playlist.m3u8", // Url to sign
    "8ba11a7a-f924-8e79-beab-97d111191ad4", // Token Key
    360000, // Expiration time in seconds
    NULL, // User IP
    true,
    "/smaple_video/"); // Directory token
  2. @ToshY ToshY revised this gist Jun 19, 2020. 1 changed file with 124 additions and 126 deletions.
    250 changes: 124 additions & 126 deletions BunnyVOD.php
    Original file line number Diff line number Diff line change
    @@ -1,157 +1,155 @@
    <?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)
    > 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
    */

    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)
    /*
    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))
    {
    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;
    $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}"];
    }

    // 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/");

    ?>


    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="./videojs-7.5.5.css" rel="stylesheet" type="text/css">
    <link href="./videojs-quality-selector.css" rel="stylesheet" type="text/css">
    <link href="./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
    <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/videojs-quality-selector@1.2.4/dist/css/quality-selector.css" rel="stylesheet" type="text/css">
    <link href="https://cdn.jsdelivr.net/npm/@silvermine/videojs-chromecast@1.2.1/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=""
    >
    </p>
    </video>
    </body>
    <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="./videojs-7.5.5.js"></script>
    <script src="./videojs-quality-selector.min.js"></script>
    <script src="./videojs-chromecast.min.js"></script>
    <script src="https://vjs.zencdn.net/7.5.5/video.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@silvermine/videojs-quality-selector@1.2.4/dist/js/silvermine-videojs-quality-selector.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@silvermine/videojs-chromecast@1.2.1/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[source.url];
    return titles[csource.url];
    }
    },
    plugins: {
    chromecast: {}
    }
    };

    // append the token query string to the TS segments
    videojs.Hls.xhr.beforeRequest = function(options) {
    if(segment.uri.includes('.ts')){
    segment.uri += '<?php echo $secure_query; ?>';
    segment.resolvedUri += '<?php echo $secure_query; ?>';
    };
    if(options.uri.includes('.ts')) options.uri += '<?php echo $query_string; ?>';
    return options;
    };

    var vjplayer = videojs("MyPlayer", options);
    </script>
    </html>
  3. @ToshY ToshY revised this gist Jun 18, 2020. 1 changed file with 0 additions and 1 deletion.
    1 change: 0 additions & 1 deletion BunnyVOD.php
    Original file line number Diff line number Diff line change
    @@ -6,7 +6,6 @@
    NOTES:
    > Credits to Dejan from BunnyCDN Support for the function for token authentication V2
    > Function seems to work correctly
    > 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)
  4. @ToshY ToshY revised this gist Jun 18, 2020. 1 changed file with 17 additions and 8 deletions.
    25 changes: 17 additions & 8 deletions BunnyVOD.php
    Original file line number Diff line number Diff line change
    @@ -4,12 +4,18 @@
    VOD HLS streaming for BunnyCDN with Token authentication V2
    > Tested with VideoJS 7.5.5, silvermine/videojs-chromecast 1.2.1, silvermine/videojs-quality-selector 1.2.3
    > Thanks to Dejan from BunnyCDN Support for the token authentication V2 PHP snippet
    NOTE: Chromecast does not seem to work (for now) - 19-5-2020
    NOTES:
    > Credits to Dejan from BunnyCDN Support for the function for token authentication V2
    > Function seems to work correctly
    > 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)
    > 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
    */

    function sign_bcdn_url($url, $securityKey, $expiration_time = 3600, $user_ip = NULL, $path_allowed = NULL, $countries_allowed = NULL, $countries_blocked = NULL)
    @@ -140,8 +146,11 @@ class="video-js vjs-16-9 vjs-big-play-centered"

    // 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;
    if(segment.uri.includes('.ts')){
    segment.uri += '<?php echo $secure_query; ?>';
    segment.resolvedUri += '<?php echo $secure_query; ?>';
    };
    return options;
    };

    var vjplayer = videojs("MyPlayer", options);
  5. @ToshY ToshY revised this gist May 19, 2020. 1 changed file with 3 additions and 0 deletions.
    3 changes: 3 additions & 0 deletions BunnyVOD.php
    Original file line number Diff line number Diff line change
    @@ -7,6 +7,9 @@
    > Tested with VideoJS 7.5.5, silvermine/videojs-chromecast 1.2.1, silvermine/videojs-quality-selector 1.2.3
    > Thanks to Dejan from BunnyCDN Support for the token authentication V2 PHP snippet
    NOTE: Chromecast does not seem to work (for now) - 19-5-2020
    */

    function sign_bcdn_url($url, $securityKey, $expiration_time = 3600, $user_ip = NULL, $path_allowed = NULL, $countries_allowed = NULL, $countries_blocked = NULL)
  6. @ToshY ToshY revised this gist May 18, 2020. 1 changed file with 1 addition and 3 deletions.
    4 changes: 1 addition & 3 deletions BunnyVOD.php
    Original file line number Diff line number Diff line change
    @@ -2,11 +2,9 @@

    /*
    VOD HLS streaming for BunnyCDN
    Token authentication V2
    VOD HLS streaming for BunnyCDN with Token authentication V2
    > Tested with VideoJS 7.5.5, silvermine/videojs-chromecast 1.2.1, silvermine/videojs-quality-selector 1.2.3
    > Thanks to Dejan from BunnyCDN Support for the token authentication V2 PHP snippet
    */
  7. @ToshY ToshY created this gist May 18, 2020.
    148 changes: 148 additions & 0 deletions BunnyVOD.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,148 @@
    <?php

    /*
    VOD HLS streaming for BunnyCDN
    Token authentication V2
    > Tested with VideoJS 7.5.5, silvermine/videojs-chromecast 1.2.1, silvermine/videojs-quality-selector 1.2.3
    > Thanks to Dejan from BunnyCDN Support for the token authentication V2 PHP snippet
    */

    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="./videojs-7.5.5.css" rel="stylesheet" type="text/css">
    <link href="./videojs-quality-selector.css" rel="stylesheet" type="text/css">
    <link href="./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://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1"></script>
    <script src="./videojs-7.5.5.js"></script>
    <script src="./videojs-quality-selector.min.js"></script>
    <script src="./videojs-chromecast.min.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[source.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>