2018-12-31 01:03:07 +01:00
import Debug from '../../conf/Debug' ;
2019-03-10 23:27:50 +01:00
import AspectRatio from '../../../common/enums/aspect-ratio.enum' ;
2018-12-31 01:03:07 +01:00
2018-05-06 21:32:18 +02:00
// računa velikost videa za približevanje/oddaljevanje
// does video size calculations for zooming/cropping
class Scaler {
// internal variables
// functions
2019-09-03 22:55:10 +02:00
constructor ( videoData ) {
2018-05-18 23:26:20 +02:00
this . conf = videoData ;
2019-09-03 22:55:10 +02:00
this . logger = videoData . logger ;
2018-05-06 21:32:18 +02:00
}
2019-03-10 23:27:50 +01:00
2019-05-05 00:09:49 +02:00
// Skrbi za "stare" možnosti, kot na primer "na širino zaslona", "na višino zaslona" in "ponastavi".
// Približevanje opuščeno.
// handles "legacy" options, such as 'fit to widht', 'fit to height' and AspectRatio.Reset. No zoom tho
modeToAr ( ar ) {
2019-05-07 23:40:13 +02:00
if ( ar . type !== AspectRatio . FitWidth && ar . type !== AspectRatio . FitHeight && ar . ratio ) {
2019-03-10 23:27:50 +01:00
return ar . ratio ;
}
2019-05-05 00:09:49 +02:00
2019-03-10 23:27:50 +01:00
var ratioOut ;
2018-05-06 21:32:18 +02:00
2018-05-22 00:19:50 +02:00
if ( ! this . conf . video ) {
2019-07-18 21:25:58 +02:00
this . logger . log ( 'error' , 'debug' , "[Scaler.js::modeToAr] No video??" , this . conf . video , "killing videoData" ) ;
2018-05-18 23:26:20 +02:00
this . conf . destroy ( ) ;
2018-05-06 21:32:18 +02:00
return null ;
}
2020-09-20 12:26:03 +02:00
if ( ! this . conf . player . dimensions ) {
2019-03-10 23:27:50 +01:00
ratioOut = screen . width / screen . height ;
2020-09-20 12:26:03 +02:00
} else {
2019-03-10 23:27:50 +01:00
ratioOut = this . conf . player . dimensions . width / this . conf . player . dimensions . height ;
2018-05-06 21:32:18 +02:00
}
2018-08-20 22:45:43 +02:00
// POMEMBNO: lastAr je potrebno nastaviti šele po tem, ko kličemo _res_setAr(). _res_setAr() predvideva,
2018-05-06 21:32:18 +02:00
// da želimo nastaviti statično (type: 'static') razmerje stranic — tudi, če funkcijo kličemo tu oz. v ArDetect.
//
2018-08-20 22:45:43 +02:00
// IMPORTANT NOTE: lastAr needs to be set after _res_setAr() is called, as _res_setAr() assumes we're
2018-05-06 21:32:18 +02:00
// setting a static aspect ratio (even if the function is called from here or ArDetect).
2018-05-22 00:19:50 +02:00
var fileAr = this . conf . video . videoWidth / this . conf . video . videoHeight ;
2018-05-06 21:32:18 +02:00
2020-09-20 12:26:03 +02:00
if ( ar . type === AspectRatio . FitWidth ) {
2019-03-10 23:27:50 +01:00
ratioOut > fileAr ? ratioOut : fileAr
ar . ratio = ratioOut ;
return ratioOut ;
2018-05-06 21:32:18 +02:00
}
2020-09-20 12:26:03 +02:00
else if ( ar . type === AspectRatio . FitHeight ) {
2019-03-10 23:27:50 +01:00
ratioOut < fileAr ? ratioOut : fileAr
ar . ratio = ratioOut ;
return ratioOut ;
2018-05-06 21:32:18 +02:00
}
2020-09-20 12:26:03 +02:00
else if ( ar . type === AspectRatio . Reset ) {
2019-07-18 21:25:58 +02:00
this . logger . log ( 'info' , 'debug' , "[Scaler.js::modeToAr] Using original aspect ratio -" , fileAr )
2019-03-10 23:27:50 +01:00
ar . ar = fileAr ;
2018-05-06 21:32:18 +02:00
return fileAr ;
}
return null ;
}
2019-03-10 23:27:50 +01:00
calculateCrop ( ar ) {
2020-12-18 01:44:45 +01:00
/ * *
* STEP 1 : NORMALIZE ASPECT RATIO
*
* Video width is normalized based on 100 % of the parent . That means if the player AR
* is narrower than video ar , we need to pre - downscale the video . This scaling already
2020-12-19 03:18:14 +01:00
* undoes any zoom that style = "height:123%" on the video element adds .
*
* There are few exceptions and additional caveatss :
* * AspectRatio . FitHeight : we don ' t want to pre - downscale the video at all , as things
* will be scaled to fit height as - is .
* * When player is wider than stream , we want to undo any height compensations site
* tacks on the video tag .
2020-12-18 01:44:45 +01:00
*
* Quick notes :
* * when I say 'video AR' , I actually mean aspect ratio after we ' ve compensated for
* any possible 'height:' stuffs in the style attribute of the video tag
* * because video width is normalized on 100 % of the parent , we don ' t need to correct
* anything when the player is wider than the video .
* /
const streamAr = this . conf . video . videoWidth / this . conf . video . videoHeight ;
const playerAr = this . conf . player . dimensions . width / this . conf . player . dimensions . height ;
2020-12-19 03:18:14 +01:00
const heightCompensationFactor = this . conf . getHeightCompensationFactor ( ) ;
const compensatedStreamAr = streamAr * heightCompensationFactor ;
2020-12-18 01:44:45 +01:00
let arCorrectionFactor = 1 ;
2020-12-20 01:00:06 +01:00
if ( ar . type !== AspectRatio . FitHeight ) {
if ( playerAr < compensatedStreamAr ) {
arCorrectionFactor = this . conf . player . dimensions . width / this . conf . video . offsetWidth ;
} else if ( ar . type !== AspectRatio . Reset ) {
arCorrectionFactor /= heightCompensationFactor ;
}
2020-12-18 01:44:45 +01:00
}
2019-05-07 23:40:13 +02:00
if ( ! this . conf . video ) {
2019-07-18 21:25:58 +02:00
this . logger . log ( 'info' , 'debug' , "[Scaler::calculateCrop] ERROR — no video detected. Conf:" , this . conf , "video:" , this . conf . video , "video dimensions:" , this . conf . video && this . conf . video . videoWidth , '× ' , this . conf . video && this . conf . video . videoHeight ) ;
2018-05-18 23:26:20 +02:00
this . conf . destroy ( ) ;
2018-05-16 23:26:47 +02:00
return { error : "no_video" } ;
}
2019-05-07 23:40:13 +02:00
if ( this . conf . video . videoWidth == 0 || this . conf . video . videoHeight == 0 ) {
// that's illegal, but not illegal enough to just blast our shit to high hell
// mr officer will let you go with a warning this time around
2019-07-18 21:25:58 +02:00
this . logger . log ( 'error' , 'debug' , "[Scaler::calculateCrop] Video has illegal dimensions. Video dimensions:" , this . conf . video && this . conf . video . videoWidth , '× ' , this . conf . video && this . conf . video . videoHeight ) ;
2019-05-07 23:40:13 +02:00
return { error : "illegal_video_dimensions" } ;
}
2018-05-16 23:26:47 +02:00
2019-03-10 23:27:50 +01:00
if ( ar . type === AspectRatio . Reset ) {
2020-12-18 01:44:45 +01:00
return { xFactor : arCorrectionFactor , yFactor : arCorrectionFactor }
2018-05-27 01:29:02 +02:00
}
2018-05-06 21:32:18 +02:00
// handle fuckie-wuckies
2019-05-05 00:09:49 +02:00
if ( ! ar . ratio ) {
2019-07-18 21:25:58 +02:00
this . logger . log ( 'error' , 'scaler' , "[Scaler::calculateCrop] no ar?" , ar . ratio , " -- we were given this mode:" , ar ) ;
2019-04-25 22:02:10 +02:00
return { error : "no_ar" , ratio : ar . ratio } ;
2018-05-06 21:32:18 +02:00
}
2019-07-18 21:25:58 +02:00
this . logger . log ( 'info' , 'scaler' , "[Scaler::calculateCrop] trying to set ar. args are: ar->" , ar . ratio , "; this.conf.player.dimensions->" , this . conf . player . dimensions . width , "× " , this . conf . player . dimensions . height , "| obj:" , this . conf . player . dimensions ) ;
2018-05-06 21:32:18 +02:00
2018-05-23 00:34:18 +02:00
if ( ( ! this . conf . player . dimensions ) || this . conf . player . dimensions . width === 0 || this . conf . player . dimensions . height === 0 ) {
2019-07-18 21:25:58 +02:00
this . logger . log ( 'error' , 'scaler' , "[Scaler::calculateCrop] ERROR — no (or invalid) this.conf.player.dimensions:" , this . conf . player . dimensions ) ;
2018-05-23 00:34:18 +02:00
return { error : "this.conf.player.dimensions_error" } ;
2018-05-06 21:32:18 +02:00
}
// zdaj lahko končno začnemo računati novo velikost videa
// we can finally start computing required video dimensions now:
// Dejansko razmerje stranic datoteke/<video> značke
// Actual aspect ratio of the file/<video> tag
2019-05-05 00:09:49 +02:00
if ( ar . type === AspectRatio . Initial || ! ar . ratio ) {
2020-12-18 01:44:45 +01:00
ar . ratio = streamAr ;
2019-05-05 00:09:49 +02:00
}
2018-05-06 21:32:18 +02:00
2020-12-18 01:44:45 +01:00
this . logger . log ( 'info' , 'scaler' , "[Scaler::calculateCrop] ar is " , ar . ratio , ", file ar is" , streamAr , ", this.conf.player.dimensions are " , this . conf . player . dimensions . width , "× " , this . conf . player . dimensions . height , "| obj:" , this . conf . player . dimensions ) ;
2018-05-06 21:32:18 +02:00
var videoDimensions = {
2018-05-24 22:49:32 +02:00
xFactor : 1 ,
yFactor : 1 ,
2018-05-06 21:32:18 +02:00
actualWidth : 0 , // width of the video (excluding pillarbox) when <video> tag height is equal to width
actualHeight : 0 , // height of the video (excluding letterbox) when <video> tag height is equal to height
}
2020-12-18 01:44:45 +01:00
if ( streamAr < playerAr ) {
if ( streamAr < ar . ratio ) {
2019-10-27 22:11:07 +01:00
// in this situation we have to crop letterbox on top/bottom of the player
// we cut it, but never more than the player
2020-12-18 01:44:45 +01:00
videoDimensions . xFactor = Math . min ( ar . ratio , playerAr ) / streamAr ;
2018-05-24 22:49:32 +02:00
videoDimensions . yFactor = videoDimensions . xFactor ;
2019-10-27 22:11:07 +01:00
} else {
// in this situation, we would be cutting pillarbox. Inside horizontal player.
// I don't think so. Except exceptions, we'll wait for bug reports.
videoDimensions . xFactor = 1 ;
videoDimensions . yFactor = 1 ;
}
2019-05-05 00:09:49 +02:00
} else {
2020-12-18 01:44:45 +01:00
if ( streamAr < ar . ratio || playerAr < ar . ratio ) {
2019-10-27 22:11:07 +01:00
// in this situation, we need to add extra letterbox on top of our letterbox
// this means we simply don't crop anything _at all_
2019-10-28 20:33:40 +01:00
videoDimensions . xFactor = 1 ;
videoDimensions . yFactor = 1 ;
2019-10-27 22:11:07 +01:00
} else {
2019-10-28 20:33:40 +01:00
// meant for handling pillarbox crop. not quite implemented.
2020-12-18 01:44:45 +01:00
videoDimensions . xFactor = streamAr / Math . min ( ar . ratio , playerAr ) ;
2019-10-28 20:33:40 +01:00
videoDimensions . yFactor = videoDimensions . xFactor ;
// videoDimensions.xFactor = Math.max(ar.ratio, playerAr) * fileAr;
// videoDimensions.yFactor = videoDimensions.xFactor;
2019-10-27 22:11:07 +01:00
}
2018-05-06 21:32:18 +02:00
}
2019-07-18 21:25:58 +02:00
this . logger . log ( 'info' , 'scaler' , "[Scaler::calculateCrop] Crop factor calculated — " , videoDimensions . xFactor ) ;
2018-05-06 21:32:18 +02:00
2021-01-30 12:16:37 +01:00
// Workaround for Chrome/Edge issue where zooming too much results in video being stretched incorrectly
/ * *
* Bug description — if the following are true :
* * user is using Chrome or Edge ( but surprisingly not Opera )
* * user is using hardware acceleration
* * user is using a noVideo card
* * user is in full screen mode
* Then the video will do Stretch . Basic no matter what you put in ` transform: scale(x,y) ` .
*
* Because this issue happens regardless of how you upscale the video ( doesn ' t matter if you use transform : scale
* or width + height or anything else ) , the aspect ratio needs to be limited _before _ applying arCorrectionFactor
* ( note that arCorrectionFactor is usually <= 1 , as it conpensates for zooming that height = [ > 100 % ] on < video >
* style attribute does ) .
*
* This method is also repeated in calculate stretch method .
* /
if ( BrowserDetect . anyChromium && this . conf . player ? . isFullScreen && this . conf . player ? . dimensions ? . fullscreen ) {
const maxSafeAr = ( window . innerWidth * 0.995 ) / window . innerHeight ;
videoDimensions . xFactor = Math . min ( videoDimensions . xFactor , maxSafeAr ) ;
videoDimensions . yFactor = Math . min ( videoDimensions . yFactor , maxSafeAr ) ;
}
2020-12-18 01:44:45 +01:00
// correct the factors
videoDimensions . xFactor *= arCorrectionFactor ;
videoDimensions . yFactor *= arCorrectionFactor ;
2018-05-06 21:32:18 +02:00
return videoDimensions ;
}
2018-12-31 01:03:07 +01:00
}
export default Scaler ;