2019-03-02 18:58:22 +08:00
import subprocess
import numpy as np
import ffmpeg
from pathlib import Path
2020-01-21 22:43:39 +08:00
from core import pathex
from core . interact import interact as io
2019-03-02 18:58:22 +08:00
def extract_video ( input_file , output_dir , output_ext = None , fps = None ) :
input_file_path = Path ( input_file )
output_path = Path ( output_dir )
2019-03-20 03:53:27 +08:00
2019-03-02 18:58:22 +08:00
if not output_path . exists ( ) :
output_path . mkdir ( exist_ok = True )
2019-03-20 03:53:27 +08:00
2019-03-02 18:58:22 +08:00
if input_file_path . suffix == ' .* ' :
2020-01-21 22:43:39 +08:00
input_file_path = pathex . get_first_file_by_stem ( input_file_path . parent , input_file_path . stem )
2019-03-02 18:58:22 +08:00
else :
if not input_file_path . exists ( ) :
input_file_path = None
2019-03-20 03:53:27 +08:00
2019-03-02 18:58:22 +08:00
if input_file_path is None :
io . log_err ( " input_file not found. " )
return
2019-03-20 03:53:27 +08:00
2019-03-02 18:58:22 +08:00
if fps is None :
2020-01-21 22:43:39 +08:00
fps = io . input_int ( " Enter FPS " , 0 , help_message = " How many frames of every second of the video will be extracted. 0 - full fps " )
2019-03-20 03:53:27 +08:00
2019-03-23 22:27:38 +08:00
if output_ext is None :
2020-01-21 22:43:39 +08:00
output_ext = io . input_str ( " Output image format " , " png " , [ " png " , " jpg " ] , help_message = " png is lossless, but extraction is x10 slower for HDD, requires x10 more disk space than jpg. " )
2019-03-23 22:27:38 +08:00
2020-01-21 22:43:39 +08:00
for filename in pathex . get_image_paths ( output_path , [ ' . ' + output_ext ] ) :
2019-03-02 18:58:22 +08:00
Path ( filename ) . unlink ( )
job = ffmpeg . input ( str ( input_file_path ) )
2019-03-20 03:53:27 +08:00
2019-03-23 22:27:38 +08:00
kwargs = { ' pix_fmt ' : ' rgb24 ' }
2019-03-02 18:58:22 +08:00
if fps != 0 :
kwargs . update ( { ' r ' : str ( fps ) } )
2019-03-23 22:27:38 +08:00
if output_ext == ' jpg ' :
2019-03-23 22:28:50 +08:00
kwargs . update ( { ' q:v ' : ' 2 ' } ) #highest quality for jpg
2019-03-23 22:27:38 +08:00
2019-03-02 18:58:22 +08:00
job = job . output ( str ( output_path / ( ' %5d . ' + output_ext ) ) , * * kwargs )
2019-03-20 03:53:27 +08:00
2019-03-02 18:58:22 +08:00
try :
job = job . run ( )
except :
io . log_err ( " ffmpeg fail, job commandline: " + str ( job . compile ( ) ) )
def cut_video ( input_file , from_time = None , to_time = None , audio_track_id = None , bitrate = None ) :
input_file_path = Path ( input_file )
if input_file_path is None :
io . log_err ( " input_file not found. " )
return
2019-03-20 03:53:27 +08:00
2019-03-02 18:58:22 +08:00
output_file_path = input_file_path . parent / ( input_file_path . stem + " _cut " + input_file_path . suffix )
2019-03-20 03:53:27 +08:00
2019-03-02 18:58:22 +08:00
if from_time is None :
2020-01-21 22:43:39 +08:00
from_time = io . input_str ( " From time " , " 00:00:00.000 " )
2019-03-20 03:53:27 +08:00
2019-03-02 18:58:22 +08:00
if to_time is None :
2020-01-21 22:43:39 +08:00
to_time = io . input_str ( " To time " , " 00:00:00.000 " )
2019-03-20 03:53:27 +08:00
2019-03-02 18:58:22 +08:00
if audio_track_id is None :
2020-01-21 22:43:39 +08:00
audio_track_id = io . input_int ( " Specify audio track id. " , 0 )
2019-03-20 03:53:27 +08:00
2019-03-02 18:58:22 +08:00
if bitrate is None :
2020-01-21 22:43:39 +08:00
bitrate = max ( 1 , io . input_int ( " Bitrate of output file in MB/s " , 25 ) )
2019-03-02 18:58:22 +08:00
2020-01-28 16:24:45 +08:00
kwargs = { " c:v " : " libx264 " ,
2019-03-02 18:58:22 +08:00
" b:v " : " %d M " % ( bitrate ) ,
" pix_fmt " : " yuv420p " ,
}
2019-03-20 03:53:27 +08:00
2019-03-02 18:58:22 +08:00
job = ffmpeg . input ( str ( input_file_path ) , ss = from_time , to = to_time )
2019-03-20 03:53:27 +08:00
2019-03-02 18:58:22 +08:00
job_v = job [ ' v:0 ' ]
job_a = job [ ' a: ' + str ( audio_track_id ) + ' ? ' ]
2019-03-20 03:53:27 +08:00
2019-03-02 22:06:06 +08:00
job = ffmpeg . output ( job_v , job_a , str ( output_file_path ) , * * kwargs ) . overwrite_output ( )
2019-03-20 03:53:27 +08:00
2019-03-02 18:58:22 +08:00
try :
job = job . run ( )
except :
io . log_err ( " ffmpeg fail, job commandline: " + str ( job . compile ( ) ) )
2019-03-20 03:53:27 +08:00
2019-03-02 18:58:22 +08:00
def denoise_image_sequence ( input_dir , ext = None , factor = None ) :
input_path = Path ( input_dir )
2019-03-20 03:53:27 +08:00
2019-03-02 18:58:22 +08:00
if not input_path . exists ( ) :
io . log_err ( " input_dir not found. " )
return
2019-03-20 03:53:27 +08:00
2020-03-07 19:51:30 +08:00
image_paths = [ Path ( filepath ) for filepath in pathex . get_image_paths ( input_path ) ]
# Check extension of all images
image_paths_suffix = None
for filepath in image_paths :
if image_paths_suffix is None :
image_paths_suffix = filepath . suffix
else :
if filepath . suffix != image_paths_suffix :
io . log_err ( f " All images in { input_path . name } should be with the same extension. " )
return
2019-03-20 03:53:27 +08:00
2019-03-02 18:58:22 +08:00
if factor is None :
2020-03-07 19:51:30 +08:00
factor = np . clip ( io . input_int ( " Denoise factor? " , 7 , add_info = " 1-20 " ) , 1 , 20 )
# Rename to temporary filenames
for i , filepath in io . progress_bar_generator ( enumerate ( image_paths ) , " Renaming " , leave = False ) :
src = filepath
dst = filepath . parent / ( f ' { i + 1 : 06 } _ { filepath . name } ' )
try :
src . rename ( dst )
except :
io . log_error ( ' fail to rename %s ' % ( src . name ) )
return
# Rename to sequental filenames
for i , filepath in io . progress_bar_generator ( enumerate ( image_paths ) , " Renaming " , leave = False ) :
src = filepath . parent / ( f ' { i + 1 : 06 } _ { filepath . name } ' )
dst = filepath . parent / ( f ' { i + 1 : 06 } { filepath . suffix } ' )
try :
src . rename ( dst )
except :
io . log_error ( ' fail to rename %s ' % ( src . name ) )
return
2019-03-20 03:53:27 +08:00
2020-03-07 19:51:30 +08:00
# Process image sequence in ffmpeg
2019-11-09 03:04:51 +08:00
kwargs = { }
2020-03-07 19:51:30 +08:00
if image_paths_suffix == ' .jpg ' :
2019-11-09 03:04:51 +08:00
kwargs . update ( { ' q:v ' : ' 2 ' } )
2020-01-26 01:58:19 +08:00
2019-03-02 18:58:22 +08:00
job = ( ffmpeg
2020-03-07 19:51:30 +08:00
. input ( str ( input_path / ( ' %6d ' + image_paths_suffix ) ) )
2019-03-02 18:58:22 +08:00
. filter ( " hqdn3d " , factor , factor , 5 , 5 )
2020-03-07 19:51:30 +08:00
. output ( str ( input_path / ( ' %6d ' + image_paths_suffix ) ) , * * kwargs )
2019-03-20 03:53:27 +08:00
)
2019-03-02 18:58:22 +08:00
try :
job = job . run ( )
except :
io . log_err ( " ffmpeg fail, job commandline: " + str ( job . compile ( ) ) )
2019-03-20 03:53:27 +08:00
2020-03-07 19:51:30 +08:00
# Rename to temporary filenames
for i , filepath in io . progress_bar_generator ( enumerate ( image_paths ) , " Renaming " , leave = False ) :
src = filepath . parent / ( f ' { i + 1 : 06 } { filepath . suffix } ' )
dst = filepath . parent / ( f ' { i + 1 : 06 } _ { filepath . name } ' )
try :
src . rename ( dst )
except :
io . log_error ( ' fail to rename %s ' % ( src . name ) )
return
# Rename to initial filenames
for i , filepath in io . progress_bar_generator ( enumerate ( image_paths ) , " Renaming " , leave = False ) :
src = filepath . parent / ( f ' { i + 1 : 06 } _ { filepath . name } ' )
dst = filepath
try :
src . rename ( dst )
except :
io . log_error ( ' fail to rename %s ' % ( src . name ) )
return
2020-01-28 16:24:45 +08:00
def video_from_sequence ( input_dir , output_file , reference_file = None , ext = None , fps = None , bitrate = None , include_audio = False , lossless = None ) :
2019-03-20 03:53:27 +08:00
input_path = Path ( input_dir )
2019-03-02 18:58:22 +08:00
output_file_path = Path ( output_file )
reference_file_path = Path ( reference_file ) if reference_file is not None else None
2019-03-20 03:53:27 +08:00
2019-03-02 18:58:22 +08:00
if not input_path . exists ( ) :
io . log_err ( " input_dir not found. " )
return
2019-03-20 03:53:27 +08:00
2019-03-02 18:58:22 +08:00
if not output_file_path . parent . exists ( ) :
output_file_path . parent . mkdir ( parents = True , exist_ok = True )
return
2019-03-20 03:53:27 +08:00
2019-03-02 18:58:22 +08:00
out_ext = output_file_path . suffix
2019-03-20 03:53:27 +08:00
2019-03-02 18:58:22 +08:00
if ext is None :
2020-01-21 22:43:39 +08:00
ext = io . input_str ( " Input image format (extension) " , " png " )
2019-03-20 03:53:27 +08:00
2019-03-02 18:58:22 +08:00
if lossless is None :
2020-01-21 22:43:39 +08:00
lossless = io . input_bool ( " Use lossless codec " , False )
2019-03-20 03:53:27 +08:00
2019-03-02 18:58:22 +08:00
video_id = None
audio_id = None
ref_in_a = None
if reference_file_path is not None :
if reference_file_path . suffix == ' .* ' :
2020-01-21 22:43:39 +08:00
reference_file_path = pathex . get_first_file_by_stem ( reference_file_path . parent , reference_file_path . stem )
2019-03-02 18:58:22 +08:00
else :
if not reference_file_path . exists ( ) :
reference_file_path = None
2019-03-20 03:53:27 +08:00
2019-03-02 18:58:22 +08:00
if reference_file_path is None :
io . log_err ( " reference_file not found. " )
return
#probing reference file
probe = ffmpeg . probe ( str ( reference_file_path ) )
#getting first video and audio streams id with fps
for stream in probe [ ' streams ' ] :
if video_id is None and stream [ ' codec_type ' ] == ' video ' :
video_id = stream [ ' index ' ]
fps = stream [ ' r_frame_rate ' ]
2019-03-20 03:53:27 +08:00
2019-03-02 18:58:22 +08:00
if audio_id is None and stream [ ' codec_type ' ] == ' audio ' :
audio_id = stream [ ' index ' ]
if audio_id is not None :
#has audio track
ref_in_a = ffmpeg . input ( str ( reference_file_path ) ) [ str ( audio_id ) ]
2019-03-20 03:53:27 +08:00
2019-03-02 18:58:22 +08:00
if fps is None :
#if fps not specified and not overwritten by reference-file
2020-01-21 22:43:39 +08:00
fps = max ( 1 , io . input_int ( " Enter FPS " , 25 ) )
2019-03-20 03:53:27 +08:00
2019-03-02 18:58:22 +08:00
if not lossless and bitrate is None :
2020-01-21 22:43:39 +08:00
bitrate = max ( 1 , io . input_int ( " Bitrate of output file in MB/s " , 16 ) )
2019-03-20 03:53:27 +08:00
2020-01-21 22:43:39 +08:00
input_image_paths = pathex . get_image_paths ( input_path )
2019-03-20 03:53:27 +08:00
2019-08-28 02:05:22 +08:00
i_in = ffmpeg . input ( ' pipe: ' , format = ' image2pipe ' , r = fps )
2020-01-26 01:58:19 +08:00
2019-03-02 18:58:22 +08:00
output_args = [ i_in ]
2019-03-20 03:53:27 +08:00
2020-01-28 16:24:45 +08:00
if include_audio and ref_in_a is not None :
2019-03-02 18:58:22 +08:00
output_args + = [ ref_in_a ]
2019-03-20 03:53:27 +08:00
2019-03-02 18:58:22 +08:00
output_args + = [ str ( output_file_path ) ]
2019-03-20 03:53:27 +08:00
2019-03-02 18:58:22 +08:00
output_kwargs = { }
2019-03-20 03:53:27 +08:00
2019-03-02 18:58:22 +08:00
if lossless :
2020-01-28 16:24:45 +08:00
output_kwargs . update ( { " c:v " : " libx264 " ,
" crf " : " 0 " ,
" pix_fmt " : " yuv420p " ,
2019-03-02 18:58:22 +08:00
} )
else :
2020-01-28 16:24:45 +08:00
output_kwargs . update ( { " c:v " : " libx264 " ,
2019-03-02 18:58:22 +08:00
" b:v " : " %d M " % ( bitrate ) ,
" pix_fmt " : " yuv420p " ,
} )
2020-03-07 19:51:30 +08:00
2020-01-28 16:24:45 +08:00
if include_audio and ref_in_a is not None :
output_kwargs . update ( { " c:a " : " aac " ,
" b:a " : " 192k " ,
2020-02-29 12:55:17 +08:00
" ar " : " 48000 " ,
" strict " : " experimental "
2020-01-28 16:24:45 +08:00
} )
2019-03-20 03:53:27 +08:00
2019-03-02 18:58:22 +08:00
job = ( ffmpeg . output ( * output_args , * * output_kwargs ) . overwrite_output ( ) )
2019-08-28 02:05:22 +08:00
2020-01-26 01:58:19 +08:00
try :
2019-08-28 02:05:22 +08:00
job_run = job . run_async ( pipe_stdin = True )
2020-01-26 01:58:19 +08:00
2019-08-28 02:05:22 +08:00
for image_path in input_image_paths :
with open ( image_path , " rb " ) as f :
2020-01-26 01:58:19 +08:00
image_bytes = f . read ( )
2019-08-28 02:05:22 +08:00
job_run . stdin . write ( image_bytes )
2020-01-26 01:58:19 +08:00
2019-08-28 02:05:22 +08:00
job_run . stdin . close ( )
job_run . wait ( )
2019-03-02 18:58:22 +08:00
except :
io . log_err ( " ffmpeg fail, job commandline: " + str ( job . compile ( ) ) )