def return_with_progress(stream_spec): args = ffmpeg.compile(stream_spec) return subprocess.Popen(args, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
def run_async(self): if not self.ff_segment_out: raise Exception('Command is not ready') if logger.isEnabledFor(logging.DEBUG): logger.debug("FFmpeg CLI command: '%s'", " ".join(ffmpeg.compile(self.ff_segment_out))) return self.ff_segment_out.run_async()
def test__run_async(mocker, pipe_stdin, pipe_stdout, pipe_stderr, cwd): process__mock = mock.Mock() popen__mock = mocker.patch.object(subprocess, 'Popen', return_value=process__mock) stream = _get_simple_example() process = ffmpeg.run_async( stream, pipe_stdin=pipe_stdin, pipe_stdout=pipe_stdout, pipe_stderr=pipe_stderr, cwd=cwd, ) assert process is process__mock expected_stdin = subprocess.PIPE if pipe_stdin else None expected_stdout = subprocess.PIPE if pipe_stdout else None expected_stderr = subprocess.PIPE if pipe_stderr else None (args, ), kwargs = popen__mock.call_args assert args == ffmpeg.compile(stream) assert kwargs == dict( stdin=expected_stdin, stdout=expected_stdout, stderr=expected_stderr, cwd=cwd, )
def _estublish_cmd(self, scenes: List[Scene]): inputfile = self.input_media_path.as_posix() outputfile = self.output_media_path.as_posix() stream = ffmpeg.input(inputfile) video_streams = list() audio_streams = list() for scene in scenes: start = scene.get_startat() duration = scene.get_interval() v_clip_stream = ffmpeg.trim( stream, start=start, duration=duration) v_clip_stream = ffmpeg.setpts(v_clip_stream, 'PTS-STARTPTS') a_clip_stream = ffmpeg.filter_( stream, 'atrim', start=start, duration=duration) a_clip_stream = ffmpeg.filter_( a_clip_stream, 'asetpts', 'PTS-STARTPTS') video_streams.append(v_clip_stream) audio_streams.append(a_clip_stream) v_stream = ffmpeg.concat( *video_streams, n=len(video_streams), v=1, a=0) a_stream = ffmpeg.concat( *audio_streams, n=len(audio_streams), v=0, a=1) stream = ffmpeg.output( v_stream, a_stream, outputfile, **self.CONFIG_720P) # ffmpeg.view(stream) # Debug self.stream = stream return ' '.join(ffmpeg.compile(stream))
def receive(self, text_data): text_data_json = json.loads(text_data) params = text_data_json params['username'] = '******' input_vid = ffmpeg.input(params['url']) vid = input_vid.trim(start=params['start_time'], end=params['end_time']).setpts('PTS-STARTPTS') aud = input_vid.filter_('atrim', start=params['start_time'], end=params['end_time']).filter_('asetpts', 'PTS-STARTPTS') joined = ffmpeg.concat(vid, aud, v=1, a=1).node stream = ffmpeg.output(joined[0], joined[1], f"out_t_{params['username']}.mp4") stream = stream.overwrite_output() log = LogController.add_log(params['username'], params['start_time'], params['end_time'], params['url']) cmd = ffmpeg.compile(stream) process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding="utf-8") for line in process.stdout: duration_res = re.search(r'\sDuration: (?P<duration>\S+)', line) if duration_res is not None: duration = duration_res.groupdict()['duration'] duration = re.sub(r',', '', duration) result = re.search(r'\stime=(?P<time>\S+)', line) if result is not None: elapsed_time = result.groupdict()['time'] progress = (get_seconds(elapsed_time) / get_seconds(duration)) * 100 print("进度:%3.2f" % progress + "%") # time.sleep(1) self.send(text_data=json.dumps({'progress': progress})) process.wait() if process.poll() == 0: self.send(text_data=json.dumps({'progress': 100.00})) self.send(text_data=json.dumps({'msg': 'success'}))
def test(orgasmos): import ffmpeg from datetime import datetime stream = ffmpeg.input( "data\\upload\\orgasmos\\1-x-ti\\pqaY1OJ9eWOzWgVZDn-L1TAnkcdnJrUR.mp3") stream = ffmpeg.filter(stream, 'loudnorm') concatList = [] selected = {1: stream} for _ in range(5): id = 1 stream = selected.get(id) streams = ffmpeg.filter_multi_output(stream, 'asplit') selected[id] = streams.stream(0) stream = streams.stream(1) concatList.append(stream) fullStream = ffmpeg.concat(*concatList, v=0, a=1) outputFilename = "mix-test-{:d}.m4a".format(int( datetime.now().timestamp())) outputPath = basePath.joinpath("mixed").joinpath(outputFilename) fullStream = fullStream.output(str(outputPath)) cmd = ffmpeg.compile(fullStream) print(cmd) ffmpeg.view(fullStream, filename=str(outputPath) + ".png") # ffmpeg.run(fullStream) pass
def extract_audio_into_file(video, save_dir='', ext_format='mp3', force_overwrite=False, verbose=False): """ Extract_audio_into_file: @params: video: str Paths to input video save_dir: str or None Path to save output stacked video files, if None, save under working dir. ext_format: str Extesion format for output audio file, i.e. `mp3`, `.mp3`, `wav`, `acc`. force_overwrite: bool If False, try to firstly load available audio file and trim info json files. If True, just extract audio and calc trim_info_dict. verbose: bool Print verbose information, mainly for debug. @return: audio_file: str, path of saved audio file. """ if verbose: print('===> Video: ', video) if not ext_format.startswith('.'): ext_format = '.' + ext_format audio_file = osp.splitext(video)[0] + ext_format if save_dir: basename = osp.basename(audio_file) if not osp.isdir(save_dir): os.makedirs(save_dir) audio_file = osp.join(save_dir, basename) if verbose: print('===> audio_file saved into: ', audio_file) if not osp.isfile(audio_file) or force_overwrite: in_stream = ffmpeg.input(video) out_stream = ffmpeg.output(in_stream.audio, audio_file) if verbose: cmdline = ffmpeg.compile(out_stream) print('===> ffmpeg cmdline:', cmdline) out_stream.run() else: print('===> audio file already exists at {}, skip'.format(audio_file)) return audio_file
def __init__(self, filename, out_filename): os.makedirs(os.path.dirname(out_filename), exist_ok=True) self.out_filename = out_filename self.log_filename = os.path.splitext(self.out_filename)[0] + ".log" self.stderr = open(self.log_filename, "w", encoding="utf8") self.pipe_read, self.pipe_write = os.pipe() self.pipe_read_file = os.fdopen(self.pipe_read) probe = ffmpeg.probe(filename) duration = float(probe["format"]["duration"]) seconds = int(duration) milliseconds = int((duration - seconds) * 1000) self.duration = datetime.timedelta(seconds=seconds, milliseconds=milliseconds) info = [s for s in probe["streams"] if s["codec_type"] == "video"][0] self.width = info["width"] self.height = info["height"] source_bitrate = get_video_bitrate(probe) # Pick bitrate based on resolution, 1080p (8Mbps), 720p (5Mbps), smaller (3Mbps) bitrate = 3000 if self.height > 720: bitrate = 8000 elif self.height > 480: bitrate = 5000 # Don't exceed the source bitrate as our target if bitrate > source_bitrate: bitrate = source_bitrate encoding_args = { # HWAccel for RPi4, may need to pick a different encoder # for HW accel on other systems "c:v": "h264_v4l2m2m", "num_output_buffers": 32, "num_capture_buffers": 16, "b:v": f"{bitrate}k", "c:a": "copy", "progress": f"pipe:{self.pipe_write}" } self.start = datetime.datetime.now() in_stream = ffmpeg.input(filename) video = in_stream.video.filter("format", **{"pix_fmts": "yuv420p"}) enc = ffmpeg.output(video, in_stream.audio, self.out_filename, **encoding_args) args = ffmpeg.compile(enc, overwrite_output=True) self.proc = subprocess.Popen(args, stdout=subprocess.DEVNULL, stderr=self.stderr, pass_fds=[self.pipe_write]) self.encode_stats = {} self.encode_error = False
def run(self): global compiling compiling = True print("compiling started!") print(self.attr, self.amb) self.start_button.emit(False) util.clean_up() download.playlist_download(self, self.attr['playlist'], self.attr['config'].options_dict['audio_bitrate']) if self.attr['config'].options_dict['ambience']: download.ambience_download(self, self.amb, self.attr['config'].options_dict['audio_bitrate']) tracklist_length = util.generate_tracklist(self.attr['config']) if self.attr['config'].options_dict['stretch_image']: self.attr['thumbnail'] = util.stretch_image(self.attr['thumbnail']) title = self.attr['title'] if title == "": title = self.attr['playlist'][self.attr['playlist'].index("=")+1:] ffmpeg.compile(self, title, self.attr['thumbnail'], int(self.attr['config'].options_dict['audio_bitrate'])*1000, int(self.attr['config'].options_dict['video_bitrate'])*1000, self.attr['config'].options_dict['normalize_audio'], self.attr['config'].options_dict['ambience'], tracklist_length) self.progress_update.emit(['format', ""]) self.progress_update.emit(['increment', 0]) util.clean_up() compiling = False self.start_button.emit(True) print("compiling finished!")
def createVideoClip(clip, outputfile, fps, size=[256, 256]): vf = clip.shape[0] args = [ #'ffmpeg', '-y', # overwrite output file if it exists '-f', 'rawvideo', '-s', '%dx%d' % (size[1], size[0]), # '256x256', # size of one frame '-pix_fmt', 'rgb24', '-r', str(fps), # frames per second '-an', # Tells FFMPEG not to expect any audio '-i', '-', # The input comes from a pipe '-vcodec', 'libx264', '-b:v', '1500k', '-vframes', str(vf), # 5*25 '-s', '%dx%d' % (size[1], size[0]), # '256x256', # size of one frame outputfile ] process = (ffmpeg.input('pipe:', format='rawvideo', pix_fmt='rgb24', s='{}x{}'.format(size[1], size[0])).output( outputfile, pix_fmt='yuv420p', format='mp4', video_bitrate='1500k', r=str(fps), s='{}x{}'.format(size[1], size[0])).overwrite_output()) command = ffmpeg.compile(process, overwrite_output=True) #command = ffmpeg.get_args(args, overwrite_output=True) # sfolder+'/'+name pipe = sp.Popen(command, stdin=sp.PIPE, stderr=sp.PIPE) out, err = pipe.communicate(clip.tostring()) pipe.wait() pipe.terminate() print(err)
def __init__( self, input_path: pathlib.Path, input_width: int, input_height: int, frame_rate: float, processing_queue: multiprocessing.Queue, processing_settings: tuple, pause: Synchronized, ignore_max_image_pixels=True, ) -> None: threading.Thread.__init__(self) self.running = False self.input_path = input_path self.input_width = input_width self.input_height = input_height self.processing_queue = processing_queue self.processing_settings = processing_settings self.pause = pause # this disables the "possible DDoS" warning if ignore_max_image_pixels: Image.MAX_IMAGE_PIXELS = None self.exception = None self.decoder = subprocess.Popen( ffmpeg.compile( ffmpeg.input(input_path, r=frame_rate)["v"].output( "pipe:1", format="rawvideo", pix_fmt="rgb24", vsync="cfr").global_args("-hide_banner").global_args( "-nostats").global_args("-nostdin").global_args( "-loglevel", LOGURU_FFMPEG_LOGLEVELS.get( os.environ.get("LOGURU_LEVEL", "INFO").lower()), ), overwrite_output=True, ), env={"AV_LOG_FORCE_COLOR": "TRUE"}, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) # start the PIPE printer to start printing FFmpeg logs self.pipe_printer = PipePrinter(self.decoder.stderr) self.pipe_printer.start()
def vid2gif(src, dst, output_width="-1", output_height="-1", start_time=-1.0, stop_time=-1.0, fps=-1): # ffmpeg -y -i in.mp4 -t 30 -filter_complex "fps=10,scale=-1:-1:flags=lanczos,split[x][y];[x]palettegen[z];[y][z]paletteuse" out.gif in_stream = ffmpeg.input(src, ss=start_time, t=(stop_time-start_time)) scale_input = in_stream if fps >= 1: stream = ffmpeg.filter(in_stream['v'], 'fps', fps) scale_input = stream stream = ffmpeg.filter(scale_input, 'scale', output_width, output_height, 'lanczos') palette = ffmpeg.filter(in_stream['v'], 'palettegen') stream = ffmpeg.filter(stream, palette, 'paletteuse') stream = stream.overwrite_output(dst, format='gif') print(ffmpeg.compile(stream)) ffmpeg.run(stream,overwrite_output=True) ffmpeg.run(palette)
def convert_video_progress_bar(source: str, dest: str, manager=None): if manager is None: manager = enlighten.get_manager() stream = ffmpeg.input(source) stream = ffmpeg.output(stream, dest, vcodec='libx265', crf='28') args = ffmpeg.compile(stream, 'ffmpeg') args.insert(1, '-progress pipe:1') args = map(lambda x: '"' + x + '"' if '\\' in x or '/' in x else x, args) args = list(args) name = source.rsplit(os.path.sep, 1)[-1] proc = expect.spawn(' '.join(args), encoding='utf-8') pbar = None try: proc.expect(pattern_duration) total = sum( map(lambda x: float(x[1]) * 60**x[0], enumerate(reversed( proc.match.groups()[0].strip().split(':'))))) cont = 0 pbar = manager.counter(total=100, desc=name, unit='%', bar_format=BAR_FMT, counter_format=COUNTER_FMT) while True: proc.expect(pattern_progress) progress = sum( map( lambda x: float(x[1]) * 60**x[0], enumerate( reversed(proc.match.groups()[0].strip().split(':'))))) percent = progress / total * 100 pbar.update(percent - cont) cont = percent except expect.EOF: pass finally: if pbar is not None: pbar.close() proc.expect(expect.EOF) res = proc.before res += proc.read() exitstatus = proc.wait() if exitstatus: raise ffmpeg.Error('ffmpeg', '', res)
def generate_cmd(self, output: FileDesc, quiet: bool = True, y: bool = True, accurate_seek: bool = False, other_args: List[str] = []) -> List[str]: """ 生成变换用的命令 :param output: 输出文件 :param quiet: 静默模式,对应 ffmpeg 的 -v quiet :param y: 不进行确认,对应 ffmpeg 的 -y :param accurate_seek: 精准时间切割,对应 ffmpeg 的 -accurate_seek -avoid_negative_ts 1 :param other_args: 其它 ffmpeg 的参数 :returns: 变换用的命令 :raises AssertionError """ global_args = [] if self.now_duration is not None: self.input.update({ 'ss': str(self.now_duration[0]), 't': str(self.now_duration[1]), }) if accurate_seek: self.input.update({ 'accurate_seek': None, 'avoid_negative_ts': '1', }) if quiet: global_args += ['-v', 'quiet'] global_args += other_args stream = ffmpeg.output( self.stream, **self.__get_file_parameters(output), ) if global_args: stream = ffmpeg.nodes.GlobalNode(stream, 'my_args', global_args).stream() return ffmpeg.compile(stream, overwrite_output=y)
def custom_ffmpeg_run(output, cmd): full_cmd = ffmpeg.compile(output, cmd=cmd) # print(' '.join([f'"{x}"' for x in full_cmd])) filter_str = None for i in range(0, len(full_cmd)): x = full_cmd[i] if x == '-filter_complex': full_cmd[i] = '-filter_complex_script' filter_str = full_cmd[i + 1] full_cmd[i + 1] = 'filter.txt' with open('filter.txt', 'w', encoding='utf8') as f: f.write(filter_str) # print(' '.join([f'"{x}"' for x in full_cmd])) import subprocess args = full_cmd process = subprocess.Popen(args) out, err = process.communicate(input) retcode = process.poll() if retcode: raise Error('ffmpeg', out, err)
"b:v": "800k", "ac": 1, # Mono # "b:a": "128k", "acodec": "aac", # copy } preFileStream = ffmpeg.input(PRE_FILE, **preFile_input_args) inputStream = ffmpeg.input(INPUT_FILE_PATH, **input_args) a1 = preFileStream.audio a2 = inputStream.audio inputStream = ffmpeg.filter(inputStream, 'scale', size='1920x1080', force_original_aspect_ratio='decrease') inputStream = ffmpeg.filter(inputStream, 'pad', '1920', '1080', '(ow-iw)/2', '(oh-ih)/2') inputStream = ffmpeg.overlay(inputStream, OVERLAY_FILE) inputStream = ffmpeg.filter(inputStream, "fade", type='in', start_time=0, duration=1) # inputStream = ffmpeg.filter(inputStream, "fade", type='out', duration=1) stream = ffmpeg.concat(preFileStream, a1, inputStream, a2, v=1, a=1) OUTPUT_PATH = "../output/" + "[" + Utils.sToTimeFormat(TRIM_START, "%H:%M:%S.%f") + "-" + Utils.sToTimeFormat(TRIM_END, "%H:%M:%S.%f") + "]" + INPUT_FILE_NAME if not os.path.exists("../output/"): os.makedirs("../output/") # if os.path.exists(OUTPUT_PATH): # os.remove(OUTPUT_PATH) stream = ffmpeg.output(stream, OUTPUT_PATH, **output_args) stream = ffmpeg.overwrite_output(stream) ffmpeg.run(stream) print(ffmpeg.compile(stream))
def add_audio(in_file=None, out_file=None, combined_file=None, VERBOSE=False, DEBUG=False): """ Combines audio data from source video file and annotated video into one file. :param in_file: str path to original video source file :param out_file: str path to annotated video output file. :param combined_file: str path file to new write :param VERBOSE: verbose flag TODO: an async progress meter would be nice. """ if VERBOSE: print("\nProcessing audio...") analysis_start_time = time.time() # log_level = 'quiet' log_level = 'error' # log_level = 'info' # log_level = 'verbose' # log_level = 'debug' video_source_file = ffmpeg.input(out_file) audio_source_file = ffmpeg.input(in_file) video_component = video_source_file.video audio_component = audio_source_file.audio # Using a constant rate factor of 18 gives results almost indistinguishable # from the original opencv video. It also halves the file size. output = ( ffmpeg.concat(video_component, audio_component, v=1, a=1).output( combined_file, pix_fmt='yuv420p', movflags='faststart', hls_time=10, hls_list_size=0, crf=18, format='mov', shortest=None, ).global_args('-loglevel', log_level).overwrite_output( ) # I'm confusing it, and it asks to overwrite otherwise?? ) if DEBUG: command_line = ' '.join(ffmpeg.compile( output)) # Just for debugging to see what it's sending. print("ffmpeg audio merge command line: {}".format(command_line)) output.run() analysis_end_time = time.time() if VERBOSE: print("Processed audio in {:2.1f} seconds.\n".format( analysis_end_time - analysis_start_time)) if os.path.exists(combined_file): try: os.remove(out_file) except Exception as error: sys.exit( "\n\nOops. Cannot remove intermediate video file!\n{}\n{}\n". format(out_file, error))
import ffmpeg from pathlib import Path import json file = Path(r"C:\Users\Jimmy\PycharmProjects\ffmpeg\videos\big_buck_bunny_720p_1mb.mp4") input_file = str(file) probe = ffmpeg.probe(filename=input_file) #print(json.dumps(probe, indent=2)) # Convert input file to stream object input_stream = ffmpeg.input(filename=input_file) # Produces the output stream object, but does not run it yet. # out_put = ffmpeg.output(input_stream, "out_put_test.mp4", format="mp4", vcodec="libx264", acodec=[""]) out_put = ffmpeg.output(input_stream["0"], input_stream["1"], input_stream["1"], "out_put_test.mkv", **{"c:v": "libx264"}, **{"c:a:0": "copy"}, **{"c:a:1": "aac"}, **{"b:a:1": "128k"}, ac=2) # Adds -y out_put = ffmpeg.overwrite_output(out_put) # Set some global args, note args go to the whole command line on a stream. # We could use the progress parameter here and get them web sockets going out_put = out_put.global_args('-loglevel', 'info', "-strict", "-2") print(json.dumps(ffmpeg.compile(out_put), indent=2)) ffmpeg.run(out_put)
def ffmpeg_out(self, current_layer: int, *args, override_nb_frames=False, **kwargs): # NOQA """output with ffmpeg This takes in all normal ffmpeg output args. This is complex because we have to deal with default args and multiple output styles :param current_layer: the current index of the operation :param total_layers: the number of total layers to offset progress """ total_layers = kwargs.pop('total_layers', self.total_layers) self.logger.debug("ffmpeg_out - {}/{}".format(current_layer, total_layers)) if self.ffmpeg_path != 'ffmpeg': self.logger.debug("direct ffmpeg") assert (os.path.isfile(self.ffmpeg_path)), 'invalid ffmpeg path' kwargs['vcodec'] = kwargs.get('vcodec', self.vcodec) kwargs['threads'] = kwargs.get('threads', self.thread_alloc) if self.current_codec != kwargs['vcodec']: self.logger.debug("vcodec") if self.bit_rate is not None: kwargs['b'] = self.bit_rate kwargs['maxrate'] = self.bit_rate kwargs['bufsize'] = 2 * self.bit_rate kwargs['profile'] = 'high' kwargs['preset'] = 'slow' self.logger.debug("ffmpeg args - \n" + json.dumps(kwargs, indent=2)) ffout = ffmpeg.output(*args, **kwargs).global_args( '-loglevel', self.verbosity, '-hide_banner').overwrite_output() if self.pipe_ffmpeg: ffmpeg.run(ffout, cmd=self.ffmpeg_path, overwrite_output=True) else: try: args = ffmpeg.compile(ffout, cmd=self.ffmpeg_path, overwrite_output=True) process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) for line in process.stdout: if 'frame' in line and 'speed' in line: groups = re.search( r'frame=\s*([0-9]*).*speed=\s*([0-9.]*)', line) # noqa W605 frame_percent = int(groups.group(1)) / ( self.total_time * self.frame_rate) if not override_nb_frames and self.nb_frames is not None and self.nb_frames > 0: frame_percent = int( groups.group(1)) / self.nb_frames float_speed = 0.0 try: float_speed = float(groups.group(2)) except ValueError: pass real_percent = frame_percent / total_layers + current_layer / total_layers if self.readable: sys.stdout.write("\033[K") # clear previous line self.logger.info( '\r({}/{}) action progress is {:.3%} at a speed of {:}x' .format(current_layer, total_layers, frame_percent, float_speed)) else: self.logger.info('{:.3%}|{:.3f}x'.format( real_percent, float_speed)) self.logger.handlers[1].flush() except Exception as e: self.logger.exception(e) raise
def __init__( self, input_path: pathlib.Path, frame_rate: float, output_path: pathlib.Path, output_width: int, output_height: int, total_frames: int, processed_frames: ListProxy, processed: Synchronized, pause: Synchronized, copy_audio: bool = True, copy_subtitle: bool = True, copy_data: bool = False, copy_attachments: bool = False, ) -> None: threading.Thread.__init__(self) self.running = False self.input_path = input_path self.output_path = output_path self.total_frames = total_frames self.processed_frames = processed_frames self.processed = processed self.pause = pause # stores exceptions if the thread exits with errors self.exception = None # create FFmpeg input for the original input video self.original = ffmpeg.input(input_path) # define frames as input frames = ffmpeg.input( "pipe:0", format="rawvideo", pix_fmt="rgb24", vsync="cfr", s=f"{output_width}x{output_height}", r=frame_rate, ) # copy additional streams from original file # https://ffmpeg.org/ffmpeg.html#Stream-specifiers-1 additional_streams = [ # self.original["1:v?"], self.original["a?"] if copy_audio is True else None, self.original["s?"] if copy_subtitle is True else None, self.original["d?"] if copy_data is True else None, self.original["t?"] if copy_attachments is True else None, ] # run FFmpeg and produce final output self.encoder = subprocess.Popen( ffmpeg.compile( ffmpeg.output( frames, *[s for s in additional_streams if s is not None], str(self.output_path), vcodec="libx264", vsync="cfr", pix_fmt="yuv420p", crf=17, preset="veryslow", # acodec="libfdk_aac", # cutoff=20000, r=frame_rate, map_metadata=1, metadata="comment=Processed with Video2X", ) .global_args("-hide_banner") .global_args("-nostats") .global_args( "-loglevel", LOGURU_FFMPEG_LOGLEVELS.get( os.environ.get("LOGURU_LEVEL", "INFO").lower() ), ), overwrite_output=True, ), env={"AV_LOG_FORCE_COLOR": "TRUE"}, stdin=subprocess.PIPE, stderr=subprocess.PIPE, ) # start the PIPE printer to start printing FFmpeg logs self.pipe_printer = PipePrinter(self.encoder.stderr) self.pipe_printer.start()
def ffmpeg_process(file_, ffmpegPath, ffprobePath, basePath): log.info("starting processing: {}".format(file_)) filename, ext = os.path.splitext(file_) outputFile = filename + "_post" + ext log.info("output path: {}".format(outputFile)) # ffprobe to get media info jProbe = ffmpeg.probe(file_, cmd=ffprobePath)["streams"] # json load vProbe = [ jProbe[indx] for indx, stream in enumerate(jProbe) if stream["codec_type"] == "video" ][0] streamInfo = { "width": int(vProbe["width"]), "height": int(vProbe["height"]), "start": float(vProbe["start_time"]), "duration": float(vProbe["duration"]), "frames": int(vProbe["nb_frames"]), "fps": int(vProbe["nb_frames"]) / float(vProbe["duration"]) } log.debug("streamInfo: {}".format(streamInfo)) # random choices CODE_DUR = 30 rCode = str(random.randint(0, 9999)).zfill(4) # zero pad to 4 places colors = [ "red", "orange", "yellow", "green", "blue", "purple", "black", "white" ] rColor = random.choice(colors) rPer = random.randrange( 5, 75) / 100 # percentage through the video to start code rStFrame = math.floor(streamInfo["frames"] * rPer) rStTime = math.floor(rStFrame * 1 / streamInfo["fps"]) log.info("Random choices: color: {}, code: {}, stTime {}".format( rColor, rCode, rStTime)) input = ffmpeg.input(file_) audioOrig = input.audio soundBite = ffmpeg.input( os.path.join(basePath, "resources", "audio", "beep.mp3")) soundBite = soundBite.filter("adelay", rStTime * 1000).filter("apad") # ms to s audio = ffmpeg.filter([audioOrig, soundBite], "amerge") video = input.video video = ffmpeg.drawtext(video, text=rCode, x=40, y=40, fontfile=os.path.join(basePath, "resources", "fonts", "OpenSans-Regular.ttf"), fontsize=80, fontcolor=rColor, box=True, boxcolor="gray", boxborderw=10, enable="".join([ "between", "(t,", str(rStTime), ",", str(rStTime + CODE_DUR), ")" ])) video = ffmpeg.drawtext(video, text="LegalTechnicality.com", x=40, y="h-th-40", fontfile=os.path.join(basePath, "resources", "fonts", "OpenSans-Regular.ttf"), fontsize=60, alpha=0.75, fontcolor="white") output = ffmpeg.output(video, audio, outputFile) # **{"ac": 1} fullCmd = ffmpeg.compile(output, cmd=ffmpegPath, overwrite_output=True) log.debug("ffmpeg command to be called: {}".format(fullCmd)) try: stdout, stderr = ffmpeg.run(output, cmd=ffmpegPath, quiet=False, capture_stdout=False, capture_stderr=False, overwrite_output=True) # strings are falsy; False if empty if stdout: log.debug("ffmpeg output: {}".format(stdout.decode("utf-8"))) if stderr: # actually a false error -- just the ffmpeg output log.debug("ffmpeg output: {}".format(stderr.decode("utf-8"))) log.info("sucessfully processed file") except ffmpeg.Error as e: log.exception("failed to process file") log.exception(e.stderr)
def ffmpegConcat(orgs): import ffmpeg import random import json from datetime import datetime max_nb_layers = 20 hard_limit = 30000 dur_limit = 600 tags_not = { "Couple", "Electric", 'Threesome', 'Public transport', 'Male stranger' } tags_must = {"Alone"} tags_may = {"Powerful", "Finger"} contactList = [] amixList = [] length = 0 selected = dict() selectedOrgs = [] for _ in range(hard_limit): id = random.randint(0, len(orgs) - 1) # id = random.randint(0, 5) org = orgs[id] tags = set(org.get("tags")) if tags_not & tags: # print(tags_not & tags) continue if tags_must - tags: # print(tags_must - tags) continue if not tags_may & tags: # print(tags_may & tags) continue stream = selected.get(id, None) if not stream: stream = ffmpeg.input(str(getAudioFilePath(org))) stream = ffmpeg.filter(stream, 'loudnorm') streams = ffmpeg.filter_multi_output(stream, 'asplit') selected[id] = streams.stream(0) stream = streams.stream(1) contactList.append(stream) audioLength = float(org.get("duration", "30")) length += audioLength if length >= dur_limit: contactStream = ffmpeg.concat(*contactList, v=0, a=1) amixList.append(contactStream) contactList = [] length = 0 selectedOrgs.append(org) if len(amixList) >= max_nb_layers: break else: if contactList: contactStream = ffmpeg.concat(*contactList, v=0, a=1) amixList.append(contactStream) fullStream = ffmpeg.filter(amixList, 'amix', inputs=len(amixList), duration='longest') fullStream = ffmpeg.filter(fullStream, 'atrim', duration=dur_limit) outputPath = basePath.joinpath("mixed").joinpath("mix-{:d}.m4a".format( int(datetime.now().timestamp()))) metadata = { 'length': dur_limit, 'nb': len(selectedOrgs), 'nb_dist': len(selected), 'mix_layers': len(amixList), 'tags_not': list(tags_not), 'tags_must': list(tags_must), 'tags_may': list(tags_may), 'tags': list(getTags(selectedOrgs)) } metadataJson = json.dumps(metadata, ensure_ascii=False, sort_keys=False) outputParam = { 'metadata': 'comment="{}"'.format(metadataJson), 'acodec': 'aac' } fullStream = fullStream.output(str(outputPath), **outputParam) cmd = ffmpeg.compile(fullStream) print(cmd) ffmpeg.view(fullStream, filename=str(outputPath) + ".png") ffmpeg.run(fullStream, overwrite_output=True) printTags(selectedOrgs) pass
def get_r128_loudness(audio_filepaths, *, calc_peak=True, enable_ffmpeg_threading=True, ffmpeg_path=None, start_evt=None): """ Get R128 loudness loudness level and sample peak. """ if start_evt is not None: start_evt.wait() logger().info("Analyzing loudness of file%s %s..." % ("s" if (len(audio_filepaths) > 1) else "", ", ".join( repr(audio_filepath) for audio_filepath in audio_filepaths))) # build command line ffmpeg_inputs = [] if not enable_ffmpeg_threading: additional_ffmpeg_args = {"threads": 1} # single decoding thread else: additional_ffmpeg_args = dict() for audio_filepath in audio_filepaths: ffmpeg_input = ffmpeg.input(audio_filepath, **additional_ffmpeg_args).audio ffmpeg_inputs.append(ffmpeg_input) output_streams = [] ffmpeg_r128_streams = [] for ffmpeg_input in ffmpeg_inputs: if calc_peak: split_streams = ffmpeg_input.filter_multi_output("asplit", outputs=2) ffmpeg_rg_stream, ffmpeg_r128_stream = split_streams[ 0], split_streams[1] ffmpeg_rg_stream = ffmpeg_rg_stream.filter( "aformat", sample_fmts="s16", channel_layouts="stereo") ffmpeg_rg_stream = ffmpeg_rg_stream.filter("replaygain") output_streams.append(ffmpeg_rg_stream) else: ffmpeg_r128_stream = ffmpeg_input ffmpeg_r128_stream = ffmpeg_r128_stream.filter( "aformat", sample_fmts="s16", sample_rates="48000", channel_layouts="stereo") ffmpeg_r128_stream = ffmpeg_r128_stream.filter( "afifo") # needed for FFmpeg < 4.1 ffmpeg_r128_streams.append(ffmpeg_r128_stream) if len(audio_filepaths) > 1: ffmpeg_r128_merged = ffmpeg.concat(*ffmpeg_r128_streams, n=len(ffmpeg_r128_streams), v=0, a=1) else: ffmpeg_r128_merged = ffmpeg_r128_streams[0] ffmpeg_r128_merged = ffmpeg_r128_merged.filter("ebur128", framelog="verbose") output_streams.append(ffmpeg_r128_merged) if (get_ffmpeg_lib_versions()["libavfilter"] >= 0x06526400) and (not enable_ffmpeg_threading): additional_ffmpeg_args = { "filter_complex_threads": 1 } # single filter thread else: additional_ffmpeg_args = dict() cmd = ffmpeg.compile(ffmpeg.output(*output_streams, os.devnull, **additional_ffmpeg_args, f="null").global_args( "-hide_banner", "-nostats"), cmd=ffmpeg_path or "ffmpeg") # run logger().debug(cmd_to_string(cmd)) output = subprocess.run(cmd, check=True, stdin=subprocess.DEVNULL, stderr=subprocess.PIPE).stderr output = output.decode("utf-8", errors="replace").splitlines() if calc_peak: # parse replaygain filter output sample_peaks = [] for line in reversed(output): if line.startswith("[Parsed_replaygain_") and ("] track_peak = " in line): sample_peaks.append(float(line.rsplit("=", 1)[1])) if len(sample_peaks) == len(audio_filepaths): break sample_peak = max(sample_peaks) else: sample_peak = None # parse r128 filter output for i in reversed(range(len(output))): line = output[i] if line.startswith("[Parsed_ebur128_") and line.endswith("Summary:"): break output = filter(lambda x: x and not x.startswith("[Parsed_replaygain_"), map(str.strip, output[i:])) r128_stats = dict( tuple(map(str.strip, line.split(":", 1))) for line in output if not line.endswith(":")) r128_stats = {k: float(v.split(" ", 1)[0]) for k, v in r128_stats.items()} return r128_stats["I"], sample_peak
def trim(request): if request.path == '/favicon.ico': abort(404) # Ignore browser requests for favicon request.path = request.path.strip('/').split('/') _source_params = request.path[1] _source_file = request.path[2] _time = int(time.time()) _params = dict() _params['operation'] = request.path[0] _params['source_file'] = request.path[2] # Extract parameters from URL field for param in _source_params.split(','): try: _split = param.split(':') _key = _split[0] _value = _split[1] if len(_split) > 1 else None if _key in _allowed_params: _params[_key] = _value else: abort(400) except: abort(400) ## Generate hash _hash = generate_hash("{}:{}".format(dumps(_params, sort_keys=True), _source_file)) ## Check for existing entry in datastore #_entity = read_in_datastore(_hash) #if _entity: # # todo: 301/302/307 redirect # print('Already found.') ## Generate signed URL _params['signed_url'] = request_signed_url(SOURCE_BUCKET_NAME, _source_file) if not _params['signed_url']: return abort(404) ## Create args for ffmpeg.input _input_kwargs = ffmpeg_input_args(**_params) _output_kwargs = ffmpeg_output_args(**_params) job = ffmpeg.input(_params['signed_url'], **_input_kwargs) job = ffmpeg.output( job, '{}/{}_{}.{}'.format( LOCAL_DESTINATION_PATH, _time, _hash, 'mp4' if _params['operation'] == 'trim' else 'jpg'), **_output_kwargs) try: out, err = ffmpeg.run(job, cmd=FFMPEG_BINARY_PATH, capture_stderr=True, capture_stdout=True) #logging.info(out.decode('utf-8').replace('\n', ' ')) #logging.error(err.decode('utf-8').replace('\n', ' ')) except ffmpeg.Error as e: abort(500) logging.error(e.stderr.decode().replace('\n', ' ')) logging.error(e.stderr) logging.error(e) _info = { 'params': _source_params, 'js_params': _params, 'ffmpeg_input_args': _input_kwargs, 'ffmpeg_output_args': _output_kwargs, 'ffmpeg_command': " ".join(ffmpeg.compile(job)), 'source_file': _source_file, 'hash': _hash } logging.info(_info) # todo: wrap in try except response = make_response( send_file('{}/{}_{}.{}'.format( LOCAL_DESTINATION_PATH, _time, _hash, 'mp4' if _params['operation'] == 'trim' else 'jpg'))) response.headers['X-Query-Hash'] = _hash insert_to_datastore( _hash, '{}/{}_{}.{}'.format( LOCAL_DESTINATION_PATH, _time, _hash, 'mp4' if _params['operation'] == 'trim' else 'jpg')) return response
def get_r128_loudness( audio_filepaths: Sequence[str], *, calc_peak: bool = True, enable_ffmpeg_threading: bool = True, ffmpeg_path: Optional[str] = None, start_evt: Optional[threading.Event] = None, ) -> Tuple[float, Optional[float]]: """Get R128 loudness loudness level and sample peak.""" if start_evt is not None: start_evt.wait() logger().info("Analyzing loudness of file%s %s..." % ( "s" if (len(audio_filepaths) > 1) else "", ", ".join(repr(audio_filepath) for audio_filepath in audio_filepaths), )) # build command line ffmpeg_inputs = [] if not enable_ffmpeg_threading: additional_ffmpeg_args = {"threads": 1} # single decoding thread else: additional_ffmpeg_args = dict() for audio_filepath in audio_filepaths: ffmpeg_input = ffmpeg.input(audio_filepath, **additional_ffmpeg_args).audio ffmpeg_inputs.append(ffmpeg_input) output_streams = [] ffmpeg_r128_streams = [] for ffmpeg_input in ffmpeg_inputs: if calc_peak: split_streams = ffmpeg_input.filter_multi_output("asplit", outputs=2) ffmpeg_rg_stream, ffmpeg_r128_stream = split_streams[ 0], split_streams[1] ffmpeg_rg_stream = ffmpeg_rg_stream.filter( "aformat", sample_fmts="s16", channel_layouts="stereo") ffmpeg_rg_stream = ffmpeg_rg_stream.filter("replaygain") output_streams.append(ffmpeg_rg_stream) else: ffmpeg_r128_stream = ffmpeg_input ffmpeg_r128_stream = ffmpeg_r128_stream.filter( "aformat", sample_fmts="s16", sample_rates="48000", channel_layouts="stereo") ffmpeg_r128_stream = ffmpeg_r128_stream.filter( "afifo") # needed for FFmpeg < 4.1 ffmpeg_r128_streams.append(ffmpeg_r128_stream) if len(audio_filepaths) > 1: ffmpeg_r128_merged = ffmpeg.concat(*ffmpeg_r128_streams, n=len(ffmpeg_r128_streams), v=0, a=1) else: ffmpeg_r128_merged = ffmpeg_r128_streams[0] ffmpeg_r128_merged = ffmpeg_r128_merged.filter("ebur128", framelog="verbose") output_streams.append(ffmpeg_r128_merged) if (get_ffmpeg_lib_versions()["libavfilter"] >= 0x06526400) and (not enable_ffmpeg_threading): additional_ffmpeg_args = { "filter_complex_threads": 1 } # single filter thread else: additional_ffmpeg_args = dict() cmd = ffmpeg.compile( ffmpeg.output(*output_streams, os.devnull, **additional_ffmpeg_args, f="null").global_args("-hide_banner", "-nostats"), cmd=ffmpeg_path or "ffmpeg", ) # workaround https://github.com/kkroening/ffmpeg-python/issues/161 opt_index = cmd.index("-filter_complex") with tempfile.TemporaryDirectory(prefix="r128gain_") as tmp_dir: tmp_script_filepath = os.path.join(tmp_dir, "ffmpeg_filters") with open(tmp_script_filepath, "wt") as f: f.write(cmd[opt_index + 1]) cmd[opt_index] = "-filter_complex_script" cmd[opt_index + 1] = tmp_script_filepath # run logger().debug(cmd_to_string(cmd)) output = subprocess.run(cmd, check=True, stdin=subprocess.DEVNULL, stderr=subprocess.PIPE).stderr output_lines = output.decode("utf-8", errors="replace").splitlines() if calc_peak: # parse replaygain filter output sample_peaks = [] for line in reversed(output_lines): if line.startswith("[Parsed_replaygain_") and ("] track_peak = " in line): sample_peaks.append(float(line.rsplit("=", 1)[1])) if len(sample_peaks) == len(audio_filepaths): break sample_peak: Optional[float] = max(sample_peaks) else: sample_peak = None # parse r128 filter output for i in reversed(range(len(output_lines))): line = output_lines[i] if line.startswith("[Parsed_ebur128_") and line.endswith("Summary:"): break output_lines_r128 = filter( lambda x: x and not x.startswith("[Parsed_replaygain_"), map(str.strip, output_lines[i:])) r128_stats_raw: Dict[str, str] = dict( tuple(map(str.strip, line.split(":", 1))) # type: ignore for line in output_lines_r128 if not line.endswith(":")) r128_stats: Dict[str, float] = { k: float(v.split(" ", 1)[0]) for k, v in r128_stats_raw.items() } return r128_stats["I"], sample_peak
def start_recording(self): """ Start recording the stream defined on self.stream_uri :return: boolean if the recording started sucessfully """ # if we are dealing with a USB camera or RTSP stream we # treat them differently, for example remove the socket TCP I/O timeout (stimeout) parsed_uri = urlparse(self.stream_uri) if parsed_uri.scheme == "rtsp": stream_input = ffmpeg.input( self.stream_uri, nostdin=None, use_wallclock_as_timestamps=1, stimeout=self._def_stimeout, fflags="+genpts", rtsp_transport='tcp') # stimeout in microsecondss else: stream_input = ffmpeg.input(self.stream_uri) # store the files in segments to prevent corruption segment_fpath = self.prepare_filepath_for_segment( self.output_filepath, self._def_segment_fname_size) # ffmpeg -use_wallclock_as_timestamps 1 -fflags +genpts -rtsp_transport tcp -stimeout 3000000 # -i rtsp://admin:[email protected]:554/stream0 -f segment -b:v 900k -an -flags +global_header -map 0 # -map_metadata -1 -movflags +frag_keyframe+separate_moof+omit_tfhd_offset+empty_moov -reset_timestamps 1 # -segment_format matroska # -segment_list /tmp/stored_streams/2.stream_192_168_3_22_554.2019-08-18.14.02.53_video_list.txt # -segment_list_type ffconcat -segment_time 20 -strict 2 -vcodec copy -use_wallclock_as_timestamps 1 # -fflags +genpts /tmp/stored_streams/2.stream_192_168_3_22_554.2019-08-18.14.02.53-%03d.mkv -y output_arguments = [ ("strict", 2), ("f", "segment"), ("map", 0), ("segment_time", self._def_segment_time), ("segment_format", self._def_segment_format), ("segment_list", self.segment_filelist), ("segment_list_type", "ffconcat"), ("vcodec", self._def_vcodec), ("video_bitrate", self._def_bitrate), ("flags", "+global_header"), ("reset_timestamps", 1), ("map_metadata", -1), ("use_wallclock_as_timestamps", 1), ("fflags", "+genpts"), ("movflags", "+frag_keyframe+separate_moof+omit_tfhd_offset+empty_moov"), ] # if there is no audio codec then use the an # flag to remove audio from the recorded stream if self._def_acodec != "": output_arguments.append(("acodec", self._def_acodec)) else: output_arguments.append(("an", None)) ffmpeg_output_streams = [ ffmpeg.output(stream_input, segment_fpath, **OrderedDict(output_arguments)) ] output_streams = ffmpeg.merge_outputs(*ffmpeg_output_streams) output_streams = ffmpeg.overwrite_output(output_streams) debug_command = ffmpeg.compile(output_streams) print("ffmpeg command: {}".format(' '.join(debug_command))) self.start_time = time.time() self.proc = ( # ALERT: https://stackoverflow.com/questions/16523746/ffmpeg-hangs-when-run-in-background # clean the stderr / stdout regularly to prevent this process for freezing ffmpeg.run_async(output_streams, pipe_stdout=True, pipe_stderr=True, overwrite_output=True)) start_timeout = time.time() is_reached_size = False while self.proc.poll() is None and time.time( ) - start_timeout < self._def_timeout_secs and not is_reached_size: try: if os.path.getsize(self.get_last_segment_output_filepath() ) > self._def_min_video_size_bytes: is_reached_size = True except OSError as e: pass time.sleep(0.1) if self.proc.poll() is None: return True else: self.start_time = 0 self.out, self.err = self.proc.communicate() return False
base_rgb = np.array(command["color-change"]["from-color"]) to_bgr = np.flip(np.array(command["color-change"]["to-color"])) for y in range(lu[1], rd[1] + 1): for x in range(lu[0], rd[0] + 1): if (frame[y, x] == base_rgb).all(): # 指定の色と一致してたら色を差し替える frame[y, x] = to_bgr # rgbじゃなくてbgrで格納されてるので if "alpha-blend" in command: alpha = command["alpha-blend"]["alpha"] to_bgr = np.flip(np.array(command["alpha-blend"]["to-color"])) frame = frame * alpha + to_bgr * (1 - alpha) frame = frame.astype("uint8") """ frame = cv2.cvtColor(frame, cv2.COLOR_RGBA2RGB) out.write(frame) out.release() output_movie_file = str(tmp_dir_path / setting.output_file) movie_input = ffmpeg.input(temp_file) audio_input = ffmpeg.input(setting.audio_file) output = ffmpeg.output(movie_input, audio_input, output_movie_file) print(ffmpeg.compile(output)) ffmpeg.run(output) yyyymmddhhmmss = datetime.datetime.now().strftime("%Y%m%d%H%M%S") result_dir = title_dir / f"archive/{yyyymmddhhmmss}/" shutil.copytree(tmp_dir_path, result_dir) shutil.copytree(materials_dir, result_dir / "materials")
probe = ffmpeg.probe(input_file) # print(json.dumps(probe, indent=2)) # for stream in probe['streams']: # print(stream) # convert the input file to a stream object input_stream = ffmpeg.input(filename=input_file) # Produces the output stream object, but doesn't run it yet. out_put = ffmpeg.output(input_stream["0"], input_stream["1"], input_stream["1"], "done/out_put_test.mkv", **{"c:v": "libx264"}, **{"c:a:0": "copy"}, **{"c:a:1": "aac"}, **{"b:a:1": "128k"}, ac=2) # Adds -y to the ffmpeg command out_put = ffmpeg.overwrite_output(out_put) # Set some global args, note args go to the whole command line on a stream. # We could use the progress parameter here and get them web sockets going out_put = out_put.global_args('-loglevel', 'info', "-strict", "-2") print(json.dumps(ffmpeg.compile(out_put), indent=2)) print(" ".join(ffmpeg.compile(out_put))) # ffmpeg.run(out_put)