Офлайн-скачивание и воспроизведение видео

Автор: Николай СапуновОбновлено: август 202614 мин чтения
Содержание статьи +

TL;DR

Дать зрителю скачать серию, чтобы посмотреть её в самолёте, – это не кнопка, которую добавляют в плеер, а отдельный путь воспроизведения со своим менеджером загрузок, своей хранимой лицензией и своими правилами. То, что позволяет скачанному видео играть без сети, называется постоянная лицензия (persistent license): небольшой пакет с ключами расшифровки и правилами, записанный на устройство, пока оно ещё онлайн; для веба это описано в стандарте W3C Encrypted Media Extensions, а нативно его реализуют FairPlay на Apple, Widevine на Android и PlayReady на Windows и многих телевизорах. Для офлайна вы ничего не перешифровываете – скачанное видео это тот же файл с шифрованием cbcs, что вы уже стримите, так что новое здесь только тип лицензии. Дальше загрузкой правят два таймера: окно хранения (сколько дней живёт лицензия) и окно воспроизведения (стартует при первом запуске), и оба задаёт правообладатель – он же решает, разрешён ли офлайн вообще.

Почему это важно

Загрузки – это то, как стриминговый сервис удерживает время просмотра в дороге, в полёте и там, где сеть слабая или платная, и это заметная функция почти любого подписного приложения, поэтому продакт-менеджера спросят о ней рано. Ловушка – считать «загрузки» небольшой задачей фронтенда, тогда как на деле это функция защиты контента: ошибитесь в модели лицензии, и сохранённый фильм либо откажется играть в режиме полёта, либо, хуже, будет играть вечно после конца аренды и нарушит договор со студией. Эта статья объясняет офлайн-скачивание и воспроизведение простым языком, чтобы вы могли оценить объём работ, разобрать SDK плеера у подрядчика и точно поговорить и с инженерами, и с правообладателями. Это глубокое продолжение раздела про офлайн в статье про воспроизведение на iOS и Android; рядом стоит матрица клиентов OTT, которая показывает все экраны, что нужно покрыть.

Офлайн – это отдельный путь воспроизведения, а не настройка

Начнём с модели в голове, потому что именно её команды путают первой. Когда зритель смотрит стрим, плеер постоянно тянет из сети две вещи: маленькие куски видео, называемые сегментами, и – для защищённого контента – лицензию, пакет данных с ключом расшифровки и правилами использования. И то и другое приходит по запросу, и ни то ни другое не должно пережить сессию. Офлайн переворачивает это. Приложение скачивает всё заранее, пока связь ещё есть, и пишет на устройство, чтобы позже, с выключенной сетью, плеер читал из локальной памяти, а не из интернета.

Эта единственная разница – «скачать заранее и сохранить» вместо «тянуть по запросу» – расходится по всему стеку. Нужен менеджер загрузок, который надёжно тянет тайтл целиком в фоне и переживает закрытие приложения. Нужно защищённое место на устройстве для зашифрованного видео. И, что удивляет неспециалистов, нужна особая лицензия, которую разрешено записать на диск и прочитать снова без сервера в зоне доступа. Стриминговый код не делает ничего из этого, поэтому офлайн строят и тестируют как отдельный путь, а не прикручивают к плееру.

Рисунок 1. Офлайн – это два скачивания, не одно: медиа и постоянная лицензия, захваченные онлайн и прочитанные позже без сервера в зоне доступа.

Два скачивания, никогда не одно

Самая полезная фраза в этой статье: офлайн-загрузка это два скачивания, не одно. Первое очевидно – сегменты видео и аудио, те же файлы, что CDN уже отдаёт для стрима. Второе – лицензия, и именно она решает, заиграет ли что-нибудь.

Когда вы стримите, плеер просит у вашего сервера лицензий – сервиса, выдающего ключи расшифровки после проверки прав зрителя – лицензию каждый раз при старте защищённого тайтла, и эта лицензия выбрасывается в конце сессии. Для офлайна плеер должен запросить другой тип лицензии во время загрузки, пока устройство онлайн, и сохранить её рядом с медиа. Позже, в режиме полёта, плеер читает эту хранимую лицензию, получает ключ и расшифровывает видео локально. Забудете второе скачивание – медиа на устройстве есть, но каждый кадр чёрный, потому что ключ не приехал. Как устроен сам обмен ключами, разобрано в статье про серверы лицензий и доставку ключей; здесь важно одно: офлайн значит, что вы захватываете и лицензию, и захватываете её рано.

Постоянная лицензия решает всё

Тип лицензии, которой разрешено жить на устройстве, называется постоянная лицензия (persistent license), и это точное техническое понятие, а не маркетинговое слово. Веб-стандарт, управляющий воспроизведением в браузере, – спецификация W3C Encrypted Media Extensions, обычно сокращаемая до EME, рекомендация W3C 2017 года – определяет три типа сессии расшифровки. Сессия temporary не хранит ничего: лицензия и ключи исчезают в конце сессии, это обычный стриминг. Сессия persistent-license – офлайновая: по словам стандарта, лицензия «будет сохранена» (will be persisted), а запись о её уничтожении тоже должна сохраняться при удалении ключей. Третий тип, persistent-usage-record, хранит только журнал того, что играли, – для отчётности. Офлайн-скачивание это тип persistent-license, и стандарт помечает его поддержку как необязательную – первый намёк, что офлайн доступен не везде.

Нативные системы зеркалят это под своими именами. На устройствах Apple система защиты контента FairPlay Streaming возвращает ключ, помеченный сервером как временный или постоянный; когда он постоянный, устройство пишет офлайн-ключ, который сможет прочитать позже. На Android Widevine от Google выдаёт офлайн-лицензию, которую плеер хранит как маленький идентификатор. На Windows и многих смарт-ТВ PlayReady от Microsoft держит постоянную лицензию в энергонезависимой памяти, которая «живёт всё время существования хранилища или до наступления ограничения по времени». Три вендора, одна идея: лицензия, которой по политике разрешено пережить соединение с сетью.

Отсюда два следствия, и оба важны для оценки. Во-первых, постоянную лицензию надо получить, пока устройство онлайн – достать её позже в режиме полёта нельзя, поэтому поток загрузки обязан получить и проверить её прежде, чем скажет зрителю «готово к офлайну». Во-вторых, постоянная лицензия чувствительнее стриминговой, потому что ключ теперь лежит на устройстве, и правообладатели требуют более высокую планку безопасности для её выдачи – обычно аппаратную защиту – поэтому офлайн часто блокируют на устройствах с root/jailbreak и на чисто программных путях воспроизведения.

Тот же зашифрованный файл, другой тип лицензии

Вот успокаивающая часть для всех, кто боится, что офлайн удвоит счёт за кодирование: нет. Видео, которое скачивает зритель, – это тот же файл, что вы уже стримите. Современный стриминг шифрует каждый тайтл один раз по схеме cbcs зонтичного стандарта Common Encryption (ISO/IEC 23001-7), упакованного в контейнер CMAF fragmented-MP4 (ISO/IEC 23000-19), и этот один зашифрованный пакет уже играет под FairPlay, Widevine и PlayReady. Мы разбираем это схождение в статье Multi-DRM: один workflow, все устройства, а детали схемы – в CENC, CTR и CBCS простыми словами.

Офлайн переиспользует всё это. Вы не делаете «офлайн-копию» каталога. Вы скачиваете те же сегменты cbcs и связываете их с постоянной лицензией вместо временной. Держите две идеи раздельно: медиа не меняется; меняется только тип лицензии. Именно это различие позволяет сервису добавить загрузки без второго кодирования тысяч часов видео.

Реальность по платформам: iOS, Android и веб

Как и стриминг, офлайн строят под каждую платформу, и детали различаются. Таблица ниже – карта покрытия; читайте по столбцу, который оцениваете. Столбец «Нативная поддержка» – главный: офлайн комфортен на двух мобильных платформах и по-настоящему труден в обычном браузере.

ВозможностьiOS / iPadOSAndroidВеб-браузер
API загрузкиAVAssetDownloadURLSessionMedia3 DownloadManagerService Worker + IndexedDB (best-effort)
DRMFairPlay (постоянный ключ)Widevine (офлайн-лицензия)EME persistent-license (где есть)
Объект лицензииПостоянный ключ FairPlaykeySetId из OfflineLicenseHelperPersistent MediaKeySession
Где хранится медиаУправляемое хранилищеКэш загрузок приложенияIndexedDB (временный)
Поддержка срокаДа (срок в ключе)Да (срок в лицензии)Да (срок в лицензии)
Нативная поддержкаСильнаяСильнаяСлабая / часто непрактична

На iOS вы скачиваете HLS-контент через AVAssetDownloadURLSession, управляющий AVAssetDownloadTask, и для защищённых тайтлов дополнительно запрашиваете постоянный ключ FairPlay во время загрузки и сохраняете его; фреймворк Apple сам управляет зашифрованным хранилищем. На Android движок ExoPlayer внутри Jetpack Media3 использует DownloadManager (запущенный из фонового DownloadService) для медиа, а помощник OfflineLicenseHelper скачивает офлайн-лицензию Widevine, возвращая маленький keySetId, который вы храните и передаёте плееру, чтобы разблокировать файл. Минимальный набросок этого свопа на Android – иллюстративный, не продакшен-код:

// 1) Пока онлайн: заранее получаем офлайн-лицензию Widevine.
val keySetId = offlineLicenseHelper.downloadLicense(format)  // сохраняем этот blob

// 2) Позже, офлайн: собираем источник с сохранённым ключом, без URL лицензии.
val drmConfig = MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID)
    .setKeySetId(keySetId)          // используем сохранённый ключ вместо URL сервера
    .build()

В вебе офлайн – слабый угол. Тип сессии persistent-license в EME существует, и плееры вроде Shaka Player умеют хранить контент и лицензии в локальной базе браузера (IndexedDB). Но поддержка неровная по браузерам, постоянные лицензии есть не везде, а память браузера явно best-effort – операционная система может забрать её, когда на устройстве кончается место, так что «скачанный» фильм может просто исчезнуть. Для надёжных загрузок реалистичный ответ – нативное приложение или хотя бы установленная обёртка; веб-путь это бонус, а не фундамент. Веб-механика DRM лежит в статьях про стек браузерного DRM (EME) и веб-воспроизведение.

Рисунок 2. Карта покрытия офлайна. Зелёные ячейки поддержаны нативно; столбец веба в основном янтарный, потому что память браузера best-effort, а постоянные лицензии неровные.

Два таймера: окно хранения и окно воспроизведения

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

Первый таймер – окно хранения (storage / rental duration): сколько лицензии разрешено жить на устройстве после выдачи. Частое значение – 30 дней. Скачайте фильм сегодня, не открывайте – лицензия всё равно истечёт через 30 дней. На сервере лицензий это одно число: 30 дней это 30 × 24 × 3600 = 2 592 000 секунд, и по их истечении устройство удаляет лицензию, и загрузка перестаёт играть.

Второй таймер – окно воспроизведения (playback duration): более короткий отсчёт, стартующий при первом нажатии «play». Частое значение – 48 часов. Классическое правило аренды «30 дней, чтобы начать, 48 часов после начала» – это ровно два таймера в работе: окно хранения даёт месяц на старт, а нажатие play запускает 48-часовое окно, которое дальше правит всем. PlayReady даже поддерживает режим «real-time expiration» для очень коротких, точно отмеренных аренд. Модель двух таймеров задаётся в политике лицензий, которую мы разбираем целиком в статье Политика лицензий: аренда, офлайн и output control.

Рисунок 3. Каждой загрузкой правят два таймера. Окно хранения считается от загрузки; окно показа – от первого запуска; побеждает более ранний дедлайн.

Разберём короткий пример вслух. Зритель скачивает фильм 1-го числа с окном хранения 30 дней и окном показа 48 часов. Если он не откроет его, фильм истечёт 31-го – таймер хранения. Если он нажмёт play 20-го, стартует таймер показа, и фильм перестанет работать 22-го, за восемь дней до конца окна хранения, – потому что всегда побеждает более ранний из двух дедлайнов. Задача приложения – показывать зрителю актуальный таймер: «Доступно ещё 29 дней» до старта и «Истекает через 47 часов» после нажатия play. Ошибка в этом сообщении – самое частое обращение в поддержку по офлайну.

Подписная загрузка работает так же механически, просто с более дружелюбными числами: окно хранения часто привязано к подписке (загрузка работает, пока активна подписка и устройство периодически отмечается), а окна показа может не быть вовсе. Механизм – постоянная лицензия со сроком – идентичен; меняются только значения политики.

Математика памяти: сколько весит сезон на телефоне

Другое число, которое удивляет команды, – место на диске, и стоит показать арифметику, потому что она определяет продуктовое решение. Поток 1080p на 5 мегабит в секунду тратит 5 ÷ 8 = 0,625 мегабайта каждую секунду. За час это 0,625 × 3600 ≈ 2250 мегабайт – около 2,25 ГБ в час. 45-минутная серия – это примерно 1,7 ГБ в таком качестве, а сезон из десяти серий – около 17 ГБ, достаточно, чтобы одним сериалом забить свободное место на бюджетном телефоне.

