Если вам нужно добавить на свой сайт видео, то может возникнуть вопрос, где его хостить и как потом раздавать. В этом посте разберем варианты и рассмотрим примеры использования Yandex Object Storage.

Формат MP4 знаком, наверное, всем. Но если мы хотим показывать видео на сайте эффективно, то стоит задуматься: лучшее ли это решение для нашей задачи — просто выложить все видео одним файлом MP4?

HLS

Протокол HLS (HTTP Live Streaming) был предложен в 2009 году и к настоящему времени де-факто стал стандартом в области адаптивного видеостриминга.

HLS был разработан Apple как замена их собственной разработки QuickTime Streaming Server, а также как альтернатива другому популярному на тот момент протоколу — Real-Time Messaging Protocol (RTMP). Этот протокол, выпущенный Adobe в 2002 году, использовал технологию Flash, чтобы передавать видео с низкой задержкой через интернет. Не смотря на то, что Flash давно «умер», RTMP до сих пор остается популярным протоколом для видеотрансляций.

Что же такое HLS?

Хотя HLS не так широко известен, но он, наряду с MPEG-DASH и SRT, остается одним из главных протоколов, которые обеспечивают доставку видео- и аудиоконтента в современном интернете. Велики шансы, что вы пользуетесь им каждый день, даже не задумываясь об этом. Особенно если вы — пользователь экосистемы Apple.

Хотя HLS и был разработан в Apple, он широко поддерживается и на других платформах: Linux, Windows, смартфонах с Android и iOS, OTT-устройствах, Smart TV, а также в браузерах Google Chrome, Safari, Android Browser, Microsoft Edge, Chrome for Android и Opera Mobile. На части платформ поддержка нативная, в других же она реализована через JS-плееры.

Тот факт, что потоковая передача HLS обычно осуществляется через видеоплеер HTML5, делает HLS универсальным и широко совместимым. Его часто называют «HTML5-видео», что не совсем точно. Видеоплеер HTML5 просто совместим с HLS.

Что значит «протокол»?

HLS — это протокол потоковой передачи видео. Но что это означает? Возможно, вы слышали термин «кодек», но важно отметить, что потоковый протокол — это не кодек. Протокол — это более широкая категория.
Потоковый протокол — это стандартизированный метод передачи видеоконтента между устройствами. Протокол может предполагать использование определенного кодека (или кодеков), который будет использоваться для сжатия и распаковки видео- и аудиоконтента.
Например, HLS поддерживает множество популярных кодеков:

  • Audio: AAC-LC, HE-AAC+ v1 & v2, xHE-AAC, Apple Lossless, FLAC

  • Video: H.265, H.264

Преимущества HLS

HLS предназначен для обеспечения надежности и динамической адаптации к сетевым условиям путем оптимизации (т. е. ухудшения или улучшения) воспроизведения видео для доступных скоростей интернета. Кроме того, он «заботится» о разных аспектах, которые связаны с доставкой видеоконтента зрителям:

  • Стриминг с адаптивным битрейтом (Adaptive Bitrate Streaming) — HLS подстраивает качество видео под текущую скорость интернет-канала. Это позволяет авторам предлагать несколько видеопотоков в разном качестве, а плееру переключаться между ними незаметно.

Пример среднего битрейта видео (Кбит/с), источник: Apple

  • Совместимость со многими устройствами — HLS гарантирует, что ваш видеоконтент будет воспроизводиться на любом устройстве, которое может запустить совместимый плеер (например, HTML5).

  • Субтитры — HLS поддерживает скрытые субтитры. Это позволяет авторам встроить несколько потоков субтитров (например, на разных языках).

  • Возможность менять аудио дорожку — HLS поддерживает несколько потоков аудио, между которыми можно переключаться.

  • Возможность вставлять рекламу — HLS использует для этого технологии VPAID и VAST.

  • Масштабирование — HLS обладает высокой масштабируемостью для доставки видеофайлов и потокового контента через глобальные сети доставки контента (CDN). В отличие от протокола RTMP, который работает совместно с Flash Player, HLS может легко масштабироваться для доставки с помощью обычных веб-серверов через CDN. Распределяя рабочую нагрузку по сети серверов, CDN приспосабливаются к всплескам вирусной аудитории и гораздо большим, чем ожидалось, живым аудиториям. CDN также помогают улучшить качество просмотра, кешируя аудио- и видеосегменты. Для сравнения, поддержка CDN для RTMP быстро снижается. RTMP также требует использования выделенного сервера потоковой передачи, что делает его более ресурсоемким для развертывания.

  • Защита от пиратства — реализуется через поддержку большого количества технологий DRM.

