init
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
"""Сборка команд ffmpeg/ffprobe. Запись — строго копированием (без перекодирования)."""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
|
||||
from ..config import Camera, Storage
|
||||
|
||||
|
||||
def record_args(cam: Camera, storage: Storage, input_url: str | None = None) -> list[str]:
|
||||
"""ffmpeg для записи основного потока сегментами mp4 копированием.
|
||||
|
||||
-c copy => CPU почти не используется. -strftime даёт имена сегментов по времени.
|
||||
input_url — источник (по умолчанию рестрим go2rtc: камера держит одну сессию).
|
||||
"""
|
||||
# Плоско в каталоге камеры: дата в имени файла. Каталог создаётся один раз
|
||||
# (recorder_task), новых подкаталогов не нужно — корректно переживает полночь.
|
||||
# Подкаталог даты не используем: этот ffmpeg-билд не знает -strftime_mkdir.
|
||||
out_template = os.path.join(storage.path, cam.id, "%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(storage.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
|
||||
Reference in New Issue
Block a user