#!/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())