Вот более подробный пост про то, как можно ограничить доступ к видео при помощи шифрования AES-128.

Недостатки HLS

HLS имеет некоторые ограничения, но они связаны с конкретными проектными решениями, которые были приняты при разработке протокола.

Часть этих ограничений связана с задержкой. HLS ставит во главу угла комфорт пользователя, а не низкую задержку, поэтому live-контент, который транслируется с использованием протокола HLS, не является достаточно «живым». Зритель увидит его с задержкой до 30 секунд. Таким образом, HLS — не лучший выбор для таких приложений, как веб-конференции или управление устройствами, где необходимо видео в реальном времени (камеры и дроны). В этом случае лучше использовать более быстрый потоковый протокол, например WebRTC (Web Real-Time Communications).

Задержка в HLS возникает из-за того, что протокол разбивает видео на множество многосекундных фрагментов (сегментов), которые обычно имеют длительность 2–6 секунд. И поскольку протокол HLS также должен буферизировать несколько таких небольших сегментов одновременно, задержка может составлять десятки секунд. Однако если задержка или плохие сетевые условия не являются проблемой, то HLS — это протокол, который вам нужен.

Итак, почему стоит использовать именно HLS?

  • С HLS создатели контента могут подготовить версии своего видео для нескольких различных интернет-каналов и условий воспроизведения: 3G, 4G, LTE, медленный публичный Wi‑Fi, быстрый домашний интернет.

  • Различные поставщики CDN постепенно отказываются от поддержки RTMP, заявляя, что его развертывание становится слишком дорогим. Вместо этого набирают популярность такие протоколы, как HLS, SRT и MPEG-DASH.

  • Adobe перестал поддерживать технологию, на которую опирается RTMP. Поэтому однажды ваш процесс потоковой передачи RTMP станет технологически устаревшим и потеряет поддержку со стороны производителя. Это лишь вопрос времени.

  • HLS оптимизирует доставку аудио и видео на обширный спектр мобильных, настольных, планшетных и OTT-устройств.

  • HLS позволяет доставлять видео по запросу с помощью шифрования и аутентификации.

  • HLS значительно снижает затраты на CDN. При этом он обеспечивает только оптимальный битрейт для клиентской сети и избегает сценария «частичного воспроизведения и полной загрузки», который присущ прогрессивной потоковой передаче HTTP. Например, когда всё видео представлено одним файлом MP4.

  • Apple App Store требует, чтобы приложения с десятиминутным (и более) видео использовали HLS.

Практическая часть

Надеюсь, я смог вас убедить, что сегодня для распространения видеоконтента через интернет стоит смотреть в сторону HLS.

Давайте теперь на примере рассмотрим способ, как нам использовать объектное хранилище в Yandex.Cloud.

Object Storage — это решение для хранения больших объемов данных за относительно небольшие деньги. Оно идеально подходит для чего-то вроде видеофайлов, которые, как правило, имеют довольно большой объем. Доступ к файлам (или объектам, как их часто называют) осуществляется через HTTP(S), что делает Object Storage отличным решением для хранения и обслуживания ваших HLS-видео.

Для начала нам понадобится бакет в хранилище. Если у вас его нет, то вот инструкция, как его создать.

Подготовка видео

Если у вас уже есть видео в формате HLS, то смело переходите к следующему разделу.

Если же его нет, то вам понадобится утилита ffmpeg, при помощи которой вы сможете конвертировать видео и аудио во множество форматов, в том числе HLS.

Установка ffmpeg

Windows

  1. Скачайте последнюю версию отсюда.

  2. Разархивируйте.

  3. Откройте консоль в папке.

  4. Выполните команду ./ffmpeg — вы должны будете увидеть информацию о версии ffmpeg.

Mac OS X

  1. Установите homebrew.

  2. Выполните команду brew install ffmpeg

  3. Вызовите ffmpeg — вы должны будете увидеть информацию о версии ffmpeg.

Ubuntu

Выполните команды:

 sudo add-apt-repository ppa:mc3man/trusty-media   
 sudo apt-get update   
 sudo apt-get install -y ffmpeg

CentOS или Fedora

Выполните команду:

yum install -y ffmpeg

