Онлайн-трансляция (стриминг) видео с веб-камеры через Firebase
В ходе изучения темы машинного обучения (и компьютерного зрения в частности) у меня возникла мысль попробовать осуществить онлайн-трансляцию видео с домашней веб-камеры на своём сайте через Firebase. Идея показалась мне интересной, т.к. в будущем можно было бы демонстрировать работу различных алгоритмов распознавания объектов в реальном времени. При этом использование Firebase в качестве промежуточного звена избавило бы от необходимости решать проблему получения данных с «серого» IP.
Для реализации необходимо было реализовать следующие компоненты:
- Скрипт на Python, который бы запускался на компьютере с подключённой веб-камерой, получал с неё кадры с заданной периодичностью и сохранял их в некую переменную в Firebase (сервер)
- Веб-страница с кодом JavaScript, отслеживающим обновление этой переменной и динамически отображающим её содержимое на странице (клиент)
С Firebase из веб-приложения на Vue я уже работал и опыт был очень позитивный, взаимодействовать с веб-камерой из кода на Python я как раз недавно научился. Серьёзных проблем с реализацией не предвиделось и я с интересом приступил к делу 🙂
Подготовка
Для начала я создал новый проект в Google Cloud Console (GCP) и првязал к нему новый проект в консоли Firebase. Здесь я не буду подробно описывать эти шаги, а инструкции при необходимости можно легко найти в интернете.
Далее в консоли Firebase необходимо добавить новую Realtime Database, в которой создать единственный дочерний элемент frame
с пустым значением, в нём будет храниться последний отправленный кадр в формате base64. Этот формат позволяет сохранять изображения в виде строки, которую также можно использовать в качестве атрибута src
тега img
. Также на время тестирования я установил разрешения на чтение и запись без авторизации на вкладке «Правила»:
{
"rules": {
".read": true,
".write": true
}
}
В дальнейшем, разумеется, необходимо будет ограничить доступ к данным. В первую очередь это касается операций записи.
Переходим к написанию кода.
Сервер
Для работы с Firebase я использовал рекомендованную в документации библиотеку Pyrebase. Данные для подключения загружаются из файла конфигурации firebase_config.json
. Далее выполняется подключение к Firebase и запускается процесс видеозахвата с камеры. Видеотрансляция отображается в новом окне, при этом каждую секунду в Firebase сохраняется новый кадр видео. Процесс можно остановить нажатием на клавиши «q».
streamer.py
import base64
import json
import time
import cv2
import pyrebase
from imutils.video import VideoStream, FPS
def init_stream():
print('[INFO] connecting to firebase...')
with open('firebase_config.json') as file:
firebase_config = json.load(file)
firebase = pyrebase.initialize_app(firebase_config)
db = firebase.database()
print('[INFO] starting video stream...')
vs = VideoStream(src=0).start()
time.sleep(2.0)
fps = FPS().start()
start_time = time.time()
while True:
frame = vs.read()
cv2.imshow('Webcam live stream', frame)
if time.time() - start_time >= 1:
frame_string = base64.b64encode(cv2.imencode('.jpg', frame)[1]).decode()
try:
db.update({'frame': f'data:image/jpeg;base64,{frame_string}'})
except Exception as e:
print(e)
start_time = time.time()
if cv2.waitKey(1) & 0xFF == ord('q'):
break
fps.update()
fps.stop()
print(f'[INFO] elapsed time: {fps.elapsed():.2f}')
print(f'[INFO] approx. FPS: {fps.fps():.2f}')
cv2.destroyAllWindows()
vs.stop()
if __name__ == '__main__':
init_stream()
Клиент
Веб-страница с пустым изображением и подключением необходимых библиотек Firebase.
web_client/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Webcam live stream</title>
</head>
<body>
<img src="" id="camView">
<!-- The core Firebase JS SDK is always required and must be listed first -->
<script src="https://www.gstatic.com/firebasejs/7.17.2/firebase-app.js"></script>
<!-- SDKs for Firebase products
https://firebase.google.com/docs/web/setup#available-libraries -->
<script src="https://www.gstatic.com/firebasejs/7.17.2/firebase-database.js"></script>
<script src="main.js"></script>
</body>
</html>
После загрузки страницы на ней выполняется JavaScript-код, также использующий для подключения к Firebase файл firebase_config.json
. После успешного подключения создаётся «слушатель» события изменения содержимого frame
в базе, автоматически загружающий его в атрибут src
изображения с идентификатором camView
.
web_client/main.js
document.addEventListener('DOMContentLoaded', function () {
fetch('../firebase_config.json')
.then(response => response.json())
.then(data => {
// App's Firebase configuration
let firebaseConfig = data;
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
// Get a reference to the database service
let db = firebase.database();
// Listen for value updates
let ref = firebase.database().ref('frame');
ref.on('value', function(snapshot) {
document.getElementById('camView').src = snapshot.val();
});
});
});
Результат
Система работает как и ожидалось! Несмотря на размер кадра около 100 Кб, даже при отправке 5 кадров в секунду изображение на клиенте обновляется практически без задержек. Этого вполне достаточно для демонстрации и на данный момент результат меня полностью устраивает. Единственное, что меня смущает, это объём передаваемых данных. Даже если отправлять по одному кадру в секунду, уложиться в бесплатную квоту в 384 Мб/сутки всё равно не получится. Над этим вопросом надо будет поработать.
В любом случае цель достигнута, Firebase отлично справился с задачей, а решение без проблем можно внедрять в реальных проектах. 😎
Репозиторий: https://github.com/RiseLab/webcam_stream_firebase
Демо-клиент: https://riselab.github.io/webcam_stream_firebase