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:
26
capture.py
26
capture.py
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user