#!/usr/bin/env python3
"""
RTSP stream with YOLOv8 ball detection.
Pushes to mediamtx RTSP server using ffmpeg.

Usage:
  1. Start mediamtx: /tmp/mediamtx &
  2. Run this script: python3 stream_with_detection.py [source]
  3. View stream: vlc rtsp://pickle:8554/live
"""

import cv2
import subprocess
import time
import sys
from ultralytics import YOLO

BALL_CLASS_ID = 32  # sports ball in COCO
WIDTH = 1280
HEIGHT = 720
FPS = 15  # Lower FPS for stable streaming
RTSP_URL = "rtsp://localhost:8554/live"


def main():
    source = sys.argv[1] if len(sys.argv) > 1 else "/opt/nvidia/deepstream/deepstream/samples/streams/sample_1080p_h264.mp4"

    print("Loading YOLOv8n...")
    model = YOLO("yolov8n.pt")
    try:
        model.to("cuda")
        print("Using CUDA")
    except:
        print("Using CPU")

    print(f"Opening: {source}")
    cap = cv2.VideoCapture(source)
    if not cap.isOpened():
        print("ERROR: Cannot open source")
        return 1

    # FFmpeg command to push RTSP
    ffmpeg_cmd = [
        'ffmpeg',
        '-y',
        '-f', 'rawvideo',
        '-vcodec', 'rawvideo',
        '-pix_fmt', 'bgr24',
        '-s', f'{WIDTH}x{HEIGHT}',
        '-r', str(FPS),
        '-i', '-',
        '-c:v', 'libx264',
        '-preset', 'ultrafast',
        '-tune', 'zerolatency',
        '-g', str(FPS * 2),
        '-f', 'rtsp',
        '-rtsp_transport', 'tcp',
        RTSP_URL
    ]

    print("Starting ffmpeg RTSP publisher...")
    ffmpeg = subprocess.Popen(
        ffmpeg_cmd,
        stdin=subprocess.PIPE,
        stdout=subprocess.DEVNULL,
        stderr=subprocess.DEVNULL
    )

    print(f"\n{'='*50}")
    print(f"RTSP STREAM: rtsp://pickle:8554/live")
    print(f"{'='*50}")
    print("Press Ctrl+C to stop\n")

    frame_count = 0
    start = time.time()
    total_detections = 0
    frame_time = 1.0 / FPS

    try:
        while True:
            loop_start = time.time()

            ret, frame = cap.read()
            if not ret:
                cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
                continue

            frame = cv2.resize(frame, (WIDTH, HEIGHT))

            # Detection
            results = model(frame, verbose=False, classes=[BALL_CLASS_ID], conf=0.25)
            det_count = 0
            for r in results:
                for box in r.boxes:
                    x1, y1, x2, y2 = map(int, box.xyxy[0])
                    conf = float(box.conf[0])
                    cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 3)
                    cv2.putText(frame, f"Ball {conf:.2f}", (x1, y1-10),
                               cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
                    det_count += 1
                    total_detections += 1

            # FPS overlay
            frame_count += 1
            elapsed = time.time() - start
            fps = frame_count / elapsed
            cv2.putText(frame, f"FPS: {fps:.1f} | Det: {det_count}", (10, 30),
                       cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 255), 2)

            # Push to ffmpeg
            try:
                ffmpeg.stdin.write(frame.tobytes())
            except BrokenPipeError:
                print("FFmpeg pipe broken, restarting...")
                break

            if frame_count % 100 == 0:
                print(f"Frame {frame_count}, FPS: {fps:.1f}, Total Det: {total_detections}")

            # Rate limiting
            proc_time = time.time() - loop_start
            if proc_time < frame_time:
                time.sleep(frame_time - proc_time)

    except KeyboardInterrupt:
        print("\nStopping...")
    finally:
        elapsed = time.time() - start
        print(f"\nProcessed {frame_count} frames in {elapsed:.1f}s")
        print(f"Average FPS: {frame_count/elapsed:.1f}")
        print(f"Total detections: {total_detections}")
        cap.release()
        ffmpeg.stdin.close()
        ffmpeg.wait()

    return 0


if __name__ == "__main__":
    sys.exit(main())
