mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Merge pull request #2064 from nyanmisaka/fmp4-hls
Add initial profile for HEVC over FMP4-HLS
This commit is contained in:
commit
79d92c01da
9 changed files with 267 additions and 49 deletions
|
@ -34,7 +34,6 @@ function getDeviceProfile(item, options = {}) {
|
|||
profile = window.NativeShell.AppHost.getDeviceProfile(profileBuilder);
|
||||
} else {
|
||||
const builderOpts = getBaseProfileOptions(item);
|
||||
builderOpts.enableSsaRender = (item && !options.isRetry && appSettings.get('subtitleburnin') !== 'allcomplexformats');
|
||||
profile = profileBuilder(builderOpts);
|
||||
}
|
||||
|
||||
|
|
|
@ -147,6 +147,8 @@ import toast from '../toast/toast';
|
|||
|
||||
showHideQualityFields(context, user, apiClient);
|
||||
|
||||
context.querySelector('#selectAllowedAudioChannels').value = userSettings.allowedAudioChannels();
|
||||
|
||||
apiClient.getCultures().then(allCultures => {
|
||||
populateLanguages(context.querySelector('#selectAudioLanguage'), allCultures);
|
||||
|
||||
|
@ -189,6 +191,7 @@ import toast from '../toast/toast';
|
|||
}
|
||||
|
||||
context.querySelector('.chkPlayDefaultAudioTrack').checked = user.Configuration.PlayDefaultAudioTrack || false;
|
||||
context.querySelector('.chkPreferFmp4HlsContainer').checked = userSettings.preferFmp4HlsContainer();
|
||||
context.querySelector('.chkEnableCinemaMode').checked = userSettings.enableCinemaMode();
|
||||
context.querySelector('.chkEnableNextVideoOverlay').checked = userSettings.enableNextVideoInfoOverlay();
|
||||
context.querySelector('.chkExternalVideoPlayer').checked = appSettings.enableSystemExternalPlayers();
|
||||
|
@ -224,10 +227,11 @@ import toast from '../toast/toast';
|
|||
setMaxBitrateFromField(context.querySelector('.selectVideoInternetQuality'), false, 'Video');
|
||||
setMaxBitrateFromField(context.querySelector('.selectMusicInternetQuality'), false, 'Audio');
|
||||
|
||||
userSettingsInstance.allowedAudioChannels(context.querySelector('#selectAllowedAudioChannels').value);
|
||||
user.Configuration.AudioLanguagePreference = context.querySelector('#selectAudioLanguage').value;
|
||||
user.Configuration.PlayDefaultAudioTrack = context.querySelector('.chkPlayDefaultAudioTrack').checked;
|
||||
user.Configuration.EnableNextEpisodeAutoPlay = context.querySelector('.chkEpisodeAutoPlay').checked;
|
||||
|
||||
userSettingsInstance.preferFmp4HlsContainer(context.querySelector('.chkPreferFmp4HlsContainer').checked);
|
||||
userSettingsInstance.enableCinemaMode(context.querySelector('.chkEnableCinemaMode').checked);
|
||||
|
||||
userSettingsInstance.enableNextVideoInfoOverlay(context.querySelector('.chkEnableNextVideoOverlay').checked);
|
||||
|
|
|
@ -4,6 +4,16 @@
|
|||
${HeaderAudioSettings}
|
||||
</h2>
|
||||
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" id="selectAllowedAudioChannels" label="${LabelAllowedAudioChannels}">
|
||||
<option value="-1">${Auto}</option>
|
||||
<option value="1">${LabelSelectMono}</option>
|
||||
<option value="2">${LabelSelectStereo}</option>
|
||||
<option value="6">5.1 ${LabelSelectAudioChannels}</option>
|
||||
<option value="8">7.1 ${LabelSelectAudioChannels}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" id="selectAudioLanguage" label="${LabelAudioLanguagePreference}"></select>
|
||||
</div>
|
||||
|
@ -49,6 +59,14 @@
|
|||
${TabAdvanced}
|
||||
</h2>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkPreferFmp4HlsContainer" />
|
||||
<span>${PreferFmp4HlsContainer}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${PreferFmp4HlsContainerHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription cinemaModeOptions">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkEnableCinemaMode" />
|
||||
|
|
|
@ -94,6 +94,15 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxListContainer">
|
||||
<div class="checkboxList">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkAllowHevcEncoding" />
|
||||
<span>${AllowHevcEncoding}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tonemappingOptions hide">
|
||||
<div class="checkboxListContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
|
@ -218,11 +227,13 @@
|
|||
</select>
|
||||
<div class="fieldDescription">${EncoderPresetHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtH265Crf" pattern="[0-9]*" min="0" max="51" step="1" label="${LabelH265Crf}" />
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtH264Crf" pattern="[0-9]*" min="0" max="51" step="1" label="${LabelH264Crf}" />
|
||||
<div class="fieldDescription">${H264CrfHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" id="selectDeinterlaceMethod" label="${LabelDeinterlaceMethod}">
|
||||
<option value="yadif">${Yadif}</option>
|
||||
|
|
|
@ -15,6 +15,7 @@ import alert from '../../components/alert';
|
|||
page.querySelector('#chkDecodingColorDepth10Hevc').checked = config.EnableDecodingColorDepth10Hevc;
|
||||
page.querySelector('#chkDecodingColorDepth10Vp9').checked = config.EnableDecodingColorDepth10Vp9;
|
||||
page.querySelector('#chkHardwareEncoding').checked = config.EnableHardwareEncoding;
|
||||
page.querySelector('#chkAllowHevcEncoding').checked = config.AllowHevcEncoding;
|
||||
$('#selectVideoDecoder', page).val(config.HardwareAccelerationType);
|
||||
$('#selectThreadCount', page).val(config.EncodingThreadCount);
|
||||
$('#txtDownMixAudioBoost', page).val(config.DownMixAudioBoost);
|
||||
|
@ -34,6 +35,7 @@ import alert from '../../components/alert';
|
|||
page.querySelector('#txtTonemappingParam').value = config.TonemappingParam || '';
|
||||
page.querySelector('#selectEncoderPreset').value = config.EncoderPreset || '';
|
||||
page.querySelector('#txtH264Crf').value = config.H264Crf || '';
|
||||
page.querySelector('#txtH265Crf').value = config.H265Crf || '';
|
||||
page.querySelector('#selectDeinterlaceMethod').value = config.DeinterlaceMethod || '';
|
||||
page.querySelector('#chkDoubleRateDeinterlacing').checked = config.DeinterlaceDoubleRate;
|
||||
page.querySelector('#chkEnableSubtitleExtraction').checked = config.EnableSubtitleExtraction || false;
|
||||
|
@ -87,6 +89,7 @@ import alert from '../../components/alert';
|
|||
config.TonemappingParam = form.querySelector('#txtTonemappingParam').value || '0';
|
||||
config.EncoderPreset = form.querySelector('#selectEncoderPreset').value;
|
||||
config.H264Crf = parseInt(form.querySelector('#txtH264Crf').value || '0');
|
||||
config.H265Crf = parseInt(form.querySelector('#txtH265Crf').value || '0');
|
||||
config.DeinterlaceMethod = form.querySelector('#selectDeinterlaceMethod').value;
|
||||
config.DeinterlaceDoubleRate = form.querySelector('#chkDoubleRateDeinterlacing').checked;
|
||||
config.EnableSubtitleExtraction = form.querySelector('#chkEnableSubtitleExtraction').checked;
|
||||
|
@ -99,6 +102,7 @@ import alert from '../../components/alert';
|
|||
config.EnableDecodingColorDepth10Hevc = form.querySelector('#chkDecodingColorDepth10Hevc').checked;
|
||||
config.EnableDecodingColorDepth10Vp9 = form.querySelector('#chkDecodingColorDepth10Vp9').checked;
|
||||
config.EnableHardwareEncoding = form.querySelector('#chkHardwareEncoding').checked;
|
||||
config.AllowHevcEncoding = form.querySelector('#chkAllowHevcEncoding').checked;
|
||||
ApiClient.updateNamedConfiguration('encoding', config).then(function () {
|
||||
updateEncoder(form);
|
||||
}, function () {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import appSettings from './settings/appSettings';
|
||||
import * as userSettings from './settings/userSettings';
|
||||
import browser from './browser';
|
||||
/* eslint-disable indent */
|
||||
|
||||
|
@ -5,7 +7,7 @@ import browser from './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;
|
||||
}
|
||||
|
@ -14,6 +16,7 @@ import browser from './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/, '') ||
|
||||
|
@ -205,8 +208,7 @@ import browser from './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');
|
||||
}
|
||||
}
|
||||
|
@ -246,8 +248,8 @@ import browser from './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)) {
|
||||
|
@ -295,7 +297,9 @@ import browser from './browser';
|
|||
export default 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();
|
||||
|
||||
|
@ -311,12 +315,13 @@ import browser from './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/, '')
|
||||
|
@ -351,16 +356,19 @@ import browser from './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) {
|
||||
|
@ -368,16 +376,31 @@ import browser from './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');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -413,38 +436,54 @@ import browser from './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');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -604,18 +643,34 @@ import browser from './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) {
|
||||
|
@ -711,6 +766,36 @@ import browser from './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="hev1.2.4.L183"').replace(/no/, '')) {
|
||||
maxHevcLevel = 183;
|
||||
hevcProfiles = 'main|main 10';
|
||||
}
|
||||
|
||||
const h264CodecProfileConditions = [
|
||||
{
|
||||
Condition: 'NotEquals',
|
||||
|
@ -732,6 +817,27 @@ import browser from './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',
|
||||
|
@ -739,6 +845,13 @@ import browser from './browser';
|
|||
Value: 'true',
|
||||
IsRequired: false
|
||||
});
|
||||
|
||||
hevcCodecProfileConditions.push({
|
||||
Condition: 'NotEquals',
|
||||
Property: 'IsInterlaced',
|
||||
Value: 'true',
|
||||
IsRequired: false
|
||||
});
|
||||
}
|
||||
|
||||
if (maxVideoWidth) {
|
||||
|
@ -748,12 +861,21 @@ import browser from './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',
|
||||
|
@ -763,6 +885,15 @@ import browser from './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 = {
|
||||
|
@ -790,6 +921,12 @@ import browser from './browser';
|
|||
Conditions: h264CodecProfileConditions
|
||||
});
|
||||
|
||||
profile.CodecProfiles.push({
|
||||
Type: 'Video',
|
||||
Codec: 'hevc',
|
||||
Conditions: hevcCodecProfileConditions
|
||||
});
|
||||
|
||||
const globalVideoConditions = [];
|
||||
|
||||
if (globalMaxVideoBitrate) {
|
||||
|
@ -825,7 +962,7 @@ import browser from './browser';
|
|||
Method: 'External'
|
||||
});
|
||||
}
|
||||
if (options.enableSsaRender) {
|
||||
if (options.enableSsaRender !== false && (!options.isRetry && appSettings.get('subtitleburnin') !== 'allcomplexformats')) {
|
||||
profile.SubtitleProfiles.push({
|
||||
Format: 'ass',
|
||||
Method: 'External'
|
||||
|
|
|
@ -114,6 +114,33 @@ export class UserSettings {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set 'Allowed Audio Channels'.
|
||||
* @param {string|undefined} val - 'Allowed Audio Channels'.
|
||||
* @return {string} 'Allowed Audio Channels'.
|
||||
*/
|
||||
allowedAudioChannels(val) {
|
||||
if (val !== undefined) {
|
||||
return this.set('allowedAudioChannels', val, false);
|
||||
}
|
||||
|
||||
return this.get('allowedAudioChannels', false) || '-1';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set 'Perfer fMP4-HLS Container' state.
|
||||
* @param {boolean|undefined} val - Flag to enable 'Perfer fMP4-HLS Container' or undefined.
|
||||
* @return {boolean} 'Prefer fMP4-HLS Container' state.
|
||||
*/
|
||||
preferFmp4HlsContainer(val) {
|
||||
if (val !== undefined) {
|
||||
return this.set('preferFmp4HlsContainer', val.toString(), false);
|
||||
}
|
||||
|
||||
val = this.get('preferFmp4HlsContainer', false);
|
||||
return val === 'true';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set 'Cinema Mode' state.
|
||||
* @param {boolean|undefined} val - Flag to enable 'Cinema Mode' or undefined.
|
||||
|
@ -457,6 +484,8 @@ export const importFrom = currentSettings.importFrom.bind(currentSettings);
|
|||
export const set = currentSettings.set.bind(currentSettings);
|
||||
export const get = currentSettings.get.bind(currentSettings);
|
||||
export const serverConfig = currentSettings.serverConfig.bind(currentSettings);
|
||||
export const allowedAudioChannels = currentSettings.allowedAudioChannels.bind(currentSettings);
|
||||
export const preferFmp4HlsContainer = currentSettings.preferFmp4HlsContainer.bind(currentSettings);
|
||||
export const enableCinemaMode = currentSettings.enableCinemaMode.bind(currentSettings);
|
||||
export const enableNextVideoInfoOverlay = currentSettings.enableNextVideoInfoOverlay.bind(currentSettings);
|
||||
export const enableThemeSongs = currentSettings.enableThemeSongs.bind(currentSettings);
|
||||
|
|
|
@ -252,7 +252,7 @@
|
|||
"Guide": "Guide",
|
||||
"GuideProviderLogin": "Login",
|
||||
"GuideProviderSelectListings": "Select Listings",
|
||||
"H264CrfHelp": "The Constant Rate Factor (CRF) is the default quality setting for the x264 encoder. You can set the values between 0 and 51, where lower values would result in better quality (at the expense of higher file sizes). Sane values are between 18 and 28. The default for x264 is 23, so you can use this as a starting point.",
|
||||
"H264CrfHelp": "The Constant Rate Factor (CRF) is the default quality setting for the x264 and x265 encoder. You can set the values between 0 and 51, where lower values would result in better quality (at the expense of higher file sizes). Sane values are between 18 and 28. The default for x264 is 23, and for x265 is 28, so you can use this as a starting point.",
|
||||
"EncoderPresetHelp": "Choose a faster value to improve performance, or a slower value to improve quality.",
|
||||
"HDPrograms": "HD programs",
|
||||
"HardwareAccelerationWarning": "Enabling hardware acceleration may cause instability in some environments. Ensure that your operating system and video drivers are fully up to date. If you have difficulty playing video after enabling this, you'll need to change the setting back to None.",
|
||||
|
@ -589,7 +589,8 @@
|
|||
"LabelGroupMoviesIntoCollections": "Group movies into collections",
|
||||
"LabelGroupMoviesIntoCollectionsHelp": "When displaying movie lists, movies in a collection will be displayed as one grouped item.",
|
||||
"LabelH264Crf": "H264 encoding CRF:",
|
||||
"LabelEncoderPreset": "H264 and H265 encoding preset:",
|
||||
"LabelH265Crf": "H265 encoding CRF:",
|
||||
"LabelEncoderPreset": "Encoding preset:",
|
||||
"LabelHardwareAccelerationType": "Hardware acceleration:",
|
||||
"LabelHardwareAccelerationTypeHelp": "Hardware acceleration requires additional configuration.",
|
||||
"LabelHomeNetworkQuality": "Home network quality:",
|
||||
|
@ -1428,5 +1429,12 @@
|
|||
"SubtitleVerticalPositionHelp": "Line number where text appears. Positive numbers indicate top down. Negative numbers indicate bottom up.",
|
||||
"Preview": "Preview",
|
||||
"LabelMaxMuxingQueueSize": "Max muxing queue size:",
|
||||
"LabelMaxMuxingQueueSizeHelp": "Maximum number of packets that can be buffered while waiting for all streams to initialize. Try to increase it if you still encounter \"Too many packets buffered for output stream\" error in ffmpeg logs. The recommended value is 2048."
|
||||
"LabelMaxMuxingQueueSizeHelp": "Maximum number of packets that can be buffered while waiting for all streams to initialize. Try to increase it if you still encounter \"Too many packets buffered for output stream\" error in ffmpeg logs. The recommended value is 2048.",
|
||||
"PreferFmp4HlsContainer": "Prefer fMP4-HLS Media Container",
|
||||
"PreferFmp4HlsContainerHelp": "Prefer to use fMP4 as the default container for HLS, making it possible to direct stream HEVC content on supported devices.",
|
||||
"AllowHevcEncoding": "Allow encoding in HEVC format",
|
||||
"LabelAllowedAudioChannels": "Maximum Allowed Audio Channels",
|
||||
"LabelSelectAudioChannels": "Channels",
|
||||
"LabelSelectMono": "Mono",
|
||||
"LabelSelectStereo": "Stereo"
|
||||
}
|
||||
|
|
|
@ -197,7 +197,7 @@
|
|||
"GuestStar": "特邀明星",
|
||||
"GuideProviderLogin": "登入",
|
||||
"GuideProviderSelectListings": "选择列表",
|
||||
"H264CrfHelp": "The Constant Rate Factor (CRF) 是 x264 编码器的默认质量设置。您可以设置介于0和51之间的值, 其中较低的值将导致更好的质量 (以更高的文件大小为代价)。正常值介于18和28之间。x264 的默认值为 23, 因此可以将其用作起始点。",
|
||||
"H264CrfHelp": "The Constant Rate Factor (CRF) 是 x264 编码器的默认质量设置。您可以设置介于0和51之间的值, 其中较低的值将导致更好的质量 (以更高的文件大小为代价)。正常值介于 18 和 28 之间。x264 的默认值为 23,x265 的默认值为 28,因此可以将其用作起始点。",
|
||||
"EncoderPresetHelp": "选择一个更快的值以提升性能,或者选择一个更慢的值以提升质量。",
|
||||
"HDPrograms": "高清节目",
|
||||
"HardwareAccelerationWarning": "启动硬件加速可能在某些环境下导致系统不稳定。请确认你的操作系统和显卡驱动程序是最新的。如果你在开启此项后播放视频时遇到困难,那么你需要将此选项设置回“没有”。",
|
||||
|
@ -493,7 +493,8 @@
|
|||
"LabelGroupMoviesIntoCollections": "批量添加电影到集合",
|
||||
"LabelGroupMoviesIntoCollectionsHelp": "显示电影列表时,同一集合的电影将显示为一个分组。",
|
||||
"LabelH264Crf": "H264 CRF 编码质量等级:",
|
||||
"LabelEncoderPreset": "H264 和 H265 编码预设:",
|
||||
"LabelH265Crf": "H265 CRF 编码质量等级:",
|
||||
"LabelEncoderPreset": "编码预设:",
|
||||
"LabelHardwareAccelerationType": "硬件加速:",
|
||||
"LabelHardwareAccelerationTypeHelp": "硬件加速需要额外配置。",
|
||||
"LabelHomeNetworkQuality": "家庭网络质量:",
|
||||
|
@ -1431,5 +1432,12 @@
|
|||
"OptionAllowContentDownload": "允许媒体下载",
|
||||
"HeaderDeleteDevices": "删除所有设备",
|
||||
"DeleteDevicesConfirmation": "您确定要删除所有设备吗?所有其他会话将被注销。用户下次登录时,设备会重新出现。",
|
||||
"DeleteAll": "删除全部"
|
||||
"DeleteAll": "删除全部",
|
||||
"PreferFmp4HlsContainer": "优先使用 fMP4-HLS 媒体容器",
|
||||
"PreferFmp4HlsContainerHelp": "优先使用 fMP4 作为 HLS 播放的默认容器,从而可以在支持的设备上直接串流 HEVC 格式的内容。",
|
||||
"AllowHevcEncoding": "允许以 HEVC 格式编码",
|
||||
"LabelAllowedAudioChannels": "允许的最大声道数量",
|
||||
"LabelSelectAudioChannels": "声道",
|
||||
"LabelSelectMono": "单声道",
|
||||
"LabelSelectStereo": "立体声"
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue