2019-01-09 22:59:03 +00:00
from . import HydrusConstants as HC
from . import HydrusData
from . import HydrusExceptions
from . import HydrusImageHandling
from . import HydrusPaths
from . import HydrusThreading
2014-06-18 21:53:48 +00:00
import numpy
2013-07-10 20:25:57 +00:00
import os
2014-06-18 21:53:48 +00:00
import re
import subprocess
2015-09-23 21:21:02 +00:00
import sys
2013-02-19 00:11:43 +00:00
import traceback
2014-05-28 21:03:24 +00:00
import threading
import time
2013-02-19 00:11:43 +00:00
2015-11-18 22:44:07 +00:00
if HC . PLATFORM_LINUX or HC . PLATFORM_OSX :
2014-05-28 21:03:24 +00:00
2015-11-18 22:44:07 +00:00
FFMPEG_PATH = os . path . join ( HC . BIN_DIR , ' ffmpeg ' )
2014-05-28 21:03:24 +00:00
2015-11-18 22:44:07 +00:00
elif HC . PLATFORM_WINDOWS :
2014-05-28 21:03:24 +00:00
2015-11-18 22:44:07 +00:00
FFMPEG_PATH = os . path . join ( HC . BIN_DIR , ' ffmpeg.exe ' )
2014-05-28 21:03:24 +00:00
2016-12-07 22:12:52 +00:00
if not os . path . exists ( FFMPEG_PATH ) :
FFMPEG_PATH = os . path . basename ( FFMPEG_PATH )
2014-05-28 21:03:24 +00:00
2017-06-28 20:23:21 +00:00
def CheckFFMPEGError ( lines ) :
2018-06-20 20:20:22 +00:00
if len ( lines ) == 0 :
raise HydrusExceptions . MimeException ( ' Could not parse that file--no FFMPEG output given. ' )
2017-06-28 20:23:21 +00:00
if " No such file or directory " in lines [ - 1 ] :
raise IOError ( " File not found! " )
if ' Invalid data ' in lines [ - 1 ] :
raise HydrusExceptions . MimeException ( ' FFMPEG could not parse. ' )
2016-12-14 21:19:07 +00:00
def GetFFMPEGVersion ( ) :
2017-11-15 22:35:49 +00:00
2016-12-14 21:19:07 +00:00
cmd = [ FFMPEG_PATH , ' -version ' ]
try :
2019-01-16 22:40:53 +00:00
sbp_kwargs = HydrusData . GetSubprocessKWArgs ( text = True )
2019-01-09 22:59:03 +00:00
2019-01-16 22:40:53 +00:00
proc = subprocess . Popen ( cmd , stdout = subprocess . PIPE , stderr = subprocess . PIPE , * * sbp_kwargs )
2019-01-09 22:59:03 +00:00
except FileNotFoundError :
return ' no ffmpeg found '
2016-12-14 21:19:07 +00:00
except Exception as e :
2019-01-09 22:59:03 +00:00
HydrusData . ShowException ( e )
return ' unable to execute ffmpeg '
2016-12-14 21:19:07 +00:00
2019-01-09 22:59:03 +00:00
infos = proc . stdout . read ( )
2016-12-14 21:19:07 +00:00
proc . terminate ( )
del proc
lines = infos . splitlines ( )
if len ( lines ) > 0 :
# typically 'ffmpeg version [VERSION] Copyright ...
top_line = lines [ 0 ]
if top_line . startswith ( ' ffmpeg version ' ) :
top_line = top_line . replace ( ' ffmpeg version ' , ' ' )
if ' ' in top_line :
version_string = top_line . split ( ' ' ) [ 0 ]
return version_string
return ' unknown '
2017-06-28 20:23:21 +00:00
# bits of this were originally cribbed from moviepy
def GetFFMPEGInfoLines ( path , count_frames_manually = False ) :
# open the file in a pipe, provoke an error, read output
cmd = [ FFMPEG_PATH , " -i " , path ]
if count_frames_manually :
if HC . PLATFORM_WINDOWS :
cmd + = [ " -f " , " null " , " NUL " ]
else :
cmd + = [ " -f " , " null " , " /dev/null " ]
2019-01-16 22:40:53 +00:00
sbp_kwargs = HydrusData . GetSubprocessKWArgs ( text = True )
2017-06-28 20:23:21 +00:00
2019-01-16 22:40:53 +00:00
proc = subprocess . Popen ( cmd , bufsize = 10 * * 5 , stdout = subprocess . PIPE , stderr = subprocess . PIPE , * * sbp_kwargs )
2017-06-28 20:23:21 +00:00
2019-01-09 22:59:03 +00:00
info = proc . stderr . read ( )
2017-06-28 20:23:21 +00:00
proc . wait ( )
proc . communicate ( )
del proc
lines = info . splitlines ( )
2017-11-15 22:35:49 +00:00
try :
CheckFFMPEGError ( lines )
except :
HydrusData . Print ( ' FFMPEG had problem with file: ' + path )
raise
2017-06-28 20:23:21 +00:00
return lines
2017-06-07 22:05:15 +00:00
def GetFFMPEGVideoProperties ( path , count_frames_manually = False ) :
2014-06-25 20:37:06 +00:00
2017-07-05 21:09:28 +00:00
lines = GetFFMPEGInfoLines ( path , count_frames_manually )
2014-06-25 20:37:06 +00:00
2017-06-28 20:23:21 +00:00
if not ParseFFMPEGHasVideo ( lines ) :
raise HydrusExceptions . MimeException ( ' File did not appear to have a video stream! ' )
2014-06-25 20:37:06 +00:00
2017-06-28 20:23:21 +00:00
resolution = ParseFFMPEGVideoResolution ( lines )
2014-06-25 20:37:06 +00:00
2017-06-28 20:23:21 +00:00
duration = ParseFFMPEGDuration ( lines )
2014-06-25 20:37:06 +00:00
2017-06-28 20:23:21 +00:00
if duration is None :
fps = ParseFFMPEGFPS ( lines )
if fps is None :
2017-07-12 20:03:45 +00:00
fps = 24 # screw it, let's just put one in there
2017-06-28 20:23:21 +00:00
if not count_frames_manually :
count_frames_manually = True
lines = GetFFMPEGInfoLines ( path , count_frames_manually )
num_frames = ParseFFMPEGNumFramesManually ( lines )
2019-01-09 22:59:03 +00:00
duration = num_frames / fps
2017-06-28 20:23:21 +00:00
else :
2017-08-02 21:32:54 +00:00
num_frames = None
2017-06-28 20:23:21 +00:00
if not count_frames_manually :
fps = ParseFFMPEGFPS ( lines )
it_was_accurate = fps is not None
if it_was_accurate :
num_frames = duration * fps
if num_frames != int ( num_frames ) : # we want whole numbers--anything else suggests start_offset is off or whatever
2017-07-27 00:47:13 +00:00
if os . path . getsize ( path ) < 30 * 1048576 : # but only defer to a super precise +/- 1-frame manual count in this case when the file is small
it_was_accurate = False
2017-06-28 20:23:21 +00:00
if not it_was_accurate :
count_frames_manually = True
lines = GetFFMPEGInfoLines ( path , count_frames_manually )
if count_frames_manually :
2017-08-02 21:32:54 +00:00
try :
num_frames = ParseFFMPEGNumFramesManually ( lines )
except HydrusExceptions . MimeException :
if num_frames is None :
raise
2017-06-28 20:23:21 +00:00
duration_in_ms = int ( duration * 1000 )
2018-11-14 23:10:55 +00:00
num_frames = int ( num_frames )
2014-06-25 20:37:06 +00:00
2017-06-28 20:23:21 +00:00
return ( resolution , duration_in_ms , num_frames )
2014-06-25 20:37:06 +00:00
2017-06-28 20:23:21 +00:00
def GetMime ( path ) :
2016-08-17 20:07:22 +00:00
2017-06-28 20:23:21 +00:00
lines = GetFFMPEGInfoLines ( path )
2016-08-17 20:07:22 +00:00
2017-06-28 20:23:21 +00:00
try :
2016-08-17 20:07:22 +00:00
2017-06-28 20:23:21 +00:00
mime_text = ParseFFMPEGMimeText ( lines )
2016-08-17 20:07:22 +00:00
2017-06-28 20:23:21 +00:00
except HydrusExceptions . MimeException :
2016-02-24 21:42:54 +00:00
2017-06-28 20:23:21 +00:00
return HC . APPLICATION_UNKNOWN
2016-02-24 21:42:54 +00:00
2017-06-28 20:23:21 +00:00
if ' matroska ' in mime_text or ' webm ' in mime_text :
2016-06-01 20:04:15 +00:00
2019-01-09 22:59:03 +00:00
# a webm has at least vp8/vp9 video and optionally vorbis audio
2016-06-01 20:04:15 +00:00
2019-01-09 22:59:03 +00:00
has_webm_video = False
has_webm_audio = True
if ParseFFMPEGHasVideo ( lines ) :
video_format = ParseFFMPEGVideoFormat ( lines )
webm_video_formats = ( ' vp8 ' , ' vp9 ' )
has_webm_video = True in ( webm_video_format in video_format for webm_video_format in webm_video_formats )
( has_audio , audio_format ) = ParseFFMPEGAudio ( lines )
if has_audio and ' vorbis ' not in audio_format :
has_webm_audio = False
if has_webm_video and has_webm_audio :
return HC . VIDEO_WEBM
else :
return HC . VIDEO_MKV
2016-02-24 21:42:54 +00:00
2017-06-28 20:23:21 +00:00
elif mime_text in ( ' mpeg ' , ' mpegvideo ' , ' mpegts ' ) :
2017-05-24 20:28:24 +00:00
2017-06-28 20:23:21 +00:00
return HC . VIDEO_MPEG
2017-05-24 20:28:24 +00:00
2017-06-28 20:23:21 +00:00
elif mime_text == ' flac ' :
2017-05-24 20:28:24 +00:00
2017-06-28 20:23:21 +00:00
return HC . AUDIO_FLAC
2017-05-24 20:28:24 +00:00
2017-06-28 20:23:21 +00:00
elif mime_text == ' mp3 ' :
2017-05-24 20:28:24 +00:00
2017-06-28 20:23:21 +00:00
return HC . AUDIO_MP3
2017-05-24 20:28:24 +00:00
2017-07-05 21:09:28 +00:00
elif ' mp4 ' in mime_text :
return HC . VIDEO_MP4
2017-06-28 20:23:21 +00:00
elif mime_text == ' ogg ' :
2016-12-07 22:12:52 +00:00
2017-06-28 20:23:21 +00:00
return HC . AUDIO_OGG
2016-12-07 22:12:52 +00:00
2017-06-28 20:23:21 +00:00
elif mime_text == ' asf ' :
2016-12-07 22:12:52 +00:00
2017-06-28 20:23:21 +00:00
if ParseFFMPEGHasVideo ( lines ) :
2016-12-07 22:12:52 +00:00
2017-06-28 20:23:21 +00:00
return HC . VIDEO_WMV
2016-12-07 22:12:52 +00:00
else :
2017-06-28 20:23:21 +00:00
return HC . AUDIO_WMA
2016-12-07 22:12:52 +00:00
2014-06-18 21:53:48 +00:00
2017-06-28 20:23:21 +00:00
return HC . APPLICATION_UNKNOWN
2017-06-14 21:19:11 +00:00
2017-06-28 20:23:21 +00:00
def HasVideoStream ( path ) :
2016-08-17 20:07:22 +00:00
2017-06-28 20:23:21 +00:00
lines = GetFFMPEGInfoLines ( path )
2017-06-07 22:05:15 +00:00
2017-06-28 20:23:21 +00:00
return ParseFFMPEGHasVideo ( lines )
2014-06-18 21:53:48 +00:00
2017-06-28 20:23:21 +00:00
def ParseFFMPEGAudio ( lines ) :
2014-06-18 21:53:48 +00:00
2017-06-28 20:23:21 +00:00
# this is from the old stuff--might be helpful later when we add audio
2014-06-18 21:53:48 +00:00
2017-12-13 22:33:07 +00:00
lines_audio = [ l for l in lines if ' Audio: ' in l ]
2017-06-28 20:23:21 +00:00
audio_found = lines_audio != [ ]
2019-01-09 22:59:03 +00:00
audio_format = None
2014-06-18 21:53:48 +00:00
2017-06-28 20:23:21 +00:00
if audio_found :
2019-01-09 22:59:03 +00:00
2017-06-28 20:23:21 +00:00
line = lines_audio [ 0 ]
2019-01-09 22:59:03 +00:00
2017-06-28 20:23:21 +00:00
try :
2019-01-09 22:59:03 +00:00
2017-06-28 20:23:21 +00:00
match = re . search ( " [0-9]* Hz " , line )
2019-01-09 22:59:03 +00:00
2017-06-28 20:23:21 +00:00
audio_fps = int ( line [ match . start ( ) + 1 : match . end ( ) ] )
2019-01-09 22:59:03 +00:00
2017-06-28 20:23:21 +00:00
except :
2019-01-09 22:59:03 +00:00
2017-06-28 20:23:21 +00:00
audio_fps = ' unknown '
2019-01-09 22:59:03 +00:00
try :
match = re . search ( ' (?<=Audio \ : \ s).+?(?=,) ' , line )
audio_format = match . group ( )
except :
audio_format = ' unknown '
return ( audio_found , audio_format )
2017-06-28 20:23:21 +00:00
def ParseFFMPEGDuration ( lines ) :
2014-06-18 21:53:48 +00:00
# get duration (in seconds)
2014-06-25 20:37:06 +00:00
# Duration: 00:00:02.46, start: 0.033000, bitrate: 1069 kb/s
2014-06-18 21:53:48 +00:00
try :
2017-06-28 20:23:21 +00:00
line = [ l for l in lines if ' Duration: ' in l ] [ 0 ]
if ' Duration: N/A ' in line :
return None
2014-06-25 20:37:06 +00:00
if ' start: ' in line :
2016-08-24 18:36:56 +00:00
m = re . search ( ' (start \\ : ) ' + ' -?[0-9]+ \\ .[0-9]* ' , line )
2014-06-25 20:37:06 +00:00
start_offset = float ( line [ m . start ( ) + 7 : m . end ( ) ] )
2016-08-24 18:36:56 +00:00
if abs ( start_offset ) > 1.0 : # once had a file with start offset of 957499 seconds jej
start_offset = 0
else :
start_offset = 0
2014-06-25 20:37:06 +00:00
2014-06-18 21:53:48 +00:00
match = re . search ( " [0-9][0-9]:[0-9][0-9]:[0-9][0-9].[0-9][0-9] " , line )
2019-01-09 22:59:03 +00:00
hms = list ( map ( float , line [ match . start ( ) + 1 : match . end ( ) ] . split ( ' : ' ) ) )
2014-06-18 21:53:48 +00:00
2017-06-28 20:23:21 +00:00
if len ( hms ) == 1 :
duration = hms [ 0 ]
elif len ( hms ) == 2 :
duration = 60 * hms [ 0 ] + hms [ 1 ]
elif len ( hms ) == 3 :
duration = 3600 * hms [ 0 ] + 60 * hms [ 1 ] + hms [ 2 ]
2019-01-23 22:19:16 +00:00
if duration == 0 :
return None
2017-06-28 20:23:21 +00:00
duration - = start_offset
return duration
except :
raise HydrusExceptions . MimeException ( ' Error reading duration! ' )
def ParseFFMPEGFPS ( lines ) :
try :
line = ParseFFMPEGVideoLine ( lines )
# get the frame rate
2018-10-17 21:00:09 +00:00
possible_results = [ ]
2017-06-28 20:23:21 +00:00
match = re . search ( " ( [0-9]*.| )[0-9]* tbr " , line )
if match is not None :
2018-10-17 21:00:09 +00:00
tbr = line [ match . start ( ) : match . end ( ) ] . split ( ' ' ) [ 1 ]
2017-06-28 20:23:21 +00:00
2018-10-17 21:00:09 +00:00
tbr_fps_is_likely_garbage = match is None or tbr . endswith ( ' k ' ) or float ( tbr ) > 60
2017-06-28 20:23:21 +00:00
2018-10-17 21:00:09 +00:00
if not tbr_fps_is_likely_garbage :
2017-06-28 20:23:21 +00:00
2018-10-17 21:00:09 +00:00
possible_results . append ( float ( tbr ) )
2017-06-28 20:23:21 +00:00
2018-10-17 21:00:09 +00:00
#
match = re . search ( " ( [0-9]*.| )[0-9]* fps " , line )
if match is not None :
fps = line [ match . start ( ) : match . end ( ) ] . split ( ' ' ) [ 1 ]
2017-06-28 20:23:21 +00:00
fps_is_likely_garbage = match is None or fps . endswith ( ' k ' ) or float ( fps ) > 60
2018-10-17 21:00:09 +00:00
if not fps_is_likely_garbage :
2017-06-28 20:23:21 +00:00
2018-10-17 21:00:09 +00:00
possible_results . append ( float ( fps ) )
2017-06-28 20:23:21 +00:00
2018-10-17 21:00:09 +00:00
if len ( possible_results ) == 0 :
return None
else :
# in some cases, fps is 0.77 and tbr is incorrectly 20. extreme values cause bad results. let's try erroring on the side of slow
# tbh in these cases, the frame are prob going to get counted manually anyway due to no neat ints at the end, so nbd
return min ( possible_results )
2014-06-25 20:37:06 +00:00
2014-06-18 21:53:48 +00:00
except :
2017-06-28 20:23:21 +00:00
raise HydrusExceptions . MimeException ( ' Error estimating framerate! ' )
def ParseFFMPEGHasVideo ( lines ) :
try :
video_line = ParseFFMPEGVideoLine ( lines )
except HydrusExceptions . MimeException :
return False
return True
def ParseFFMPEGMimeText ( lines ) :
2016-08-17 20:07:22 +00:00
try :
( input_line , ) = [ l for l in lines if l . startswith ( ' Input #0 ' ) ]
# Input #0, matroska, webm, from 'm.mkv':
text = input_line [ 10 : ]
mime_text = text . split ( ' , from ' ) [ 0 ]
2017-06-28 20:23:21 +00:00
return mime_text
2016-08-17 20:07:22 +00:00
except :
2017-06-28 20:23:21 +00:00
raise HydrusExceptions . MimeException ( ' Error reading mime! ' )
2016-08-17 20:07:22 +00:00
2017-06-28 20:23:21 +00:00
def ParseFFMPEGNumFramesManually ( lines ) :
try :
2017-05-31 21:50:53 +00:00
frame_lines = [ l for l in lines if l . startswith ( ' frame= ' ) ]
2017-06-28 20:23:21 +00:00
l = frame_lines [ - 1 ] # there will be several of these, counting up as the file renders. we hence want the final one
while ' ' in l :
2017-05-31 21:50:53 +00:00
2017-06-28 20:23:21 +00:00
l = l . replace ( ' ' , ' ' )
2017-05-31 21:50:53 +00:00
2017-06-28 20:23:21 +00:00
num_frames = int ( l . split ( ' ' ) [ 1 ] )
return num_frames
except :
raise HydrusExceptions . MimeException ( ' Error counting number of frames! ' )
2019-01-09 22:59:03 +00:00
def ParseFFMPEGVideoFormat ( lines ) :
line = ParseFFMPEGVideoLine ( lines )
try :
match = re . search ( ' (?<=Video \ : \ s).+?(?=,) ' , line )
video_format = match . group ( )
except :
video_format = ' unknown '
return video_format
2017-06-28 20:23:21 +00:00
def ParseFFMPEGVideoLine ( lines ) :
2017-05-31 21:50:53 +00:00
2014-06-18 21:53:48 +00:00
# get the output line that speaks about video
2019-01-16 22:40:53 +00:00
# the ^\sStream is to exclude the 'title' line, when it exists, includes the string 'Video: ', ha ha
lines_video = [ l for l in lines if re . search ( ' ^ \ s*Stream ' , l ) is not None and ' Video: ' in l and not ( ' Video: png ' in l or ' Video: jpg ' in l ) ] # mp3 says it has a 'png' video stream
2014-06-18 21:53:48 +00:00
2017-06-28 20:23:21 +00:00
if len ( lines_video ) == 0 :
raise HydrusExceptions . MimeException ( ' Could not find video information! ' )
line = lines_video [ 0 ]
return line
def ParseFFMPEGVideoResolution ( lines ) :
2014-06-18 21:53:48 +00:00
2017-06-28 20:23:21 +00:00
try :
line = ParseFFMPEGVideoLine ( lines )
2014-06-18 21:53:48 +00:00
# get the size, of the form 460x320 (w x h)
match = re . search ( " [0-9]*x[0-9]*(,| ) " , line )
2014-06-25 20:37:06 +00:00
2017-06-28 20:23:21 +00:00
resolution = list ( map ( int , line [ match . start ( ) : match . end ( ) - 1 ] . split ( ' x ' ) ) )
2014-06-25 20:37:06 +00:00
2017-11-01 20:37:39 +00:00
sar_match = re . search ( " SAR [0-9]*:[0-9]* " , line )
if sar_match is not None :
# ' SAR 2:3 '
sar_string = line [ sar_match . start ( ) : sar_match . end ( ) ]
# '2:3'
sar_string = sar_string [ 5 : - 1 ]
( sar_w , sar_h ) = sar_string . split ( ' : ' )
( sar_w , sar_h ) = ( int ( sar_w ) , int ( sar_h ) )
( x , y ) = resolution
x * = sar_w
x / / = sar_h
resolution = ( x , y )
2017-06-28 20:23:21 +00:00
return resolution
except :
2017-11-01 20:37:39 +00:00
raise HydrusExceptions . MimeException ( ' Error parsing resolution! ' )
2014-06-25 20:37:06 +00:00
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
# This was built from moviepy's FFMPEG_VideoReader
class VideoRendererFFMPEG ( object ) :
2015-11-18 22:44:07 +00:00
2014-06-25 20:37:06 +00:00
def __init__ ( self , path , mime , duration , num_frames , target_resolution , pix_fmt = " rgb24 " ) :
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
self . _path = path
self . _mime = mime
2019-01-09 22:59:03 +00:00
self . _duration = duration / 1000.0
2014-06-25 20:37:06 +00:00
self . _num_frames = num_frames
2014-05-28 21:03:24 +00:00
self . _target_resolution = target_resolution
2015-03-04 22:44:32 +00:00
self . lastread = None
2019-01-09 22:59:03 +00:00
self . fps = self . _num_frames / self . _duration
2014-05-28 21:03:24 +00:00
2016-07-06 21:13:15 +00:00
if self . fps == 0 :
self . fps = 24
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
self . pix_fmt = pix_fmt
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
if pix_fmt == ' rgba ' : self . depth = 4
else : self . depth = 3
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
( x , y ) = self . _target_resolution
2015-11-25 22:00:57 +00:00
2014-06-25 20:37:06 +00:00
bufsize = self . depth * x * y
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
self . process = None
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
self . bufsize = bufsize
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
self . initialize ( )
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
def __del__ ( self ) :
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
self . close ( )
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
def close ( self ) :
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
if self . process is not None :
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
self . process . terminate ( )
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
self . process . stdout . close ( )
self . process . stderr . close ( )
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
self . process = None
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
def initialize ( self , start_index = 0 ) :
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
self . close ( )
2017-06-28 20:23:21 +00:00
if self . _mime in ( HC . IMAGE_APNG , HC . IMAGE_GIF ) :
2014-05-28 21:03:24 +00:00
2018-02-07 23:40:33 +00:00
do_ss = False
2014-06-25 20:37:06 +00:00
ss = 0
self . pos = 0
skip_frames = start_index
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
else :
2014-05-28 21:03:24 +00:00
2018-02-07 23:40:33 +00:00
if start_index == 0 :
2018-02-14 21:47:18 +00:00
do_ss = False
2018-02-07 23:40:33 +00:00
else :
2018-02-14 21:47:18 +00:00
do_ss = True
2018-02-07 23:40:33 +00:00
2019-01-09 22:59:03 +00:00
ss = start_index / self . fps
2014-06-25 20:37:06 +00:00
self . pos = start_index
skip_frames = 0
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
( w , h ) = self . _target_resolution
2014-05-28 21:03:24 +00:00
2018-02-07 23:40:33 +00:00
cmd = [ FFMPEG_PATH ]
if do_ss :
cmd . extend ( [ ' -ss ' , " %.03f " % ss ] )
cmd . extend ( [ ' -i ' , self . _path ,
2014-06-25 20:37:06 +00:00
' -loglevel ' , ' quiet ' ,
' -f ' , ' image2pipe ' ,
" -pix_fmt " , self . pix_fmt ,
" -s " , str ( w ) + ' x ' + str ( h ) ,
2017-05-31 21:50:53 +00:00
' -vsync ' , ' 0 ' ,
2018-02-07 23:40:33 +00:00
' -vcodec ' , ' rawvideo ' , ' - ' ] )
2015-06-17 20:01:41 +00:00
2014-05-28 21:03:24 +00:00
2019-01-09 22:59:03 +00:00
sbp_kwargs = HydrusData . GetSubprocessKWArgs ( )
self . process = subprocess . Popen ( cmd , bufsize = self . bufsize , stdout = subprocess . PIPE , stderr = subprocess . PIPE , * * sbp_kwargs )
2014-05-28 21:03:24 +00:00
2016-07-27 21:53:34 +00:00
if skip_frames > 0 :
self . skip_frames ( skip_frames )
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
def skip_frames ( self , n ) :
2014-05-28 21:03:24 +00:00
2018-11-14 23:10:55 +00:00
n = int ( n )
2014-06-25 20:37:06 +00:00
( w , h ) = self . _target_resolution
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
for i in range ( n ) :
2014-05-28 21:03:24 +00:00
2015-09-09 22:04:39 +00:00
if self . process is not None :
self . process . stdout . read ( self . depth * w * h )
self . process . stdout . flush ( )
2014-06-25 20:37:06 +00:00
self . pos + = 1
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
def read_frame ( self ) :
2016-07-20 19:57:10 +00:00
if self . pos == self . _num_frames :
self . initialize ( )
2014-05-28 21:03:24 +00:00
2016-07-06 21:13:15 +00:00
if self . process is None :
result = self . lastread
2014-06-25 20:37:06 +00:00
else :
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
( w , h ) = self . _target_resolution
nbytes = self . depth * w * h
s = self . process . stdout . read ( nbytes )
if len ( s ) != nbytes :
2018-01-10 22:41:51 +00:00
if self . lastread is None :
raise Exception ( ' Unable to render that video! Please send it to hydrus dev so he can look at it! ' )
2014-06-25 20:37:06 +00:00
result = self . lastread
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
self . close ( )
2014-05-28 21:03:24 +00:00
else :
2014-06-25 20:37:06 +00:00
result = numpy . fromstring ( s , dtype = ' uint8 ' ) . reshape ( ( h , w , len ( s ) / / ( w * h ) ) )
self . lastread = result
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
self . pos + = 1
return result
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
def set_position ( self , pos ) :
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
rewind = pos < self . pos
jump_a_long_way_ahead = pos > self . pos + 60
2018-09-05 20:52:32 +00:00
if rewind or jump_a_long_way_ahead :
self . initialize ( pos )
else :
self . skip_frames ( pos - self . pos )
2014-05-28 21:03:24 +00:00
2016-12-07 22:12:52 +00:00