Video источник

Архитектура video source в картографическом рендеринге

В MapLibre GL JS предусмотрен специальный тип источника данных video, предназначенный для наложения видеопотока на карту в виде геопривязанного слоя. Этот механизм реализует концепцию текстурирования карты динамическим медиа-контентом, где видео рассматривается как непрерывная растровая поверхность, ограниченная географическими координатами.

Видео-источник не является геоданными в классическом смысле (GeoJSON или векторные тайлы). Он представляет собой WebGL-текстуру, которая обновляется кадр за кадром и синхронизируется с рендерингом карты. Основой служит объект HTMLVideoElement, который используется браузером как источник пиксельного потока.

Ключевое свойство video source заключается в том, что он позволяет:

  • накладывать аэровидеосъёмку на карту;
  • визуализировать динамические процессы (движение объектов, погодные явления);
  • использовать видеоматериалы как временные слои наблюдений;
  • интегрировать камеры наблюдения с геопривязкой.

Базовая структура video source

Источник типа video задаётся через метод addSource, где указываются URL видеопотока и координаты привязки:

map.addSource('video-layer', {
  type: 'video',
  urls: [
    'https://example.com/video.mp4'
  ],
  coordinates: [
    [-122.51596391201019, 37.56238816766053],
    [-122.51467645168304, 37.56410183312965],
    [-122.51309394836426, 37.563391708549425],
    [-122.51423120498657, 37.56161849366671]
  ]
});

Здесь:

  • urls — массив источников видео (обычно один URL, но возможны fallback-варианты);
  • coordinates — массив из четырёх точек, определяющих географический прямоугольник наложения;
  • порядок координат критичен: они задаются по часовой стрелке или против часовой, формируя полигон.

Геопривязка видеопотока

Видео в MapLibre GL JS всегда проецируется на плоскость карты через четырёхугольную трансформацию. Каждая точка соответствует углу видеокадра:

  1. верхний левый угол;
  2. верхний правый угол;
  3. нижний правый угол;
  4. нижний левый угол.

Такая схема позволяет выполнять аффинное преобразование изображения в экранное пространство карты с учётом:

  • масштаба;
  • поворота карты;
  • перспективной проекции;
  • наклона камеры (pitch).

Видео не «знает» о географии — геопривязка полностью определяется координатами, заданными разработчиком.


Добавление слоя поверх video source

После определения источника необходимо создать слой raster, который будет отображать видеоданные:

map.addLayer({
  id: 'video-layer-display',
  type: 'raster',
  source: 'video-layer'
});

Внутренне MapLibre обрабатывает видео как текстуру WebGL и применяет её к кваду (two triangles forming a rectangle), который соответствует координатам источника.


Управление воспроизведением

Видео-источник тесно связан с API HTMLVideoElement. Поведение воспроизведения зависит от браузера и политики autoplay.

Часто используется ручное управление:

const videoSource = document.createElement('video');
videoSource.src = 'https://example.com/video.mp4';
videoSource.loop = true;
videoSource.muted = true;
videoSource.play();

Далее видео передаётся в MapLibre:

map.addSource('video-layer', {
  type: 'video',
  urls: [videoSource.src],
  coordinates: [...]
});

В более сложных сценариях можно использовать живые потоки (HLS через hls.js или WebRTC), но MapLibre работает только с декодированным видеопотоком браузера.


Использование потокового видео (live stream)

Для интеграции потокового видео применяется промежуточный слой, который обеспечивает доступ к MediaStream:

navigator.mediaDevices.getUserMedia({ video: true })
  .then(stream => {
    const video = document.createElement('video');
    video.srcObject = stream;
    video.play();

    map.addSource('live-camera', {
      type: 'video',
      urls: [],
      video: video,
      coordinates: [...]
    });
  });

Такой подход используется в системах:

  • городского видеонаблюдения;
  • мониторинга дорожного движения;
  • дрон-съёмки в реальном времени.

Координатная трансформация и WebGL pipeline

Внутренне MapLibre GL JS преобразует видео в текстуру WebGL через следующий pipeline:

  1. получение кадра из HTMLVideoElement;
  2. копирование в GPU-текстуру;
  3. привязка текстуры к геометрии quad;
  4. применение матрицы проекции камеры карты;
  5. рендеринг в сцене вместе с другими слоями.

