Hls.js: Native playback in desktop Safari

Created on 21 Jan 2016  Â·  20Comments  Â·  Source: video-dev/hls.js

Safari have native support of HLS in video tag. How i can to play it without MediaSource?

Question Safari

Most helpful comment

It seems so. You can test in the console with Apple HLS testing stream on Safari/desktop:

var video = document.querySelector('video');

video.addEventListener('timeupdate', function() {
  console.log(video.videoWidth + 'x' + video.videoHeight + ' - ' + video.width + 'x' + video.height);
});

All 20 comments

As simple as this:

<video src="http://xyz.zy/mystream.m3u8">

Btw. this is not hls.js related question.

This solution does not allow to choose quality.
I hope that hls.js can help.

Sorry, there is no way to manually change quality using <video> element.

Hls.js can't help you, cause it's not relevant in this case.

I propose to support playback without MediaSource for safari in hls.js. I can help.

@Anonym-tsk to be perfectly clear:
there is no support for quality changing in HTML5 <video> (MediaElement) element API - neither standard one nor webkit vendor prefixed (used in Safari). When you're using native Safari HLS support you're doomed to Safari (Quick Time to be specyfic) ABR algorithm. Because of this it's impossible to implement that functionality in hls.js. There is nothing you can do about that.

In native HLS mode, the browser is solely responsible for managing all the details of the streaming session, specifically: variant manifest downloading & parsing, initial quality selection, stream manifest(s) downloading & parsing, fragments downloading, quality switch heuristics, etc. ... one way to emulate what you're describing would be to download the variant manifest by yourself (through XHR), parse it to display a menu with the different advertised qualities, and then feed a simple video tag with a stream manifest directly. But of course, you loose the ABR native support in this case.

thats totally right.

on top on that i figured out that you have more todo then a simple
fallback to native video element when you want to use hls.js.

there are also a lot of mixed browser versions out there that
doesnt support MSE. like firefox <39, opera and so on.

the only way i found yet was to implement a browser revisions check in
JS to support all browsers with MSE and to inform the user that he has
to update or to switch the browser to see the video.

the interesting thing was that we inform the user that we has
to switch or update, we is doing this instead of getting an black screen
where the video doesnt start up.

some snipset that i use to detect all browsers, maybe that can help.

i do not think that this checks are a part of hls.js, or?

let me know if you have an better way.

'use strict';

class onutils {

constructor() {
    this.deb = '';
}

// get browser
getBrowser() {
    var ua = navigator.userAgent,tem,
    M =

ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i)
|| [];

    if (/trident/i.test(M[1])) {
        tem =/\brv[ :]+(\d+)/g.exec(ua) || [];
        return 'IE';
    }
    if (M[1] === 'Chrome') {
        tem = ua.match(/\bOPR\/(\d+)/);
        if (tem != null) {
            return 'Opera';
        }
    }
    M = M[2] ? [M[1], M[2]] : [navigator.appName,

navigator.appVersion, '-?'];
if ((tem = ua.match(/version\/(\d+)/i)) !=null) {
M.splice(1,1,tem[1]);
}
return M;

 }

function to call:
// global variable
var isHlsNative = 0;

  // simple browser detection!
  // br[0] = browser type
  // br[1] = browser version

  switch (br[0]) {

    case "Firefox": {
      // detect version
      if (br[1] < 39) {
        alert("false version");
      }
      isHlsNative = 0;
      break;
    }

    case "Safari": {
      isHlsNative = 1;
      break;
    }

    case "Chrome": {
      isHlsNative = 0;
      break;
    }

  }

}

export default onutils;

Am 21.01.16 um 16:12 schrieb Radosław Włodkowski:

@Anonym-tsk https://github.com/Anonym-tsk to be perfectly clear:
there is no support for quality changing in HTML5 |

—
Reply to this email directly or view it on GitHub
https://github.com/dailymotion/hls.js/issues/193#issuecomment-173601270.

@pyke369 i know it, but i think that workaround like this can work:

function parseMasterPlaylist(playlist) {
  var qualityList = [];
  // ... parse playlist here ...
  // [{name: 'HD', bandwidth: 2736401, url: 'hd.m3u8'}, {name: 'SD', bandwidth: 1292319, url: 'sd.m3u8'}]
  return qualityList;
}

