Fix Windows compatibility: pipe streamlink→ffmpeg manually

On Windows, asyncio subprocess stdin can't accept another process's
StreamReader directly. Use a background task to forward data instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-05 10:46:21 +01:00
parent 1560ecadd6
commit 402d44111e

View File

@@ -1,8 +1,22 @@
import asyncio import asyncio
import struct
from collections.abc import AsyncIterator from collections.abc import AsyncIterator
async def _pipe_stream(source: asyncio.StreamReader, dest: asyncio.StreamWriter):
"""Forward data from streamlink stdout to ffmpeg stdin."""
try:
while True:
chunk = await source.read(65536)
if not chunk:
break
dest.write(chunk)
await dest.drain()
except (BrokenPipeError, ConnectionResetError):
pass
finally:
dest.close()
async def capture_frames( async def capture_frames(
channel: str, quality: str, interval: int channel: str, quality: str, interval: int
) -> AsyncIterator[bytes]: ) -> AsyncIterator[bytes]:
@@ -35,13 +49,15 @@ async def capture_frames(
ffmpeg_proc = await asyncio.create_subprocess_exec( ffmpeg_proc = await asyncio.create_subprocess_exec(
*ffmpeg_cmd, *ffmpeg_cmd,
stdin=streamlink_proc.stdout, stdin=asyncio.subprocess.PIPE,
stdout=asyncio.subprocess.PIPE, stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.DEVNULL, stderr=asyncio.subprocess.DEVNULL,
) )
# Release streamlink's stdout so ffmpeg owns the pipe # Forward streamlink → ffmpeg in background
streamlink_proc.stdout = None pipe_task = asyncio.create_task(
_pipe_stream(streamlink_proc.stdout, ffmpeg_proc.stdin)
)
try: try:
buf = b"" buf = b""
@@ -59,13 +75,13 @@ async def capture_frames(
break break
eoi = buf.find(b"\xff\xd9", soi + 2) eoi = buf.find(b"\xff\xd9", soi + 2)
if eoi == -1: if eoi == -1:
# Keep from SOI onward, discard junk before
buf = buf[soi:] buf = buf[soi:]
break break
frame = buf[soi : eoi + 2] frame = buf[soi : eoi + 2]
buf = buf[eoi + 2 :] buf = buf[eoi + 2 :]
yield frame yield frame
finally: finally:
pipe_task.cancel()
for proc in (ffmpeg_proc, streamlink_proc): for proc in (ffmpeg_proc, streamlink_proc):
try: try:
proc.terminate() proc.terminate()