Видео для примера

Возьмем какой-нибудь видеофайл и посмотрим информацию о нем:

ffprobe -hide_banner sample.mkv

В консоли появится приблизительно следующее:

Input #0, matroska,webm, from 'sample.mkv':
  Metadata:
    title           : Big Buck Bunny
    encoder         : libebml v0.7.8 + libmatroska v0.8.1
    creation_time   : 2008-11-17T23:28:21.000000Z
  Duration: 00:09:56.48, start: 0.000000, bitrate: 9733 kb/s
    Stream #0:0: Video: h264 (Main), yuv420p(tv, bt709, progressive), 1920x1080, SAR 1:1 DAR 16:9, 24 fps, 24 tbr, 1k tbn, 2k tbc (default)
    Metadata:
      title           : Big Buck Bunny
    Stream #0:1: Audio: ac3, 48000 Hz, 5.1(side), fltp, 448 kb/s (default)
    Metadata:
      title           : AC3 5.1 448kbps
    Stream #0:2: Subtitle: subrip (default)
    Metadata:
      title           : Intro

Конвертация

Подготовим команду для конвертации видео:

export filename=sample
mkdir $filename
ffmpeg -i $filename.mkv -vf \ scale=w=1280:h=720:force_original_aspect_ratio=decrease \
 -map 0:v:0 -map 0:a:0 \
 -c:a:0 -ar 48000 \
 -c:v h264 -profile:v main -crf 20 -sc_threshold 0 \
 -g 48 -keyint_min 48 -hls_time 4 \
 -hls_playlist_type vod \
 -b:v 2800k -maxrate 2996k -bufsize 4200k 
 -b:a 128k \
 -hls_segment_filename $filename/720p_%03d.ts $filename/720p.m3u8

Теперь подробно разберем, что означают все эти ключи:

  • -i sample.mp4 — задает sample.mp4 в качестве входного файла.

  • -vf "scale=w=1280:h=720:force_original_aspect_ratio=decrease" — масштабирует видео до максимальных размеров в пределах заданного разрешения 1280x720 с сохранением соотношения сторон.

  • -c:a:0 aac -ar 48000 -ac:a:0 2 -b:a:0 128k — задает аудиокодек AAC с частотой дискретизации 48кГц и битрейтом 128K.

  • -ac:a:0 2 — указывает, что если в первом аудиопотоке (a:0) больше двух каналов, нужно смиксовать их в стереосигнал.

  • -c:v h264 — устанавливает видеокодек H.264, который является стандартным кодеком для HLS. Это кодек без аппаратного ускорения. Если вы будете конвертировать видео на MacOS X, то можете попробовать использовать значение h264_videotoolbox. Для других платформ также есть кодеки с поддержкой аппаратного ускорения.

  • -profile:v main — устанавливает профиль кодека H.264 в main (это означает включение поддержки всех современных устройств). Подробнее в статье.

  • -crf 20 — Constant Rate Factor, высокоуровневая настройка качества.

  • -g 48 -keyint_min 48 — указывает, что нужно создавать ключевой кадр (I-frame) через каждые 48 кадров (~2 сек) — это важно, влияет на корректную нарезку на сегменты.

  • -sc_threshold 0 — не создавать ключевые кадры при смене сцены.

  • -b:v:0 2500k -maxrate 2675k -bufsize 3750k — ограничить битрейт видео. Конкретные значения стоит подбирать исходя из особенностей вашего видео. Подробнее в разделе «Как правильно выбрать битрейт».

  • -hls_time 4 — указывает длину сегмента в секундах. Реальная длина будет зависеть от ключевых кадров.

  • -hls_playlist_type vod — добавляет тег #EXT-X-PLAYLIST-TYPE:VOD и сохраняет все сегменты в плейлист.

  • -hls_segment_filename sample/720p_%03d.ts — явным образом задает имена файлов для сегментов.

  • sample/720p.m3u8 — указывает путь до плейлиста, также говорит ffmpeg выводить его в формате HLS (файл .m3u8).

Эта команда сгенерирует HLS-плейлист и сегменты и сложит их в папку sample.

Утилита ffmpeg поддерживает несколько входных и выходных файлов и все результаты могут быть сгенерированы параллельно одной длинной командой.

Очень важно, чтобы помимо параметров разрешения и битрейта команды были идентичны, тогда результаты будут правильно выровнены, то есть ключевые кадры будут установлены в нужных местах, чтобы обеспечить плавное переключение между ними «на лету». 

