Джин + Websocket Без использования Виджет отображает RTSP в HTML

Go

адрес проекта:gin-rtsp

В фоновой разработке мы столкнулись с необходимостью стыковки видеопотока камеры дисплея. В настоящее время видеопотоки основных камер, таких как Hikvision и Dahua, в основном используют протокол RTSP. Однако HTML-страница не может напрямую воспроизводить видеопоток протокола RTSP.После запроса информации о различных веб-страницах, воспроизводящих RTSP, есть несколько решений:

  • Разработка и воспроизведение подключаемых модулей: для воспроизведения используйте ActiveX и другие подключаемые модули браузера Страницы управления браузером Hikvision и Dahua воспроизводят видео, устанавливая подключаемые модули браузера. Воспроизведение видео стабильное и задержка небольшая, но технические требования высокие, и есть проблемы совместимости с современными браузерами типа хрома, которые я не хочу рассматривать.

  • RTSP в HLS: используйте FFMPEG для преобразования RTSP в HLS и передачи потока на потоковый сервер, если он установлен.nginx-rtmp-moduleNginx модуля был протестирован с этим решением.Воспроизведение протокола HLS на ПК и мобильных браузерах очень стабильно, но задержка прямой трансляции с использованием протокола HLS очень велика, по крайней мере, около 15 секунд.Для видео с низкой задержкой спрос может только пройти.

  • RTSP в RTMP: Как и в предыдущей схеме, используйте FFMPEG для преобразования RTSP в RTMP и отправки его на потоковый сервер для распространения и воспроизведения. По сравнению с HLS задержка очень низкая. Я уже планировал использовать эту схему, но Библиотека video.js, используемая во внешнем интерфейсе, всегда Иногда видео не может быть загружено, и для воспроизведения RTMP необходимо использовать Flash.В таких браузерах, как chrome, загрузка отключена по умолчанию и постепенно прекращена, и ее можно только отменить .

  • WebSocket: Абсолютный всемогущийGithubпоявился одинJSMpegВ проекте используется схема преобразования видео FFMPEG в MPEG1 и передачи его во внешний интерфейс для прямого декодирования и воспроизведения через прокси-сервер WebSocket. Проверено, задержка низкая, плагины не требуются, качество картинки можно регулировать по мере необходимости, эффект очень хороший.

JSMpegПрокси-сервер WebSocket в примере проекта использует JS, который просто реализует функцию воспроизведения одного видеоисточника. Наш фон использует структуру Gin для golang, и будет несколько веб-клиентов, воспроизводящих несколько видеопотоков. К счастью, после прочтения JS-кода принцип работы этого прокси WebSocket не сложен, а также очень удобно интегрировать WebSocket в Gin. Запишите мой план интеграции здесь.

основной модуль

  • Интерфейс API: получение потоковых данных FFMPEG и HTTP-запрос клиента, преобразование RTSP-адреса, необходимого клиенту для воспроизведения, в соответствующий адрес WebSocket, и клиент может напрямую воспроизводить видео через этот адрес WebSocket, чтобы вовремя выпустить непросмотренное видео. Видеопоток устроен так, что клиенту необходимо циклически запрашивать этот интерфейс каждые 60 секунд во время воспроизведения, если запрос не будет получен по истечении заданного времени, фон закроет видеопоток.

  • Преобразование видео FFMPEG: после получения запроса от внешнего интерфейса запустите Горутин для вызова команды FFMPEG системы для преобразования указанного видеопотока RTSP и отправки его на соответствующий интерфейс в фоновом режиме, а также автоматически завершите преобразование по тайм-ауту. задача.

  • WebSocket Manager: управление клиентами WebSocket, добавление клиентов, запрашивающих один и тот же адрес WebSocket, в группу, трансляция соответствующего видеопотока RTSP каждой группе, удаление отключенных клиентов в группе и освобождение неактивных групп.

Вот общее введение в точки реализации этих трех основных модулей.

API-интерфейс

API получает данные Json, отправленные клиентом, и содержит адрес воспроизводимого RTSP-потока в следующем формате:

{
    "url":"rtsp://admin:admin@192.168.1.11:554/cam/realmonitor?channel=1&subtype=0"
}

Когда нескольким клиентам необходимо воспроизвести один и тот же адрес потока RTSP, необходимо убедиться, что соответствующие адреса WebSocket одинаковы.Здесь UUID v3 используется для хеширования адресов RTSP, чтобы гарантировать, что возвращаемые адреса одинаковы.

service/rtsptrans.go

processCh := uuid.NewV3(uuid.NamespaceURL, splitList[1]).String()
playURL := fmt.Sprintf("/stream/live/%s", processCh)

Видеоданные, преобразованные с помощью FFMPEG, также будут отправлены обратно на сервер по протоколу HTTP, и каждый кадр байтовых данных будет'\n'В конце на языке го можно пройтиbufioмодуль для чтения таких данных.

api/rtsp.go

bodyReader := bufio.NewReader(c.Request.Body)

for {
	data, err := bodyReader.ReadBytes('\n')
	if err != nil {
		break
	}
}

Преобразование видео FFMPEG

Модуль преобразования видео запустит подпроцесс FFMPEG для преобразования видеопотока RTSP после получения адреса потока RTSP, который необходимо преобразовать.exec.CommandЧто нужно сделать:

service/rtsptrans.go

