2017-09-24 01:54:46 +02:00
if ( Debug . debug )
console . log ( "Loading: ArDetect" ) ;
2017-12-29 23:34:40 +01:00
var _ard _console _stop = "background: #000; color: #f41" ;
var _ard _console _start = "background: #000; color: #00c399" ;
2017-10-02 00:27:01 +02:00
2017-09-27 02:26:47 +02:00
var _ard _currentAr ;
2017-12-29 23:34:40 +01:00
var _ard _setup _timer ;
var _ard _timer
2017-09-27 02:26:47 +02:00
// kjer vzemamo vzorce za blackbox/stuff. 9 vzorcev. Če spremenimo velikost vzorca, moramo spremeniti tudi vrednosti v tej tabeli
// vrednosti v tabeli so na osminskih intervalih od [0, <sample height * 4> - 4].
// we sample these lines in blackbox/stuff. 9 samples. If we change the canvas sample size, we have to correct these values as well
// samples are every eighth between [0, <sample height * 4> - 4].
2017-12-31 18:26:59 +01:00
var _ard _sampleLines = [ 0 , 360 , 720 , 1080 , 1440 , 1800 , 2160 , 2520 , 2876 ] ;
var _ard _sampleCols = [ 128 , 256 , 384 , 512 , 640 , 768 , 896 , 1024 , 1125 ] ;
2017-09-27 02:26:47 +02:00
2017-12-31 18:26:59 +01:00
var _ard _canvasWidth ;
var _ard _canvasHeight ;
var _ard _canvasDrawWindowHOffset = 0 ;
2017-09-27 02:26:47 +02:00
// **** FUNCTIONS **** //
2017-09-24 01:54:46 +02:00
2017-12-31 18:26:59 +01:00
var _arSetup = function ( cwidth , cheight ) {
2017-12-29 23:34:40 +01:00
if ( Debug . debug )
console . log ( "%c[ArDetect::_ard_setup] Starting automatic aspect ratio detection" , _ard _console _start ) ;
2017-12-31 18:26:59 +01:00
2017-12-30 18:36:08 +01:00
this . _halted = false ;
2017-12-29 23:34:40 +01:00
2017-12-31 18:26:59 +01:00
var existingCanvas = document . getElementById ( "uw_ArDetect_canvas" ) ;
if ( existingCanvas ) {
if ( Debug . debug )
console . log ( "[ArDetect::_ard_setup] existing canvas found. REMOVING KEBAB removing kebab\n\n\n\n(im hungry and you're not authorized to have it)" ) ;
existingCanvas . remove ( ) ;
if ( Debug . debug )
console . log ( "[ArDetect::_ard_setup] canvas removed" ) ;
}
var vid = document . getElementsByTagName ( "video" ) [ 0 ] ;
2017-09-24 01:54:46 +02:00
if ( vid === undefined ) {
2017-12-29 23:34:40 +01:00
_ard _setup _timer = setTimeout ( _arSetup , 1000 ) ;
2017-09-24 01:54:46 +02:00
return ;
}
2017-10-02 00:27:01 +02:00
// imamo video, pa tudi problem. Ta problem bo verjetno kmalu popravljen, zato setup začnemo hitreje kot prej
// we have a video, but also a problem. This problem will prolly be fixed very soon, so setup is called with
// less delay than before
2017-12-31 18:26:59 +01:00
if ( vid . videoWidth === 0 || vid . videoHeight === 0 ) {
2017-12-29 23:34:40 +01:00
_ard _setup _timer = setTimeout ( _arSetup , 100 ) ;
2017-10-02 00:27:01 +02:00
return ;
}
2017-12-31 18:26:59 +01:00
2017-09-24 01:54:46 +02:00
var canvas = document . createElement ( "canvas" ) ;
canvas . style . position = "absolute" ;
//todo: change those values to push canvas off-screen
2017-12-31 18:26:59 +01:00
_ard _canvasWidth = cwidth ? cwidth : Settings . arDetect . hSamples ;
_ard _canvasHeight = cheight ? cheight : Settings . arDetect . vSamples ;
2017-09-24 01:54:46 +02:00
2017-10-17 22:17:51 +02:00
if ( Debug . showArDetectCanvas ) {
2017-10-02 00:27:01 +02:00
canvas . style . left = "200px" ;
canvas . style . top = "780px" ;
canvas . style . zIndex = 10000 ;
}
else {
canvas . style . left = "-20000px" ;
canvas . style . top = "-1080px" ;
canvas . style . zIndex = - 10000 ;
}
canvas . id = "uw_ArDetect_canvas" ;
2017-09-24 01:54:46 +02:00
var test = document . getElementsByTagName ( "body" ) [ 0 ] ;
test . appendChild ( canvas ) ;
var context = canvas . getContext ( "2d" ) ;
// do setup once
// tho we could do it for every frame
2017-12-31 18:26:59 +01:00
if ( cwidth && cheight ) {
var canvasWidth = cwidth ;
var canvasHeight = cheight ;
var canvasScaleFactor = cheight / vid . videoHeight ;
}
else {
var canvasScaleFactor = _ard _canvasWidth / vid . videoWidth ;
var canvasWidth = vid . videoWidth * canvasScaleFactor ;
var canvasHeight = vid . videoHeight * canvasScaleFactor ;
}
2017-09-24 01:54:46 +02:00
canvas . width = canvasWidth ;
canvas . height = canvasHeight ;
2017-12-31 18:26:59 +01:00
try {
// determine where to sample
var ncol = Settings . arDetect . staticSampleCols ;
var nrow = Settings . arDetect . staticSampleRows ;
var colSpacing = _ard _canvasWidth / ncol ;
var rowSpacing = ( _ard _canvasHeight * 4 ) / nrow ;
_ard _sampleLines = [ ] ;
_ard _sampleCols = [ ] ;
for ( var i = 0 ; i < ncol ; i ++ ) {
if ( i < ncol - 1 )
_ard _sampleCols . push ( Math . round ( colSpacing * i ) ) ;
else {
_ard _sampleCols . push ( Math . round ( colSpacing * i ) - 1 ) ;
}
}
for ( var i = 0 ; i < nrow ; i ++ ) {
if ( i < ncol - 5 )
_ard _sampleLines . push ( Math . round ( rowSpacing * i ) ) ;
else {
_ard _sampleLines . push ( Math . round ( rowSpacing * i ) - 4 ) ;
}
}
}
catch ( ex ) {
console . log ( "%c[ArDetect::_arSetup] something went terribly wrong when calcuating sample colums." , Settings . colors . criticalFail ) ;
console . log ( "settings object:" , Settings ) ;
console . log ( "error:" , ex ) ;
}
2018-02-15 22:59:31 +01:00
_ard _resetBlackLevel ( ) ;
2017-12-29 23:34:40 +01:00
this . _forcehalt = false ;
2018-02-12 23:28:31 +01:00
// if we're restarting ArDetect, we need to do this in order to force-recalculate aspect ratio
GlobalVars . lastAr = { type : "auto" , ar : null } ;
2017-09-24 01:54:46 +02:00
_ard _vdraw ( vid , context , canvasWidth , canvasHeight , false ) ;
} ;
2017-12-31 18:26:59 +01:00
var _ard _canvasReadyForDrawWindow = function ( ) {
if ( Debug . debug )
console . log ( "%c[ArDetect::_ard_canvasReadyForDrawWindow] (?)" , "color: #44f" , _ard _canvasHeight == window . innerHeight , "(ard_height:" , _ard _canvasHeight , "| window height:" , window . innerHeight , ")" ) ;
return _ard _canvasHeight == window . innerHeight
}
2017-09-24 01:54:46 +02:00
2017-12-31 18:26:59 +01:00
var _ard _processAr = function ( video , width , height , edge _h , edge _w , fallbackMode ) {
2017-09-24 01:54:46 +02:00
// width, height —> canvas/sample
2018-02-15 00:17:58 +01:00
// edge_w -—> null/undefined, because we don't autocorrect pillarbox yet
2017-12-31 18:26:59 +01:00
2018-01-18 00:11:03 +01:00
if ( Debug . debug && Debug . debugArDetect ) {
console . log ( "[ArDetect::_ard_processAr] processing ar. sample width:" , width , "; sample height:" , height , "; edge top:" , edge _h ) ;
2017-12-31 18:26:59 +01:00
}
2017-09-27 02:26:47 +02:00
// if we don't specify these things, they'll have some default values.
if ( edge _h === undefined ) {
edge _h = 0 ;
edge _w = 0 ;
}
2017-09-24 01:54:46 +02:00
var letterbox = 2 * edge _h ;
var trueHeight = height - letterbox ;
2017-12-31 18:26:59 +01:00
if ( fallbackMode ) {
if ( edge _h > 1 && edge _h < 20 )
return ;
// let's add some safety border to avoid automatic ar toggling between 21:9 and 16:9
trueHeight += 6 ;
}
2017-09-24 01:54:46 +02:00
var trueAr = width / trueHeight ;
2017-10-02 00:27:01 +02:00
ArDetect . detectedAr = trueAr ;
2017-09-24 01:54:46 +02:00
// poglejmo, če se je razmerje stranic spremenilo
// check if aspect ratio is changed:
2018-02-12 23:28:31 +01:00
if ( GlobalVars . lastAr . type == "auto" && GlobalVars . lastAr . ar != null ) {
// spremembo lahko zavrnemo samo, če uporabljamo avtomatski način delovanja in če smo razmerje stranic
// že nastavili.
//
// we can only deny aspect ratio changes if we use automatic mode and if aspect ratio was set from here.
var arDiff = trueAr - GlobalVars . lastAr . ar ;
if ( arDiff < 0 )
arDiff = - arDiff ;
var arDiff _percent = arDiff / trueAr ;
// ali je sprememba v mejah dovoljenega? Če da -> fertik
// is ar variance within acceptable levels? If yes -> we done
2018-01-02 03:36:29 +01:00
if ( Debug . debug && Debug . debugArDetect )
2018-02-12 23:28:31 +01:00
console . log ( "%c[ArDetect::_ard_processAr] new aspect ratio varies from the old one by this much:\n" , "color: #aaf" , "old Ar" , GlobalVars . lastAr . ar , "current ar" , trueAr , "arDiff (absolute):" , arDiff , "ar diff (relative to new ar)" , arDiff _percent ) ;
if ( arDiff < trueAr * Settings . arDetect . allowedArVariance ) {
if ( Debug . debug && Debug . debugArDetect )
console . log ( "%c[ArDetect::_ard_processAr] aspect ratio change denied — diff %:" , "background: #740; color: #fa2" , arDiff _percent )
return ;
}
else if ( Debug . debug && Debug . debugArDetect ) {
console . log ( "%c[ArDetect::_ard_processAr] aspect ratio change accepted — diff %:" , "background: #153; color: #4f9" , arDiff _percent )
}
2018-01-02 03:36:29 +01:00
}
2017-10-02 00:27:01 +02:00
2018-02-12 23:28:31 +01:00
if ( Debug . debug )
console . log ( "[ArDetect::_ard_processAr] attempting to fix aspect ratio. New aspect ratio: " , trueAr ) ;
2017-09-24 01:54:46 +02:00
2018-02-12 23:28:31 +01:00
// POMEMBNO: GlobalVars.lastAr je potrebno nastaviti šele po tem, ko kličemo _res_setAr(). _res_setAr() predvideva,
// da želimo nastaviti statično (type: 'static') razmerje stranic — tudi, če funkcijo kličemo tu oz. v ArDetect.
//
// IMPORTANT NOTE: GlobalVars.lastAr needs to be set after _res_setAr() is called, as _res_setAr() assumes we're
// setting a static aspect ratio (even if the function is called from here or ArDetect).
Resizer . setAr ( trueAr ) ;
GlobalVars . lastAr = { type : "auto" , ar : trueAr } ;
2017-09-24 01:54:46 +02:00
}
var _ard _vdraw = function ( vid , context , w , h , conf ) {
2018-02-12 23:28:31 +01:00
try {
2017-12-29 23:34:40 +01:00
if ( this . _forcehalt )
return ;
2017-12-31 18:26:59 +01:00
var fallbackMode = false ;
2018-02-15 00:17:58 +01:00
var startTime = performance . now ( ) ;
2018-02-15 22:59:31 +01:00
var baseTimeout = Settings . arDetect . timer _playing ;
var triggerTimeout ;
2018-02-15 00:17:58 +01:00
var guardLineResult = true ; // true if success, false if fail. true by default
2018-02-15 22:59:31 +01:00
var imageDetectResult = false ; // true if we detect image along the way. false by default
2018-02-15 00:17:58 +01:00
2018-02-15 22:59:31 +01:00
var sampleCols = _ard _sampleCols ;
2017-12-29 23:34:40 +01:00
2018-02-15 22:59:31 +01:00
var how _far _treshold = 8 ; // how much can the edge pixel vary (*4)
2017-09-24 01:54:46 +02:00
2018-02-12 23:28:31 +01:00
if ( vid == null || vid . paused || vid . ended || Status . arStrat != "auto" ) {
2017-09-24 01:54:46 +02:00
// we slow down if paused, no detection
2017-12-31 18:26:59 +01:00
_ard _timer = setTimeout ( _ard _vdraw , Settings . arDetect . timer _paused , vid , context , w , h ) ;
2017-09-24 01:54:46 +02:00
return false ;
}
2017-12-31 18:26:59 +01:00
try {
context . drawImage ( vid , 0 , 0 , w , h ) ;
}
catch ( ex ) {
if ( Debug . debug )
console . log ( "%c[ArDetect::_ard_vdraw] can't draw image on canvas. Trying canvas.drawWindow instead" , "color:#000; backgroud:#f51;" , ex ) ;
try {
if ( _ard _canvasReadyForDrawWindow ( ) ) {
context . drawWindow ( window , _ard _canvasDrawWindowHOffset , 0 , w , h , "rgba(0,0,0,1)" ) ;
if ( Debug . debug )
console . log ( "%c[ArDetect::_ard_vdraw] canvas.drawImage seems to have worked" , "color:#000; backgroud:#2f5;" ) ;
fallbackMode = true ;
}
else {
// canvas needs to be resized, so let's change setup
_ard _stop ( ) ;
var newCanvasWidth = window . innerHeight * 1.77 ;
var newCanvasHeight = window . innerHeight ;
if ( Settings . miscFullscreenSettings . videoFloat == "center" )
_ard _canvasDrawWindowHOffset = Math . round ( ( window . innerWidth - newCanvasWidth ) * 0.5 ) ;
else if ( Settings . miscFullscreenSettings . videFloat == "left" )
_ard _canvasDrawWindowHOffset = 0 ;
else
_ard _canvasDrawWindowHOffset = window . innerWidth - newCanvasWidth ;
_arSetup ( newCanvasWidth , newCanvasHeight ) ;
return ;
}
}
catch ( ex ) {
if ( Debug . debug )
console . log ( "%c[ArDetect::_ard_vdraw] okay this didnt work either" , "color:#000; backgroud:#f51;" , ex ) ;
_ard _timer = setTimeout ( _ard _vdraw , Settings . arDetect . timer _error , vid , context , w , h ) ;
return ;
}
// _ard_timer = setTimeout(_ard_vdraw, Settings.arDetect.timer_error, vid, context, w, h);
// return;
}
2018-02-15 00:17:58 +01:00
2017-09-24 01:54:46 +02:00
var cimg = [ ] ;
var cols = [ ] ;
2018-02-16 00:19:08 +01:00
for ( var i = 0 ; i < sampleCols . length ; i ++ ) {
2018-02-15 22:59:31 +01:00
//where-x, where-y, how wide, how tall
//random col, first y, 1 pix wide, all pixels tall
cols [ i ] = context . getImageData ( sampleCols [ i ] , 0 , 1 , h ) . data ;
}
2017-09-24 01:54:46 +02:00
2018-02-15 22:59:31 +01:00
// fast test to see if aspect ratio is correct. If we detect anything darker than blackLevel, we modify
// blackLevel to the new lowest value
var isLetter = true ;
var currentMaxVal ;
var currentMax _a , currentMax _b ;
2018-02-16 00:19:08 +01:00
var bottom _r = cols [ 0 ] . length - 4 ;
2018-02-15 22:59:31 +01:00
var bottom _g = bottom _r + 1 ;
var bottom _b = bottom _r + 2 ;
2017-09-24 01:54:46 +02:00
for ( var i in cols ) {
2018-02-15 22:59:31 +01:00
// get biggest brightness in the top and bottom row across all three RGB components
currentMax _a = cols [ i ] [ 0 ] > cols [ i ] [ 1 ] ? cols [ i ] [ 0 ] : cols [ i ] [ 1 ] ;
2018-02-16 00:19:08 +01:00
currentMax _b = cols [ i ] [ bottom _r ] > cols [ i ] [ bottom _g ] ? cols [ i ] [ bottom _r ] : cols [ i ] [ bottom _g ] ;
currentMaxVal = cols [ i ] [ 2 ] > cols [ i ] [ bottom _b ] ? cols [ i ] [ 2 ] : cols [ i ] [ bottom _b ] ;
2018-02-15 22:59:31 +01:00
currentMax _a = currentMax _a > currentMax _b ? currentMax _a : currentMax _b ;
currentMaxVal = currentMaxVal > currentMax _a ? currentMaxVal : currentMax _a ;
2017-09-24 01:54:46 +02:00
// if any of those points fails this check, we aren't letterboxed
2018-02-16 00:19:08 +01:00
isLetter &= currentMaxVal <= ( GlobalVars . arDetect . blackLevel + Settings . arDetect . blackbarTreshold ) ;
2018-02-15 22:59:31 +01:00
// any single point on that list could be the darkest black, so we still check if we can lower blackLevel
if ( currentMaxVal < GlobalVars . arDetect . blackLevel ) {
GlobalVars . arDetect . blackLevel = currentMaxVal ;
}
2017-09-24 01:54:46 +02:00
}
if ( ! isLetter ) {
2018-01-27 22:25:25 +01:00
// Če ne zaznamo letterboxa, kličemo reset. Lahko, da je bilo razmerje stranic popravljeno na roke. Možno je tudi,
// da je letterbox izginil.
// If we don't detect letterbox, we reset aspect ratio to aspect ratio of the video file. The aspect ratio could
// have been corrected manually. It's also possible that letterbox (that was there before) disappeared.
2017-12-31 18:26:59 +01:00
if ( Debug . debug ) {
console . log ( "%c[ArDetect::_ard_vdraw] no edge detected. canvas has no edge." , "color: #aaf" ) ;
}
2018-01-27 22:25:25 +01:00
Resizer . reset ( ) ;
2018-02-12 23:28:31 +01:00
GlobalVars . lastAr = { type : "auto" , ar : null } ;
2017-09-27 02:26:47 +02:00
2018-02-15 22:59:31 +01:00
triggerTimeout = _ard _getTimeout ( baseTimeout , startTime ) ;
_ard _timer = setTimeout ( _ard _vdraw , triggerTimeout , vid , context , w , h ) ; //no letterbox, no problem
2017-09-24 01:54:46 +02:00
return ;
}
2017-09-27 02:26:47 +02:00
2018-02-15 00:17:58 +01:00
// let's do a quick test to see if we're on a black frame
// TODO: reimplement but with less bullshit
2017-09-27 02:26:47 +02:00
2018-02-15 00:17:58 +01:00
// poglejmo, če obrežemo preveč.
2018-02-15 22:59:31 +01:00
// let's check if we're cropping too much (or whatever)
2018-02-15 00:17:58 +01:00
var guardLineOut ;
2018-02-15 22:59:31 +01:00
var imageDetectOut ;
2018-02-15 00:17:58 +01:00
if ( Settings . arDetect . guardLine . enabled ) {
guardLineOut = _ard _guardLineCheck ( )
guardLineResult = guardLineOut . success ;
2017-09-27 02:26:47 +02:00
2018-02-15 22:59:31 +01:00
if ( ! guardLineResult ) { // add new ssamples to our sample columns
for ( var col of guardLineOut . offenders ) {
sampleCols . push ( col )
2017-09-24 01:54:46 +02:00
}
}
2018-02-15 22:59:31 +01:00
imageDetectOut = _ard _guardLineImageDetect ( ) ;
imageDetectResult = imageDetectOut . success ;
2017-09-27 02:26:47 +02:00
2018-02-15 22:59:31 +01:00
// če sta obe funkciji uspeli, potem se razmerje stranic ni spremenilo.
// if both succeed, then aspect ratio hasn't changed.
if ( imageDetectResult && guardLineResult ) {
triggerTimeout = _ard _getTimeout ( baseTimeout , startTime ) ;
_ard _timer = setTimeout ( _ard _vdraw , triggerTimeout , vid , context , w , h ) ; //no letterbox, no problem
return ;
2017-09-27 02:26:47 +02:00
}
}
2017-09-24 01:54:46 +02:00
2018-02-15 22:59:31 +01:00
// pa poglejmo, kje se končajo črne letvice na vrhu in na dnu videa.
// let's see where black bars end.
GlobalVars . sampleCols _current = sampleCols . length ;
var blackbarSamples = _ard _findBlackbarLimits ( context , sampleCols ) ;
var edgeCandidates = _ard _edgeDetect ( context , blackbarSamples ) ;
2018-02-16 00:19:08 +01:00
var edgePost = _ard _edgePostprocess ( edgeCandidates , context . canvas . height ) ;
2018-02-15 22:59:31 +01:00
if ( edgePost . status == "ar_known" ) {
_ard _processAr ( vid , w , h , edgePost . blackbarWidth , null , fallbackMode ) ;
triggerTimeout = _ard _getTimeout ( baseTimeout , startTime ) ;
_ard _timer = setTimeout ( _ard _vdraw , triggerTimeout , vid , context , w , h ) ; //no letterbox, no problem
return ;
2017-09-24 01:54:46 +02:00
}
2018-02-15 22:59:31 +01:00
else {
triggerTimeout = _ard _getTimeout ( baseTimeout , startTime ) ;
_ard _timer = setTimeout ( _ard _vdraw , triggerTimeout , vid , context , w , h ) ; //no letterbox, no problem
return ;
2017-09-27 02:26:47 +02:00
}
2018-02-12 23:28:31 +01:00
}
catch ( e ) {
if ( Debug . debug )
console . log ( "%c[ArDetect::_ard_vdraw] vdraw has crashed for some reason ???. Error here:" , "color: #000; background: #f80" , e ) ;
_ard _timer = setTimeout ( _ard _vdraw , Settings . arDetect . timer _playing , vid , context , w , h ) ;
}
2017-09-24 01:54:46 +02:00
}
2018-02-16 00:19:08 +01:00
function _ard _guardLineCheck ( context ) {
2018-02-15 00:17:58 +01:00
// this test tests for whether we crop too aggressively
// if this test is passed, then aspect ratio probably didn't change from wider to narrower. However, further
// checks are needed to determine whether aspect ratio got wider.
// if this test fails, it returns a list of offending points.
// if the upper edge is null, then edge hasn't been detected before. This test is pointless, therefore it
// should succeed by default.
if ( GlobalVars . arDetect . guardLine . top == null )
return { success : true } ;
2018-02-15 22:59:31 +01:00
var blackbarTreshold = GlobalVars . arDetect . blackLevel + Settings . arDetect . blackbarTreshold ;
2018-02-15 00:17:58 +01:00
var edges = GlobalVars . arDetect . guardLine ;
var start = parseInt ( _ard _canvasWidth * Settings . arDetect . guardLine . ignoreEdgeMargin ) ;
var width = _ard _canvasWidth - ( start << 1 ) ;
var offenders = [ ] ;
var firstOffender = - 1 ;
var offenderCount = - 1 ;
// TODO: implement logo check.
// preglejmo obe vrstici
// check both rows
for ( var edge of [ edges . top , edges . bottom ] ) {
var row = context . getImageData ( start , edges . top , width , 1 ) . data ;
2018-02-16 00:19:08 +01:00
for ( var i = 0 ; i < row . length ; i += 4 ) {
2018-02-15 00:17:58 +01:00
// we track sections that go over what's supposed to be a black line, so we can suggest more
// columns to sample
if ( row [ i ] > blackbarTreshold || row [ i + 1 ] > blackbarTreshold || row [ i + 2 ] > blackbarTreshold ) {
if ( firstOffender < 0 ) {
firstOffender = i >> 2 ;
offenderCount ++ ;
offenders . push ( { x : firstOffender , width : 1 } )
}
else {
offenders [ offenderCount ] . width ++
}
}
else {
// is that a black pixel again? Let's reset the 'first offender'
firstOffender = - 1 ;
}
}
}
// če nismo našli nobenih prekrškarjev, vrnemo uspeh. Drugače vrnemo seznam prekrškarjev
// vrnemo tabelo, ki vsebuje sredinsko točko vsakega prekrškarja (x + width*0.5)
//
// if we haven't found any offenders, we return success. Else we return list of offenders
// we return array of middle points of offenders (x + (width >> 1) for every offender)
if ( offenderCount == - 1 )
return { success : true } ;
var ret = new Array ( offenders . length ) ;
for ( var o in offenders ) {
ret [ o ] = offenders [ o ] . x + ( offenders [ o ] . width >> 2 ) ;
}
2018-02-15 22:59:31 +01:00
return { success : false , offenders : ret } ;
}
2018-02-16 00:19:08 +01:00
function _ard _guardLineImageDetect ( context ) {
2018-02-15 22:59:31 +01:00
if ( GlobalVars . arDetect . guardLine . top == null )
return { success : false } ;
var blackbarTreshold = GlobalVars . arDetect . blackLevel + Settings . arDetect . blackbarTreshold ;
var edges = GlobalVars . arDetect . guardLine ;
var start = parseInt ( _ard _canvasWidth * Settings . arDetect . guardLine . ignoreEdgeMargin ) ;
var width = _ard _canvasWidth - ( start << 1 ) ;
// TODO: implement logo check.
// preglejmo obe vrstici
// check both rows
var complyingCount = 0 ;
var complyingTreshold = ( context . canvas . width * Settings . arDetect . guardLine . imageTestTreshold ) << 1 ;
for ( var edge of [ edges . top , edges . bottom ] ) {
var row = context . getImageData ( start , edges . top , width , 1 ) . data ;
2018-02-16 00:19:08 +01:00
for ( var i = 0 ; i < row . length ; i += 4 ) {
2018-02-15 22:59:31 +01:00
// we track sections that go over what's supposed to be a black line, so we can suggest more
// columns to sample
if ( row [ i ] > blackbarTreshold || row [ i + 1 ] > blackbarTreshold || row [ i + 2 ] > blackbarTreshold ) {
complyingCount ++ ;
if ( complyingCount > complyingTreshold ) {
return { success : true }
}
}
}
}
return { success : false } ;
2018-02-15 00:17:58 +01:00
}
2018-02-16 00:19:08 +01:00
function _ard _findBlackbarLimits ( context , cols ) {
2018-02-15 00:17:58 +01:00
var data = [ ] ;
var middle , bottomStart , blackbarTreshold , top , bottom ;
var res = [ ] ;
middle = context . canvas . height << 1 // x2 = middle of data column
2018-02-16 00:19:08 +01:00
bottomStart = ( context . canvas . height - 1 ) << 2 ; // (height - 1) x 4 = bottom pixel
blackbarTreshold = GlobalVars . arDetect . blackLevel + Settings . arDetect . blackbarTreshold ;
var found ;
2018-02-15 00:17:58 +01:00
for ( var col of cols ) {
2018-02-16 00:19:08 +01:00
found = false ;
data = context . getImageData ( col , 0 , 1 , context . canvas . height ) . data ;
2018-02-15 00:17:58 +01:00
for ( var i = 0 ; i < middle ; i += 4 ) {
if ( data [ i ] > blackbarTreshold || data [ i + 1 ] > blackbarTreshold || data [ i + 2 ] > blackbarTreshold ) {
top = ( i >> 2 ) - 1 ;
found = true ;
break ;
}
}
if ( ! found )
top = - 1 ; // universal "not found" mark. We don't break because the bottom side can still give good info
2018-02-16 00:19:08 +01:00
found = false ; // reset
2018-02-15 00:17:58 +01:00
for ( var i = bottomStart ; i > middle ; i -= 4 ) {
if ( data [ i ] > blackbarTreshold || data [ i + 1 ] > blackbarTreshold || data [ i + 2 ] > blackbarTreshold ) {
bottom = ( i >> 2 ) + 1 ;
found = true ;
break ;
}
}
if ( ! found )
bottom = - 1 ;
2018-02-15 22:59:31 +01:00
res . push ( { col : col , bottom : bottom , top : top , bottomRelative : context . canvas . height - bottom } ) ;
2018-02-15 00:17:58 +01:00
}
2018-02-16 00:19:08 +01:00
if ( Debug . debug && Debug . debugArDetect )
console . log ( "[ArDetect::_ard_findBlackbarLimits] found some candidates for black bar limits" , res ) ;
2018-02-15 00:17:58 +01:00
return res ;
}
2018-02-15 22:59:31 +01:00
2018-02-16 00:19:08 +01:00
function _ard _edgeDetect ( context , samples ) {
2018-02-15 22:59:31 +01:00
var edgeCandidatesTop = { } ;
var edgeCandidatesBottom = { } ;
2018-02-15 00:17:58 +01:00
2018-02-15 22:59:31 +01:00
var sampleWidthBase = Settings . arDetect . edgeDetection . sampleWidth ;
var halfSample = sampleWidthBase >> 1 ;
var detections ;
var detectionTreshold = Settings . arDetect . edgeDetection . detectionTreshold ;
var canvasWidth = context . canvas . width ;
var canvasHeight = context . canvas . height ;
2018-02-15 00:17:58 +01:00
2018-02-15 22:59:31 +01:00
var sampleStart , sampleWidth ;
var imageData = [ ] ;
var blackEdgeViolation = false ;
2018-02-16 00:19:08 +01:00
var blackbarTreshold = GlobalVars . arDetect . blackLevel + Settings . arDetect . blackbarTreshold ;
2018-02-15 22:59:31 +01:00
var topEdgeCount = 0 ;
var bottomEdgeCount = 0 ;
2018-02-15 00:17:58 +01:00
2018-02-16 00:19:08 +01:00
2018-02-15 00:17:58 +01:00
for ( sample of samples ) {
2018-02-15 22:59:31 +01:00
// determine size of the square
sampleStart = sample . col - halfSample ;
if ( sampleStart < 0 )
sampleStart = 0 ;
2018-02-15 00:17:58 +01:00
2018-02-15 22:59:31 +01:00
sampleWidth = ( sample . col + halfSample >= canvasWidth ) ?
( sample . col - canvasWidth + sampleWidthBase ) : sampleWidthBase ;
2018-02-16 00:19:08 +01:00
// sample.top - 1 -> should be black (we assume a bit of margin in case of rough edges)
// sample.top + 2 -> should be color
2018-02-15 22:59:31 +01:00
// we must also check for negative values, which mean something went wrong.
2018-02-16 00:19:08 +01:00
if ( sample . top > 1 ) {
2018-02-15 22:59:31 +01:00
// check whether black edge gets any non-black values. non-black -> insta fail
2018-02-16 00:19:08 +01:00
imageData = context . getImageData ( sampleStart , sample . top - 1 , sampleWidth , 1 ) . data ;
2018-02-15 22:59:31 +01:00
for ( var i = 0 ; i < imageData . length ; i += 4 ) {
if ( imageData [ i ] > blackbarTreshold ||
imageData [ i + 1 ] > blackbarTreshold ||
imageData [ i + 2 ] > blackbarTreshold ) {
blackEdgeViolation = true ;
2018-02-16 00:19:08 +01:00
if ( Debug . debug && Debug . debugArDetect && Debug . arDetect . edgeDetect )
console . log ( ( "[ArDetect::_ard_edgeDetect] detected black edge violation at i=" + i + "; sample.top=" + sample . top + "\n--" ) , imageData , context . getImageData ( sampleStart , sample . top - 2 , sampleWidth , 1 ) ) ;
2018-02-15 22:59:31 +01:00
break ;
}
}
// if black edge isn't black, we don't check the image part either
if ( ! blackEdgeViolation ) {
2018-02-16 00:19:08 +01:00
imageData = context . getImageData ( sampleStart , sample . top + 2 , sampleWidth , 1 ) . data ;
2018-02-15 22:59:31 +01:00
detections = 0 ;
for ( var i = 0 ; i < imageData . length ; i += 4 ) {
if ( imageData [ i ] > blackbarTreshold ||
imageData [ i + 1 ] > blackbarTreshold ||
imageData [ i + 2 ] > blackbarTreshold ) {
detections ++ ;
}
}
2018-02-16 00:19:08 +01:00
console . log ( "detections:" , detections , imageData , context . getImageData ( sampleStart , sample . top - 2 , sampleWidth , 1 ) ) ;
2018-02-15 22:59:31 +01:00
if ( detections >= detectionTreshold ) {
2018-02-16 00:19:08 +01:00
console . log ( "detection!" ) ;
2018-02-15 22:59:31 +01:00
if ( edgeCandidatesTop [ sample . top ] != undefined )
edgeCandidatesTop [ sample . top ] . count ++ ;
2018-02-16 00:19:08 +01:00
else {
topEdgeCount ++ ; // only count distinct
2018-02-15 22:59:31 +01:00
edgeCandidatesTop [ sample . top ] = { top : sample . top , count : 1 } ;
2018-02-16 00:19:08 +01:00
}
2018-02-15 22:59:31 +01:00
}
}
}
// sample.bottom -> should be black
// sample.bottom-2 -> should be non-black
if ( sample . bottom > 0 ) {
2018-02-16 00:19:08 +01:00
imageData = context . getImageData ( sampleStart , sample . bottom , sampleWidth , 1 ) . data ;
2018-02-15 22:59:31 +01:00
for ( var i = 0 ; i < imageData . length ; i += 4 ) {
if ( imageData [ i ] > blackbarTreshold ||
imageData [ i + 1 ] > blackbarTreshold ||
imageData [ i + 2 ] > blackbarTreshold ) {
blackEdgeViolation = true ;
2018-02-16 00:19:08 +01:00
console . log ( ( "[ArDetect::_ard_edgeDetect] detected black edge violation at i=" + i + "; sample.top=" + sample . top + "\n--" ) , imageData , context . getImageData ( sampleStart , sample . top - 2 , sampleWidth , 1 ) ) ;
2018-02-15 22:59:31 +01:00
break ;
}
}
// if black edge isn't black, we don't check the image part either
if ( ! blackEdgeViolation ) {
2018-02-16 00:19:08 +01:00
imageData = context . getImageData ( sampleStart , sample . bottom - 2 , sampleWidth , 1 ) . data ;
2018-02-15 22:59:31 +01:00
detections = 0 ;
for ( var i = 0 ; i < imageData . length ; i += 4 ) {
if ( imageData [ i ] > blackbarTreshold ||
imageData [ i + 1 ] > blackbarTreshold ||
imageData [ i + 2 ] > blackbarTreshold ) {
detections ++ ;
}
}
if ( detections >= detectionTreshold ) {
// use bottomRelative for ez sort
2018-02-16 00:19:08 +01:00
console . log ( "detection!" ) ;
2018-02-15 22:59:31 +01:00
if ( edgeCandidatesBottom [ sample . bottomRelative ] != undefined )
2018-02-16 00:19:08 +01:00
edgeCandidatesBottom [ sample . bottomRelative ] . count ++ ;
else {
bottomEdgeCount ++ ; // only count distinct edges
edgeCandidatesBottom [ sample . bottomRelative ] = { bottom : sample . bottom , bottomRelative : sample . bottomRelative , count : 1 } ;
}
2018-02-15 22:59:31 +01:00
}
}
}
2018-02-15 00:17:58 +01:00
}
2018-02-15 22:59:31 +01:00
return {
edgeCandidatesTop : edgeCandidatesTop ,
edgeCandidatesTopCount : topEdgeCount ,
edgeCandidatesBottom : edgeCandidatesBottom ,
edgeCandidatesBottomCount : bottomEdgeCount
} ;
2018-02-15 00:17:58 +01:00
}
2018-02-16 00:19:08 +01:00
function _ard _edgePostprocess ( edges , canvasHeight ) {
2018-02-15 22:59:31 +01:00
var edgesTop = [ ] ;
var edgesBottom = [ ] ;
var alignMargin = canvasHeight * Settings . arDetect . allowedMisaligned ;
var missingEdge = edges . edgeCandidatesTopCount == 0 || edges . edgeCandidatesBottomCount == 0 ;
2018-02-15 00:17:58 +01:00
2018-02-15 22:59:31 +01:00
// pretvorimo objekt v tabelo
// convert objects to array
2018-02-16 00:19:08 +01:00
console . log ( edges . edgeCandidatesTop ) ;
2018-02-15 22:59:31 +01:00
if ( edges . edgeCandidatesTopCount > 0 ) {
2018-02-16 00:19:08 +01:00
for ( var e in edges . edgeCandidatesTop ) {
var edge = edges . edgeCandidatesTop [ e ] ;
2018-02-15 22:59:31 +01:00
edgesTop . push ( { distance : edge . top , count : edge . count } ) ;
}
}
if ( edges . edgeCandidatesBottomCount > 0 ) {
2018-02-16 00:19:08 +01:00
for ( var e in edges . edgeCandidatesBottom ) {
var edge = edges . edgeCandidatesBottom [ e ] ;
2018-02-15 22:59:31 +01:00
edgesBottom . push ( { distance : edge . bottomRelative , count : edge . count } ) ;
}
}
2018-02-16 00:19:08 +01:00
console . log ( "count top:" , edges . edgeCandidatesTopCount , "edges:" , edges , "edgesTop[]" , edgesTop ) ;
2018-02-15 22:59:31 +01:00
// če za vsako stran (zgoraj in spodaj) poznamo vsaj enega kandidata, potem lahko preverimo nekaj
// stvari
if ( ! missingEdge ) {
// predvidevamo, da je logo zgoraj ali spodaj, nikakor pa ne na obeh straneh hkrati.
// če kanal logotipa/watermarka ni vključil v video, potem si bosta razdaliji (edge.distance) prvih ključev
// zgornjega in spodnjega roba približno enaki
//
// we'll assume that no youtube channel is rude enough to put channel logo/watermark both on top and the bottom
// of the video. If logo's not included in the video, distances (edge.distance) of the first two keys should be
// roughly equal. Let's check for that.
if ( edgesTop [ 0 ] . distance >= edgesBottom [ 0 ] . distance - alignMargin &&
edgesTop [ 0 ] . distance <= edgesBottom [ 0 ] . distance + alignMargin ) {
var blackbarWidth = edgesTop [ 0 ] . distance > edgesBottom [ 0 ] . distance ?
edgesTop [ 0 ] . distance : edgesBottom [ 0 ] . distance ;
return { status : "ar_known" , blackbarWidth : blackbarWidth } ;
}
// torej, lahko da je na sliki watermark. Lahko, da je slika samo ornh črna. Najprej preverimo za watermark
// it could be watermark. It could be a dark frame. Let's check for watermark first.
if ( edgesTop [ 0 ] . distance < edgesBottom [ 0 ] . distance &&
edgesTop [ 0 ] . count < edgesBottom [ 0 ] . count &&
edgesTop [ 0 ] . count < GlobalVars . arDetect . sampleCols * Settings . arDetect . edgeDetection . logoTreshold ) {
// možno, da je watermark zgoraj. Preverimo, če se kateri od drugih potencialnih robov na zgornjem robu
// ujema s prvim spodnjim (+/- variance). Če je temu tako, potem bo verjetno watermark. Logo mora imeti
// manj vzorcev kot navaden rob.
if ( edgesTop [ 0 ] . length > 1 ) {
var lowMargin = edgesBottom [ 0 ] . distance - alignMargin ;
var highMargin = edgesBottom [ 0 ] . distance + alignMargin ;
for ( var i = 1 ; i < edgesTop . length ; i ++ ) {
if ( edgesTop [ i ] . distance >= lowMargin && edgesTop [ i ] . distance <= highMargin ) {
// dobili smo dejanski rob. vrnimo ga
// we found the actual edge. let's return that.
var blackbarWidth = edgesTop [ i ] . distance > edgesBottom [ 0 ] . distance ?
edgesTop [ i ] . distance : edgesBottom [ 0 ] . distance ;
return { status : "ar_known" , blackbarWidth : blackbarWidth } ;
}
}
}
}
if ( edgesBottom [ 0 ] . distance < edgesTop [ 0 ] . distance &&
edgesBottom [ 0 ] . count < edgesTop [ 0 ] . count &&
edgesBottom [ 0 ] . count < GlobalVars . arDetect . sampleCols * Settings . arDetect . edgeDetection . logoTreshold ) {
if ( edgesBottom [ 0 ] . length > 1 ) {
var lowMargin = edgesTop [ 0 ] . distance - alignMargin ;
var highMargin = edgesTop [ 0 ] . distance + alignMargin ;
for ( var i = 1 ; i < edgesBottom . length ; i ++ ) {
if ( edgesBottom [ i ] . distance >= lowMargin && edgesTop [ i ] . distance <= highMargin ) {
// dobili smo dejanski rob. vrnimo ga
// we found the actual edge. let's return that.
var blackbarWidth = edgesBottom [ i ] . distance > edgesTop [ 0 ] . distance ?
edgesBottom [ i ] . distance : edgesTop [ 0 ] . distance ;
return { status : "ar_known" , blackbarWidth : blackbarWidth } ;
}
}
}
}
}
else {
// zgornjega ali spodnjega roba nismo zaznali. Imamo še en trik, s katerim lahko poskusimo
// določiti razmerje stranic
// either the top or the bottom edge remains undetected, but we have one more trick that we
// can try. It also tries to work around logos.
var edgeDetectionTreshold = GlobalVars . arDetect . sampleCols * Settings . arDetect . edgeDetection . singleSideConfirmationTreshold ;
if ( edges . edgeCandidatesTopCount == 0 && edges . edgeCandidatesBottomCount != 0 ) {
for ( var edge of edgesBottom ) {
if ( edge . count >= edgeDetectionTreshold )
return { status : "ar_known" , blackbarWidth : edge . distance }
}
}
if ( edges . edgeCandidatesTopCount != 0 && edges . edgeCandidatesBottomCount == 0 ) {
for ( var edge of edgesTop ) {
if ( edge . count >= edgeDetectionTreshold )
return { status : "ar_known" , blackbarWidth : edge . distance }
}
}
}
// če pridemo do sem, nam ni uspelo nič. Razmerje stranic ni znano
// if we reach this bit, we have failed in determining aspect ratio. It remains unknown.
return { status : "ar_unknown" }
2018-02-15 00:17:58 +01:00
}
2017-12-29 23:34:40 +01:00
var _ard _stop = function ( ) {
if ( Debug . debug ) {
console . log ( "%c[ArDetect::_ard_stop] Stopping automatic aspect ratio detection" , _ard _console _stop ) ;
}
this . _forcehalt = true ;
2017-12-30 18:36:08 +01:00
this . _halted = true ;
2017-12-29 23:34:40 +01:00
clearTimeout ( _ard _timer ) ;
clearTimeout ( _ard _setup _timer ) ;
}
2017-09-24 01:54:46 +02:00
2018-02-15 22:59:31 +01:00
var _ard _resetBlackLevel = function ( ) {
GlobalVars . arDetect . blackLevel = Settings . arDetect . blackLevel _default ;
}
2017-12-30 18:36:08 +01:00
var _ard _isRunning = function ( ) {
return ! this . _halted ;
}
2018-02-15 22:59:31 +01:00
function _ard _getTimeout ( baseTimeout , startTime ) {
2018-02-16 00:19:08 +01:00
baseTimeout -= ( performance . now ( ) - startTime ) ;
2018-02-15 22:59:31 +01:00
return baseTimeout > Settings . arDetect . minimumTimeout ? baseTimeout : Settings . arDetect . minimumTimeout ;
}
2017-09-24 01:54:46 +02:00
var ArDetect = {
2017-12-29 23:34:40 +01:00
_forcehalt : false ,
2017-12-30 18:36:08 +01:00
_halted : false ,
2017-09-24 01:54:46 +02:00
arSetup : _arSetup ,
2017-12-30 12:55:58 +01:00
init : _arSetup ,
2017-09-24 01:54:46 +02:00
vdraw : _ard _vdraw ,
2017-10-02 00:27:01 +02:00
detectedAr : 1 ,
2017-12-29 23:34:40 +01:00
arChangedCallback : function ( ) { } ,
stop : _ard _stop ,
2018-02-15 22:59:31 +01:00
isRunning : _ard _isRunning ,
resetBlackLevel : _ard _resetBlackLevel
2017-09-24 01:54:46 +02:00
}