change that ffmpeg executes in a seperat thread
ffmpegwrapper executes ffmpeg now in a seperat thread. This changes was made to add a plattform independent method with that you can read the output from ffmpegs output. Previous solution was only unix compatible.
This commit is contained in:
parent
d807d636fb
commit
113a0a1f00
|
|
@ -9,12 +9,14 @@
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
import os
|
from subprocess import Popen, PIPE, STDOUT
|
||||||
|
|
||||||
from fcntl import fcntl, F_SETFL, F_GETFL
|
|
||||||
from select import select
|
|
||||||
from subprocess import Popen, PIPE
|
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
from threading import Thread
|
||||||
|
|
||||||
|
try:
|
||||||
|
from Queue import Queue, Empty
|
||||||
|
except ImportError:
|
||||||
|
from queue import Queue, Empty
|
||||||
|
|
||||||
from .options import OptionStore, Option
|
from .options import OptionStore, Option
|
||||||
|
|
||||||
|
|
@ -54,6 +56,55 @@ class Output(OptionStore):
|
||||||
return chain(OptionStore.__iter__(self), [self.file])
|
return chain(OptionStore.__iter__(self), [self.file])
|
||||||
|
|
||||||
|
|
||||||
|
class FFmpegProcess(object):
|
||||||
|
|
||||||
|
def __init__(self, command):
|
||||||
|
self.command = list(command)
|
||||||
|
self.queue = Queue()
|
||||||
|
self.process = None
|
||||||
|
|
||||||
|
def _queue_output(self, out, queue):
|
||||||
|
line = ''
|
||||||
|
while self.process.poll() is None:
|
||||||
|
chunk = out.read(1).decode('utf-8')
|
||||||
|
if chunk == '':
|
||||||
|
continue
|
||||||
|
line += chunk
|
||||||
|
if chunk in ('\n', '\r'):
|
||||||
|
queue.put(line)
|
||||||
|
line = ''
|
||||||
|
out.close()
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.process = Popen(self.command, bufsize=1,
|
||||||
|
stdin=PIPE, stdout=PIPE, stderr=STDOUT)
|
||||||
|
thread = Thread(target=self._queue_output,
|
||||||
|
args=(self.process.stdout, self.queue))
|
||||||
|
thread.deamon = True
|
||||||
|
thread.start()
|
||||||
|
return self
|
||||||
|
|
||||||
|
def readlines(self, keepends=False):
|
||||||
|
while self.process.poll() is None:
|
||||||
|
try:
|
||||||
|
line = self.queue.get(timeout=0.1)
|
||||||
|
if keepends:
|
||||||
|
yield line
|
||||||
|
else:
|
||||||
|
yield line.rstrip('\r\n')
|
||||||
|
except Empty:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
if self.process:
|
||||||
|
return getattr(self.process, name)
|
||||||
|
raise AttributeError
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return self.readlines()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class FFmpeg(OptionStore):
|
class FFmpeg(OptionStore):
|
||||||
"""This class represent the FFmpeg command.
|
"""This class represent the FFmpeg command.
|
||||||
|
|
||||||
|
|
@ -68,37 +119,23 @@ class FFmpeg(OptionStore):
|
||||||
|
|
||||||
def __init__(self, binary="ffmpeg", *args):
|
def __init__(self, binary="ffmpeg", *args):
|
||||||
self.binary = binary
|
self.binary = binary
|
||||||
|
self.process = None
|
||||||
OptionStore.__init__(self, *args)
|
OptionStore.__init__(self, *args)
|
||||||
|
|
||||||
def run(self):
|
|
||||||
"""Execute through subprocess the :attr:`binary` with all Options
|
|
||||||
that are appended in this Store as arguments.
|
|
||||||
"""
|
|
||||||
self.pipe = Popen(list(self), stderr=PIPE)
|
|
||||||
fcntl(self.pipe.stderr.fileno(), F_SETFL,
|
|
||||||
fcntl(self.pipe.stderr.fileno(), F_GETFL) | os.O_NONBLOCK)
|
|
||||||
return self
|
|
||||||
|
|
||||||
def wait_for_data(self):
|
|
||||||
""" As long as the :attr:`binary` is executed through :meth:`run`
|
|
||||||
we process a syscall to check if ffmpeg has written to stderr. If
|
|
||||||
ffmpeg has written to stderr we return true. If the binary isn't
|
|
||||||
running any more we return False.
|
|
||||||
|
|
||||||
This is a helper class to deal with the unbuffered output from ffmpeg
|
|
||||||
"""
|
|
||||||
while self.pipe.poll() is None:
|
|
||||||
ready = select([self.pipe.stderr.fileno()], [], [])[0]
|
|
||||||
if ready:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def poll(self):
|
|
||||||
"""Check if :attr:`binary` is running"""
|
|
||||||
return self.pipe.poll()
|
|
||||||
|
|
||||||
def add_option(self, key, value):
|
def add_option(self, key, value):
|
||||||
self._list.insert(0, Option({key: value}))
|
self._list.insert(0, Option({key: value}))
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
return FFmpegProcess(self).run()
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
self.process = self.run()
|
||||||
|
return self.process
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_value, traceback):
|
||||||
|
if self.process.poll() is None:
|
||||||
|
self.process.terminate()
|
||||||
|
self.process = None
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return chain([self.binary], OptionStore.__iter__(self))
|
return chain([self.binary], OptionStore.__iter__(self))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue