finished aux.Command, working volume detection pre-pass

This commit is contained in:
Overwatch 2014-08-15 16:57:04 +02:00
parent 5c3df8f2fc
commit b165bb628d
2 changed files with 82 additions and 56 deletions

94
aux.py
View file

@ -5,28 +5,46 @@ import queue
import subprocess import subprocess
import sys import sys
import threading import threading
import time
import over # FIXME
# -------------------------------------------------- # --------------------------------------------------
def capture_output(stream, fifo): def capture_output(stream, fifo):
while True: while True:
chunk = stream.read1(100) chunk = stream.read1(128)
if chunk: if chunk:
fifo.put(chunk) fifo.put(chunk)
else: else:
fifo.put(None) # indicates a process has terminated
break break
stream.close() stream.close()
class Command: 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): def __init__(self, sequence):
self.__dict__["sequence"] = list(sequence) self.__dict__["sequence"] = list(sequence)
self.__dict__["thread"] = None self.__dict__["thread"] = None
self.__dict__["fifo"] = None self.__dict__["fifo"] = None
self.__dict__["terminated"] = False
def __setattr__(self, name, value): def __setattr__(self, name, value):
found = False found = False
@ -53,11 +71,10 @@ class Command:
return out return out
def run(self, async=False, stderr=False): def run(self, stderr=False):
""" """
Executes the command in the current environment. Executes the command in the current environment.
async return immediatelly, use poll_output to get output
stderr capture stderr instead of stdout stderr capture stderr instead of stdout
""" """
@ -67,54 +84,53 @@ class Command:
target=capture_output, target=capture_output,
args=(self.process.stderr if stderr else self.process.stdout, self.fifo) 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.daemon = True # thread dies with the program
self.thread.start() self.thread.start()
if not async: def get_output(self, blocking=False):
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):
""" """
Returns the output of a currently running process and clears the buffer. 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():
return None
else:
buffer = [] buffer = []
if self.terminated:
return None
if blocking:
buffer.append(self.fifo.get())
while not self.fifo.empty(): while not self.fifo.empty():
buffer.append(self.fifo.get_nowait()) buffer.append(self.fifo.get_nowait()) # FIXME nowait needed?
if None in buffer:
self.__dict__["terminated"] = True
if len(buffer) == 1:
return None
else:
assert(buffer[-1] is None)
del buffer[-1]
return b"".join(buffer) return b"".join(buffer)
@property def get_all_output(self):
def running(self): buffer = []
return self.process.poll() is None
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
# -------------------------------------------------- # --------------------------------------------------

View file

@ -52,7 +52,8 @@ if __name__ == "__main__":
identify_cmd = Command(command.identify) identify_cmd = Command(command.identify)
identify_cmd.INFILE = tgt 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")) identify_dict = json.loads(identify_raw.decode("utf-8"))
info = over.core.types.ndict() info = over.core.types.ndict()
@ -95,29 +96,38 @@ if __name__ == "__main__":
norm_pre_cmd = Command(command.normalize_prepass) norm_pre_cmd = Command(command.normalize_prepass)
norm_pre_cmd.INFILE = tgt 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") pb = over.core.textui.ProgressBar(50, int(info.video_fps.value * info.duration), "frames")
while True: output_buffer = []
probably_dead = not norm_pre_cmd.running
while True:
time.sleep(.25) time.sleep(.25)
o = norm_pre_cmd.poll_output() out = norm_pre_cmd.get_output()
if o: if out:
if b"frame=" in o: output_buffer.append(out)
frame_id = re.findall(b"frame= *(\d+) ", o)[0]
if b"frame=" in out:
frame_id = re.findall(b"frame= *(\d+) ", out)[0]
pb.update(int(frame_id)) pb.update(int(frame_id))
elif b"mean_volume: " in o: elif out is None:
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)
elif not norm_pre_cmd.running and probably_dead:
break 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)) _print("detected volume %.2f dB, correction %.2f dB" %(info.mean_volume, info.volume_correction))