jellyfin-web/dashboard-ui/bower_components/emby-webcomponents/browserdeviceprofile.js

612 lines
18 KiB
JavaScript
Raw Normal View History

2016-01-18 12:07:26 -07:00
define(['browser'], function (browser) {
2015-12-26 11:35:53 -07:00
2016-01-06 13:16:16 -07:00
function canPlayH264() {
var v = document.createElement('video');
return !!(v.canPlayType && v.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"').replace(/no/, ''));
}
2016-09-02 05:30:47 -07:00
function canPlayH265() {
return false;
}
2015-12-26 11:35:53 -07:00
var _supportsTextTracks;
function supportsTextTracks() {
if (_supportsTextTracks == null) {
_supportsTextTracks = document.createElement('video').textTracks != null;
}
// For now, until ready
return _supportsTextTracks;
}
var _canPlayHls;
function canPlayHls(src) {
if (_canPlayHls == null) {
2016-01-18 12:07:26 -07:00
_canPlayHls = canPlayNativeHls() || canPlayHlsWithMSE();
2015-12-26 11:35:53 -07:00
}
return _canPlayHls;
}
function canPlayNativeHls() {
var media = document.createElement('video');
if (media.canPlayType('application/x-mpegURL').replace(/no/, '') ||
media.canPlayType('application/vnd.apple.mpegURL').replace(/no/, '')) {
return true;
}
return false;
}
2016-01-18 12:07:26 -07:00
function canPlayHlsWithMSE() {
2016-02-28 14:31:45 -07:00
if (window.MediaSource != null && !browser.firefox) {
2016-01-23 12:03:59 -07:00
// text tracks dont work with this in firefox
2016-02-26 10:15:06 -07:00
return true;
2016-01-23 12:03:59 -07:00
}
return false;
}
function canPlayAudioFormat(format) {
var typeString;
2016-06-25 13:18:23 -07:00
if (format == 'flac') {
if (browser.tizen) {
return true;
}
2016-07-27 22:19:24 -07:00
if (isEdgeUniversal()) {
return true;
}
2016-06-25 13:18:23 -07:00
}
else if (format == 'wma') {
if (browser.tizen) {
return true;
}
2016-07-27 22:19:24 -07:00
if (isEdgeUniversal()) {
return true;
}
2016-06-25 13:18:23 -07:00
}
else if (format == 'opus') {
2016-01-23 12:03:59 -07:00
typeString = 'audio/ogg; codecs="opus"';
2016-02-06 13:31:52 -07:00
if (document.createElement('audio').canPlayType(typeString).replace(/no/, '')) {
return true;
}
return false;
}
if (format == 'webma') {
2016-01-23 12:03:59 -07:00
typeString = 'audio/webm';
} else {
typeString = 'audio/' + format;
}
if (document.createElement('audio').canPlayType(typeString).replace(/no/, '')) {
return true;
}
2016-01-18 12:07:26 -07:00
return false;
}
2016-07-27 22:19:24 -07:00
function isEdgeUniversal() {
2016-03-10 10:59:32 -07:00
2016-07-27 22:19:24 -07:00
if (browser.edge) {
2016-03-11 20:29:37 -07:00
2016-04-29 21:02:36 -07:00
var userAgent = navigator.userAgent.toLowerCase();
2016-07-27 22:19:24 -07:00
if (userAgent.indexOf('msapphost') != -1) {
return true;
}
}
return false;
}
function testCanPlayMkv(videoTestElement) {
if (videoTestElement.canPlayType('video/x-matroska') ||
videoTestElement.canPlayType('video/mkv')) {
return true;
}
var userAgent = navigator.userAgent.toLowerCase();
// Unfortunately there's no real way to detect mkv support
if (browser.chrome) {
2016-04-29 21:02:36 -07:00
2016-03-11 20:29:37 -07:00
// Not supported on opera tv
if (browser.operaTv) {
return false;
}
2016-04-29 21:02:36 -07:00
// Filter out browsers based on chromium that don't support mkv
if (userAgent.indexOf('vivaldi') != -1 || userAgent.indexOf('opera') != -1) {
return false;
}
2016-03-11 20:29:37 -07:00
2016-03-10 10:59:32 -07:00
return true;
}
2016-03-10 13:07:23 -07:00
if (browser.tizen) {
2016-03-10 10:59:32 -07:00
return true;
}
2016-07-27 22:19:24 -07:00
if (isEdgeUniversal()) {
return true;
}
2016-03-10 10:59:32 -07:00
return false;
}
function testCanPlayTs() {
2016-03-10 21:25:56 -07:00
return browser.tizen || browser.web0s;
2016-03-10 13:07:23 -07:00
}
2016-03-10 10:59:32 -07:00
2016-07-28 13:21:54 -07:00
function getDirectPlayProfileForVideoContainer(container, videoAudioCodecs) {
2016-03-10 13:07:23 -07:00
var supported = false;
2016-07-28 13:21:54 -07:00
var profileContainer = container;
2016-03-10 13:07:23 -07:00
switch (container) {
2016-07-27 22:19:24 -07:00
case 'asf':
supported = browser.tizen || isEdgeUniversal();
2016-07-28 13:21:54 -07:00
videoAudioCodecs = [];
2016-07-27 22:19:24 -07:00
break;
2016-03-10 13:07:23 -07:00
case 'avi':
2016-07-28 13:21:54 -07:00
supported = isEdgeUniversal();
break;
2016-03-10 13:07:23 -07:00
case 'mpg':
case 'mpeg':
2016-07-28 13:21:54 -07:00
supported = isEdgeUniversal();
break;
case '3gp':
case 'flv':
2016-03-10 13:07:23 -07:00
case 'mts':
case 'trp':
case 'vob':
case 'vro':
supported = browser.tizen;
break;
2016-07-28 13:21:54 -07:00
case 'mov':
supported = browser.chrome || isEdgeUniversal();
break;
2016-03-10 13:07:23 -07:00
case 'm2ts':
2016-07-28 13:21:54 -07:00
supported = browser.tizen || browser.web0s || isEdgeUniversal();
2016-03-10 13:07:23 -07:00
break;
2016-07-27 22:19:24 -07:00
case 'wmv':
supported = browser.tizen || browser.web0s || isEdgeUniversal();
2016-07-28 13:21:54 -07:00
videoAudioCodecs = [];
2016-07-27 22:19:24 -07:00
break;
2016-03-11 20:29:37 -07:00
case 'ts':
2016-07-28 13:21:54 -07:00
supported = browser.tizen || browser.web0s || isEdgeUniversal();
profileContainer = 'ts,mpegts';
2016-03-11 20:29:37 -07:00
break;
2016-03-10 13:07:23 -07:00
default:
break;
2016-03-10 10:59:32 -07:00
}
2016-03-10 13:07:23 -07:00
if (!supported) {
return null;
2016-03-10 10:59:32 -07:00
}
2016-03-10 13:07:23 -07:00
return {
2016-07-28 13:21:54 -07:00
Container: profileContainer,
Type: 'Video',
AudioCodec: videoAudioCodecs.join(',')
2016-03-10 13:07:23 -07:00
};
2016-03-10 10:59:32 -07:00
}
2016-03-10 13:07:23 -07:00
function getMaxBitrate() {
2016-03-10 10:59:32 -07:00
2016-05-23 20:06:51 -07:00
// 10mbps
if (browser.xboxOne) {
return 10000000;
}
if (browser.ps4) {
return 8000000;
}
2016-03-10 10:59:32 -07:00
var userAgent = navigator.userAgent.toLowerCase();
2016-03-10 13:07:23 -07:00
if (browser.tizen) {
2016-03-10 10:59:32 -07:00
2016-03-10 13:07:23 -07:00
// 2015 models
if (userAgent.indexOf('tizen 2.3') != -1) {
return 20000000;
}
2016-03-10 10:59:32 -07:00
2016-03-10 13:07:23 -07:00
// 2016 models
return 40000000;
2016-03-10 10:59:32 -07:00
}
2016-03-10 13:07:23 -07:00
return 100000000;
2016-03-10 10:59:32 -07:00
}
2016-04-04 12:55:39 -07:00
return function (options) {
2015-12-26 11:35:53 -07:00
2016-04-04 12:55:39 -07:00
options = options || {};
2016-05-14 11:02:06 -07:00
var physicalAudioChannels = options.audioChannels || 2;
2016-03-10 13:07:23 -07:00
var bitrateSetting = getMaxBitrate();
2015-12-26 11:35:53 -07:00
2016-01-23 12:03:59 -07:00
var videoTestElement = document.createElement('video');
2015-12-26 11:35:53 -07:00
2016-01-23 12:03:59 -07:00
var canPlayWebm = videoTestElement.canPlayType('video/webm').replace(/no/, '');
2016-03-10 10:59:32 -07:00
2016-07-27 22:19:24 -07:00
var canPlayMkv = testCanPlayMkv(videoTestElement);
2016-03-10 10:59:32 -07:00
var canPlayTs = testCanPlayTs();
2015-12-26 11:35:53 -07:00
var profile = {};
profile.MaxStreamingBitrate = bitrateSetting;
profile.MaxStaticBitrate = 100000000;
profile.MusicStreamingTranscodingBitrate = Math.min(bitrateSetting, 192000);
profile.DirectPlayProfiles = [];
2016-01-07 22:44:45 -07:00
var videoAudioCodecs = [];
2016-02-13 19:26:51 -07:00
var hlsVideoAudioCodecs = [];
2016-01-13 22:18:46 -07:00
2016-01-25 13:28:29 -07:00
var supportsMp3VideoAudio = videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.69"').replace(/no/, '') ||
videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.6B"').replace(/no/, '');
2016-01-13 22:18:46 -07:00
// Only put mp3 first if mkv support is there
// Otherwise with HLS and mp3 audio we're seeing some browsers
2016-09-02 05:30:47 -07:00
if (videoTestElement.canPlayType('audio/mp4; codecs="ac-3"').replace(/no/, '') || isEdgeUniversal() || browser.tizen) {
2016-02-02 19:12:02 -07:00
// safari is lying
if (!browser.safari) {
videoAudioCodecs.push('ac3');
2016-02-13 19:26:51 -07:00
// This works in edge desktop, but not mobile
2016-08-06 14:22:00 -07:00
// TODO: Retest this on mobile
if (!browser.edge || !browser.touch) {
2016-02-13 19:26:51 -07:00
hlsVideoAudioCodecs.push('ac3');
}
2016-02-02 19:12:02 -07:00
}
2016-01-30 11:31:31 -07:00
}
2016-03-10 10:59:32 -07:00
var mp3Added = false;
if (canPlayMkv || canPlayTs) {
2016-06-26 09:21:10 -07:00
if (supportsMp3VideoAudio) {
2016-03-10 10:59:32 -07:00
mp3Added = true;
2016-01-13 22:18:46 -07:00
videoAudioCodecs.push('mp3');
2016-02-13 19:26:51 -07:00
hlsVideoAudioCodecs.push('mp3');
2016-01-13 22:18:46 -07:00
}
2016-01-07 22:44:45 -07:00
}
2016-01-25 13:28:29 -07:00
if (videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.40.2"').replace(/no/, '')) {
2016-01-07 22:44:45 -07:00
videoAudioCodecs.push('aac');
2016-02-13 19:26:51 -07:00
hlsVideoAudioCodecs.push('aac');
2016-01-07 22:44:45 -07:00
}
2016-03-10 10:59:32 -07:00
if (!mp3Added && supportsMp3VideoAudio) {
videoAudioCodecs.push('mp3');
hlsVideoAudioCodecs.push('mp3');
2016-01-13 22:18:46 -07:00
}
2016-01-07 22:44:45 -07:00
2016-07-31 10:58:06 -07:00
if (isEdgeUniversal()) {
2016-08-06 14:22:00 -07:00
//videoAudioCodecs.push('dca');
//videoAudioCodecs.push('dts');
2016-08-02 18:32:16 -07:00
//videoAudioCodecs.push('truehd');
2016-07-31 10:58:06 -07:00
}
2016-09-02 05:30:47 -07:00
var mp4VideoCodecs = [];
2016-01-23 12:03:59 -07:00
if (canPlayH264()) {
2016-09-02 05:30:47 -07:00
mp4VideoCodecs.push('h264');
}
if (canPlayH265()) {
mp4VideoCodecs.push('h265');
mp4VideoCodecs.push('hevc');
}
if (mp4VideoCodecs.length) {
2015-12-26 11:35:53 -07:00
profile.DirectPlayProfiles.push({
Container: 'mp4,m4v',
Type: 'Video',
2016-09-02 05:30:47 -07:00
VideoCodec: mp4VideoCodecs.join(','),
2016-01-07 22:44:45 -07:00
AudioCodec: videoAudioCodecs.join(',')
2015-12-26 11:35:53 -07:00
});
}
2016-09-02 05:30:47 -07:00
if (canPlayMkv && mp4VideoCodecs.length) {
2015-12-26 11:35:53 -07:00
profile.DirectPlayProfiles.push({
2016-07-28 13:21:54 -07:00
Container: 'mkv',
2015-12-26 11:35:53 -07:00
Type: 'Video',
2016-09-02 05:30:47 -07:00
VideoCodec: mp4VideoCodecs.join(','),
2016-01-07 22:44:45 -07:00
AudioCodec: videoAudioCodecs.join(',')
2015-12-26 11:35:53 -07:00
});
2016-08-01 22:55:52 -07:00
if (isEdgeUniversal()) {
profile.DirectPlayProfiles.push({
Container: 'mkv',
Type: 'Video',
VideoCodec: 'vc1',
AudioCodec: videoAudioCodecs.join(',')
});
}
2015-12-26 11:35:53 -07:00
}
2016-03-10 13:07:23 -07:00
// These are formats we can't test for but some devices will support
2016-07-28 13:21:54 -07:00
['m2ts', 'mov', 'wmv', 'ts', 'asf', 'avi', 'mpg', 'mpeg'].map(function (container) {
return getDirectPlayProfileForVideoContainer(container, videoAudioCodecs);
}).filter(function (i) {
2016-03-10 13:07:23 -07:00
return i != null;
}).forEach(function (i) {
profile.DirectPlayProfiles.push(i);
});
2016-03-10 10:59:32 -07:00
2016-07-28 13:21:54 -07:00
['opus', 'mp3', 'aac', 'flac', 'webma', 'wma', 'wav'].filter(canPlayAudioFormat).forEach(function (audioFormat) {
2015-12-26 11:35:53 -07:00
2016-01-23 12:03:59 -07:00
profile.DirectPlayProfiles.push({
Container: audioFormat == 'webma' ? 'webma,webm' : audioFormat,
Type: 'Audio'
});
2016-02-21 10:22:41 -07:00
// aac also appears in the m4a container
if (audioFormat == 'aac') {
profile.DirectPlayProfiles.push({
Container: 'm4a',
AudioCodec: audioFormat,
Type: 'Audio'
});
}
2015-12-26 11:35:53 -07:00
});
if (canPlayWebm) {
profile.DirectPlayProfiles.push({
Container: 'webm',
Type: 'Video'
});
}
profile.TranscodingProfiles = [];
2016-01-23 12:03:59 -07:00
['opus', 'mp3', 'aac'].filter(canPlayAudioFormat).forEach(function (audioFormat) {
profile.TranscodingProfiles.push({
Container: audioFormat,
Type: 'Audio',
AudioCodec: audioFormat,
Context: 'Streaming',
Protocol: 'http'
});
profile.TranscodingProfiles.push({
Container: audioFormat,
Type: 'Audio',
AudioCodec: audioFormat,
Context: 'Static',
Protocol: 'http'
});
2015-12-26 11:35:53 -07:00
});
2016-06-26 09:21:10 -07:00
var copyTimestamps = false;
if (browser.chrome) {
copyTimestamps = true;
}
2015-12-26 11:35:53 -07:00
// Can't use mkv on mobile because we have to use the native player controls and they won't be able to seek it
2016-06-26 09:21:10 -07:00
if (canPlayMkv && options.supportsCustomSeeking && !browser.tizen) {
2015-12-26 11:35:53 -07:00
profile.TranscodingProfiles.push({
Container: 'mkv',
Type: 'Video',
2016-01-06 13:16:16 -07:00
AudioCodec: videoAudioCodecs.join(','),
2015-12-26 11:35:53 -07:00
VideoCodec: 'h264',
Context: 'Streaming',
2016-06-26 09:21:10 -07:00
CopyTimestamps: copyTimestamps
2015-12-26 11:35:53 -07:00
});
}
2016-07-10 00:01:25 -07:00
if (canPlayTs && options.supportsCustomSeeking && !browser.tizen && !browser.web0s) {
2016-03-10 10:59:32 -07:00
profile.TranscodingProfiles.push({
Container: 'ts',
Type: 'Video',
AudioCodec: videoAudioCodecs.join(','),
VideoCodec: 'h264',
Context: 'Streaming',
2016-06-26 09:21:10 -07:00
CopyTimestamps: copyTimestamps,
2016-05-14 11:02:06 -07:00
// If audio transcoding is needed, limit channels to number of physical audio channels
// Trying to transcode to 5 channels when there are only 2 speakers generally does not sound good
MaxAudioChannels: physicalAudioChannels.toString()
2016-03-10 10:59:32 -07:00
});
}
2015-12-26 11:35:53 -07:00
if (canPlayHls()) {
profile.TranscodingProfiles.push({
Container: 'ts',
Type: 'Video',
2016-02-13 19:26:51 -07:00
AudioCodec: hlsVideoAudioCodecs.join(','),
2015-12-26 11:35:53 -07:00
VideoCodec: 'h264',
Context: 'Streaming',
2016-04-20 11:51:47 -07:00
Protocol: 'hls'
2015-12-26 11:35:53 -07:00
});
}
2016-05-13 22:40:01 -07:00
// Put mp4 ahead of webm
if (browser.firefox) {
profile.TranscodingProfiles.push({
Container: 'mp4',
Type: 'Video',
AudioCodec: videoAudioCodecs.join(','),
VideoCodec: 'h264',
Context: 'Streaming',
2016-05-14 11:02:06 -07:00
Protocol: 'http'
// Edit: Can't use this in firefox because we're seeing situations of no sound when downmixing from 6 channel to 2
//MaxAudioChannels: physicalAudioChannels.toString()
2016-05-13 22:40:01 -07:00
});
}
2015-12-26 11:35:53 -07:00
if (canPlayWebm) {
profile.TranscodingProfiles.push({
Container: 'webm',
Type: 'Video',
AudioCodec: 'vorbis',
VideoCodec: 'vpx',
Context: 'Streaming',
2016-05-14 11:02:06 -07:00
Protocol: 'http',
// If audio transcoding is needed, limit channels to number of physical audio channels
// Trying to transcode to 5 channels when there are only 2 speakers generally does not sound good
MaxAudioChannels: physicalAudioChannels.toString()
2015-12-26 11:35:53 -07:00
});
}
profile.TranscodingProfiles.push({
Container: 'mp4',
Type: 'Video',
2016-01-06 13:16:16 -07:00
AudioCodec: videoAudioCodecs.join(','),
2015-12-26 11:35:53 -07:00
VideoCodec: 'h264',
Context: 'Streaming',
2016-05-14 11:02:06 -07:00
Protocol: 'http',
// If audio transcoding is needed, limit channels to number of physical audio channels
// Trying to transcode to 5 channels when there are only 2 speakers generally does not sound good
MaxAudioChannels: physicalAudioChannels.toString()
2015-12-26 11:35:53 -07:00
});
profile.TranscodingProfiles.push({
Container: 'mp4',
Type: 'Video',
2016-01-06 13:16:16 -07:00
AudioCodec: videoAudioCodecs.join(','),
2015-12-26 11:35:53 -07:00
VideoCodec: 'h264',
Context: 'Static',
Protocol: 'http'
});
profile.ContainerProfiles = [];
profile.CodecProfiles = [];
profile.CodecProfiles.push({
Type: 'Audio',
Conditions: [{
Condition: 'LessThanEqual',
Property: 'AudioChannels',
Value: '2'
}]
});
2016-01-13 13:58:12 -07:00
var videoAudioChannels = '6';
2015-12-30 10:02:11 -07:00
2016-01-25 13:28:29 -07:00
// Handle he-aac not supported
if (!videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.40.5"').replace(/no/, '')) {
profile.CodecProfiles.push({
Type: 'VideoAudio',
Codec: 'aac',
Conditions: [
{
Condition: 'NotEquals',
Property: 'AudioProfile',
Value: 'HE-AAC'
},
{
Condition: 'LessThanEqual',
Property: 'AudioChannels',
Value: videoAudioChannels
},
2016-05-14 11:02:06 -07:00
{
Condition: 'LessThanEqual',
Property: 'AudioBitrate',
Value: '128000'
},
2016-01-25 13:28:29 -07:00
{
Condition: 'Equals',
Property: 'IsSecondaryAudio',
Value: 'false',
IsRequired: 'false'
}
]
});
}
2015-12-26 11:35:53 -07:00
profile.CodecProfiles.push({
Type: 'VideoAudio',
Conditions: [
2015-12-30 10:02:11 -07:00
{
Condition: 'LessThanEqual',
Property: 'AudioChannels',
Value: videoAudioChannels
},
2015-12-26 11:35:53 -07:00
{
Condition: 'Equals',
Property: 'IsSecondaryAudio',
Value: 'false',
IsRequired: 'false'
}
]
});
var maxLevel = '41';
if (browser.chrome && !browser.mobile) {
maxLevel = '51';
}
2015-12-26 11:35:53 -07:00
profile.CodecProfiles.push({
Type: 'Video',
Codec: 'h264',
Conditions: [
{
Condition: 'NotEquals',
Property: 'IsAnamorphic',
Value: 'true',
IsRequired: false
},
{
Condition: 'EqualsAny',
Property: 'VideoProfile',
Value: 'high|main|baseline|constrained baseline'
},
{
Condition: 'LessThanEqual',
Property: 'VideoLevel',
Value: maxLevel
2015-12-26 11:35:53 -07:00
}]
});
profile.CodecProfiles.push({
Type: 'Video',
Codec: 'vpx',
Conditions: [
{
Condition: 'NotEquals',
Property: 'IsAnamorphic',
Value: 'true',
IsRequired: false
}]
});
// Subtitle profiles
// External vtt or burn in
profile.SubtitleProfiles = [];
if (supportsTextTracks()) {
profile.SubtitleProfiles.push({
Format: 'vtt',
Method: 'External'
});
}
profile.ResponseProfiles = [];
profile.ResponseProfiles.push({
Type: 'Video',
Container: 'm4v',
MimeType: 'video/mp4'
});
2016-07-28 13:21:54 -07:00
if (browser.chrome) {
profile.ResponseProfiles.push({
Type: 'Video',
Container: 'mov',
MimeType: 'video/webm'
});
}
2015-12-26 11:35:53 -07:00
return profile;
2016-04-04 12:55:39 -07:00
};
2015-12-26 11:35:53 -07:00
});