Files
CoRE.Vision/backend/app/recorder/ffmpeg.py
T
2026-06-02 14:02:09 +05:00

65 lines
3.0 KiB
Python

"""Сборка команд ffmpeg/ffprobe. Запись — строго копированием (без перекодирования)."""
from __future__ import annotations
import asyncio
import json
import os
from ..config import Camera
def record_args(cam: Camera, out_root: str, segment_seconds: int,
input_url: str | None = None, sub_dir: str | None = None) -> list[str]:
"""ffmpeg для записи основного потока сегментами mp4 копированием.
-c copy => CPU почти не используется. -strftime даёт имена сегментов по времени.
out_root — корень активного хранилища (внутри пишем подкаталог камеры).
sub_dir — имя подпапки камеры (номер слота, напр. «05»); по умолчанию — id камеры.
input_url — источник (по умолчанию рестрим go2rtc: камера держит одну сессию).
"""
# Подпапка даты: <root>/<sub_dir>/<YYYY-MM-DD>/<YYYY-MM-DD_HH-MM-SS>.mp4.
# Этот ffmpeg-билд НЕ знает -strftime_mkdir, поэтому каталоги даты (сегодня+завтра)
# заранее создаёт recorder_task при старте и индексатор каждые 30с (переживает полночь).
out_template = os.path.join(out_root, sub_dir or cam.id, "%Y-%m-%d", "%Y-%m-%d_%H-%M-%S.mp4")
return [
"ffmpeg",
"-nostdin",
"-hide_banner",
"-loglevel", "warning",
"-rtsp_transport", "tcp",
"-timeout", "10000000", # 10s на установку соединения (микросекунды)
"-i", input_url or cam.record_url,
"-an", # звук не пишем (камеры обычно без него)
"-c:v", "copy",
"-f", "segment",
"-segment_time", str(segment_seconds),
"-segment_atclocktime", "1", # резать по часам: кратно segment_time от 00:00
"-segment_clocktime_offset", "0",
"-segment_format", "mp4",
"-reset_timestamps", "1",
"-strftime", "1",
out_template,
]
async def probe_duration(path: str) -> tuple[float | None, int | None]:
"""ffprobe: длительность (сек) и размер файла (байт)."""
try:
size = os.path.getsize(path)
except OSError:
size = None
proc = await asyncio.create_subprocess_exec(
"ffprobe", "-v", "quiet", "-print_format", "json", "-show_format", path,
stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.DEVNULL,
)
out, _ = await proc.communicate()
duration = None
if proc.returncode == 0 and out:
try:
data = json.loads(out)
duration = float(data["format"]["duration"])
except (json.JSONDecodeError, KeyError, ValueError):
duration = None
return duration, size