From 3be99ce9f333d27e327c2f5ca8189c1a6dc5cd31 Mon Sep 17 00:00:00 2001 From: iperov Date: Mon, 9 Aug 2021 22:53:01 +0400 Subject: [PATCH] fix ffmpeg --- xlib/ffmpeg/__init__.py | 2 +- xlib/ffmpeg/ffmpeg.py | 24 +++------------ xlib/io/IOThreadLinesReader.py | 56 ++++++++++++++++++++++++++++++++++ xlib/io/__init__.py | 3 +- xlib/player/VideoFilePlayer.py | 19 ++++++------ 5 files changed, 73 insertions(+), 31 deletions(-) create mode 100644 xlib/io/IOThreadLinesReader.py diff --git a/xlib/ffmpeg/__init__.py b/xlib/ffmpeg/__init__.py index e6b614f..8cdaaf6 100644 --- a/xlib/ffmpeg/__init__.py +++ b/xlib/ffmpeg/__init__.py @@ -1 +1 @@ -from .ffmpeg import probe, run, run_play \ No newline at end of file +from .ffmpeg import probe, run \ No newline at end of file diff --git a/xlib/ffmpeg/ffmpeg.py b/xlib/ffmpeg/ffmpeg.py index 4551420..537bf84 100644 --- a/xlib/ffmpeg/ffmpeg.py +++ b/xlib/ffmpeg/ffmpeg.py @@ -1,8 +1,8 @@ import json import subprocess +from typing import Union - -def run(args, pipe_stdin=False, pipe_stdout=False, pipe_stderr=False, quiet_std_err=False): +def run(args, pipe_stdin=False, pipe_stdout=False, pipe_stderr=False, quiet_stderr=False) -> Union[subprocess.Popen,None]: """ run ffmpeg process @@ -10,11 +10,12 @@ def run(args, pipe_stdin=False, pipe_stdout=False, pipe_stderr=False, quiet_std_ otherwise None """ args = ['ffmpeg'] + args + stdin_stream = subprocess.PIPE if pipe_stdin else None stdout_stream = subprocess.PIPE if pipe_stdout else None stderr_stream = subprocess.PIPE if pipe_stderr else None - if quiet_std_err and not pipe_stderr: + if quiet_stderr and not pipe_stderr: stderr_stream = subprocess.DEVNULL try: @@ -53,20 +54,3 @@ def probe(filename): raise Exception('ffprobe', out, err) return json.loads(out.decode('utf-8')) - -def run_play(): - args = ['ffplay', - '-f', 'rawvideo', - '-pix_fmt', 'bgr24', - '-s', '256x144', - '-' - ] - stdin_stream = subprocess.PIPE - stdout_stream = None - stderr_stream = None#subprocess.DEVNULL - - try: - return subprocess.Popen(args, stdin=stdin_stream, stdout=stdout_stream, stderr=stderr_stream) - except Exception as e: - print('ffplay exception: ', e) - return None diff --git a/xlib/io/IOThreadLinesReader.py b/xlib/io/IOThreadLinesReader.py new file mode 100644 index 0000000..68e6c12 --- /dev/null +++ b/xlib/io/IOThreadLinesReader.py @@ -0,0 +1,56 @@ +import threading +import time +from io import IOBase +from typing import List +from collections import deque + +class IOThreadLinesReader: + """ + continuously reads lines from IO in background thread. + """ + + def __init__(self, io : IOBase, max_lines=None): + self._io = io + self._lock = threading.Lock() + self._lines = deque(maxlen=max_lines) + + threading.Thread(target=self._proc, daemon=True).start() + + def _proc(self): + io = self._io + lock = self._lock + lines = self._lines + + while not io.closed and io.readable(): + line = io.readline() + lock.acquire() + lines.append(line.decode('utf-8').rstrip()) + lock.release() + if len(line) == 0: + break + time.sleep(0.01) + + def get_lines(self, wait_new=True, till_eof=False) -> List[str]: + """ + """ + lock = self._lock + lines = self._lines + + result = [] + while True: + + if len(lines) != 0: + lock.acquire() + result += lines + lines.clear() + lock.release() + + if till_eof and len(result[-1]) != 0: + continue + + return result + + if not till_eof and not wait_new: + return None + time.sleep(0.001) + diff --git a/xlib/io/__init__.py b/xlib/io/__init__.py index f38276c..a4d9c97 100644 --- a/xlib/io/__init__.py +++ b/xlib/io/__init__.py @@ -1 +1,2 @@ -from .IO import FormattedMemoryViewIO, FormattedFileIO \ No newline at end of file +from .IO import FormattedMemoryViewIO, FormattedFileIO +from .IOThreadLinesReader import IOThreadLinesReader \ No newline at end of file diff --git a/xlib/player/VideoFilePlayer.py b/xlib/player/VideoFilePlayer.py index 1aadf88..6165a5d 100644 --- a/xlib/player/VideoFilePlayer.py +++ b/xlib/player/VideoFilePlayer.py @@ -3,10 +3,10 @@ from typing import Tuple import numpy as np from xlib import ffmpeg as lib_ffmpeg +from xlib import io as lib_io from .FramePlayer import FramePlayer - class VideoFilePlayer(FramePlayer): """ Play video track from the video file using subprocess ffmpeg. @@ -125,22 +125,23 @@ class VideoFilePlayer(FramePlayer): '-map', f'0:v:{self._stream_idx}', 'pipe:'] - self._ffmpeg_proc = lib_ffmpeg.run (args, pipe_stdout=True, pipe_stderr=True) - return self._ffmpeg_proc is not None + self._ffmpeg_proc = ffmpeg_proc = lib_ffmpeg.run (args, pipe_stdout=True, pipe_stderr=True) + self._ffmpeg_proc_stderr_lines = None + if ffmpeg_proc is not None: + self._ffmpeg_proc_stderr_lines = lib_io.IOThreadLinesReader(ffmpeg_proc.stderr, max_lines=5) + return True + return False def _ffmpeg_next_frame(self, frames_idx_offset=1): frame_buffer = None while frames_idx_offset != 0: frame_buffer = self._ffmpeg_proc.stdout.read(self._ffmpeg_height*self._ffmpeg_width*3) + if len(frame_buffer) == 0: - err = self._ffmpeg_proc.stderr.read() - err_lines = err.decode('utf-8').split('\r\n') - err = '\r\n'.join(err_lines[-5:]) - - # End reached + # unpredicted end reached + err = '\r\n'.join(self._ffmpeg_proc_stderr_lines.get_lines(till_eof=True)[-5:]) self._ffmpeg_stop() - return None, err frames_idx_offset -= 1