@ -3,6 +3,7 @@ import datetime
import logging
import os
import subprocess
from hashlib import sha256
from urlparse import urlparse
from uuid import uuid4
@ -21,14 +22,16 @@ class RaceNotFound(Exception):
class CantFindStart ( Exception ) :
def __init__ ( self , racer , found) :
def __init__ ( self , racer , racer_number, found, path ) :
self . racer = racer
self . racer_number = racer_number
self . found = found
self . path = path
def __str__ ( self ) :
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 :
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 ) :
@ -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 ) )
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
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_dir = os . path . join ( base_dir , " reviews " , output_name )
if not os . path . exists ( 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 )
if os . path . exists ( 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 = [ ]
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 ( ) )
start_path = os . path . join ( output_dir , " start- {} - {} .mp4 " . format ( racer_number , nonce ) )
logger . info ( " Cutting start for racer {} ( {} ) " . format ( racer_number , racer ) )
start_start , start_end = add_range ( start , start_range )
cut_to_file ( logger , start_path , base_dir , racer , start_start , start_end )
logger . info ( " Running blackdetect " )
args = [
' ffmpeg ' , ' -hide_banner ' ,
' -i ' , start_path ,
' -vf ' , ' blackdetect=d=0.1 ' ,
' -f ' , ' null ' , ' /dev/null '
]
proc = subprocess . Popen ( args , stderr = subprocess . PIPE )
out , err = proc . communicate ( )
if proc . wait ( ) != 0 :
raise Exception ( " ffmpeg exited {} \n {} " . format ( proc . wait ( ) , err ) )
lines = [
line for line in err . strip ( ) . split ( ' \n ' )
if line . startswith ( ' [blackdetect @ ' )
]
if len ( lines ) == 1 :
line , = lines
black_end = line . split ( ' ' ) [ 4 ]
assert black_end . startswith ( ' black_end: ' )
time_offset = float ( black_end . split ( ' : ' ) [ 1 ] )
else :
found = len ( lines )
logger . warning ( " Unable to detect start (expected 1 black interval, but found {} ) " . format ( found ) )
raise CantFindStart ( racer , found )
time_offset = datetime . timedelta ( seconds = time_offset )
racer_number = racer_index + 1
if time_offset is None :
start_path = os . path . join ( output_dir , " start- {} - {} .mp4 " . format ( racer_number , nonce ) )
logger . info ( " Cutting start for racer {} ( {} ) " . format ( racer_number , racer ) )
start_start , start_end = add_range ( start , start_range )
cut_to_file ( logger , start_path , base_dir , racer , start_start , start_end )
logger . info ( " Running blackdetect " )
args = [
' ffmpeg ' , ' -hide_banner ' ,
' -i ' , start_path ,
' -vf ' , ' blackdetect=d=0.1 ' ,
' -f ' , ' null ' , ' /dev/null '
]
proc = subprocess . Popen ( args , stderr = subprocess . PIPE )
out , err = proc . communicate ( )
if proc . wait ( ) != 0 :
raise Exception ( " ffmpeg exited {} \n {} " . format ( proc . wait ( ) , err ) )
lines = [
line for line in err . strip ( ) . split ( ' \n ' )
if line . startswith ( ' [blackdetect @ ' )
]
if len ( lines ) == 1 :
line , = lines
black_end = line . split ( ' ' ) [ 4 ]
assert black_end . startswith ( ' black_end: ' )
time_offset = float ( black_end . split ( ' : ' ) [ 1 ] )
else :
found = len ( lines )
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
# time since their actual start.