«Залейте видео в <video> и всё будет работать» — так делают на лендинге с одним 5-секундным роликом. Если у вас полнометражные видео, обучающие курсы, прямые трансляции или платный контент — нужен полноценный стриминг с адаптивным битрейтом, нормальным CDN и плеером, который не уронит браузер. Разбираем.
Простой <video> — когда хватает
<video src="/video/hero.mp4" autoplay muted playsinline loop poster="/video/hero.jpg" />
Подходит, если:
- Видео < 30 секунд и < 5 МБ.
- Один файл одного качества.
- Не платный контент.
- Аудитория с быстрым интернетом.
Для всего остального — стриминг.
HLS vs DASH
Два основных протокола адаптивного стриминга. Видео нарезается на короткие сегменты (по 2-10 сек) разного качества, плеер выбирает подходящее по скорости сети.
| Критерий | HLS | DASH |
|---|---|---|
| Кто разработчик | Apple | MPEG / международный стандарт |
| Манифест | .m3u8 | .mpd |
| Контейнер сегмента | TS / fMP4 | fMP4 |
| Поддержка Safari | нативно | через MSE |
| Поддержка Chrome | через hls.js | нативно или через shaka |
| Стандарт в РФ | HLS (90% случаев) | реже |
| DRM | FairPlay | Widevine / PlayReady |
Прагматично — выбирайте HLS. Один формат покрывает всё, в т.ч. iPhone (где DASH без MSE не работает).
Создание HLS из исходника
ffmpeg — стандарт:
ffmpeg -i source.mp4 \
-filter_complex \
"[0:v]split=3[v1][v2][v3]; \
[v1]scale=w=1920:h=1080[v1out]; \
[v2]scale=w=1280:h=720[v2out]; \
[v3]scale=w=854:h=480[v3out]" \
-map [v1out] -c:v:0 libx264 -b:v:0 5000k \
-map [v2out] -c:v:1 libx264 -b:v:1 2800k \
-map [v3out] -c:v:2 libx264 -b:v:2 1400k \
-map a:0 -map a:0 -map a:0 \
-var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2" \
-f hls -hls_time 6 -hls_playlist_type vod \
-master_pl_name master.m3u8 \
output_%v/playlist.m3u8
На выходе: master.m3u8 (выбор качества) + папки с сегментами для каждого качества. Загружаете в S3-совместимое хранилище, раздаёте через CDN.
Готовые сервисы
| Сервис | Особенность | Цена |
|---|---|---|
| MUX | топовое API, аналитика, оплата проблематична | $0.005/мин encoding |
| Cloudflare Stream | дёшево, простое API | $5/1000 мин хранения |
| Bunny Stream | дёшево, российский трафик ОК | $1/1000 мин |
| VK Видео API | российский, но ограниченный API | бесплатно с ограничениями |
| Yandex Video Cloud | российский, под новостные / VOD | контракт |
| Kinescope | российский, конкурент Vimeo | от 2 000 ₽/мес |
| Свой стек (ffmpeg + S3 + CDN) | контроль, нужна команда | стоимость инфры |
Для российского проекта 2026 — Kinescope или свой стек на Yandex/Selectel CDN. MUX и Cloudflare — отлично, но с оплатой сложно.
Плееры
| Плеер | Технология | Когда |
|---|---|---|
| video.js | универсальный, плагины | стандарт по умолчанию |
| hls.js | HLS на всех браузерах | если нужен только HLS |
| Shaka Player | DASH + HLS, Google | сложные сценарии |
| Plyr | минималистичный UI | лендинги, простые случаи |
| MUX Player | если используете MUX | brand consistency |
"use client";
import Hls from "hls.js";
import { useEffect, useRef } from "react";
export function HlsVideo({ src, poster }) {
const ref = useRef<HTMLVideoElement>(null);
useEffect(() => {
const video = ref.current!;
if (video.canPlayType("application/vnd.apple.mpegurl")) {
video.src = src;
} else if (Hls.isSupported()) {
const hls = new Hls();
hls.loadSource(src);
hls.attachMedia(video);
return () => hls.destroy();
}
}, [src]);
return <video ref={ref} controls poster={poster} playsInline />;
}
Safari читает HLS нативно, для остальных — hls.js.
Адаптивный битрейт
Плеер сам выбирает качество исходя из пропускной способности и буфера. Главное — иметь несколько качеств в манифесте (минимум 3: 480p, 720p, 1080p).
В UI:
- Auto (по умолчанию) — плеер сам решает.
- Ручной выбор — пользователь может зафиксировать.
- Индикатор текущего качества — особенно для платного контента.
Защита контента
«Чтобы не скачивали» — частый запрос. Реальность:
- Запретить полностью — нельзя. Любое видео, которое можно посмотреть, можно записать (хотя бы экран).
- Усложнить — да. DRM (Widevine, FairPlay, PlayReady), signed URLs с TTL, токены доступа на сегменты.
- Watermarking — разный сегмент для разных пользователей, можно отследить, кто слил.
Простая защита (для базы):
// signed URL на манифест
const token = jwt.sign({ video: id, exp: Date.now() / 1000 + 3600 }, SECRET);
const url = `https://cdn.example.ru/videos/${id}/master.m3u8?token=${token}`;
CDN проверяет токен — если истёк или подделан, 403. Для пиратов это не препятствие, но 95% «случайных скачиваний» отсекает.
Live-стриминг
Прямой эфир — отдельная история. Вебинары, события, спортивные трансляции.
Стек:
- Источник (камера, OBS) шлёт RTMP на ingress-сервер.
- Сервер (nginx-rtmp, Restreamer, MediaMTX) пакует в HLS/LL-HLS.
- CDN раздаёт сегменты.
- Плеер показывает.
Задержка: классический HLS — 20-30 секунд (3 сегмента по 6-10 сек). LL-HLS — 2-5 секунд. WebRTC — < 1 секунды (но сложно масштабировать на 10к+ зрителей).
Аналитика
Что мерять:
- Старт воспроизведения (% пользователей, начавших).
- Удержание (минута за минутой, где пользователи уходят).
- Переключения качества (если много — сеть слабая или плеер плохо подобран).
- Buffering events (плохой пользовательский опыт).
- Среднее время просмотра.
MUX Data — стандарт, но платный. Свой подход — посылать события на свой бэкенд + Plausible/PostHog.
Стоимость инфраструктуры
Пример: 1000 уникальных просмотров в день среднего видео (10 минут, 720p, 2 Мбит/с):
- Трафик: 1000 × 10 мин × 60 сек × 2 Мбит/с / 8 = 150 ГБ/день, 4.5 ТБ/мес.
- Yandex CDN: ~₽3-7/ГБ → 14-30 тыс. ₽/мес.
- Bunny CDN: ~$0.005/ГБ → ~$22/мес.
- Свой VPS как origin (S3 + CDN кеш) — ещё пара тыс. ₽.
Для маленького проекта подъёмно. Для миллионов просмотров — нужно тщательно считать.
Контент в РФ и юрисдикция
Размещение видео с пользовательским контентом подпадает под Закон об ОРИ (организаторах распространения информации). Для маленьких проектов с собственным контентом — обычно вне зоны риска. Для UGC-платформ — отдельная юридическая работа.
Также: видео для российской аудитории с зарубежного CDN — может быть медленно или временами недоступно. Используйте российский CDN как primary.
Итого
Для лендинга — <video> с MP4. Для блога с обучающими роликами — Kinescope или Bunny Stream, минимум возни. Для платформы с тысячами часов видео — свой стек на ffmpeg + S3 + CDN с продуманной защитой и аналитикой. Главное — заранее посчитать трафик, иначе CDN-счёт может приехать неожиданным сюрпризом.
Частые вопросы
HLS или MP4 для лендинга?
MP4, если ролик короткий (до 30 сек) и одного качества. HLS — когда нужна адаптивность к сети, длительные видео, защита контента. На лендинге обычно автозапускающееся видео в hero — это MP4 с muted+playsinline, 5 МБ максимум, иначе мобильные пользователи будут проклинать.
Сколько стоит загрузить и хранить видео?
Зависит от объёма. Для 100 видео по 10 мин в 1080p — ~50-100 ГБ хранения, S3-совместимый Yandex Object Storage от 1.5 ₽/ГБ/мес = 75-150 ₽/мес за хранение. Энкодинг — единоразово, тратит CPU (час 1080p в HLS на быстром сервере — 5-15 минут). Самые большие расходы — трафик отдачи, не хранение.
Можно ли защитить видео от скачивания полностью?
Нет. Любое видео, которое отображается, можно записать с экрана. Можно усложнить: DRM (Widevine + FairPlay) блокирует браузерное скачивание и простую запись через расширения. Watermarking позволит отследить, кто слил. Но 100% защита невозможна, и не стоит тратить на неё месяцы — лучше инвестируйте в качество контента.
Kinescope или свой стек?
Kinescope — если у вас 5-50 видео и не хочется заниматься энкодингом, CDN, аналитикой. От 2-5 тыс. ₽/мес, всё работает из коробки. Свой стек — если 1000+ видео, нужен полный контроль и кастомная защита. Точка перехода — обычно ~10-20 тыс. ₽/мес расходов на Kinescope, тогда self-hosted уже выгоден.
LL-HLS или WebRTC для прямого эфира?
Зависит от задачи. LL-HLS (2-5 сек задержки) — для трансляций один-к-многим (вебинары, спорт): легко масштабируется через CDN, сотни тысяч зрителей. WebRTC (< 1 сек) — для интерактивных форматов (онлайн-аукционы, обучение с диалогом): сложнее масштабировать, нужен SFU. Для большинства бизнес-вебинаров достаточно LL-HLS.
Как сделать subtitle и многоязычные дорожки?
HLS поддерживает: WebVTT для субтитров, отдельные альтернативные аудио-дорожки. В манифесте — #EXT-X-MEDIA:TYPE=SUBTITLES,... и TYPE=AUDIO,.... Плееры (hls.js, video.js) автоматически дают переключатель в UI. Для простого случая (один язык субтитров) хватает обычного <track src="ru.vtt" kind="subtitles"> в video теге.
Какое разрешение и битрейт оптимально?
Стандартный набор: 480p @ 1.4 Mbps, 720p @ 2.8 Mbps, 1080p @ 5 Mbps. Если контент критичен по качеству (фильмы) — добавьте 1440p @ 8 Mbps. Для лекций и говорящих голов хватает 720p, 1080p редко нужно. Кодек H.264 — универсально, H.265/HEVC — на 30-50% меньше при том же качестве, но не везде поддерживается без аппаратной декодировки.