135 lines
4.2 KiB
Python
135 lines
4.2 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
RTSP Server для стриминга с CSI камеры на Jetson Orin.
|
||
Использует GStreamer RTSP Server.
|
||
"""
|
||
|
||
import os
|
||
import gi
|
||
gi.require_version('Gst', '1.0')
|
||
gi.require_version('GstRtspServer', '1.0')
|
||
from gi.repository import Gst, GstRtspServer, GLib
|
||
|
||
# Инициализация GStreamer
|
||
Gst.init(None)
|
||
|
||
# Параметры из environment
|
||
WIDTH = os.environ.get('CAMERA_WIDTH', '1920')
|
||
HEIGHT = os.environ.get('CAMERA_HEIGHT', '1080')
|
||
FPS = os.environ.get('CAMERA_FPS', '30')
|
||
RTSP_PORT = os.environ.get('RTSP_PORT', '8554')
|
||
|
||
|
||
def create_pipeline():
|
||
"""
|
||
Создаёт GStreamer pipeline для CSI камеры.
|
||
Пробуем разные источники в порядке приоритета.
|
||
"""
|
||
|
||
# Вариант 1: nvarguscamerasrc (CSI камера на Jetson) с NVIDIA hardware encoder
|
||
# Используем nvv4l2h264enc для аппаратного кодирования без EGL
|
||
csi_pipeline = (
|
||
f'nvarguscamerasrc ! '
|
||
f'video/x-raw(memory:NVMM),width={WIDTH},height={HEIGHT},framerate={FPS}/1,format=NV12 ! '
|
||
f'nvv4l2h264enc bitrate=4000000 preset-level=1 insert-sps-pps=true ! '
|
||
f'h264parse ! '
|
||
f'rtph264pay name=pay0 pt=96'
|
||
)
|
||
|
||
# Вариант 2: v4l2src (USB камера) с NVIDIA hardware encoder
|
||
v4l2_pipeline = (
|
||
f'v4l2src device=/dev/video0 ! '
|
||
f'video/x-raw,width={WIDTH},height={HEIGHT},framerate={FPS}/1 ! '
|
||
f'nvvidconv ! video/x-raw(memory:NVMM),format=NV12 ! '
|
||
f'nvv4l2h264enc bitrate=4000000 preset-level=1 insert-sps-pps=true ! '
|
||
f'h264parse ! '
|
||
f'rtph264pay name=pay0 pt=96'
|
||
)
|
||
|
||
# Вариант 3: тестовый источник с software encoder (fallback)
|
||
test_pipeline = (
|
||
f'videotestsrc is-live=true ! '
|
||
f'video/x-raw,width={WIDTH},height={HEIGHT},framerate={FPS}/1 ! '
|
||
f'videoconvert ! '
|
||
f'x264enc tune=zerolatency bitrate=4000 speed-preset=ultrafast ! '
|
||
f'rtph264pay name=pay0 pt=96'
|
||
)
|
||
|
||
# Пробуем CSI камеру сначала
|
||
try:
|
||
test = Gst.parse_launch(csi_pipeline.replace('rtph264pay name=pay0 pt=96', 'fakesink'))
|
||
test.set_state(Gst.State.PLAYING)
|
||
# Даём время на инициализацию
|
||
import time
|
||
time.sleep(2)
|
||
state = test.get_state(Gst.CLOCK_TIME_NONE)
|
||
test.set_state(Gst.State.NULL)
|
||
if state[1] == Gst.State.PLAYING:
|
||
print("Using CSI camera (nvarguscamerasrc)")
|
||
return csi_pipeline
|
||
except Exception as e:
|
||
print(f"CSI camera not available: {e}")
|
||
|
||
# Пробуем USB камеру
|
||
if os.path.exists('/dev/video0'):
|
||
print("Using USB camera (v4l2src)")
|
||
return v4l2_pipeline
|
||
|
||
# Fallback на тестовый источник
|
||
print("No camera found, using test source")
|
||
return test_pipeline
|
||
|
||
|
||
class RTSPServer:
|
||
def __init__(self):
|
||
self.server = GstRtspServer.RTSPServer()
|
||
self.server.set_service(RTSP_PORT)
|
||
|
||
# Создаём factory для стрима
|
||
factory = GstRtspServer.RTSPMediaFactory()
|
||
pipeline = create_pipeline()
|
||
print(f"Pipeline: {pipeline}")
|
||
factory.set_launch(f'( {pipeline} )')
|
||
factory.set_shared(True)
|
||
|
||
# Добавляем mount point
|
||
mount_points = self.server.get_mount_points()
|
||
mount_points.add_factory('/stream', factory)
|
||
|
||
# Запускаем сервер
|
||
self.server.attach(None)
|
||
|
||
print(f"\n{'='*50}")
|
||
print(f"RTSP Server started!")
|
||
print(f"Stream URL: rtsp://<device-ip>:{RTSP_PORT}/stream")
|
||
print(f"{'='*50}\n")
|
||
|
||
|
||
def main():
|
||
import signal
|
||
import sys
|
||
|
||
server = RTSPServer()
|
||
loop = GLib.MainLoop()
|
||
|
||
def signal_handler(sig, frame):
|
||
print("Shutting down...")
|
||
loop.quit()
|
||
sys.exit(0)
|
||
|
||
signal.signal(signal.SIGINT, signal_handler)
|
||
signal.signal(signal.SIGTERM, signal_handler)
|
||
|
||
print("RTSP server running. Waiting for connections...")
|
||
sys.stdout.flush()
|
||
|
||
try:
|
||
loop.run()
|
||
except Exception as e:
|
||
print(f"Error in main loop: {e}")
|
||
sys.exit(1)
|
||
|
||
|
||
if __name__ == '__main__':
|
||
main()
|