From b165bb628da1d950fa6037643e3202eb2c9d8cf6 Mon Sep 17 00:00:00 2001 From: Overwatch Date: Fri, 15 Aug 2014 16:57:04 +0200 Subject: [PATCH] finished aux.Command, working volume detection pre-pass --- aux.py | 98 ++++++++++++++++++++++++++++++--------------------- over-video.py | 40 +++++++++++++-------- 2 files changed, 82 insertions(+), 56 deletions(-) diff --git a/aux.py b/aux.py index 4329926..2be8b60 100644 --- a/aux.py +++ b/aux.py @@ -5,28 +5,46 @@ import queue import subprocess import sys import threading -import time - -import over # FIXME # -------------------------------------------------- def capture_output(stream, fifo): while True: - chunk = stream.read1(100) + chunk = stream.read1(128) if chunk: fifo.put(chunk) else: + fifo.put(None) # indicates a process has terminated break stream.close() class Command: + """ + A shell command with argument substitution and output capture. + + >>> c = Command(["process.sh", "-x", "ARGUMENT"]) + >>> c.ARGUMENT = "file.txt" + >>> c.dump() + ['process.sh', '-x', 'file.txt'] + >>> c.run(stderr=False) # capture stdout + >>> c.get_output() + b'some output' + >>> c.get_output() + b'' # there was no output since the last call + >>> c.get_output() + b'more of it\nand some more' + >>> c.get_output() + None # indicates the process ended and there is no more output + + """ + def __init__(self, sequence): self.__dict__["sequence"] = list(sequence) self.__dict__["thread"] = None self.__dict__["fifo"] = None + self.__dict__["terminated"] = False def __setattr__(self, name, value): found = False @@ -53,11 +71,10 @@ class Command: return out - def run(self, async=False, stderr=False): + def run(self, stderr=False): """ Executes the command in the current environment. - async return immediatelly, use poll_output to get output stderr capture stderr instead of stdout """ @@ -67,54 +84,53 @@ class Command: target=capture_output, args=(self.process.stderr if stderr else self.process.stdout, self.fifo) ) + self.__dict__["terminated"] = False self.thread.daemon = True # thread dies with the program self.thread.start() - - if not async: - buffer = [] - probably_dead = False - - while True: - probably_dead = self.process.poll() is not None - - chunk = self.poll_output() - - if chunk: - buffer.append(chunk) - else: - time.sleep(0.01) - - if not chunk and self.process.poll() is not None and probably_dead: - break - - return b"".join(buffer) - - else: - return None - def poll_output(self): + def get_output(self, blocking=False): """ Returns the output of a currently running process and clears the buffer. - Returns None if no process is running. + Returns None if no process is running and no more output is available. - Blocking - always returns at least one char. + blocking block until some output is available or the process terminates """ - if self.fifo.empty(): + buffer = [] + + if self.terminated: return None - else: - buffer = [] + if blocking: + buffer.append(self.fifo.get()) + + while not self.fifo.empty(): + buffer.append(self.fifo.get_nowait()) # FIXME nowait needed? + + if None in buffer: + self.__dict__["terminated"] = True - while not self.fifo.empty(): - buffer.append(self.fifo.get_nowait()) - - return b"".join(buffer) + if len(buffer) == 1: + return None + else: + assert(buffer[-1] is None) + del buffer[-1] + + return b"".join(buffer) - @property - def running(self): - return self.process.poll() is None + def get_all_output(self): + buffer = [] + + while True: + chunk = self.get_output(blocking=True) + + if chunk is None: + break + else: + buffer.append(chunk) + + return b''.join(buffer) if buffer else None # -------------------------------------------------- diff --git a/over-video.py b/over-video.py index 2c90250..73be4b6 100755 --- a/over-video.py +++ b/over-video.py @@ -52,7 +52,8 @@ if __name__ == "__main__": identify_cmd = Command(command.identify) identify_cmd.INFILE = tgt - identify_raw = identify_cmd.run() + identify_cmd.run() + identify_raw = identify_cmd.get_all_output() identify_dict = json.loads(identify_raw.decode("utf-8")) info = over.core.types.ndict() @@ -95,29 +96,38 @@ if __name__ == "__main__": norm_pre_cmd = Command(command.normalize_prepass) norm_pre_cmd.INFILE = tgt - norm_pre_cmd.run(async=True, stderr=True) + norm_pre_cmd.run(stderr=True) pb = over.core.textui.ProgressBar(50, int(info.video_fps.value * info.duration), "frames") + output_buffer = [] + while True: - probably_dead = not norm_pre_cmd.running - time.sleep(.25) - o = norm_pre_cmd.poll_output() + out = norm_pre_cmd.get_output() - if o: - if b"frame=" in o: - frame_id = re.findall(b"frame= *(\d+) ", o)[0] - pb.update(int(frame_id)) + if out: + output_buffer.append(out) - elif b"mean_volume: " in o: - info.mean_volume = float(re.findall(b"mean_volume: (-\d+\.\d+) dB", o)[0]) - info.volume_correction = info.mean_volume - main.cfg.normalize_mean - else: - print(o) + if b"frame=" in out: + frame_id = re.findall(b"frame= *(\d+) ", out)[0] + pb.update(int(frame_id)) - elif not norm_pre_cmd.running and probably_dead: + elif out is None: break + output = b''.join(output_buffer) + + if b"mean_volume: " in output: + info.mean_volume = float(re.findall(b"mean_volume: (-\d+\.\d+) dB", output)[0]) + info.volume_correction = info.mean_volume - main.cfg.normalize_mean + else: + _print("§runexpected ffmpeg output§/, dump follows", prefix.fail, suffix=":\n") + print(output) + raise RuntimeError + + pb.blank() _print("detected volume %.2f dB, correction %.2f dB" %(info.mean_volume, info.volume_correction)) + +