function getQualityList(playlistUrl, callback) {
  var xhr = new XMLHttpRequest();
  xhr.open('GET', playlistUrl, true);
  xhr.onreadystatechange = function() {
    if (xhr.readyState !== 4 || xhr.status !== 200) {
      return;
    }
    var qualityList = parseMasterPlaylist(xhr.responseText);
    qualityList.push({name: 'Auto', bandwidth: 0, url: playlistUrl});
    callback(qualityList);
  }
  xhr.send();
}

function setVideoQuality(quality) {
  // quality is an item from qualityList array
  var time = video.currentTime;
  video.src = quality.url;
  video.currentTime = time;
}

getQualityList('master.m3u8', function(qualityList) {
  playVideo(qualityList);
});

Probably will be gaps when the quality switching, but with quality "Auto" we have native ABR.

You mean you would pass the variant manifest in auto mode and parse-the-variant-and-pass à unitary stream manifest in selected-quality-mode? Yes, that makes total sense and we get best of both worlds this way.

Yes, master playlist for auto-quality and native ABR, and simple stream playlist for each quality.

this is clever, cool!

thx for sharing.

Am 22.01.16 um 09:44 schrieb Nikolay Vasilchuk:

@pyke369 https://github.com/pyke369 i know it, but i think that
workaround like this can work:

function parseMasterPlaylist(playlist) {
var qualityList = [];
// ... parse playlist here ...
// [{name: 'HD', bandwidth: 2736401, url: 'hd.m3u8'}, {name: 'SD',
bandwidth: 1292319, url: 'sd.m3u8'}]
return qualityList;
}

function getQualityList(playlistUrl, callback) {
var xhr = new XMLHttpRequest();
xhr.open('GET', , true);
xhr.onreadystatechange = function() {
if (xhr.readyState !== 4 || xhr.status !== 200) {
return;
}
var qualityList = parseMasterPlaylist(xhr.responseText);
qualityList.push({name: 'Auto', bandwidth: 0, url: playlistUrl});
callback(qualityList);
}
xhr.send();
}

function setVideoQuality(quality) {
// quality is an item from qualityList array
var time = video.currentTime;
video.src = quality.url;
video.currentTime = time;
}

Probably will be gaps when the quality switching, but with quality
"Auto" we have native ABR.

—
Reply to this email directly or view it on GitHub
https://github.com/dailymotion/hls.js/issues/193#issuecomment-173846894.

I repackaged the part of video.js which detects hls support here,

https://github.com/iambumblehead/canplayhls/blob/master/canplayhls.js

there is an npm package as well, https://www.npmjs.com/package/canplayhls

In native Safari HLS streaming is there a way to detect the bitrate ( quality ) change? Something like "onQualityChange"? I'd like to update my bitrate menu showing the current quality selected by the native Safari video tag.

Thanks

@ThiagoMiranda it's impossible

Monitoring videoWith and/or videoHeight of HTMLVideoElement periodically should do the trick. You get the intrinsic resolution, you can "estimate" the current playing quality.

@dighan even if my player has a static width and height?
Either way, thanks for the response =)

It seems so. You can test in the console with Apple HLS testing stream on Safari/desktop:

var video = document.querySelector('video');

video.addEventListener('timeupdate', function() {
  console.log(video.videoWidth + 'x' + video.videoHeight + ' - ' + video.width + 'x' + video.height);
});

@dighan interesting thing that i've discovered on Safari:
If you have an auto playlist file the maximum "videoHeight" depends on the video element container size and not only on your connection speed.
Unfortunately making it fullscreen doesn't change this behavior.

Would it be possible to use native playback under safari to copy video image on a canvas or webgl texture?

@isometriq Yes you can do that, please refer to the documentation of CanvasContext.drawImage method.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

nickcartery picture nickcartery  Â·  4Comments

shalommeoded picture shalommeoded  Â·  3Comments

sbrez picture sbrez  Â·  3Comments

harbinha picture harbinha  Â·  3Comments

fredvb picture fredvb  Â·  3Comments