Мы создадим четыре версии с разрешениями:

  • 1080p 1920x1080 (original)

  • 720p 1280x720

  • 480p 842x480

  • 360p 640x360

export filename=sample
mkdir $filename
ffmpeg -hide_banner -y -i $filename.mkv \
  -vf scale=w=640:h=360:force_original_aspect_ratio=decrease -c:a aac -ar 48000 -ac 2 -c:v h264 -profile:v main -crf 20 -sc_threshold 0 -g 48 -keyint_min 48 -hls_time 4 -hls_playlist_type vod  -b:v 800k -maxrate 856k -bufsize 1200k -b:a 96k -hls_segment_filename $filename/360p_%03d.ts $filename/360p.m3u8 \
  -vf scale=w=842:h=480:force_original_aspect_ratio=decrease -c:a aac -ar 48000 -ac 2 -c:v h264 -profile:v main -crf 20 -sc_threshold 0 -g 48 -keyint_min 48 -hls_time 4 -hls_playlist_type vod -b:v 1400k -maxrate 1498k -bufsize 2100k -b:a 128k -hls_segment_filename $filename/480p_%03d.ts $filename/480p.m3u8 \
  -vf scale=w=1280:h=720:force_original_aspect_ratio=decrease -c:a aac -ar 48000 -ac 2 -c:v h264 -profile:v main -crf 20 -sc_threshold 0 -g 48 -keyint_min 48 -hls_time 4 -hls_playlist_type vod -b:v 2800k -maxrate 2996k -bufsize 4200k -b:a 128k -hls_segment_filename $filename/720p_%03d.ts $filename/720p.m3u8 \
  -vf scale=w=1920:h=1080:force_original_aspect_ratio=decrease -c:a aac -ar 48000 -ac 2 -c:v h264 -profile:v main -crf 20 -sc_threshold 0 -g 48 -keyint_min 48 -hls_time 4 -hls_playlist_type vod -b:v 5000k -maxrate 5350k -bufsize 7500k -b:a 192k -hls_segment_filename $filename/1080p_%03d.ts $filename/1080p.m3u8

Мастер-плейлист

HLS-плеер должен знать, что существует несколько версий нашего видео, поэтому мы создаем мастер-плейлист HLS, чтобы указать их и сохранить вместе с другими плейлистами и сегментами в файл playlist.m3u8.

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=640x360
360p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1400000,RESOLUTION=842x480
480p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2800000,RESOLUTION=1280x720
720p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1920x1080
1080p.m3u8

Как правильно выбрать битрейт

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

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

Вот некоторые хорошие значения по умолчанию:

Загрузка данных

Убедитесь, что тип контента списка воспроизведения и сегментов видео (файлов .ts) установлен в application/x-mpegURL и video/MP2T соответственно. Если вы загружаете файлы при помощи s3cmd, то он проставит корректные заголовок Content-type будет установлен корректно.

Настройка бакета

Для того чтобы видеоплееры могли получить доступ к видеофайлам, необходимо указать настройки CORS в бакете.

Это максимально широкое правило для примера.

Все готово

Если вы хотите ограничить круг сайтов, куда можно будет встраивать видео, то прочитайте этот пост.

Теперь можно выкладывать видео на свой сайт.

import React, { useRef, useState } from "react"

import Layout from "../components/layout"
import ReactPlayer from "react-player"
import * as Hls from "hls.js"

const DemoPage = () => {
  const player = useRef(null)
  const [levels, setLevels] = useState([])

  const handleReady = (player) => {
    const hls = player.getInternalPlayer("hls") as Hls
    setLevels(hls.levels)
  }

  const handleLevelClick = (level) => () => {
    player.current.getInternalPlayer("hls").currentLevel = level
  }

  return (
    <Layout>
      <div style={{ maxWidth: "720px", marginBottom: "1.45rem" }}>
        <ReactPlayer
          ref={player}
          onReady={handleReady}
          className="react-player"
          url="https://expample.com/sample/playlist.m3u8"
          width="100%"
          height="100%"
          controls={true}
        />
      </div>
      <div>
        {levels.map((l, index) => <div onClick={handleLevelClick(index)} key={l.name}>{l.height}</div>)}
      </div>
    </Layout>
  )
}

export default DemoPage

В пример я добавил минимальный контрол для переключения уровня качества.