Поэтому зрелые функции загрузок никогда молча не берут максимальное качество. Они предлагают выбор качества загрузки – например, «стандарт» против «высокого» – и по умолчанию ставят меньшее. Экономия большая, потому что ступень ниже использует меньший битрейт: многие сервисы качают в эффективном кодеке HEVC около 720p, выходя на ~250 МБ в час для стандарта и ~500 МБ в час для высокого против 2,25 ГБ полной ступени 1080p. Дайте зрителю этот выбор – и сезон падает с 17 ГБ до 2–4 ГБ. Многие платформы добавляют умные загрузки – автоматически тянут следующую серию и удаляют просмотренную – чтобы устройство любителя сериалов оставалось примерно постоянным, а не забивалось. Ступени, из которых берутся эти качества, – тема статьи Encoding ladder: основы.

Рисунок 4. Качество загрузки – это решение о памяти. Стандартная ступень по умолчанию режет вес сезона примерно в пять раз.

Что разрешает правообладатель – офлайн это вопрос прав

Финальный сдвиг ракурса: многое из того, что может ваша функция офлайна, – вообще не ваш выбор. Можно ли скачать тайтл, как долго идут окна хранения и показа, на скольких устройствах одновременно может лежать копия и переживёт ли загрузка просрочку платежа – всё это решения правообладателя о правах, выраженные в лицензии, которую выдаёт ваш сервер, и реализуемые DRM устройства. Некоторые студии запрещают офлайн для премиальных или ранних тайтлов целиком; тогда ваш каталог становится лоскутным, где у одних тайтлов кнопка загрузки есть, а у других нет, и это состояние должно приходить из вашей системы прав, а не из глобального тумблера.

Число устройств – часть той же картины. Крупные сервисы ограничивают, на скольких устройствах одновременно могут лежать загрузки – обычно два на стандартном тарифе и около шести на премиальном – и сколько активных загрузок держит одно устройство, часто около 100, причём тарифы с рекламой ограничены меньшим месячным лимитом. Это бизнес-правила поверх DRM, и живут они в вашей логике биллинга и прав, которую мы разбираем в статье Биллинг подписок и entitlement. Планка безопасности тоже важна: поскольку постоянный ключ лежит на устройстве, правообладатели обычно требуют аппаратную защиту (уровень L1 у Widevine или Secure Enclave у Apple) для офлайна в HD и 4K и вовсе блокируют загрузки на устройствах с root/jailbreak, где этой защите нельзя доверять.

«Частые ошибки – пять, что ломают загрузки. Первая: забыть второе скачивание – медиа сохранено, постоянной лицензии нет, и каждый офлайн-кадр чёрный; всегда получайте и проверяйте лицензию во время загрузки, пока онлайн. Вторая: пытаться достать лицензию в режиме полёта – нельзя, получайте заранее. Третья: показать не тот таймер, и зритель, нажавший play, видит «осталось 29 дней» и в шоке, когда всё умирает через 48 часов; показывайте активное окно. Четвёртая: качать по умолчанию в полном качестве и забивать телефон; дайте выбор качества и ставьте меньшее. Пятая: считать, что браузер потянет надёжный офлайн; планируйте нативное приложение, потому что память браузера best-effort и может быть очищена.»

Где здесь Фора Софт

Офлайн обычно вскрывает все слабые места стримингового продукта разом, потому что должен работать в масштабе целого каталога, на iOS и Android, по правилам прав каждого правообладателя и с постоянной лицензией, которую надо корректно получить, сохранить и просрочить на устройстве. Фора Софт строит видеостриминг, OTT/Internet TV, e-learning и телемедицину с 2005 года – 250+ проектов для 400+ клиентов за 20+ лет – так что офлайн-паттерны отсюда (постоянные лицензии FairPlay и Widevine, модель аренды с двумя таймерами, управление качеством загрузки и правила прав, что её гейтят) – часть нашей повседневной стриминговой работы. Мы вендор-нейтральны: мы превращаем офлайн-политику каждого правообладателя в поток загрузки, который держится в поле, а не перепродаём один DRM-SDK.

Ключевые выводы

  • Офлайн – это второй путь воспроизведения: менеджер загрузок, защищённое хранилище и постоянная лицензия.
  • Офлайн-загрузка это два скачивания – медиа и постоянная лицензия, полученная онлайн.
  • Вы не перекодируете; загрузка это тот же файл cbcs, но с постоянной лицензией.
  • Загрузкой правят два таймера: окно хранения и более короткое окно показа; побеждает ранний.
  • Правообладатель решает, разрешён ли офлайн, окна, число устройств и планку безопасности.
  • Надёжному офлайну нужно нативное приложение; память браузера best-effort и может быть очищена.

Что почитать дальше

Строите такую систему?

Подберём параметры кодирования под ваш контент и посчитаем стоимость доставки до старта разработки.