Read and log ffmpeg stderr output with real-time stats parsing
(self)
| 833 | logger.debug(f"Started stderr reader thread for channel {self.channel_id}") |
| 834 | |
| 835 | def _read_stderr(self): |
| 836 | """Read and log ffmpeg stderr output with real-time stats parsing""" |
| 837 | import os as _os |
| 838 | import select as _select |
| 839 | import gevent |
| 840 | |
| 841 | try: |
| 842 | stderr = self.transcode_process.stderr |
| 843 | if not stderr: |
| 844 | return |
| 845 | stderr_fd = stderr.fileno() |
| 846 | buf = b"" |
| 847 | |
| 848 | while self.running and self.transcode_process and self.transcode_process.stderr: |
| 849 | try: |
| 850 | ready, _, _ = _select.select([stderr_fd], [], [], 1.0) |
| 851 | if not ready: |
| 852 | if not self.running or not self.transcode_process: |
| 853 | break |
| 854 | continue |
| 855 | |
| 856 | chunk = _os.read(stderr_fd, 4096) |
| 857 | if not chunk: |
| 858 | break |
| 859 | |
| 860 | # Yield to the hub after each read so fetch_chunk and other |
| 861 | # greenlets can run. Without this, the byte-at-a-time loop |
| 862 | # monopolises the event loop during ffmpeg startup output, |
| 863 | # starving the data reader and preventing the buffer from filling. |
| 864 | gevent.sleep(0) |
| 865 | |
| 866 | buf += chunk |
| 867 | |
| 868 | while True: |
| 869 | cr = buf.find(b'\r') |
| 870 | nl = buf.find(b'\n') |
| 871 | if cr == -1 and nl == -1: |
| 872 | if len(buf) > 1024 and b"frame=" not in buf: |
| 873 | line_text = buf.decode('utf-8', errors='ignore').strip() |
| 874 | if line_text: |
| 875 | self._log_stderr_content(line_text) |
| 876 | buf = b"" |
| 877 | break |
| 878 | if cr != -1 and (nl == -1 or cr < nl): |
| 879 | line, buf = buf[:cr], buf[cr + 1:] |
| 880 | else: |
| 881 | line, buf = buf[:nl], buf[nl + 1:] |
| 882 | line_text = line.decode('utf-8', errors='ignore').strip() |
| 883 | if not line_text: |
| 884 | continue |
| 885 | if "frame=" in line_text: |
| 886 | self._parse_ffmpeg_stats(line_text) |
| 887 | self._log_stderr_content(line_text) |
| 888 | |
| 889 | except Exception as e: |
| 890 | logger.error(f"Error reading stderr for channel {self.channel_id}: {e}") |
| 891 | break |
| 892 |
nothing calls this directly
no test coverage detected