Вот использованный плеер и react-обертка. А вот альтернативный плеер.

Итак, в чем же плюс?

Если вы откроете консоль, то увидите, что после того как браузер загрузил несколько первых сегментов видео, загрузка приостанавливается. Это позволяет нам экономить трафик в тех случаях, когда пользователь открыл страницу с видео, начал просмотр видео и остановился. В этом случае нам не нужно загружать весь видеофайл целиком. Мы можем вовремя остановиться, а потом, если это понадобится (пользователь продолжит воспроизведение), загрузить остальное.

П.С.

Позже мы еще вернемся к теме HLS и вопросу использования сервисов Yandex.Cloud. А пока напомню, что Object Storage является одним из сервисов экосистемы serverless в Yandex.Cloud, и есть живое растущее serverless-комьюнити Yandex Serverless Ecosystem, где можно задавать вопросы и оперативно получать ответы от единомышленников и коллег. Присоединяйтесь!

И еще в Yandex.Cloud действует программа free tier. Она позволяет реализовать массу проектов бесплатно, если не выходить за лимиты.

Комментарии (11)


  1. Alexufo
    01.09.2021 11:41
    +3

    Для этого лучше использовать стриминговые медиасерверы чем велосипеды на ffmpeg

    https://habr.com/ru/post/532424/


    1. nik_the_spirit Автор
      01.09.2021 11:49
      +1

      Все так. В вашем посте описывается решение для стримминга. А тут скорее описан подход для статики, начиная с основ технологии. Хотелось показать, что в HLS нет никакой магии, а сделать это проще на элементарном примере с ffmpeg.


      1. Alexufo
        01.09.2021 16:35

        согласен, а цена за отдачу трафика не кусается на яндексе? По-моему она кусачая, даже очень.


        1. nik_the_spirit Автор
          01.09.2021 17:36

          Вот про это хотелось рассказать в следующем посте. Сейчас в превью есть CDN https://cloud.yandex.ru/services/cdn, цены на трафик оттуда ниже чем непосредственно из Yandex Object Storage.

          Степень «кусачасти» цен оценивать не берусь. Если сравнивать с публично доступными ценами конкурентов, которые я смог найти, то выглядит весьма привлекательно.


  1. ufoton
    01.09.2021 13:05
    +2

    На aws легко построить пайплайн который будет брать видео, разбивать его, создавать плейлисты.. и более того в случае необходимости отдавать dash или mss


    1. nik_the_spirit Автор
      01.09.2021 13:13
      +1

      Если вы про https://aws.amazon.com/elastictranscoder/, то да, к сожалению, в Яндекс Облаке пока нет аналогичного managed решения.


  1. andreymal
    01.09.2021 14:23
    +1

    Надеюсь, я смог вас убедить, что сегодня для распространения видеоконтента через интернет стоит смотреть в сторону HLS.

    Не смогли, так как отсутствует сравнение с MPEG-DASH

    Кстати, HLS с недавних пор получил поддержку fMP4, так что с помощью того же ffmpeg можно сгенерировать одновременно HLS и MPEG-DASH с переиспользованием одних и тех же сегментов


    1. nik_the_spirit Автор
      01.09.2021 14:39
      +1

      Вы правы, но сравнение их может потянуть на отдельный пост. Моя же цель была рассказать, что обычный MP4 одним файлом — не лучшая идея.
      Одним из аргументов в пользу HLS может служить нативная поддержка этого формата браузерами: https://caniuse.com/?search=hls. В остальном это довольно схожие по функциональности и подходу форматы, имеющие примерно одинаковую поддержку сторонними плеерами.


  1. Qwerty710
    01.09.2021 19:42
    -1

    Вот это да! Теперь чтобы смотреть видео нужен Javascript. Прогресс


  1. kidar2
    19.10.2021 20:25

    А можно ли как-то работать с видео файлами на s3 через ffmpeg?


    1. nik_the_spirit Автор
      20.10.2021 12:25
      +1

      На сколько я знаю, ffmpeg умеет сам получать доступ к файлам по HTTP(S). Нам нужно лишь сформировать ссылку. Если речь идет про бакет без публичного доступа, то можно воспользоваться предподписанной ссылкой. Например, так из сомандной строки.

      ffmpeg -i "$(aws s3 presign s3://MY_BUCKET/MY_FILE --expires 5)"

      Ну или формировать ее при помощи SDK на вашем любимом языке программирования.