2024-03-14 01:52:44 +02:00
import browser from './browser' ;
2020-11-20 12:30:16 +08:00
import appSettings from './settings/appSettings' ;
import * as userSettings from './settings/userSettings' ;
2020-08-02 15:25:20 +03:00
2023-04-19 01:56:05 -04:00
function canPlayH264 ( videoTestElement ) {
2023-07-06 13:39:48 -04:00
return ! ! ( videoTestElement . canPlayType ? . ( 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"' ) . replace ( /no/ , '' ) ) ;
2023-04-19 01:56:05 -04:00
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
function canPlayHevc ( videoTestElement , options ) {
if ( browser . tizen || browser . xboxOne || browser . web0s || options . supportsHevc ) {
return true ;
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
if ( browser . ps4 ) {
return false ;
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
// hevc main level 4.0
return ! ! videoTestElement . canPlayType
2023-03-29 00:38:22 -04:00
&& ( videoTestElement . canPlayType ( 'video/mp4; codecs="hvc1.1.L120"' ) . replace ( /no/ , '' )
|| videoTestElement . canPlayType ( 'video/mp4; codecs="hev1.1.L120"' ) . replace ( /no/ , '' )
|| videoTestElement . canPlayType ( 'video/mp4; codecs="hvc1.1.0.L120"' ) . replace ( /no/ , '' )
|| videoTestElement . canPlayType ( 'video/mp4; codecs="hev1.1.0.L120"' ) . replace ( /no/ , '' ) ) ;
2023-04-19 01:56:05 -04:00
}
2020-03-30 17:02:16 +08:00
2023-09-01 22:42:29 +08:00
function canPlayAv1 ( videoTestElement ) {
if ( browser . tizenVersion >= 5.5 || browser . web0sVersion >= 5 ) {
return true ;
}
// av1 main level 5.3
return ! ! videoTestElement . canPlayType
&& ( videoTestElement . canPlayType ( 'video/mp4; codecs="av01.0.15M.08"' ) . replace ( /no/ , '' )
&& videoTestElement . canPlayType ( 'video/mp4; codecs="av01.0.15M.10"' ) . replace ( /no/ , '' ) ) ;
}
2023-04-19 01:56:05 -04:00
let _supportsTextTracks ;
function supportsTextTracks ( ) {
if ( browser . tizen ) {
return true ;
2020-03-30 17:02:16 +08:00
}
2023-04-19 01:56:05 -04:00
if ( _supportsTextTracks == null ) {
_supportsTextTracks = document . createElement ( 'video' ) . textTracks != null ;
2020-03-30 17:02:16 +08:00
}
2023-04-19 01:56:05 -04:00
// For now, until ready
return _supportsTextTracks ;
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
let _canPlayHls ;
function canPlayHls ( ) {
if ( _canPlayHls == null ) {
_canPlayHls = canPlayNativeHls ( ) || canPlayHlsWithMSE ( ) ;
2020-03-30 17:02:16 +08:00
}
2023-04-19 01:56:05 -04:00
return _canPlayHls ;
}
function canPlayNativeHls ( ) {
if ( browser . tizen ) {
return true ;
2020-03-30 17:02:16 +08:00
}
2023-04-19 01:56:05 -04:00
const media = document . createElement ( 'video' ) ;
return ! ! ( media . canPlayType ( 'application/x-mpegURL' ) . replace ( /no/ , '' )
|| media . canPlayType ( 'application/vnd.apple.mpegURL' ) . replace ( /no/ , '' ) ) ;
}
2020-03-30 17:02:16 +08:00
2023-09-01 22:42:29 +08:00
function canPlayNativeHlsInFmp4 ( ) {
2024-03-19 17:40:13 +08:00
if ( browser . tizenVersion >= 5 || browser . web0sVersion >= 3.5 ) {
2023-09-01 22:42:29 +08:00
return true ;
}
return ( browser . iOS && browser . iOSVersion >= 11 ) || browser . osx ;
}
2023-04-19 01:56:05 -04:00
function canPlayHlsWithMSE ( ) {
// text tracks don’ t work with this in firefox
return window . MediaSource != null ; /* eslint-disable-line compat/compat */
}
2020-09-01 13:20:26 -04:00
2023-04-19 01:56:05 -04:00
function supportsAc3 ( videoTestElement ) {
if ( browser . edgeUwp || browser . tizen || browser . web0s ) {
return true ;
2020-03-30 17:02:16 +08:00
}
2023-04-19 01:56:05 -04:00
// iPhones 5c and older and old model iPads do not support AC-3/E-AC-3
// These models can only run iOS 10.x or lower
if ( browser . iOS && browser . iOSVersion < 11 ) {
return false ;
2020-03-30 17:02:16 +08:00
}
2023-04-19 01:56:05 -04:00
return videoTestElement . canPlayType ( 'audio/mp4; codecs="ac-3"' ) . replace ( /no/ , '' ) ;
}
2020-03-30 17:02:16 +08:00
2024-04-15 21:01:44 +03:00
/ * *
* Checks if the device supports DTS ( DCA ) .
* @ param { HTMLVideoElement } videoTestElement The video test element
* @ returns { boolean | null } _true _ if the device supports DTS ( DCA ) . _false _ if the device doesn ' t support DTS ( DCA ) . _null _ if support status is unknown .
* /
function canPlayDts ( videoTestElement ) {
// DTS audio is not supported by Samsung TV 2018+ (Tizen 4.0+) and LG TV 2020-2022 (webOS 5.0, 6.0 and 22) models
if ( browser . tizenVersion >= 4 || ( browser . web0sVersion >= 5 && browser . web0sVersion < 23 ) ) {
return false ;
}
if ( videoTestElement . canPlayType ( 'video/mp4; codecs="dts-"' ) . replace ( /no/ , '' )
|| videoTestElement . canPlayType ( 'video/mp4; codecs="dts+"' ) . replace ( /no/ , '' ) ) {
return true ;
}
return null ;
}
2023-04-19 01:56:05 -04:00
function supportsEac3 ( videoTestElement ) {
if ( browser . tizen || browser . web0s ) {
return true ;
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
// iPhones 5c and older and old model iPads do not support AC-3/E-AC-3
// These models can only run iOS 10.x or lower
if ( browser . iOS && browser . iOSVersion < 11 ) {
2020-03-30 17:02:16 +08:00
return false ;
}
2023-04-19 01:56:05 -04:00
return videoTestElement . canPlayType ( 'audio/mp4; codecs="ec-3"' ) . replace ( /no/ , '' ) ;
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
function supportsAc3InHls ( videoTestElement ) {
if ( browser . tizen || browser . web0s ) {
return true ;
}
2022-03-17 22:40:36 +03:00
2023-04-19 01:56:05 -04:00
if ( videoTestElement . canPlayType ) {
return videoTestElement . canPlayType ( 'application/x-mpegurl; codecs="avc1.42E01E, ac-3"' ) . replace ( /no/ , '' )
|| videoTestElement . canPlayType ( 'application/vnd.apple.mpegURL; codecs="avc1.42E01E, ac-3"' ) . replace ( /no/ , '' ) ;
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
return false ;
}
2020-03-30 17:02:16 +08:00
2024-04-26 06:00:24 +08:00
function supportsMp3InHls ( videoTestElement ) {
if ( videoTestElement . canPlayType ) {
2024-04-30 08:25:54 +08:00
return videoTestElement . canPlayType ( 'application/x-mpegurl; codecs="avc1.64001E, mp4a.40.34"' ) . replace ( /no/ , '' )
|| videoTestElement . canPlayType ( 'application/vnd.apple.mpegURL; codecs="avc1.64001E, mp4a.40.34"' ) . replace ( /no/ , '' ) ;
2024-04-26 06:00:24 +08:00
}
return false ;
}
2023-04-19 01:56:05 -04:00
function canPlayAudioFormat ( format ) {
let typeString ;
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
if ( format === 'flac' || format === 'asf' ) {
if ( browser . tizen || browser . web0s || browser . edgeUwp ) {
2020-03-30 17:02:16 +08:00
return true ;
}
2023-04-19 01:56:05 -04:00
} else if ( format === 'wma' ) {
if ( browser . tizen || browser . edgeUwp ) {
2020-03-30 17:02:16 +08:00
return true ;
}
2023-04-19 01:56:05 -04:00
} else if ( format === 'opus' ) {
if ( browser . web0s ) {
// canPlayType lies about OPUS support
return browser . web0sVersion >= 3.5 ;
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
typeString = 'audio/ogg; codecs="opus"' ;
} else if ( format === 'alac' ) {
2023-07-02 02:12:45 -04:00
if ( browser . iOS || browser . osx && browser . safari ) {
2020-07-27 21:11:12 +08:00
return true ;
}
2023-04-19 01:56:05 -04:00
} else if ( format === 'mp2' ) {
// For now
return false ;
}
2020-07-27 21:11:12 +08:00
2023-04-19 01:56:05 -04:00
if ( format === 'webma' ) {
typeString = 'audio/webm' ;
} else if ( format === 'mp2' ) {
typeString = 'audio/mpeg' ;
} else if ( ! typeString ) {
typeString = 'audio/' + format ;
2020-03-30 17:02:16 +08:00
}
2023-04-19 01:56:05 -04:00
return ! ! document . createElement ( 'audio' ) . canPlayType ( typeString ) . replace ( /no/ , '' ) ;
}
2020-06-03 12:26:25 -04:00
2023-04-19 01:56:05 -04:00
function testCanPlayMkv ( videoTestElement ) {
if ( browser . tizen || browser . web0s ) {
return true ;
2020-06-03 12:26:25 -04:00
}
2023-04-19 01:56:05 -04:00
if ( videoTestElement . canPlayType ( 'video/x-matroska' ) . replace ( /no/ , '' )
|| videoTestElement . canPlayType ( 'video/mkv' ) . replace ( /no/ , '' ) ) {
return true ;
2020-03-30 17:02:16 +08:00
}
2023-04-19 01:56:05 -04:00
if ( browser . edgeChromium && browser . windows ) {
return true ;
2020-03-30 17:02:16 +08:00
}
2023-04-19 01:56:05 -04:00
return ! ! browser . edgeUwp ;
}
function testCanPlayTs ( ) {
return browser . tizen || browser . web0s || browser . edgeUwp ;
}
function supportsMpeg2Video ( ) {
return browser . tizen || browser . web0s || browser . edgeUwp ;
}
function supportsVc1 ( videoTestElement ) {
return browser . tizen || browser . web0s || browser . edgeUwp || videoTestElement . canPlayType ( 'video/mp4; codecs="vc-1"' ) . replace ( /no/ , '' ) ;
}
2023-08-23 00:15:39 +03:00
function supportsHdr10 ( options ) {
return options . supportsHdr10 ? ? ( false // eslint-disable-line sonarjs/no-redundant-boolean
|| browser . tizen
|| browser . web0s
|| browser . safari && ( ( browser . iOS && browser . iOSVersion >= 11 ) || browser . osx )
// Chrome mobile and Firefox have no client side tone-mapping
2024-02-06 01:07:47 +08:00
// Edge Chromium 121+ fixed the tone-mapping color issue on Nvidia
|| browser . edgeChromium && ( browser . versionMajor >= 121 )
2023-08-23 00:15:39 +03:00
|| browser . chrome && ! browser . mobile
) ;
}
function supportsHlg ( options ) {
return options . supportsHlg ? ? supportsHdr10 ( options ) ;
}
function supportsDolbyVision ( options ) {
return options . supportsDolbyVision ? ? ( false // eslint-disable-line sonarjs/no-redundant-boolean
|| browser . safari && ( ( browser . iOS && browser . iOSVersion >= 13 ) || browser . osx )
) ;
}
2024-03-16 21:10:14 +02:00
function supportedDolbyVisionProfilesHevc ( videoTestElement ) {
const supportedProfiles = [ ] ;
2024-03-18 12:55:54 +02:00
// Profiles 5/8 4k@60fps
2024-03-16 21:10:14 +02:00
if ( videoTestElement . canPlayType ) {
if ( videoTestElement
. canPlayType ( 'video/mp4; codecs="dvh1.05.09"' )
2024-03-24 12:35:36 +02:00
. replace ( /no/ , '' ) ) {
supportedProfiles . push ( 5 ) ;
}
2024-03-23 18:38:41 +02:00
if ( videoTestElement
2024-03-16 21:10:14 +02:00
. canPlayType ( 'video/mp4; codecs="dvh1.08.09"' )
2024-03-24 12:35:36 +02:00
. replace ( /no/ , '' ) ) {
supportedProfiles . push ( 8 ) ;
}
2024-03-16 21:10:14 +02:00
}
return supportedProfiles ;
2024-02-06 22:13:32 +08:00
}
2023-04-19 01:56:05 -04:00
function getDirectPlayProfileForVideoContainer ( container , videoAudioCodecs , videoTestElement , options ) {
let supported = false ;
let profileContainer = container ;
const videoCodecs = [ ] ;
switch ( container ) {
case 'asf' :
case 'wmv' :
supported = browser . tizen || browser . web0s || browser . edgeUwp ;
videoAudioCodecs = [ ] ;
break ;
case 'avi' :
supported = browser . tizen || browser . web0s || browser . edgeUwp ;
// New Samsung TV don't support XviD/DivX
// Explicitly add supported codecs to make other codecs be transcoded
if ( browser . tizenVersion >= 4 ) {
2020-03-30 17:02:16 +08:00
videoCodecs . push ( 'h264' ) ;
2023-04-19 01:56:05 -04:00
if ( canPlayHevc ( videoTestElement , options ) ) {
2020-03-30 17:02:16 +08:00
videoCodecs . push ( 'hevc' ) ;
}
2023-04-19 01:56:05 -04:00
}
break ;
case 'mpg' :
case 'mpeg' :
supported = browser . tizen || browser . web0s || browser . edgeUwp ;
break ;
case 'flv' :
supported = browser . tizen ;
break ;
case '3gp' :
case 'mts' :
case 'trp' :
case 'vob' :
case 'vro' :
supported = browser . tizen ;
break ;
case 'mov' :
supported = browser . safari || browser . tizen || browser . web0s || browser . chrome || browser . edgeChromium || browser . edgeUwp ;
videoCodecs . push ( 'h264' ) ;
break ;
case 'm2ts' :
supported = browser . tizen || browser . web0s || browser . edgeUwp ;
videoCodecs . push ( 'h264' ) ;
if ( supportsVc1 ( videoTestElement ) ) {
videoCodecs . push ( 'vc1' ) ;
}
if ( supportsMpeg2Video ( ) ) {
videoCodecs . push ( 'mpeg2video' ) ;
}
break ;
case 'ts' :
supported = testCanPlayTs ( ) ;
videoCodecs . push ( 'h264' ) ;
// safari doesn't support hevc in TS-HLS
if ( ( browser . tizen || browser . web0s ) && canPlayHevc ( videoTestElement , options ) ) {
videoCodecs . push ( 'hevc' ) ;
}
if ( supportsVc1 ( videoTestElement ) ) {
videoCodecs . push ( 'vc1' ) ;
}
if ( supportsMpeg2Video ( ) ) {
videoCodecs . push ( 'mpeg2video' ) ;
}
profileContainer = 'ts,mpegts' ;
break ;
default :
break ;
2020-03-30 17:02:16 +08:00
}
2023-04-19 01:56:05 -04:00
return supported ? {
Container : profileContainer ,
Type : 'Video' ,
VideoCodec : videoCodecs . join ( ',' ) ,
AudioCodec : videoAudioCodecs . join ( ',' )
} : null ;
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
function getMaxBitrate ( ) {
return 120000000 ;
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
function getGlobalMaxVideoBitrate ( ) {
let isTizenFhd = false ;
if ( browser . tizen ) {
try {
const isTizenUhd = webapis . productinfo . isUdPanelSupported ( ) ;
isTizenFhd = ! isTizenUhd ;
console . debug ( 'isTizenFhd = ' + isTizenFhd ) ;
} catch ( error ) {
console . error ( 'isUdPanelSupported() error code = ' + error . code ) ;
2022-04-27 15:45:56 -07:00
}
2023-04-19 01:56:05 -04:00
}
2022-04-27 15:45:56 -07:00
2023-04-19 01:56:05 -04:00
let bitrate = null ;
if ( browser . ps4 ) {
bitrate = 8000000 ;
} else if ( browser . xboxOne ) {
bitrate = 12000000 ;
} else if ( browser . tizen && isTizenFhd ) {
bitrate = 20000000 ;
2020-03-30 17:02:16 +08:00
}
2023-04-19 01:56:05 -04:00
return bitrate ;
}
2023-01-22 14:08:03 -05:00
2023-04-19 01:56:05 -04:00
let maxChannelCount = null ;
2023-01-22 14:08:03 -05:00
2023-04-19 01:56:05 -04:00
function getSpeakerCount ( ) {
if ( maxChannelCount != null ) {
return maxChannelCount ;
}
2023-01-22 14:08:03 -05:00
2023-04-19 01:56:05 -04:00
maxChannelCount = - 1 ;
2022-01-17 00:08:33 +01:00
2023-04-19 01:56:05 -04:00
const AudioContext = window . AudioContext || window . webkitAudioContext || false ; /* eslint-disable-line compat/compat */
2022-01-17 00:08:33 +01:00
2023-04-19 01:56:05 -04:00
if ( AudioContext ) {
const audioCtx = new AudioContext ( ) ;
2022-01-17 00:08:33 +01:00
2023-04-19 01:56:05 -04:00
maxChannelCount = audioCtx . destination . maxChannelCount ;
2022-01-17 00:08:33 +01:00
}
2023-04-19 01:56:05 -04:00
return maxChannelCount ;
}
2022-01-17 00:08:33 +01:00
2023-04-19 01:56:05 -04:00
function getPhysicalAudioChannels ( options , videoTestElement ) {
const allowedAudioChannels = parseInt ( userSettings . allowedAudioChannels ( ) , 10 ) ;
2022-01-17 00:08:33 +01:00
2023-04-19 01:56:05 -04:00
if ( allowedAudioChannels > 0 ) {
return allowedAudioChannels ;
}
2022-06-26 20:53:47 -04:00
2023-04-19 01:56:05 -04:00
if ( options . audioChannels ) {
return options . audioChannels ;
}
2022-01-17 00:08:33 +01:00
2023-04-19 01:56:05 -04:00
const isSurroundSoundSupportedBrowser = browser . safari || browser . chrome || browser . edgeChromium || browser . firefox || browser . tv || browser . ps4 || browser . xboxOne ;
const isAc3Eac3Supported = supportsAc3 ( videoTestElement ) || supportsEac3 ( videoTestElement ) ;
const speakerCount = getSpeakerCount ( ) ;
2022-01-17 00:08:33 +01:00
2023-04-19 01:56:05 -04:00
// AC3/EAC3 hinted that device is able to play dolby surround sound.
if ( isAc3Eac3Supported && isSurroundSoundSupportedBrowser ) {
return speakerCount > 6 ? speakerCount : 6 ;
}
2022-01-17 00:08:33 +01:00
2023-04-19 01:56:05 -04:00
if ( speakerCount > 2 ) {
2022-01-17 00:08:33 +01:00
if ( isSurroundSoundSupportedBrowser ) {
2023-04-19 01:56:05 -04:00
return speakerCount ;
2022-01-17 00:08:33 +01:00
}
return 2 ;
}
2023-04-19 01:56:05 -04:00
if ( speakerCount > 0 ) {
return speakerCount ;
}
if ( isSurroundSoundSupportedBrowser ) {
return 6 ;
}
return 2 ;
}
2023-01-22 14:08:02 -05:00
/ * *
* Checks if the web engine supports secondary audio .
* @ param { HTMLVideoElement } videoTestElement The video test element
* @ returns { boolean } _true _ if the web engine supports secondary audio .
* /
export function canPlaySecondaryAudio ( videoTestElement ) {
// We rely on HTMLMediaElement.audioTracks
// It works in Chrome 79+ with "Experimental Web Platform features" enabled
return ! ! videoTestElement . audioTracks
// It doesn't work in Firefox 108 even with "media.track.enabled" enabled (it only sees the first audio track)
&& ! browser . firefox
// It seems to work on Tizen 5.5+ (2020, Chrome 69+). See https://developer.tizen.org/forums/web-application-development/video-tag-not-work-audiotracks
&& ( browser . tizenVersion >= 5.5 || ! browser . tizen )
2024-02-19 10:22:02 +00:00
&& ( browser . web0sVersion >= 4.0 || ! browser . web0sVersion ) ;
2023-01-22 14:08:02 -05:00
}
2023-04-19 01:56:05 -04:00
export default function ( options ) {
options = options || { } ;
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
const bitrateSetting = getMaxBitrate ( ) ;
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
const videoTestElement = document . createElement ( 'video' ) ;
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
const physicalAudioChannels = getPhysicalAudioChannels ( options , videoTestElement ) ;
2022-06-26 20:53:47 -04:00
2023-04-19 01:56:05 -04:00
const canPlayVp8 = videoTestElement . canPlayType ( 'video/webm; codecs="vp8"' ) . replace ( /no/ , '' ) ;
const canPlayVp9 = videoTestElement . canPlayType ( 'video/webm; codecs="vp9"' ) . replace ( /no/ , '' ) ;
const webmAudioCodecs = [ 'vorbis' ] ;
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
const canPlayMkv = testCanPlayMkv ( videoTestElement ) ;
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
const profile = {
MaxStreamingBitrate : bitrateSetting ,
MaxStaticBitrate : 100000000 ,
MusicStreamingTranscodingBitrate : Math . min ( bitrateSetting , 384000 ) ,
DirectPlayProfiles : [ ]
} ;
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
let videoAudioCodecs = [ ] ;
let hlsInTsVideoAudioCodecs = [ ] ;
let hlsInFmp4VideoAudioCodecs = [ ] ;
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
const supportsMp3VideoAudio = videoTestElement . canPlayType ( 'video/mp4; codecs="avc1.640029, mp4a.69"' ) . replace ( /no/ , '' )
2020-06-06 18:53:05 +02:00
|| videoTestElement . canPlayType ( 'video/mp4; codecs="avc1.640029, mp4a.6B"' ) . replace ( /no/ , '' )
|| videoTestElement . canPlayType ( 'video/mp4; codecs="avc1.640029, mp3"' ) . replace ( /no/ , '' ) ;
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
let supportsMp2VideoAudio = options . supportsMp2VideoAudio ;
if ( supportsMp2VideoAudio == null ) {
supportsMp2VideoAudio = browser . edgeUwp || browser . tizen || browser . web0s ;
2022-04-21 00:34:38 +02:00
2023-04-19 01:56:05 -04:00
// If the browser supports MP3, it presumably supports MP2 as well
if ( supportsMp3VideoAudio && ( browser . chrome || browser . edgeChromium || ( browser . firefox && browser . versionMajor >= 83 ) ) ) {
supportsMp2VideoAudio = true ;
2022-04-21 00:34:38 +02:00
}
2023-04-19 01:56:05 -04:00
if ( browser . android ) {
supportsMp2VideoAudio = false ;
2020-03-30 17:02:16 +08:00
}
2023-04-19 01:56:05 -04:00
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
/* eslint-disable compat/compat */
let maxVideoWidth = browser . xboxOne ? window . screen ? . width : null ;
2020-10-31 21:20:20 +08:00
2023-04-19 01:56:05 -04:00
/* eslint-enable compat/compat */
if ( options . maxVideoWidth ) {
maxVideoWidth = options . maxVideoWidth ;
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
const canPlayAacVideoAudio = videoTestElement . canPlayType ( 'video/mp4; codecs="avc1.640029, mp4a.40.2"' ) . replace ( /no/ , '' ) ;
2024-04-26 06:00:24 +08:00
const canPlayMp3VideoAudioInHls = supportsMp3InHls ( videoTestElement ) ;
2023-04-19 01:56:05 -04:00
const canPlayAc3VideoAudio = supportsAc3 ( videoTestElement ) ;
const canPlayEac3VideoAudio = supportsEac3 ( videoTestElement ) ;
const canPlayAc3VideoAudioInHls = supportsAc3InHls ( videoTestElement ) ;
// Transcoding codec is the first in hlsVideoAudioCodecs.
// Prefer AAC, MP3 to other codecs when audio transcoding.
if ( canPlayAacVideoAudio ) {
videoAudioCodecs . push ( 'aac' ) ;
hlsInTsVideoAudioCodecs . push ( 'aac' ) ;
hlsInFmp4VideoAudioCodecs . push ( 'aac' ) ;
}
2020-11-12 20:03:38 +08:00
2023-04-19 01:56:05 -04:00
if ( supportsMp3VideoAudio ) {
videoAudioCodecs . push ( 'mp3' ) ;
2024-04-26 06:00:24 +08:00
}
2023-04-19 01:56:05 -04:00
2024-04-30 08:25:54 +08:00
// Safari supports mp3 with HLS, but only in mpegts container, and the supportsMp3VideoAudio will return false.
2024-04-30 18:46:48 +08:00
if ( browser . safari || ( supportsMp3VideoAudio && ! browser . ps4 ) ) {
2024-04-26 06:00:24 +08:00
hlsInTsVideoAudioCodecs . push ( 'mp3' ) ;
}
2020-03-30 17:02:16 +08:00
2024-04-26 06:00:24 +08:00
// Most browsers won't support mp3 with HLS, so this is usually false, but just in case.
if ( canPlayMp3VideoAudioInHls ) {
2023-04-19 01:56:05 -04:00
hlsInFmp4VideoAudioCodecs . push ( 'mp3' ) ;
}
// For AC3/EAC3 remuxing.
// Do not use AC3 for audio transcoding unless AAC and MP3 are not supported.
if ( canPlayAc3VideoAudio ) {
videoAudioCodecs . push ( 'ac3' ) ;
2023-09-01 22:42:29 +08:00
if ( browser . edgeChromium ) {
hlsInFmp4VideoAudioCodecs . push ( 'ac3' ) ;
}
2023-04-19 01:56:05 -04:00
if ( canPlayEac3VideoAudio ) {
videoAudioCodecs . push ( 'eac3' ) ;
2023-09-01 22:42:29 +08:00
if ( browser . edgeChromium ) {
hlsInFmp4VideoAudioCodecs . push ( 'eac3' ) ;
}
2023-04-19 01:56:05 -04:00
}
2020-11-12 20:03:38 +08:00
2023-04-19 01:56:05 -04:00
if ( canPlayAc3VideoAudioInHls ) {
hlsInTsVideoAudioCodecs . push ( 'ac3' ) ;
hlsInFmp4VideoAudioCodecs . push ( 'ac3' ) ;
2020-11-12 20:03:38 +08:00
2023-04-19 01:56:05 -04:00
if ( canPlayEac3VideoAudio ) {
hlsInTsVideoAudioCodecs . push ( 'eac3' ) ;
hlsInFmp4VideoAudioCodecs . push ( 'eac3' ) ;
2020-03-30 17:02:16 +08:00
}
}
2023-04-19 01:56:05 -04:00
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
if ( supportsMp2VideoAudio ) {
videoAudioCodecs . push ( 'mp2' ) ;
hlsInTsVideoAudioCodecs . push ( 'mp2' ) ;
hlsInFmp4VideoAudioCodecs . push ( 'mp2' ) ;
}
2020-03-30 17:02:16 +08:00
2024-04-15 20:26:12 +03:00
let supportsDts = appSettings . enableDts ( ) || options . supportsDts ;
2023-04-19 01:56:05 -04:00
if ( supportsDts == null ) {
2024-04-15 21:01:44 +03:00
supportsDts = canPlayDts ( videoTestElement ) ;
2023-04-19 01:56:05 -04:00
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
if ( supportsDts ) {
videoAudioCodecs . push ( 'dca' ) ;
videoAudioCodecs . push ( 'dts' ) ;
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
if ( browser . tizen || browser . web0s ) {
videoAudioCodecs . push ( 'pcm_s16le' ) ;
videoAudioCodecs . push ( 'pcm_s24le' ) ;
}
2020-03-30 17:02:16 +08:00
2024-04-15 22:20:47 +03:00
if ( appSettings . enableTrueHd ( ) || options . supportsTrueHd ) {
2023-04-19 01:56:05 -04:00
videoAudioCodecs . push ( 'truehd' ) ;
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
if ( browser . tizen ) {
videoAudioCodecs . push ( 'aac_latm' ) ;
}
if ( canPlayAudioFormat ( 'opus' ) ) {
videoAudioCodecs . push ( 'opus' ) ;
webmAudioCodecs . push ( 'opus' ) ;
2020-06-06 19:16:48 +02:00
if ( browser . tizen ) {
2023-04-19 01:56:05 -04:00
hlsInTsVideoAudioCodecs . push ( 'opus' ) ;
2020-03-30 17:02:16 +08:00
}
2023-09-01 22:42:29 +08:00
if ( ! browser . safari ) {
hlsInFmp4VideoAudioCodecs . push ( 'opus' ) ;
}
2023-04-19 01:56:05 -04:00
}
2020-03-30 17:02:16 +08:00
2023-11-05 22:41:36 -03:00
// FLAC audio in video plays with a delay on Tizen
2023-11-03 21:07:37 -03:00
if ( canPlayAudioFormat ( 'flac' ) && ! browser . tizen ) {
2023-04-19 01:56:05 -04:00
videoAudioCodecs . push ( 'flac' ) ;
hlsInFmp4VideoAudioCodecs . push ( 'flac' ) ;
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
if ( canPlayAudioFormat ( 'alac' ) ) {
videoAudioCodecs . push ( 'alac' ) ;
hlsInFmp4VideoAudioCodecs . push ( 'alac' ) ;
}
2020-11-12 20:03:38 +08:00
2023-04-19 01:56:05 -04:00
videoAudioCodecs = videoAudioCodecs . filter ( function ( c ) {
return ( options . disableVideoAudioCodecs || [ ] ) . indexOf ( c ) === - 1 ;
} ) ;
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
hlsInTsVideoAudioCodecs = hlsInTsVideoAudioCodecs . filter ( function ( c ) {
return ( options . disableHlsVideoAudioCodecs || [ ] ) . indexOf ( c ) === - 1 ;
} ) ;
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
hlsInFmp4VideoAudioCodecs = hlsInFmp4VideoAudioCodecs . filter ( function ( c ) {
return ( options . disableHlsVideoAudioCodecs || [ ] ) . indexOf ( c ) === - 1 ;
} ) ;
2020-11-12 20:03:38 +08:00
2023-04-19 01:56:05 -04:00
const mp4VideoCodecs = [ ] ;
const webmVideoCodecs = [ ] ;
const hlsInTsVideoCodecs = [ ] ;
const hlsInFmp4VideoCodecs = [ ] ;
2020-03-30 17:02:16 +08:00
2023-09-01 22:42:29 +08:00
if ( canPlayAv1 ( videoTestElement )
2024-06-05 17:03:54 -04:00
&& ( browser . safari || ( ! browser . mobile && ( browser . edgeChromium || browser . firefox || browser . chrome || browser . opera ) ) ) ) {
2024-02-29 01:05:54 +08:00
// disable av1 on non-safari mobile browsers since it can be very slow software decoding
2023-09-01 22:42:29 +08:00
hlsInFmp4VideoCodecs . push ( 'av1' ) ;
}
if ( canPlayHevc ( videoTestElement , options )
2024-06-05 17:03:54 -04:00
&& ( browser . edgeChromium || browser . safari || browser . tizen || browser . web0s || ( browser . chrome && ( ! browser . android || browser . versionMajor >= 105 ) ) || ( browser . opera && ! browser . mobile ) ) ) {
2023-09-01 22:42:29 +08:00
// Chromium used to support HEVC on Android but not via MSE
2023-04-19 01:56:05 -04:00
hlsInFmp4VideoCodecs . push ( 'hevc' ) ;
}
if ( canPlayH264 ( videoTestElement ) ) {
mp4VideoCodecs . push ( 'h264' ) ;
hlsInTsVideoCodecs . push ( 'h264' ) ;
2023-09-01 22:42:29 +08:00
hlsInFmp4VideoCodecs . push ( 'h264' ) ;
2023-04-19 01:56:05 -04:00
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
if ( canPlayHevc ( videoTestElement , options ) ) {
2024-02-06 01:06:34 +08:00
mp4VideoCodecs . push ( 'hevc' ) ;
2023-04-19 01:56:05 -04:00
if ( browser . tizen || browser . web0s ) {
hlsInTsVideoCodecs . push ( 'hevc' ) ;
2020-11-12 20:03:38 +08:00
}
2023-04-19 01:56:05 -04:00
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
if ( supportsMpeg2Video ( ) ) {
mp4VideoCodecs . push ( 'mpeg2video' ) ;
}
2021-03-20 11:56:51 +03:00
2023-04-19 01:56:05 -04:00
if ( supportsVc1 ( videoTestElement ) ) {
mp4VideoCodecs . push ( 'vc1' ) ;
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
if ( browser . tizen ) {
mp4VideoCodecs . push ( 'msmpeg4v2' ) ;
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
if ( canPlayVp8 ) {
webmVideoCodecs . push ( 'vp8' ) ;
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
if ( canPlayVp9 ) {
mp4VideoCodecs . push ( 'vp9' ) ;
2024-02-29 01:27:22 +08:00
// webm support is unreliable on safari 17
if ( ! browser . safari
|| ( browser . safari && browser . versionMajor >= 15 && browser . versionMajor < 17 ) ) {
webmVideoCodecs . push ( 'vp9' ) ;
}
2023-04-19 01:56:05 -04:00
}
2020-03-30 17:02:16 +08:00
2023-09-01 22:42:29 +08:00
if ( canPlayAv1 ( videoTestElement ) ) {
2023-04-19 01:56:05 -04:00
mp4VideoCodecs . push ( 'av1' ) ;
2024-02-29 01:27:22 +08:00
// webm support is unreliable on safari 17
if ( ! browser . safari
|| ( browser . safari && browser . versionMajor >= 15 && browser . versionMajor < 17 ) ) {
webmVideoCodecs . push ( 'av1' ) ;
}
2023-04-19 01:56:05 -04:00
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
if ( canPlayVp8 || browser . tizen ) {
videoAudioCodecs . push ( 'vorbis' ) ;
}
2020-06-03 12:26:25 -04:00
2023-04-19 01:56:05 -04:00
if ( webmVideoCodecs . length ) {
profile . DirectPlayProfiles . push ( {
Container : 'webm' ,
Type : 'Video' ,
VideoCodec : webmVideoCodecs . join ( ',' ) ,
AudioCodec : webmAudioCodecs . join ( ',' )
} ) ;
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
if ( mp4VideoCodecs . length ) {
profile . DirectPlayProfiles . push ( {
Container : 'mp4,m4v' ,
Type : 'Video' ,
VideoCodec : mp4VideoCodecs . join ( ',' ) ,
AudioCodec : videoAudioCodecs . join ( ',' )
} ) ;
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
if ( canPlayMkv && mp4VideoCodecs . length ) {
profile . DirectPlayProfiles . push ( {
Container : 'mkv' ,
Type : 'Video' ,
VideoCodec : mp4VideoCodecs . join ( ',' ) ,
AudioCodec : videoAudioCodecs . join ( ',' )
} ) ;
}
// These are formats we can't test for but some devices will support
[ 'm2ts' , 'wmv' , 'ts' , 'asf' , 'avi' , 'mpg' , 'mpeg' , 'flv' , '3gp' , 'mts' , 'trp' , 'vob' , 'vro' , 'mov' ] . map ( function ( container ) {
return getDirectPlayProfileForVideoContainer ( container , videoAudioCodecs , videoTestElement , options ) ;
} ) . filter ( function ( i ) {
return i != null ;
} ) . forEach ( function ( i ) {
profile . DirectPlayProfiles . push ( i ) ;
} ) ;
[ 'opus' , 'mp3' , 'mp2' , 'aac' , 'flac' , 'alac' , 'webma' , 'wma' , 'wav' , 'ogg' , 'oga' ] . filter ( canPlayAudioFormat ) . forEach ( function ( audioFormat ) {
profile . DirectPlayProfiles . push ( {
Container : audioFormat ,
Type : 'Audio'
} ) ;
// https://www.webmproject.org/about/faq/
if ( audioFormat === 'opus' || audioFormat === 'webma' ) {
2020-06-03 12:26:25 -04:00
profile . DirectPlayProfiles . push ( {
Container : 'webm' ,
2023-04-19 01:56:05 -04:00
AudioCodec : audioFormat ,
Type : 'Audio'
2020-06-03 12:26:25 -04:00
} ) ;
}
2023-04-19 01:56:05 -04:00
// aac also appears in the m4a and m4b container
// m4a/alac only works when using safari
if ( audioFormat === 'aac' || audioFormat === 'alac' ) {
2020-03-30 17:02:16 +08:00
profile . DirectPlayProfiles . push ( {
2023-04-19 01:56:05 -04:00
Container : 'm4a' ,
AudioCodec : audioFormat ,
Type : 'Audio'
2020-03-30 17:02:16 +08:00
} ) ;
profile . DirectPlayProfiles . push ( {
2023-04-19 01:56:05 -04:00
Container : 'm4b' ,
AudioCodec : audioFormat ,
Type : 'Audio'
2020-03-30 17:02:16 +08:00
} ) ;
}
2023-04-19 01:56:05 -04:00
} ) ;
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
profile . TranscodingProfiles = [ ] ;
2020-11-01 04:15:46 +08:00
2023-10-14 20:35:55 +05:30
const hlsBreakOnNonKeyFrames = browser . iOS || browser . osx || browser . edge || ! canPlayNativeHls ( ) ;
2024-04-09 10:40:17 +08:00
let enableFmp4Hls = userSettings . preferFmp4HlsContainer ( ) ;
if ( ( browser . safari || browser . tizen || browser . web0s ) && ! canPlayNativeHlsInFmp4 ( ) ) {
enableFmp4Hls = false ;
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
if ( canPlayHls ( ) && browser . enableHlsAudio !== false ) {
profile . TranscodingProfiles . push ( {
2024-04-08 13:49:45 +08:00
Container : enableFmp4Hls ? 'mp4' : 'ts' ,
2023-04-19 01:56:05 -04:00
Type : 'Audio' ,
AudioCodec : 'aac' ,
Context : 'Streaming' ,
Protocol : 'hls' ,
MaxAudioChannels : physicalAudioChannels . toString ( ) ,
MinSegments : browser . iOS || browser . osx ? '2' : '1' ,
BreakOnNonKeyFrames : hlsBreakOnNonKeyFrames
2020-03-30 17:02:16 +08:00
} ) ;
2023-04-19 01:56:05 -04:00
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
// For streaming, prioritize opus transcoding after mp3/aac. It is too problematic with random failures
// But for static (offline sync), it will be just fine.
// Prioritize aac higher because the encoder can accept more channels than mp3
[ 'aac' , 'mp3' , 'opus' , 'wav' ] . filter ( canPlayAudioFormat ) . forEach ( function ( audioFormat ) {
profile . TranscodingProfiles . push ( {
Container : audioFormat ,
Type : 'Audio' ,
AudioCodec : audioFormat ,
Context : 'Streaming' ,
Protocol : 'http' ,
MaxAudioChannels : physicalAudioChannels . toString ( )
} ) ;
} ) ;
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
[ 'opus' , 'mp3' , 'aac' , 'wav' ] . filter ( canPlayAudioFormat ) . forEach ( function ( audioFormat ) {
profile . TranscodingProfiles . push ( {
Container : audioFormat ,
Type : 'Audio' ,
AudioCodec : audioFormat ,
Context : 'Static' ,
Protocol : 'http' ,
MaxAudioChannels : physicalAudioChannels . toString ( )
} ) ;
} ) ;
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
if ( canPlayHls ( ) && options . enableHls !== false ) {
2023-09-01 22:42:29 +08:00
if ( hlsInFmp4VideoCodecs . length && hlsInFmp4VideoAudioCodecs . length && enableFmp4Hls ) {
2023-08-27 01:02:21 +03:00
// HACK: Since there is no filter for TS/MP4 in the API, specify HLS support in general and rely on retry after DirectPlay error
// FIXME: Need support for {Container: 'mp4', Protocol: 'hls'} or {Container: 'hls', SubContainer: 'mp4'}
profile . DirectPlayProfiles . push ( {
Container : 'hls' ,
Type : 'Video' ,
VideoCodec : hlsInFmp4VideoCodecs . join ( ',' ) ,
AudioCodec : hlsInFmp4VideoAudioCodecs . join ( ',' )
} ) ;
2020-03-30 17:02:16 +08:00
profile . TranscodingProfiles . push ( {
2023-04-19 01:56:05 -04:00
Container : 'mp4' ,
2020-03-30 17:02:16 +08:00
Type : 'Video' ,
2023-04-19 01:56:05 -04:00
AudioCodec : hlsInFmp4VideoAudioCodecs . join ( ',' ) ,
VideoCodec : hlsInFmp4VideoCodecs . join ( ',' ) ,
2020-03-30 17:02:16 +08:00
Context : 'Streaming' ,
2023-04-19 01:56:05 -04:00
Protocol : 'hls' ,
2020-03-30 17:02:16 +08:00
MaxAudioChannels : physicalAudioChannels . toString ( ) ,
2023-04-19 01:56:05 -04:00
MinSegments : browser . iOS || browser . osx ? '2' : '1' ,
BreakOnNonKeyFrames : hlsBreakOnNonKeyFrames
2020-03-30 17:02:16 +08:00
} ) ;
}
2023-04-19 01:56:05 -04:00
if ( hlsInTsVideoCodecs . length && hlsInTsVideoAudioCodecs . length ) {
2023-08-27 01:02:21 +03:00
// HACK: Since there is no filter for TS/MP4 in the API, specify HLS support in general and rely on retry after DirectPlay error
// FIXME: Need support for {Container: 'ts', Protocol: 'hls'} or {Container: 'hls', SubContainer: 'ts'}
profile . DirectPlayProfiles . push ( {
Container : 'hls' ,
Type : 'Video' ,
VideoCodec : hlsInTsVideoCodecs . join ( ',' ) ,
AudioCodec : hlsInTsVideoAudioCodecs . join ( ',' )
} ) ;
2021-08-20 17:48:14 -04:00
profile . TranscodingProfiles . push ( {
2023-04-19 01:56:05 -04:00
Container : 'ts' ,
2021-08-20 17:48:14 -04:00
Type : 'Video' ,
2023-04-19 01:56:05 -04:00
AudioCodec : hlsInTsVideoAudioCodecs . join ( ',' ) ,
VideoCodec : hlsInTsVideoCodecs . join ( ',' ) ,
2021-08-20 17:48:14 -04:00
Context : 'Streaming' ,
2023-04-19 01:56:05 -04:00
Protocol : 'hls' ,
MaxAudioChannels : physicalAudioChannels . toString ( ) ,
MinSegments : browser . iOS || browser . osx ? '2' : '1' ,
BreakOnNonKeyFrames : hlsBreakOnNonKeyFrames
2021-08-20 17:48:14 -04:00
} ) ;
2020-03-30 17:02:16 +08:00
}
2023-04-19 01:56:05 -04:00
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
profile . ContainerProfiles = [ ] ;
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
profile . CodecProfiles = [ ] ;
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
const supportsSecondaryAudio = canPlaySecondaryAudio ( videoTestElement ) ;
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
const aacCodecProfileConditions = [ ] ;
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
// Handle he-aac not supported
if ( ! videoTestElement . canPlayType ( 'video/mp4; codecs="avc1.640029, mp4a.40.5"' ) . replace ( /no/ , '' ) ) {
// TODO: This needs to become part of the stream url in order to prevent stream copy
aacCodecProfileConditions . push ( {
Condition : 'NotEquals' ,
Property : 'AudioProfile' ,
Value : 'HE-AAC'
} ) ;
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
if ( ! supportsSecondaryAudio ) {
aacCodecProfileConditions . push ( {
Condition : 'Equals' ,
Property : 'IsSecondaryAudio' ,
Value : 'false' ,
IsRequired : false
} ) ;
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
if ( aacCodecProfileConditions . length ) {
profile . CodecProfiles . push ( {
Type : 'VideoAudio' ,
Codec : 'aac' ,
Conditions : aacCodecProfileConditions
} ) ;
}
if ( ! supportsSecondaryAudio ) {
profile . CodecProfiles . push ( {
Type : 'VideoAudio' ,
Conditions : [
{
Condition : 'Equals' ,
Property : 'IsSecondaryAudio' ,
Value : 'false' ,
IsRequired : false
}
]
} ) ;
}
2020-03-30 17:02:16 +08:00
2024-05-25 11:50:34 -04:00
if ( browser . web0s ) {
const flacConditions = [
// webOS doesn't seem to support FLAC with more than 2 channels
{
Condition : 'LessThanEqual' ,
Property : 'AudioChannels' ,
Value : '2' ,
IsRequired : false
}
] ;
profile . CodecProfiles . push ( {
Type : 'VideoAudio' ,
Codec : 'flac' ,
Conditions : flacConditions
} ) ;
const flacTranscodingProfiles = [ ] ;
// Split each video transcoding profile with FLAC so that the containing FLAC is only applied to 2 channels audio
profile . TranscodingProfiles . forEach ( transcodingProfile => {
if ( transcodingProfile . Type !== 'Video' ) return ;
const audioCodecs = transcodingProfile . AudioCodec . split ( ',' ) ;
if ( ! audioCodecs . includes ( 'flac' ) ) return ;
const flacTranscodingProfile = { ... transcodingProfile } ;
flacTranscodingProfile . AudioCodec = 'flac' ;
flacTranscodingProfile . ApplyConditions = [
... flacTranscodingProfile . ApplyConditions || [ ] ,
... flacConditions
] ;
flacTranscodingProfiles . push ( flacTranscodingProfile ) ;
transcodingProfile . AudioCodec = audioCodecs . filter ( codec => codec != 'flac' ) . join ( ',' ) ;
} ) ;
profile . TranscodingProfiles . push ( ... flacTranscodingProfiles ) ;
}
2023-04-19 01:56:05 -04:00
let maxH264Level = 42 ;
let h264Profiles = 'high|main|baseline|constrained baseline' ;
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
if ( browser . tizen || browser . web0s
2023-03-29 00:38:22 -04:00
|| videoTestElement . canPlayType ( 'video/mp4; codecs="avc1.640833"' ) . replace ( /no/ , '' ) ) {
2023-04-19 01:56:05 -04:00
maxH264Level = 51 ;
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
// Support H264 Level 52 (Tizen 5.0) - app only
if ( ( browser . tizenVersion >= 5 && window . NativeShell )
2023-03-29 00:38:22 -04:00
|| videoTestElement . canPlayType ( 'video/mp4; codecs="avc1.640834"' ) . replace ( /no/ , '' ) ) {
2023-04-19 01:56:05 -04:00
maxH264Level = 52 ;
}
2020-03-30 17:02:16 +08:00
2023-09-22 17:41:26 +02:00
if ( videoTestElement . canPlayType ( 'video/mp4; codecs="avc1.6e0033"' ) . replace ( /no/ , '' )
2020-03-30 17:02:16 +08:00
// These tests are passing in safari, but playback is failing
2023-09-22 17:41:26 +02:00
&& ! browser . safari && ! browser . iOS && ! browser . web0s && ! browser . edge && ! browser . mobile && ! browser . tizen
2023-04-19 01:56:05 -04:00
) {
h264Profiles += '|high 10' ;
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
let maxHevcLevel = 120 ;
let hevcProfiles = 'main' ;
2020-11-12 20:03:38 +08:00
2023-04-19 01:56:05 -04:00
// hevc main level 4.1
if ( videoTestElement . canPlayType ( 'video/mp4; codecs="hvc1.1.4.L123"' ) . replace ( /no/ , '' )
2023-03-29 00:38:22 -04:00
|| videoTestElement . canPlayType ( 'video/mp4; codecs="hev1.1.4.L123"' ) . replace ( /no/ , '' ) ) {
2023-04-19 01:56:05 -04:00
maxHevcLevel = 123 ;
}
2020-11-12 20:03:38 +08:00
2023-04-19 01:56:05 -04:00
// hevc main10 level 4.1
if ( videoTestElement . canPlayType ( 'video/mp4; codecs="hvc1.2.4.L123"' ) . replace ( /no/ , '' )
2023-03-29 00:38:22 -04:00
|| videoTestElement . canPlayType ( 'video/mp4; codecs="hev1.2.4.L123"' ) . replace ( /no/ , '' ) ) {
2023-04-19 01:56:05 -04:00
maxHevcLevel = 123 ;
hevcProfiles = 'main|main 10' ;
}
2020-11-12 20:03:38 +08:00
2023-04-19 01:56:05 -04:00
// hevc main10 level 5.1
if ( videoTestElement . canPlayType ( 'video/mp4; codecs="hvc1.2.4.L153"' ) . replace ( /no/ , '' )
2023-03-29 00:38:22 -04:00
|| videoTestElement . canPlayType ( 'video/mp4; codecs="hev1.2.4.L153"' ) . replace ( /no/ , '' ) ) {
2023-04-19 01:56:05 -04:00
maxHevcLevel = 153 ;
hevcProfiles = 'main|main 10' ;
}
2020-11-12 20:03:38 +08:00
2023-04-19 01:56:05 -04:00
// hevc main10 level 6.1
if ( videoTestElement . canPlayType ( 'video/mp4; codecs="hvc1.2.4.L183"' ) . replace ( /no/ , '' )
2023-03-29 00:38:22 -04:00
|| videoTestElement . canPlayType ( 'video/mp4; codecs="hev1.2.4.L183"' ) . replace ( /no/ , '' ) ) {
2023-04-19 01:56:05 -04:00
maxHevcLevel = 183 ;
hevcProfiles = 'main|main 10' ;
}
2022-06-17 13:10:36 -04:00
2023-09-01 22:42:29 +08:00
let maxAv1Level = 15 ; // level 5.3
const av1Profiles = 'main' ; // av1 main covers 4:2:0 8 & 10 bits
// av1 main level 6.0
if ( videoTestElement . canPlayType ( 'video/mp4; codecs="av01.0.16M.08"' ) . replace ( /no/ , '' )
&& videoTestElement . canPlayType ( 'video/mp4; codecs="av01.0.16M.10"' ) . replace ( /no/ , '' ) ) {
maxAv1Level = 16 ;
}
// av1 main level 6.1
if ( videoTestElement . canPlayType ( 'video/mp4; codecs="av01.0.17M.08"' ) . replace ( /no/ , '' )
&& videoTestElement . canPlayType ( 'video/mp4; codecs="av01.0.17M.10"' ) . replace ( /no/ , '' ) ) {
maxAv1Level = 17 ;
}
// av1 main level 6.2
if ( videoTestElement . canPlayType ( 'video/mp4; codecs="av01.0.18M.08"' ) . replace ( /no/ , '' )
&& videoTestElement . canPlayType ( 'video/mp4; codecs="av01.0.18M.10"' ) . replace ( /no/ , '' ) ) {
maxAv1Level = 18 ;
}
// av1 main level 6.3
if ( videoTestElement . canPlayType ( 'video/mp4; codecs="av01.0.19M.08"' ) . replace ( /no/ , '' )
&& videoTestElement . canPlayType ( 'video/mp4; codecs="av01.0.19M.10"' ) . replace ( /no/ , '' ) ) {
maxAv1Level = 19 ;
}
2023-04-19 01:56:05 -04:00
const h264VideoRangeTypes = 'SDR' ;
let hevcVideoRangeTypes = 'SDR' ;
let vp9VideoRangeTypes = 'SDR' ;
let av1VideoRangeTypes = 'SDR' ;
2022-06-17 13:10:36 -04:00
2023-08-23 00:15:39 +03:00
if ( supportsHdr10 ( options ) ) {
hevcVideoRangeTypes += '|HDR10' ;
vp9VideoRangeTypes += '|HDR10' ;
av1VideoRangeTypes += '|HDR10' ;
2023-04-19 01:56:05 -04:00
}
2022-06-17 13:10:36 -04:00
2023-08-23 00:15:39 +03:00
if ( supportsHlg ( options ) ) {
2024-03-16 02:11:26 +02:00
hevcVideoRangeTypes += '|HLG' ;
vp9VideoRangeTypes += '|HLG' ;
av1VideoRangeTypes += '|HLG' ;
2023-04-19 01:56:05 -04:00
}
2020-11-12 20:03:38 +08:00
2024-03-16 21:10:14 +02:00
if ( supportsDolbyVision ( options ) ) {
const profiles = supportedDolbyVisionProfilesHevc ( videoTestElement ) ;
2024-03-17 21:47:29 +02:00
if ( profiles . includes ( 5 ) ) {
2024-03-16 21:10:14 +02:00
hevcVideoRangeTypes += '|DOVI' ;
}
if ( profiles . includes ( 8 ) ) {
2024-03-17 21:47:29 +02:00
hevcVideoRangeTypes += '|DOVIWithHDR10|DOVIWithHLG|DOVIWithSDR' ;
2024-03-16 21:10:14 +02:00
}
2023-04-19 01:56:05 -04:00
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
const h264CodecProfileConditions = [
{
Condition : 'NotEquals' ,
Property : 'IsAnamorphic' ,
Value : 'true' ,
IsRequired : false
} ,
{
Condition : 'EqualsAny' ,
Property : 'VideoProfile' ,
Value : h264Profiles ,
IsRequired : false
} ,
{
Condition : 'EqualsAny' ,
Property : 'VideoRangeType' ,
Value : h264VideoRangeTypes ,
IsRequired : false
} ,
{
Condition : 'LessThanEqual' ,
Property : 'VideoLevel' ,
Value : maxH264Level . toString ( ) ,
IsRequired : false
}
] ;
const hevcCodecProfileConditions = [
{
Condition : 'NotEquals' ,
Property : 'IsAnamorphic' ,
Value : 'true' ,
IsRequired : false
} ,
{
Condition : 'EqualsAny' ,
Property : 'VideoProfile' ,
Value : hevcProfiles ,
IsRequired : false
} ,
{
Condition : 'EqualsAny' ,
Property : 'VideoRangeType' ,
Value : hevcVideoRangeTypes ,
IsRequired : false
} ,
{
Condition : 'LessThanEqual' ,
Property : 'VideoLevel' ,
Value : maxHevcLevel . toString ( ) ,
IsRequired : false
}
] ;
const vp9CodecProfileConditions = [
{
Condition : 'EqualsAny' ,
Property : 'VideoRangeType' ,
Value : vp9VideoRangeTypes ,
IsRequired : false
}
] ;
const av1CodecProfileConditions = [
2023-09-01 22:42:29 +08:00
{
Condition : 'NotEquals' ,
Property : 'IsAnamorphic' ,
Value : 'true' ,
IsRequired : false
} ,
{
Condition : 'EqualsAny' ,
Property : 'VideoProfile' ,
Value : av1Profiles ,
IsRequired : false
} ,
2023-04-19 01:56:05 -04:00
{
Condition : 'EqualsAny' ,
Property : 'VideoRangeType' ,
Value : av1VideoRangeTypes ,
IsRequired : false
2023-09-01 22:42:29 +08:00
} ,
{
Condition : 'LessThanEqual' ,
Property : 'VideoLevel' ,
Value : maxAv1Level . toString ( ) ,
IsRequired : false
2023-04-19 01:56:05 -04:00
}
] ;
if ( ! browser . edgeUwp && ! browser . tizen && ! browser . web0s ) {
h264CodecProfileConditions . push ( {
Condition : 'NotEquals' ,
Property : 'IsInterlaced' ,
Value : 'true' ,
IsRequired : false
} ) ;
2020-11-12 20:03:38 +08:00
2023-04-19 01:56:05 -04:00
hevcCodecProfileConditions . push ( {
Condition : 'NotEquals' ,
Property : 'IsInterlaced' ,
Value : 'true' ,
IsRequired : false
} ) ;
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
if ( maxVideoWidth ) {
h264CodecProfileConditions . push ( {
Condition : 'LessThanEqual' ,
Property : 'Width' ,
Value : maxVideoWidth . toString ( ) ,
IsRequired : false
} ) ;
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
hevcCodecProfileConditions . push ( {
Condition : 'LessThanEqual' ,
Property : 'Width' ,
Value : maxVideoWidth . toString ( ) ,
IsRequired : false
} ) ;
2023-09-01 22:42:29 +08:00
av1CodecProfileConditions . push ( {
Condition : 'LessThanEqual' ,
Property : 'Width' ,
Value : maxVideoWidth . toString ( ) ,
IsRequired : false
} ) ;
2023-04-19 01:56:05 -04:00
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
const globalMaxVideoBitrate = ( getGlobalMaxVideoBitrate ( ) || '' ) . toString ( ) ;
2020-11-12 20:03:38 +08:00
2023-04-19 01:56:05 -04:00
const h264MaxVideoBitrate = globalMaxVideoBitrate ;
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
const hevcMaxVideoBitrate = globalMaxVideoBitrate ;
2020-11-12 20:03:38 +08:00
2023-09-01 22:42:29 +08:00
const av1MaxVideoBitrate = globalMaxVideoBitrate ;
2023-04-19 01:56:05 -04:00
if ( h264MaxVideoBitrate ) {
h264CodecProfileConditions . push ( {
Condition : 'LessThanEqual' ,
Property : 'VideoBitrate' ,
Value : h264MaxVideoBitrate ,
IsRequired : true
} ) ;
}
2020-07-27 20:06:08 +03:00
2023-04-19 01:56:05 -04:00
if ( hevcMaxVideoBitrate ) {
hevcCodecProfileConditions . push ( {
Condition : 'LessThanEqual' ,
Property : 'VideoBitrate' ,
Value : hevcMaxVideoBitrate ,
IsRequired : true
} ) ;
}
2020-07-27 20:06:08 +03:00
2023-09-01 22:42:29 +08:00
if ( av1MaxVideoBitrate ) {
av1CodecProfileConditions . push ( {
Condition : 'LessThanEqual' ,
Property : 'VideoBitrate' ,
Value : av1MaxVideoBitrate ,
IsRequired : true
} ) ;
}
2024-02-06 01:06:34 +08:00
// Safari quirks for HEVC direct-play
if ( browser . safari ) {
// Only hvc1 & dvh1 tags are supported
hevcCodecProfileConditions . push ( {
Condition : 'EqualsAny' ,
Property : 'VideoCodecTag' ,
Value : 'hvc1|dvh1' ,
IsRequired : true
} ) ;
// Framerate above 60fps is not supported
hevcCodecProfileConditions . push ( {
Condition : 'LessThanEqual' ,
Property : 'VideoFramerate' ,
Value : '60' ,
IsRequired : true
} ) ;
}
2023-04-19 01:56:05 -04:00
// On iOS 12.x, for TS container max h264 level is 4.2
if ( browser . iOS && browser . iOSVersion < 13 ) {
2024-03-25 13:54:49 +01:00
const codecProfileTS = {
2020-07-27 20:06:08 +03:00
Type : 'Video' ,
Codec : 'h264' ,
2023-04-19 01:56:05 -04:00
Container : 'ts' ,
Conditions : h264CodecProfileConditions . filter ( ( condition ) => {
return condition . Property !== 'VideoLevel' ;
} )
} ;
2024-03-25 13:54:49 +01:00
codecProfileTS . Conditions . push ( {
2023-04-19 01:56:05 -04:00
Condition : 'LessThanEqual' ,
Property : 'VideoLevel' ,
Value : '42' ,
IsRequired : false
2020-07-27 20:06:08 +03:00
} ) ;
2024-03-25 13:54:49 +01:00
profile . CodecProfiles . push ( codecProfileTS ) ;
const codecProfileMp4 = {
Type : 'Video' ,
Codec : 'h264' ,
Container : 'mp4' ,
Conditions : h264CodecProfileConditions . filter ( ( condition ) => {
return condition . Property !== 'VideoLevel' ;
} )
} ;
codecProfileMp4 . Conditions . push ( {
Condition : 'LessThanEqual' ,
Property : 'VideoLevel' ,
Value : '42' ,
IsRequired : false
} ) ;
profile . CodecProfiles . push ( codecProfileMp4 ) ;
2023-04-19 01:56:05 -04:00
}
profile . CodecProfiles . push ( {
Type : 'Video' ,
Codec : 'h264' ,
Conditions : h264CodecProfileConditions
} ) ;
2023-10-26 01:16:00 +03:00
if ( browser . web0s && supportsDolbyVision ( options ) ) {
// Disallow direct playing of DOVI media in containers not mp4.
// This paired with the "Prefer fMP4-HLS Container" client playback setting enables DOVI playback on webOS.
profile . CodecProfiles . push ( {
Type : 'Video' ,
Container : '-mp4' ,
Codec : 'hevc' ,
Conditions : [
{
Condition : 'EqualsAny' ,
Property : 'VideoRangeType' ,
Value : 'SDR|HDR10|HLG' ,
IsRequired : false
}
]
} ) ;
}
2023-04-19 01:56:05 -04:00
profile . CodecProfiles . push ( {
Type : 'Video' ,
Codec : 'hevc' ,
Conditions : hevcCodecProfileConditions
} ) ;
profile . CodecProfiles . push ( {
Type : 'Video' ,
Codec : 'vp9' ,
Conditions : vp9CodecProfileConditions
} ) ;
profile . CodecProfiles . push ( {
Type : 'Video' ,
Codec : 'av1' ,
Conditions : av1CodecProfileConditions
} ) ;
const globalVideoConditions = [ ] ;
if ( globalMaxVideoBitrate ) {
globalVideoConditions . push ( {
Condition : 'LessThanEqual' ,
Property : 'VideoBitrate' ,
Value : globalMaxVideoBitrate
2020-11-12 20:03:38 +08:00
} ) ;
2023-04-19 01:56:05 -04:00
}
2020-11-12 20:03:38 +08:00
2023-04-19 01:56:05 -04:00
if ( maxVideoWidth ) {
globalVideoConditions . push ( {
Condition : 'LessThanEqual' ,
Property : 'Width' ,
Value : maxVideoWidth . toString ( ) ,
IsRequired : false
2022-06-17 13:10:36 -04:00
} ) ;
2023-04-19 01:56:05 -04:00
}
2022-06-17 13:10:36 -04:00
2023-04-19 01:56:05 -04:00
if ( globalVideoConditions . length ) {
2022-06-17 13:10:36 -04:00
profile . CodecProfiles . push ( {
Type : 'Video' ,
2023-04-19 01:56:05 -04:00
Conditions : globalVideoConditions
2022-06-17 13:10:36 -04:00
} ) ;
2023-04-19 01:56:05 -04:00
}
2022-06-17 13:10:36 -04:00
2023-04-19 01:56:05 -04:00
// Subtitle profiles
// External vtt or burn in
profile . SubtitleProfiles = [ ] ;
const subtitleBurninSetting = appSettings . get ( 'subtitleburnin' ) ;
if ( subtitleBurninSetting !== 'all' ) {
if ( supportsTextTracks ( ) ) {
profile . SubtitleProfiles . push ( {
Format : 'vtt' ,
Method : 'External'
2020-03-30 17:02:16 +08:00
} ) ;
}
2023-04-19 01:56:05 -04:00
if ( options . enableSsaRender !== false && ! options . isRetry && subtitleBurninSetting !== 'allcomplexformats' ) {
profile . SubtitleProfiles . push ( {
Format : 'ass' ,
Method : 'External'
2020-03-30 17:02:16 +08:00
} ) ;
2023-04-19 01:56:05 -04:00
profile . SubtitleProfiles . push ( {
Format : 'ssa' ,
Method : 'External'
2020-03-30 17:02:16 +08:00
} ) ;
}
2023-04-19 01:56:05 -04:00
}
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
profile . ResponseProfiles = [ ] ;
profile . ResponseProfiles . push ( {
Type : 'Video' ,
Container : 'm4v' ,
MimeType : 'video/mp4'
} ) ;
2020-03-30 17:02:16 +08:00
2023-04-19 01:56:05 -04:00
return profile ;
}