clean up folder
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
import argparse
|
||||
import av
|
||||
import cv2
|
||||
import platform
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
TARGET_FPS = 30
|
||||
WIDTH = 1920
|
||||
HEIGHT = 1080
|
||||
|
||||
|
||||
def list_windows_dshow_devices():
|
||||
"""
|
||||
Print DirectShow devices using ffmpeg, if available.
|
||||
"""
|
||||
cmd = ["ffmpeg", "-hide_banner", "-list_devices", "true", "-f", "dshow", "-i", "dummy"]
|
||||
print("Listing Windows DirectShow devices via ffmpeg...")
|
||||
try:
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, check=False)
|
||||
except FileNotFoundError as exc:
|
||||
raise RuntimeError("ffmpeg was not found in PATH; cannot list DirectShow devices.") from exc
|
||||
|
||||
output = (result.stderr or "") + (result.stdout or "")
|
||||
print(output.strip())
|
||||
|
||||
|
||||
def open_pyav_capture(system_name, device, pixel_format):
|
||||
"""
|
||||
Open a camera device via PyAV on Linux or Windows.
|
||||
"""
|
||||
if system_name == "linux":
|
||||
options = {
|
||||
"video_size": f"{WIDTH}x{HEIGHT}",
|
||||
"framerate": str(TARGET_FPS),
|
||||
"input_format": pixel_format or "mjpeg",
|
||||
}
|
||||
container = av.open(device or "/dev/video4", format="v4l2", options=options)
|
||||
elif system_name == "windows":
|
||||
if not device:
|
||||
raise RuntimeError(
|
||||
"On Windows, PyAV/dshow requires a camera name, for example "
|
||||
'--device "USB Camera". Use --list-devices first.'
|
||||
)
|
||||
options = {
|
||||
"video_size": f"{WIDTH}x{HEIGHT}",
|
||||
"framerate": str(TARGET_FPS),
|
||||
}
|
||||
if pixel_format:
|
||||
options["pixel_format"] = pixel_format
|
||||
container = av.open(f"video={device}", format="dshow", options=options)
|
||||
else:
|
||||
raise RuntimeError(
|
||||
f"Unsupported OS: {platform.system()}. Only Linux and Windows are supported."
|
||||
)
|
||||
|
||||
stream = container.streams.video[0]
|
||||
return container, stream
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Capture from camera via PyAV on Linux or Windows, optionally save to MP4."
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--device",
|
||||
type=str,
|
||||
default="",
|
||||
help=(
|
||||
"Linux: V4L2 device path like /dev/video4. "
|
||||
"Windows: DirectShow camera name like 'USB Camera'. "
|
||||
"If empty, Linux uses /dev/video4; Windows requires --device."
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--pixel-format",
|
||||
type=str,
|
||||
default="",
|
||||
help=(
|
||||
"Optional input pixel format / format hint. "
|
||||
"Examples: Linux 'mjpeg'; Windows may accept values such as 'mjpeg' if supported."
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--list-devices",
|
||||
action="store_true",
|
||||
help="On Windows, list DirectShow device names via ffmpeg and exit.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-o",
|
||||
"--output",
|
||||
type=str,
|
||||
default="",
|
||||
help="Output MP4 path (e.g. capture.mp4). If empty, only preview, no file.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
system_name = platform.system().lower()
|
||||
if args.list_devices:
|
||||
if system_name != "windows":
|
||||
raise RuntimeError("--list-devices is currently only implemented for Windows.")
|
||||
list_windows_dshow_devices()
|
||||
return
|
||||
|
||||
container, stream = open_pyav_capture(system_name, args.device, args.pixel_format)
|
||||
print(
|
||||
f"Using PyAV backend on {platform.system()} "
|
||||
f"with device={args.device or '/dev/video4'}"
|
||||
)
|
||||
|
||||
frame_count = 0
|
||||
t_start = time.perf_counter()
|
||||
|
||||
writer = None
|
||||
if args.output:
|
||||
fourcc = cv2.VideoWriter_fourcc(*"mp4v")
|
||||
writer = cv2.VideoWriter(
|
||||
args.output, fourcc, float(TARGET_FPS), (WIDTH, HEIGHT)
|
||||
)
|
||||
if not writer.isOpened():
|
||||
container.close()
|
||||
raise RuntimeError(f"Could not open VideoWriter for {args.output}")
|
||||
print(f"Recording to {args.output} @ {TARGET_FPS} fps, {WIDTH}x{HEIGHT}")
|
||||
|
||||
try:
|
||||
for frame in container.decode(stream):
|
||||
t0 = time.perf_counter()
|
||||
img = frame.to_ndarray(format="bgr24") # OpenCV-compatible
|
||||
t1 = time.perf_counter()
|
||||
|
||||
if writer is not None:
|
||||
writer.write(img)
|
||||
|
||||
cv2.imshow("pyav_cam", img)
|
||||
key = cv2.waitKey(1) & 0xFF
|
||||
t2 = time.perf_counter()
|
||||
|
||||
frame_count += 1
|
||||
if frame_count % 30 == 0:
|
||||
elapsed = t2 - t_start
|
||||
print(
|
||||
f"Frame {frame_count}: convert={1000*(t1-t0):.1f}ms, "
|
||||
f"imshow+waitKey={1000*(t2-t1):.1f}ms, avg_fps={frame_count/elapsed:.1f}"
|
||||
)
|
||||
|
||||
if key == ord("q"):
|
||||
break
|
||||
finally:
|
||||
container.close()
|
||||
if writer is not None:
|
||||
writer.release()
|
||||
print(f"Saved {frame_count} frames to {args.output}")
|
||||
cv2.destroyAllWindows()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user