params := []string{
	"-rtsp_transport",
	"tcp",
	"-re",
	"-i",
	rtsp,
	"-q",
	"5",
	"-f",
	"mpegts",
	"-fflags",
	"nobuffer",
	"-c:v",
	"mpeg1video",
	"-an",
	"-s",
	"960x540",
	fmt.Sprintf("http://127.0.0.1:3000/stream/upload/%s", playCh),
}

cmd := exec.Command("ffmpeg", params...)
cmd.Stdout = nil
cmd.Stderr = nil
stdin, err := cmd.StdinPipe()

Качество и разрешение видео можно настроить с помощью параметров -q и -s FFMPEG. Для простоты и stdout, и stderr команды имеют значение nil, которое можно сохранить в журнале реального проекта для облегчения устранения неполадок. Чтобы освобождать ресурсы, которые уже не воспроизводятся вовремя, после того, как клиент перестанет запрашивать в течение определенного периода времени, подпроцесс FFMPEG будет автоматически закрыт.Эта функция может быть легко реализована через выбор golang.

service/rtsptrans.go

for {
	select {
	case <-*ch:
		util.Log().Info("reflush channel %s rtsp %v", playCh, rtsp)

	case <-time.After(60 * time.Second):
		stdin.Write([]byte("q"))
		err = cmd.Wait()
		if err != nil {
			util.Log().Error("Run ffmpeg err:%v", err.Error)
		}
		return
	}
}

здесь*chКанал связан с каждой Картой и каждым подпроцессом, нужно очищать от Карты при выключении дочернего процесса, нужно рассмотреть проблему, можно использоватьsync.Mapдля обеспечения безопасности резьбы.

WebSocket Manager

WebSocket Manager отвечает за управление клиентом ws, запрашивающим видеоданные на странице, в Gin он в основном используетgithub.com/gorilla/websocketЭта библиотека для разработки связанных функций.JSMpegБиблиотека используется при подключении к WebSocketSec-WebSocket-ProtocolЭтот заголовок необходимо обработать:

upgrader := websocket.Upgrader{
	// cross origin domain
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
	// 处理 Sec-WebSocket-Protocol Header
	Subprotocols: []string{ctx.GetHeader("Sec-WebSocket-Protocol")},
}
conn, err := upgrader.Upgrade(ctx.Writer, ctx.Request, nil)

После того, как клиент ws подключится, он присвоит уникальный UUID и поместит его в группу, соответствующую URL-адресу.Клиенты в одной группе будут получать данные одного и того же видеопотока. После отключения клиента его нужно удалить из Группы, при этом пустую Группу необходимо освободить. Этот процесс также должен учитывать проблему параллелизма.WebSocket Manager может управлять каждой группой унифицированным образом, запуская горутину для отслеживания регистрации, отключения и трансляции трех соответствующих каналов golang, что может хорошо решить эту проблему. Специально реализовано вservice/wsservice.go#L75, код относительно давно не выложен.

контрольная работа

Проект должен работать в среде, где установлена ​​программа FFMPEG. Написав Dockerfile для инкапсуляции необходимой среды, вы можете использовать сборку Docker для запуска в режиме Docker.

$ docker build -t ginrtsp .
$ docker run -td -p 3000:3000 ginrtsp

Используйте встроенное преобразование FFMPEG

Отправьте адрес RTSP-потока для воспроизведения в интерфейс /stream/play, например:

POST /stream/play
{
   "url": "rtsp://admin:password@192.168.3.10:554/cam/realmonitor?channel=1&subtype=0"
}

Когда фон сможет нормально преобразовать этот RTSP-адрес, он вернет соответствующий адрес, например:

{
    "code": 0,
    "data": {
        "path": "/stream/live/5b96bff4-bdb2-3edb-9d6e-f96eda03da56"
    },
    "msg": "success"
}

редактироватьhtmlФайл view-stream.html в папке, измените URL части скрипта на этот адрес, откройте его в браузере, и вы сможете посмотреть видео.

Запуск FFMPEG вручную

Так как процесс конвертации RTSP в фоновом режиме остановится при отсутствии запроса более 60 секунд, вы также можете запустить команду ffmpeg вручную для более удобного просмотра видео в тестовом состоянии.

ffmpeg -rtsp_transport tcp -re -i 'rtsp://admin:password@192.168.3.10:554/cam/realmonitor?channel=1&subtype=0' -q 0 -f mpegts -c:v mpeg1video -an -s 960x540 http://127.0.0.1:3000/stream/upload/test

С помощью приведенной выше команды после запуска заполните соответствующий адрес в URL-адресе файла view-stream.html как /stream/upload/test и откройте его в браузере для просмотра видео.

эффект отображения

Суммировать

выгода отJSMpegСила проекта в том, что по-прежнему очень просто реализовать WebSocket для воспроизведения видеопотоков RTSP на веб-страницах. По мере развития языка golang легко добавлять функциональность WebSocket в Gin на основе готовых библиотек. Необходимо обратить внимание на добавление и удаление подпроцессов FFMPEG и клиентов WebSocket, когда параллелизм является основной проблемой.К счастью, golang имеет хорошую поддержку параллелизма по своей природе.Если вы освоите основные знания golang, такие как gouroutine, канал , и библиотеки синхронизации, вы можете справиться с этими проблемами.

Первый самостоятельный блогЧернокожий научно-технический исследовательский центр в средней школе, добро пожаловать на биржу.