Каждый кадр видео синхронизируется с render loop карты. При этом частота обновления зависит от FPS видео и производительности GPU.


Особенности работы с прозрачностью и наложением слоёв

Видео-источник не поддерживает альфа-канал в классическом смысле, однако поведение прозрачности может быть имитировано через:

  • CSS opacity слоя;
  • blend modes WebGL (в ограниченной форме);
  • маскирование через дополнительные polygon layers.

Пример изменения прозрачности:

map.setPaintProperty('video-layer-display', 'raster-opacity', 0.6);

Ограничения video source

Видео-источник имеет ряд технических ограничений, обусловленных архитектурой браузерного рендеринга:

  • отсутствие поддержки нестандартных кодеков вне браузера;
  • невозможность прямой геопривязки внутри видео;
  • зависимость от autoplay policy браузера;
  • ограничение на количество одновременно активных видео слоёв;
  • высокая нагрузка на GPU при увеличении разрешения.

Также важно учитывать, что видео всегда интерпретируется как плоская поверхность, без глубины и 3D-структуры.


Синхронизация с другими слоями карты

Video source может комбинироваться с векторными и растровыми слоями. Типичный сценарий — наложение видео на спутниковую карту:

map.addLayer({
  id: 'roads',
  type: 'line',
  source: 'vector-roads'
});

map.addLayer({
  id: 'video-overlay',
  type: 'raster',
  source: 'video-layer'
});

Порядок слоёв определяет визуальную иерархию. Видео может быть как подложкой, так и верхним слоем, в зависимости от задачи визуализации.


Практические сценарии применения

Видео-источник применяется в системах, где требуется совмещение визуального потока и географического контекста:

  • анализ трафика на перекрёстках с камер наблюдения;
  • визуализация полётов беспилотников;
  • привязка архивных аэросъёмок к картографической подложке;
  • мониторинг строительных площадок;
  • экологические наблюдения (ледники, лесные пожары, водоёмы).

Проблемы синхронизации и производительности

Основные технические сложности возникают в следующих областях:

Декодирование видео

  • нагрузка на CPU при слабом аппаратном ускорении;
  • задержки при перемотке и буферизации.

WebGL текстуры

  • необходимость постоянного обновления texture binding;
  • ограничение видеоресурсов GPU.

Рендеринг карты

  • конфликт FPS карты и FPS видео;
  • просадки при большом количестве слоёв.

Оптимизация обычно достигается через:

  • уменьшение разрешения видео;
  • ограничение FPS;
  • использование hardware decoding;
  • упрощение других слоёв сцены.

Поведение при изменении масштаба и проекции

Видео-слой автоматически трансформируется при:

  • zoom-in / zoom-out;
  • изменении pitch (наклона камеры);
  • вращении карты (bearing).

Это достигается пересчётом матрицы трансформации quad в экранные координаты. Видео остаётся «приклеенным» к географическому прямоугольнику независимо от перспективных искажений.


Динамическое обновление координат

В некоторых задачах требуется изменять геопривязку видео в реальном времени:

map.getSource('video-layer').setCoordinates([
  [-122.52, 37.56],
  [-122.51, 37.56],
  [-122.51, 37.55],
  [-122.52, 37.55]
]);

Такой механизм используется при:

  • калибровке дрон-съёмки;
  • корректировке положения камеры;
  • стабилизации изображения.

Интеграция с аналитическими слоями

Video source часто комбинируется с аналитическими overlay-слоями:

  • детекция объектов (bounding boxes поверх видео);
  • heatmap поверх дорожного трафика;
  • траектории движения поверх видеопотока.

MapLibre обеспечивает базовую отрисовку, тогда как аналитическая логика реализуется на стороне приложения.


Поведение при отсутствии видеоданных

При недоступности источника видео:

  • слой остаётся пустым;
  • может отображаться последний буферизированный кадр;
  • WebGL текстура не обновляется, но сохраняется в памяти.

Fallback-логика обычно реализуется через переключение источников или скрытие слоя:

map.setLayoutProperty('video-layer-display', 'visibility', 'none');