@ -3,6 +3,7 @@ import datetime
import logging
import logging
import os
import os
import subprocess
import subprocess
from hashlib import sha256
from urlparse import urlparse
from urlparse import urlparse
from uuid import uuid4
from uuid import uuid4
@ -21,14 +22,16 @@ class RaceNotFound(Exception):
class CantFindStart ( Exception ) :
class CantFindStart ( Exception ) :
def __init__ ( self , racer , found) :
def __init__ ( self , racer , racer_number, found, path ) :
self . racer = racer
self . racer = racer
self . racer_number = racer_number
self . found = found
self . found = found
self . path = path
def __str__ ( self ) :
def __str__ ( self ) :
if self . found > 0 :
if self . found > 0 :
return " Found multiple ( {} ) possible start points for {} " . format ( self . found , self . racer )
return " Found multiple ( {} ) possible start points for racer {} ( {} ) " . format ( self . found , self . racer_number , self . racer )
else :
else :
return " Failed to find start point for {} ". format ( self . racer )
return " Failed to find start point for racer {} ({} ) ". format ( self . racer_number , self . racer )
def ts ( dt ) :
def ts ( dt ) :
@ -77,7 +80,10 @@ def conn_from_url(url):
)
)
def review ( match_id , race_number , base_dir , db_url , start_range = ( 0 , 5 ) , finish_range = ( - 5 , 10 ) ) :
def review (
match_id , race_number , base_dir , db_url , start_range = ( 0 , 5 ) , finish_range = ( - 5 , 10 ) ,
racer1_start = None , racer2_start = None ,
) :
logger = logging . getLogger ( " review " ) . getChild ( " {} - {} " . format ( match_id , race_number ) )
logger = logging . getLogger ( " review " ) . getChild ( " {} - {} " . format ( match_id , race_number ) )
conn = conn_from_url ( db_url )
conn = conn_from_url ( db_url )
@ -111,11 +117,15 @@ def review(match_id, race_number, base_dir, db_url, start_range=(0, 5), finish_r
( racer1 , racer2 , start , duration ) , = data
( racer1 , racer2 , start , duration ) , = data
end = start + datetime . timedelta ( seconds = duration / 100. )
end = start + datetime . timedelta ( seconds = duration / 100. )
# cache hash encapsulates all input args
cache_hash = sha256 ( str ( ( match_id , race_number , start_range , finish_range , racer1_start , racer2_start ) ) )
cache_str = cache_hash . digest ( ) . encode ( ' base64 ' ) [ : 12 ]
output_name = " {} - {} - {} - {} " . format ( match_id , racer1 , racer2 , race_number )
output_name = " {} - {} - {} - {} " . format ( match_id , racer1 , racer2 , race_number )
output_dir = os . path . join ( base_dir , " reviews " , output_name )
output_dir = os . path . join ( base_dir , " reviews " , output_name )
if not os . path . exists ( output_dir ) :
if not os . path . exists ( output_dir ) :
os . makedirs ( output_dir )
os . makedirs ( output_dir )
result_name = " review_ {} _{} .mp4" . format ( * finish_range )
result_name = " review_ {} .mp4" . format ( cache_str )
result_path = os . path . join ( output_dir , result_name )
result_path = os . path . join ( output_dir , result_name )
if os . path . exists ( result_path ) :
if os . path . exists ( result_path ) :
logger . info ( " Result already exists for {} , reusing " . format ( result_path ) )
logger . info ( " Result already exists for {} , reusing " . format ( result_path ) )
@ -123,39 +133,42 @@ def review(match_id, race_number, base_dir, db_url, start_range=(0, 5), finish_r
finish_paths = [ ]
finish_paths = [ ]
for racer_ number, racer in enumerate ( ( racer1 , racer2 ) ) :
for racer_ index, ( racer , time_offset ) in enumerate ( ( ( racer1 , racer1_start ) , ( racer2 , racer2_start ) ) ) :
nonce = str ( uuid4 ( ) )
nonce = str ( uuid4 ( ) )
start_path = os . path . join ( output_dir , " start- {} - {} .mp4 " . format ( racer_number , nonce ) )
racer_number = racer_index + 1
logger . info ( " Cutting start for racer {} ( {} ) " . format ( racer_number , racer ) )
if time_offset is None :
start_start , start_end = add_range ( start , start_range )
start_path = os . path . join ( output_dir , " start- {} - {} .mp4 " . format ( racer_number , nonce ) )
cut_to_file ( logger , start_path , base_dir , racer , start_start , start_end )
logger . info ( " Cutting start for racer {} ( {} ) " . format ( racer_number , racer ) )
start_start , start_end = add_range ( start , start_range )
logger . info ( " Running blackdetect " )
cut_to_file ( logger , start_path , base_dir , racer , start_start , start_end )
args = [
' ffmpeg ' , ' -hide_banner ' ,
logger . info ( " Running blackdetect " )
' -i ' , start_path ,
args = [
' -vf ' , ' blackdetect=d=0.1 ' ,
' ffmpeg ' , ' -hide_banner ' ,
' -f ' , ' null ' , ' /dev/null '
' -i ' , start_path ,
]
' -vf ' , ' blackdetect=d=0.1 ' ,
proc = subprocess . Popen ( args , stderr = subprocess . PIPE )
' -f ' , ' null ' , ' /dev/null '
out , err = proc . communicate ( )
]
if proc . wait ( ) != 0 :
proc = subprocess . Popen ( args , stderr = subprocess . PIPE )
raise Exception ( " ffmpeg exited {} \n {} " . format ( proc . wait ( ) , err ) )
out , err = proc . communicate ( )
lines = [
if proc . wait ( ) != 0 :
line for line in err . strip ( ) . split ( ' \n ' )
raise Exception ( " ffmpeg exited {} \n {} " . format ( proc . wait ( ) , err ) )
if line . startswith ( ' [blackdetect @ ' )
lines = [
]
line for line in err . strip ( ) . split ( ' \n ' )
if len ( lines ) == 1 :
if line . startswith ( ' [blackdetect @ ' )
line , = lines
]
black_end = line . split ( ' ' ) [ 4 ]
if len ( lines ) == 1 :
assert black_end . startswith ( ' black_end: ' )
line , = lines
time_offset = float ( black_end . split ( ' : ' ) [ 1 ] )
black_end = line . split ( ' ' ) [ 4 ]
else :
assert black_end . startswith ( ' black_end: ' )
found = len ( lines )
time_offset = float ( black_end . split ( ' : ' ) [ 1 ] )
logger . warning ( " Unable to detect start (expected 1 black interval, but found {} ) " . format ( found ) )
else :
raise CantFindStart ( racer , found )
found = len ( lines )
time_offset = datetime . timedelta ( seconds = time_offset )
logger . warning ( " Unable to detect start (expected 1 black interval, but found {} ), re-cutting with timestamps " . format ( found ) )
cut_to_file ( logger , start_path , base_dir , racer , start_start , start_end , frame_counter = True )
raise CantFindStart ( racer , racer_number , found , start_path )
time_offset = datetime . timedelta ( seconds = time_offset - start_range [ 0 ] )
# start each racer's finish video at TIME_OFFSET later, so they are the same
# start each racer's finish video at TIME_OFFSET later, so they are the same
# time since their actual start.
# time since their actual start.