1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

add initial support for fMP4-HLS

This commit is contained in:
nyanmisaka 2020-11-12 20:03:38 +08:00
parent 0985909943
commit 54db12359c
9 changed files with 255 additions and 45 deletions

View file

@ -1,4 +1,4 @@
define(['browser'], function (browser) {
define(['browser', 'userSettings', 'appSettings'], function (browser, userSettings, appSettings) {
'use strict';
browser = browser.default || browser;
@ -7,7 +7,7 @@ define(['browser'], function (browser) {
return !!(videoTestElement.canPlayType && videoTestElement.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"').replace(/no/, ''));
}
function canPlayH265(videoTestElement, options) {
function canPlayHevc(videoTestElement, options) {
if (browser.tizen || browser.xboxOne || browser.web0s || options.supportsHevc) {
return true;
}
@ -16,6 +16,7 @@ define(['browser'], function (browser) {
return false;
}
// hevc main level 4.0
return !!videoTestElement.canPlayType &&
(videoTestElement.canPlayType('video/mp4; codecs="hvc1.1.L120"').replace(/no/, '') ||
videoTestElement.canPlayType('video/mp4; codecs="hev1.1.L120"').replace(/no/, '') ||
@ -207,8 +208,7 @@ define(['browser'], function (browser) {
// Explicitly add supported codecs to make other codecs be transcoded
if (browser.tizenVersion >= 4) {
videoCodecs.push('h264');
if (canPlayH265(videoTestElement, options)) {
videoCodecs.push('h265');
if (canPlayHevc(videoTestElement, options)) {
videoCodecs.push('hevc');
}
}
@ -248,8 +248,8 @@ define(['browser'], function (browser) {
case 'ts':
supported = testCanPlayTs();
videoCodecs.push('h264');
if (canPlayH265(videoTestElement, options)) {
videoCodecs.push('h265');
// safari doesn't support hevc in TS-HLS
if ((browser.tizen || browser.web0s) && canPlayHevc(videoTestElement, options)) {
videoCodecs.push('hevc');
}
if (supportsVc1(videoTestElement)) {
@ -297,7 +297,9 @@ define(['browser'], function (browser) {
return function (options) {
options = options || {};
const physicalAudioChannels = options.audioChannels || (browser.tv || browser.ps4 || browser.xboxOne ? 6 : 2);
const isSurroundSoundSupportedBrowser = browser.safari || browser.chrome || browser.edgeChromium || browser.firefox;
const allowedAudioChannels = parseInt(userSettings.allowedAudioChannels() || '-1');
const physicalAudioChannels = (allowedAudioChannels > 0 ? allowedAudioChannels : null) || options.audioChannels || (isSurroundSoundSupportedBrowser || browser.tv || browser.ps4 || browser.xboxOne ? 6 : 2);
const bitrateSetting = getMaxBitrate();
@ -313,12 +315,13 @@ define(['browser'], function (browser) {
profile.MaxStreamingBitrate = bitrateSetting;
profile.MaxStaticBitrate = 100000000;
profile.MusicStreamingTranscodingBitrate = Math.min(bitrateSetting, 192000);
profile.MusicStreamingTranscodingBitrate = Math.min(bitrateSetting, 384000);
profile.DirectPlayProfiles = [];
let videoAudioCodecs = [];
let hlsVideoAudioCodecs = [];
let hlsInTsVideoAudioCodecs = [];
let hlsInFmp4VideoAudioCodecs = [];
const supportsMp3VideoAudio = videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.69"').replace(/no/, '')
|| videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.6B"').replace(/no/, '')
@ -353,16 +356,19 @@ define(['browser'], function (browser) {
// Transcoding codec is the first in hlsVideoAudioCodecs
// Put ac3/eac3 first only when the audio channels > 2 and need transcoding
if (canPlayAc3VideoAudioInHls && physicalAudioChannels > 2) {
hlsVideoAudioCodecs.push('ac3');
hlsInTsVideoAudioCodecs.push('ac3');
hlsInFmp4VideoAudioCodecs.push('ac3');
if (canPlayEac3VideoAudio) {
hlsVideoAudioCodecs.push('eac3');
hlsInTsVideoAudioCodecs.push('eac3');
hlsInFmp4VideoAudioCodecs.push('eac3');
}
}
}
if (canPlayAacVideoAudio) {
videoAudioCodecs.push('aac');
hlsVideoAudioCodecs.push('aac');
hlsInTsVideoAudioCodecs.push('aac');
hlsInFmp4VideoAudioCodecs.push('aac');
}
if (supportsMp3VideoAudio) {
@ -370,16 +376,31 @@ define(['browser'], function (browser) {
// PS4 fails to load HLS with mp3 audio
if (!browser.ps4) {
hlsVideoAudioCodecs.push('mp3');
hlsInTsVideoAudioCodecs.push('mp3');
}
hlsInFmp4VideoAudioCodecs.push('mp3');
}
// For ac3/eac3 directstream
if (canPlayAc3VideoAudio) {
if (canPlayAc3VideoAudioInHls && hlsVideoAudioCodecs.indexOf('ac3') === -1) {
hlsVideoAudioCodecs.push('ac3');
if (canPlayEac3VideoAudio && hlsVideoAudioCodecs.indexOf('eac3') === -1) {
hlsVideoAudioCodecs.push('eac3');
if (canPlayAc3VideoAudioInHls) {
if (hlsInTsVideoAudioCodecs.indexOf('ac3') === -1) {
hlsInTsVideoAudioCodecs.push('ac3');
}
if (hlsInFmp4VideoAudioCodecs.indexOf('ac3') === -1) {
hlsInFmp4VideoAudioCodecs.push('ac3');
}
if (canPlayEac3VideoAudio) {
if (hlsInTsVideoAudioCodecs.indexOf('eac3') === -1) {
hlsInTsVideoAudioCodecs.push('eac3');
}
if (hlsInFmp4VideoAudioCodecs.indexOf('eac3') === -1) {
hlsInFmp4VideoAudioCodecs.push('eac3');
}
}
}
}
@ -415,38 +436,54 @@ define(['browser'], function (browser) {
if (canPlayAudioFormat('opus')) {
videoAudioCodecs.push('opus');
hlsVideoAudioCodecs.push('opus');
hlsInTsVideoAudioCodecs.push('opus');
webmAudioCodecs.push('opus');
}
if (canPlayAudioFormat('flac')) {
videoAudioCodecs.push('flac');
hlsInFmp4VideoAudioCodecs.push('flac');
}
if (canPlayAudioFormat('alac')) {
videoAudioCodecs.push('alac');
hlsInFmp4VideoAudioCodecs.push('alac');
}
videoAudioCodecs = videoAudioCodecs.filter(function (c) {
return (options.disableVideoAudioCodecs || []).indexOf(c) === -1;
});
hlsVideoAudioCodecs = hlsVideoAudioCodecs.filter(function (c) {
hlsInTsVideoAudioCodecs = hlsInTsVideoAudioCodecs.filter(function (c) {
return (options.disableHlsVideoAudioCodecs || []).indexOf(c) === -1;
});
hlsInFmp4VideoAudioCodecs = hlsInFmp4VideoAudioCodecs.filter(function (c) {
return (options.disableHlsVideoAudioCodecs || []).indexOf(c) === -1;
});
const mp4VideoCodecs = [];
const webmVideoCodecs = [];
const hlsVideoCodecs = [];
const hlsInTsVideoCodecs = [];
const hlsInFmp4VideoCodecs = [];
if (browser.safari || browser.tizen || browser.web0s && canPlayHevc(videoTestElement, options)) {
hlsInFmp4VideoCodecs.push('hevc');
}
if (canPlayH264(videoTestElement)) {
mp4VideoCodecs.push('h264');
hlsVideoCodecs.push('h264');
hlsInTsVideoCodecs.push('h264');
if (browser.safari || browser.tizen || browser.web0s) {
hlsInFmp4VideoCodecs.push('h264');
}
}
if (canPlayH265(videoTestElement, options)) {
mp4VideoCodecs.push('h265');
mp4VideoCodecs.push('hevc');
if (browser.tizen || browser.web0s) {
hlsVideoCodecs.push('h265');
hlsVideoCodecs.push('hevc');
if (canPlayHevc(videoTestElement, options)) {
// safari is lying on HDR and 60fps videos, use fMP4 instead
if (!browser.safari) {
mp4VideoCodecs.push('hevc');
}
}
@ -606,18 +643,34 @@ define(['browser'], function (browser) {
});
}
if (canPlayHls() && hlsVideoAudioCodecs.length && options.enableHls !== false) {
profile.TranscodingProfiles.push({
Container: 'ts',
Type: 'Video',
AudioCodec: hlsVideoAudioCodecs.join(','),
VideoCodec: hlsVideoCodecs.join(','),
Context: 'Streaming',
Protocol: 'hls',
MaxAudioChannels: physicalAudioChannels.toString(),
MinSegments: browser.iOS || browser.osx ? '2' : '1',
BreakOnNonKeyFrames: hlsBreakOnNonKeyFrames
});
if (canPlayHls() && options.enableHls !== false) {
if (hlsInFmp4VideoCodecs.length && hlsInFmp4VideoAudioCodecs.length && userSettings.preferFmp4HlsContainer() && (browser.safari || browser.tizen || browser.web0s)) {
profile.TranscodingProfiles.push({
Container: 'mp4',
Type: 'Video',
AudioCodec: hlsInFmp4VideoAudioCodecs.join(','),
VideoCodec: hlsInFmp4VideoCodecs.join(','),
Context: 'Streaming',
Protocol: 'hls',
MaxAudioChannels: physicalAudioChannels.toString(),
MinSegments: browser.iOS || browser.osx ? '2' : '1',
BreakOnNonKeyFrames: hlsBreakOnNonKeyFrames
});
}
if (hlsInTsVideoCodecs.length && hlsInTsVideoAudioCodecs.length) {
profile.TranscodingProfiles.push({
Container: 'ts',
Type: 'Video',
AudioCodec: hlsInTsVideoAudioCodecs.join(','),
VideoCodec: hlsInTsVideoCodecs.join(','),
Context: 'Streaming',
Protocol: 'hls',
MaxAudioChannels: physicalAudioChannels.toString(),
MinSegments: browser.iOS || browser.osx ? '2' : '1',
BreakOnNonKeyFrames: hlsBreakOnNonKeyFrames
});
}
}
if (canPlayVp8) {
@ -713,6 +766,36 @@ define(['browser'], function (browser) {
}
}
let maxHevcLevel = 120;
let hevcProfiles = 'main';
// hevc main level 4.1
if (videoTestElement.canPlayType('video/mp4; codecs="hvc1.1.4.L123"').replace(/no/, '') ||
videoTestElement.canPlayType('video/mp4; codecs="hev1.1.4.L123"').replace(/no/, '')) {
maxHevcLevel = 123;
}
// hevc main10 level 4.1
if (videoTestElement.canPlayType('video/mp4; codecs="hvc1.2.4.L123"').replace(/no/, '') ||
videoTestElement.canPlayType('video/mp4; codecs="hev1.2.4.L123"').replace(/no/, '')) {
maxHevcLevel = 123;
hevcProfiles = 'main|main 10';
}
// hevc main10 level 5.1
if (videoTestElement.canPlayType('video/mp4; codecs="hvc1.2.4.L153"').replace(/no/, '') ||
videoTestElement.canPlayType('video/mp4; codecs="hev1.2.4.L153"').replace(/no/, '')) {
maxHevcLevel = 153;
hevcProfiles = 'main|main 10';
}
// hevc main10 level 6.1
if (videoTestElement.canPlayType('video/mp4; codecs="hvc1.2.4.L183"').replace(/no/, '') ||
videoTestElement.canPlayType('video/mp4; codecs="hvc1.2.4.L183"').replace(/no/, '')) {
maxHevcLevel = 183;
hevcProfiles = 'main|main 10';
}
const h264CodecProfileConditions = [
{
Condition: 'NotEquals',
@ -734,6 +817,27 @@ define(['browser'], function (browser) {
}
];
const hevcCodecProfileConditions = [
{
Condition: 'NotEquals',
Property: 'IsAnamorphic',
Value: 'true',
IsRequired: false
},
{
Condition: 'EqualsAny',
Property: 'VideoProfile',
Value: hevcProfiles,
IsRequired: false
},
{
Condition: 'LessThanEqual',
Property: 'VideoLevel',
Value: maxHevcLevel.toString(),
IsRequired: false
}
];
if (!browser.edgeUwp && !browser.tizen && !browser.web0s) {
h264CodecProfileConditions.push({
Condition: 'NotEquals',
@ -741,6 +845,13 @@ define(['browser'], function (browser) {
Value: 'true',
IsRequired: false
});
hevcCodecProfileConditions.push({
Condition: 'NotEquals',
Property: 'IsInterlaced',
Value: 'true',
IsRequired: false
});
}
if (maxVideoWidth) {
@ -750,12 +861,21 @@ define(['browser'], function (browser) {
Value: maxVideoWidth.toString(),
IsRequired: false
});
hevcCodecProfileConditions.push({
Condition: 'LessThanEqual',
Property: 'Width',
Value: maxVideoWidth.toString(),
IsRequired: false
});
}
const globalMaxVideoBitrate = (getGlobalMaxVideoBitrate() || '').toString();
const h264MaxVideoBitrate = globalMaxVideoBitrate;
const hevcMaxVideoBitrate = globalMaxVideoBitrate;
if (h264MaxVideoBitrate) {
h264CodecProfileConditions.push({
Condition: 'LessThanEqual',
@ -765,6 +885,15 @@ define(['browser'], function (browser) {
});
}
if (hevcMaxVideoBitrate) {
hevcCodecProfileConditions.push({
Condition: 'LessThanEqual',
Property: 'VideoBitrate',
Value: hevcMaxVideoBitrate,
IsRequired: true
});
}
// On iOS 12.x, for TS container max h264 level is 4.2
if (browser.iOS && browser.iOSVersion < 13) {
const codecProfile = {
@ -792,6 +921,12 @@ define(['browser'], function (browser) {
Conditions: h264CodecProfileConditions
});
profile.CodecProfiles.push({
Type: 'Video',
Codec: 'hevc',
Conditions: hevcCodecProfileConditions
});
const globalVideoConditions = [];
if (globalMaxVideoBitrate) {
@ -827,7 +962,7 @@ define(['browser'], function (browser) {
Method: 'External'
});
}
if (options.enableSsaRender) {
if (options.enableSsaRender !== false && (!options.isRetry && appSettings.get('subtitleburnin') !== 'allcomplexformats')) {
profile.SubtitleProfiles.push({
Format: 'ass',
Method: 'External'