Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import os
- import time
- import socket
- import threading
- import subprocess
- from datetime import datetime
- import re
- # =============== KONFIGURACJA ===============
- RTSP_URL = "rtsp://admin:[email protected]:554/cam/realmonitor?channel=1&subtype=0&unicast=true&proto=Onvif"
- BASE_DIR = r"D:\wideo"
- RAW_DIR = os.path.join(BASE_DIR, "raw")
- CLIPS_DIR = os.path.join(BASE_DIR, "clips")
- RAW_PATH = os.path.join(RAW_DIR, "cam1_raw.mp4") # tu ffmpeg nagrywa ciągły strumień
- CAMERA_NAME = "KAMERA1"
- PRE_SECONDS = 10 # ile sekund PRZED triggerem
- POST_SECONDS = 15 # ile sekund PO triggerze
- RETENTION_DAYS = 1 # na testy, potem np. 30
- TCP_HOST = "0.0.0.0"
- TCP_PORT = 5000
- FFMPEG_EXE = "ffmpeg" # jeśli nie w PATH, podaj pełną ścieżkę, np. r"C:\ffmpeg\bin\ffmpeg.exe"
- # globalnie zapamiętany czas startu RAW-a
- raw_start_time = None
- raw_lock = threading.Lock()
- # =============== POMOCNICZE ===============
- def ensure_dirs():
- os.makedirs(RAW_DIR, exist_ok=True)
- os.makedirs(CLIPS_DIR, exist_ok=True)
- def sanitize_part_code(code: str) -> str:
- code = code.strip()
- if not code:
- code = "NO_CODE"
- return re.sub(r"[^0-9A-Za-z_-]", "_", code)
- def cleanup_old_clips():
- while True:
- now = time.time()
- cutoff = now - RETENTION_DAYS * 24 * 3600
- try:
- for fname in os.listdir(CLIPS_DIR):
- fpath = os.path.join(CLIPS_DIR, fname)
- if os.path.isfile(fpath) and os.path.getmtime(fpath) < cutoff:
- print(f"[CLEANUP] Usuwam stary klip: {fname}")
- os.remove(fpath)
- except Exception as e:
- print("[CLEANUP] Błąd cleanup:", e)
- time.sleep(24 * 3600)
- # =============== START FFMPEG (RAW) ===============
- def start_ffmpeg_raw():
- """
- Uruchamia ffmpeg, który ciągle nagrywa RTSP do RAW_PATH.
- """
- global raw_start_time
- # jeśli plik już istnieje – spróbuj go usunąć, żeby zacząć od nowa
- if os.path.exists(RAW_PATH):
- try:
- os.remove(RAW_PATH)
- except Exception:
- pass
- cmd = [
- FFMPEG_EXE,
- "-rtsp_transport", "tcp",
- "-i", RTSP_URL,
- "-c", "copy",
- "-an",
- "-reset_timestamps", "1",
- RAW_PATH,
- ]
- print("[FFMPEG] Start nagrywania RAW...")
- # odpalamy ffmpeg jako proces w tle
- proc = subprocess.Popen(
- cmd,
- stdout=subprocess.DEVNULL,
- stderr=subprocess.DEVNULL
- )
- with raw_lock:
- raw_start_time = time.time()
- print(f"[FFMPEG] RAW zapisuje się do: {RAW_PATH}")
- print(f"[FFMPEG] Czas startu RAW: {datetime.fromtimestamp(raw_start_time)}")
- # proces ffmpeg będzie sobie chodził w tle; nie czekamy na niego tutaj
- return proc
- # =============== WYCIĘCIE KLIPU ===============
- def cut_clip(part_code: str, trigger_time: float):
- """
- Wywołuje ffmpeg, żeby wyciąć klip:
- [trigger_time - PRE, trigger_time + POST]
- z pliku RAW.
- """
- with raw_lock:
- rst = raw_start_time
- if rst is None:
- print("[CLIP] RAW jeszcze nie wystartował – pomijam.")
- return
- if not os.path.exists(RAW_PATH):
- print(f"[CLIP] RAW nie istnieje: {RAW_PATH}")
- return
- window_start = trigger_time - PRE_SECONDS
- offset = window_start - rst
- if offset < 0:
- offset = 0.0 # jeśli trigger był bardzo szybko po starcie
- duration = PRE_SECONDS + POST_SECONDS
- ts_name = datetime.fromtimestamp(trigger_time).strftime("%Y_%m_%d_%H_%M_%S")
- safe_code = sanitize_part_code(part_code)
- out_name = f"{ts_name}_{safe_code}_{CAMERA_NAME}.mp4"
- out_path = os.path.join(CLIPS_DIR, out_name)
- print(f"[CLIP] Wycinam klip: {out_name}")
- print(f"[CLIP] offset={offset:.2f}s, duration={duration:.2f}s")
- cmd = [
- FFMPEG_EXE,
- "-y",
- "-ss", f"{offset:.3f}",
- "-i", RAW_PATH,
- "-t", f"{duration:.3f}",
- "-c", "copy",
- out_path,
- ]
- try:
- subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
- print(f"[CLIP] Klip zapisany: {out_path}")
- except Exception as e:
- print(f"[CLIP] Błąd wycinania: {e}")
- # =============== SERVER TCP ===============
- def tcp_server():
- srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- srv.bind((TCP_HOST, TCP_PORT))
- srv.listen(5)
- print(f"[TCP] Nasłuch na {TCP_HOST}:{TCP_PORT}")
- while True:
- conn, addr = srv.accept()
- with conn:
- try:
- data = conn.recv(1024)
- if not data:
- continue
- code = data.decode(errors="ignore").strip()
- print(f"[TCP] Kod części: {code!r} od {addr}")
- if not code:
- continue
- trigger_time = time.time()
- # wycinanie w osobnym wątku, żeby nie blokować TCP
- th = threading.Thread(target=cut_clip, args=(code, trigger_time), daemon=True)
- th.start()
- except Exception as e:
- print("[TCP] Błąd:", e)
- # =============== MAIN ===============
- def main():
- ensure_dirs()
- # startujemy ffmpeg
- ffmpeg_proc = start_ffmpeg_raw()
- # uruchamiamy sprzątanie starych klipów
- threading.Thread(target=cleanup_old_clips, daemon=True).start()
- print("=== TRIGGER + FFMPEG RECORDER URUCHOMIONY ===")
- print("RAW:", RAW_PATH)
- print("CLIPS:", CLIPS_DIR)
- print(f"Pre={PRE_SECONDS}s, Post={POST_SECONDS}s, Retencja={RETENTION_DAYS} dni\n")
- try:
- tcp_server()
- finally:
- # przy zamknięciu – spróbuj zabić ffmpeg
- try:
- ffmpeg_proc.terminate()
- except Exception:
- pass
- if __name__ == "__main__":
- main()
Advertisement
Add Comment
Please, Sign In to add comment