Доброго вам времени суток! Меня зовут Валерия, я фронтенд-разработчик из компании Doubletapp. Год своей жизни отдала на разработку видеоплеера с рекламными интеграциями. На старте была только спека, дока, единственная статья на Хабре, поясняющая за рекламу, и огроменное желание заказчика накрутить оную на видеоплеер. Пришло время и вас поближе познакомить с рекламой. Статья подойдет тем, кто хочет разобраться в специфике работы VAST и VPAID, настроить кастомное управление рекламой, разместить видеорекламу отдельным блоком на сайте (out-stream) или разбить видеоролик рекламными интеграциями (in-stream). Video Ad Serving Template или VAST — это единый стандарт для обмена видеообъявлениями. По факту — файлик формата xml. Он оформлен согласно спецификации, разрабатываемой компанией Interactive Advertising Bureau (IAB). С помощью VAST провайдеры рекламы создают интеграции в универсальном формате, собирают аналитику по переходам, просмотрам и другим взаимодействиям зрителей; могут предоставлять релевантную возрасту, месту жительства и увлечениям информацию и выстраивать более качественную маркетинговую кампанию.  Нас же интересует техническая сторона вопроса. Чтобы запустить VAST, достаточно видеоплеера и парсера для xml. Эта статья — концентрат рекламы, поэтому я не буду отвлекаться на создание hls-плеера или работу с MSE — мы возьмем стандартный <video> тег. А вот в парсинге рекламы мы пойдем по хардкору. Google IMA SDK, наверное, единственный инструмент, дающий полный контроль для работы с рекламой и открытый для использования.  Чисто для справки: есть уже готовые плееры с возможностью воспроизведения рекламы (платные и бесплатные) — Video.js, JW Player, plyr и т. п. Если покопаться, под капотом у них тот же Google IMA SDK, но библиотеки дают к нему ограниченный доступ и свой кастомный интерфейс. Есть еще Video Ads SDK от Яндекса, но, к сожалению, работает инструмент только со своими рекламными партнерами. Подытожим, IMA SDK даст нам возможность гибко управлять VAST'ами, настраивать сайд-эффекты и при необходимости менять логику воспроизведения под требования заказчика.  Этот блок можно пропустить, если вы уже знакомы с темплейтом VAST-файлов, и сразу перейти к разделу с реализацией. Самый простенький VAST имеет следующую структуру. Ее ни с чем не спутать, так как тело оборачивается в узел <VAST>. В нем могут лежать узлы AD или узел ERROR, если сервер не вернул рекламу. В узле VAST указывается версия темплейта, на момент публикации статьи последняя — 4.3. При разработке плеера я встречала васты 3.0 и 2.0 версий. Пусть это вас не смущает, реализации не сильно отличаются. Узлов AD внутри VAST начиная с 3.0 версии может быть несколько. Они будут воспроизводиться в порядке, указанном в атрибуте sequence. Если реклама в первом узле AD упала с ошибкой, загружается следующая в последовательности. Внутри AD может быть: Сама реклама — узел InLine. Информация о посреднике в передаче рекламы — узел Wrapper. Давайте сначала рассмотрим пример с узлом InLine. *это пример файла. Мы не гарантируем, что внутри VAST будет содержаться ссылка на существующий видеофайл AdSystem — название рекламного сервиса, предоставляющего рекламуAdTitle, Description — название и описание рекламыError — url, который вызовется в случае ошибкиImpression — url, который вызовется после показа первого фрейма креативаCreatives — набор креативов для показа. Один креатив = один медиафайл для воспроизведения.Creative — креатив также имеет атрибут sequence по аналогии с узлом AD. Их может быть несколько, но спецификация просит не путать Creative sequence с Ad sequence. Так как в этом случае sequence — это составные части одного рекламного объявления.Linear — тип рекламы. Может иметь атрибут skipoffset, который задает время, через которое можно пропустить рекламу. Всего IAB предоставляет три вида рекламы Linear, NonLinearAds и CompanionAds. Надеюсь, иллюстрация поможет понять, в чем разница между ними. В этой статье мы более подробно рассмотрим Linear тип рекламы: Duration — продолжительность рекламыTrackingEvents — контейнер для TrackingTracking — содержит url, который будет вызываться при наступлении события, указанного в атрибуте eventVideoClicks — может содержать узлы ClickThrough, ClickTracking и CustomClick. В нем задается поведение при клике на рекламный блокClickThrough — содержит ссылку, по которой будет совершен переход при взаимодействии с рекламным блоком (лендинг, сайт)ClickTracking — отслеживает статистику кликов, описанных в узле VideoClicks (переходы на сайт или кастомные клики)MediaFiles — контейнер для MediaFileMediaFile — содержит ссылку на рекламу. Их может быть несколько в разном качестве, с указанием атрибутов width и height. Используется для подбора разрешения на разных устройствах. Атрибут delivery указывает протокол для скачивания файла — streaming или progressive. Ранее я отмечала, что есть интеграции, обернутые во Wrapper. Это значит, что исходный VAST имеет посредника, который тоже обвешал рекламу своими событиями для мониторинга показов. В этой обертке ссылка на истинный VAST лежит в узле VASTAdTagURI. Если пройти по ссылке в нем, то вы увидите тот же InLine xml-файл, похожий на пример выше. *это пример файла, взятый из репозитория IAB https://github.com/InteractiveAdvertisingBureau/VAST_Samples. Внутри VAST может содержаться ссылка на несуществующий видеофайл. В целом этих знаний достаточно для начала разработки. Однако, если ваша душа требует подробностей, можете обратиться к документации на сайте IAB или ознакомиться с неплохой статьей на Хабре.  Все примеры я опубликовала в этом репозитории. В нем вы можете проверить работоспособность примеров, поэкспериментировать с настройками или вдохновиться кодом. Готовьте попкорн и не забудьте выключить AdBlock!) Прошу проследовать в каталог simpleExampleVast. Я советую параллельно поглядывать в код, чтобы видеть общую картину и не теряться в частях кода. А еще это мог бы быть неплохой воркшоп) Создадим базовый index.html и стили для него. IMA рекомендует располагать на одном уровне видеоэлемент и контейнер для рекламы. VAST-спецификация предназначена для передачи рекламных объявлений видеоплеерам, поэтому подбирать рекламу мы будем, смотря на исходные размеры тега <video>. adContainer располагаем абсолютом и перекрываем им весь блок видео элемента. В рамках этой статьи я заморочилась и захостила для вас рабочий VAST по этой ссылке. Можете сравнить с примерами выше. В нем нет трекинга событий, но для тренировки этого будет достаточно. Из инструментов нам понадобится только IMA SDK (вот дока).  IMA SDK можно подключить напрямую через скрипт, но мы воспользуемся пакетом @alugha/ima. Это даст нам ряд полезных фич: 1. Типизация2. Динамическая загрузка IMA SDK3. Проверка на включенный блокировщик рекламы Загружаем IMA SDK  loadImaSdk проверяет, включен ли в данный момент блокировщик рекламы на клиенте. Если блокеров нет — загружает инстанс ima, в противном случае триггерит ошибку. После загрузки управление рекламой будет осуществляться через сущность ima, которую возвращает функция loadImaSdk. Также она будет доступна в глобальных переменных window.google.ima. Чтобы типизация работала корректно, обновим глобальный интерфейс. На основе этих знаний напишем универсальную функцию для загрузки IMA SDK. Она понадобится нам для дальнейшей разработки.  Наш утиль будет принимать колбэк, который вызовется при успешной загрузке ima. О настройке IMA подробно написано в документации, поэтому я постаралась уделить внимание тому, что осталось за кадром Get Started. Обобщу знания, полученные при разработке подобного плеера. Надеюсь, это поможет вам сократить время на понимание работы инструмента и его особенностей. С помощью IMA можно: отслеживать события рекламы; описывать сайд-эффекты; получать информацию о рекламе; управлять звуком, паузой, перемоткой и т. п.; подгружать в любой момент новую рекламу; и многое другое. Манипулировать мы будем двумя элементами Все методы взаимодействия с рекламой буду описывать в кастомном классе ImaManager. Если у вас на странице несколько рекламных блоков (несколько adContainer), для каждого нового контейнера создается новый инстанс imaManager.  Инициализация сводится к тому, что мы передаем информацию о нашем adContainer в ima. Именно в этот элемент IMA будет пробрасывать рекламные блоки. Они могут представлять собой html с медиафайлом в <iframe> или <video>. Также в IMA SDK предусмотрены дополнительные контролы, например, для пропуска рекламы или таймера. Инициализируем контейнер для рекламы в классе ImaManager. и создаем загрузчик рекламы Так как объявить adsLoader для каждого ImaManager мы можем один раз, в начало метода я добавила проверку на его существование. adsLoader запрашивает xml файл и сигнализирует о его получении в событии ADS_MANAGER_LOADED или об ошибке — AD_ERROR. У ima есть критические и некритические ошибки. Подробное описание ошибок и их коды можете посмотреть здесь. Создадим метод обработки ошибок. Событие ADS_MANAGER_LOADED тригерится в момент, когда получен xml-файл. На этом этапе мы можем обращаться к adsManager. Он служит для непосредственного «общения» с рекламой. Инстанс adsManager позволяет: получать информацию о рекламе,  подписываться на события текущей рекламы,  влиять на воспроизведение рекламы — остановить, перемотать или пропустить интеграцию. Также, опционально, можно прокинуть настройки при инициализации adsManager — их список тут.  Для примера изменим ожидание загрузки медиафайла на 9 секунд (default = 8 секунд). Это значит, что если видео или баннер не загружаются в течение 9 секунд, IMA его пропустит и загрузит следующий, либо просигнализирует о завершении показа рекламы (об этом в обработке событий рекламы) В момент воспроизведения рекламы также может что-то пойти не так, поэтому добавляем обработку ошибок и инициализируем adsManager.  При инициализации adsManager нужно передать три обязательных аргумента — ширину и высоту рекламного блока при монтировании и режим отображения («NORMAL» или «FULLSCREEN»). В классе ImaManager создадим еще один метод requestAds, который будет получать ссылку на рекламу и запрашивать ее загрузку.  После вызова этой функции adsLoader начнет загрузку рекламы и после задиспатчит событие ADS_MANAGER_LOADED. В нем мы получим adsManager, задача которого — найти в xml .mp4 файл и воспроизвести его в заданном нами контейнере. И… Реклама все равно не будет воспроизводиться.  Чтобы реклама наконец появилась на странице, нужно дождаться ее загрузки и вызвать метод adsManager.start(). Так выглядит итоговый скрипт с инициализацией imaManager. В последующих примерах функцию init я перенесу в конструктор класса Класс ImaManager для этого примера находится в папке simpleExampleVast в репозитории. Запускается код по команде yarn simple-vast. Не забудьте предварительно установить зависимости. P. S. У adsManager множество методов для работы с рекламой и рекламными блоками (ведь их может быть несколько). Если вы работаете с тайпскриптом, то проанализировать их не составит труда. Для примера, я добавила методы плея, паузы и пропуска рекламы. Важно отметить: время для пропуска рекламы регулируется в атрибуте skipoffset VAST-файла. Сейчас при запуске рекламы мы видим следующую картину. Выглядит все довольно неплохо. Но кое-что мне не нравится. Во-первых, локализация. Во-вторых, если я попытаюсь изменить размеры videoContainer, то реклама к ним не адаптируется. Все дальнейшие изменения в репозитории я буду делать на основе предыдущего примера, но уже в каталоге advancedExampleVast. Задать дополнительные настройки ima мы можем глобально для всех adConteiners и точечно для каждого (настройки).  Пример ниже иллюстрирует локальные и глобальные настройки. Исключение составляет локализация — ее можно задать только для всего проекта. setAutoPlayAdBreaks мы разберем далее при работе с VMAP. Следить за размерами адаптивного блока буду через ResizeObserver. Кстати, не забывайте установить к нему полифил для корректной работы в браузерах. Полный список событий для VAST можно найти здесь. Подпишемся на некоторые из них и прологгируем. В ивентах можно получить данные о рекламе (adEvent.getAd() или adEvent.getAdData()). Возвращаемый объект может различаться в зависимости от события, в котором вы его запросили. К примеру, в событии AD_PROGRESS мы можем получать информацию о продолжительности рекламы, прогрессе и т. п. Для разнообразия функционала добавим парочку сайд-эффектов, предложенных моим воображаемым заказчиком «Я кликаю на рекламу, перехожу на сайт, потом обратно, и реклама на паузе. Хочу, чтобы я мог ее возобновить». Пожалуйста: «Мне не нравится эта пустота после показа рекламы. Пусть воспроизводится еще реклама — бесконечно». Класс ImaManager для этого примера находится в папке advancedExampleVast в репозитории. Запускается код по команде yarn advanced-vast. P. S. Таким образом можно обрабатывать и запускать VAST или VMAP. Но важно отметить, что в случае с VMAP будут запускаться только медиафайлы, отмеченные как preroll. (подробнее в описании VMAP) Ситуация: представим, что наш заказчик — медиасервис с платной подпиской, но также он предоставляет ресурсы и для бесплатного просмотра. Чтобы оплатить сервера для хостинга такого объема файлов, а еще и обеспечить хорошую пропускную способность, требуются значительные затраты, не считая оплаты лицензий на трансляцию самого контента. Большинство подобных сервисов стараются монетизировать контент, который предоставляют пользователям без подписки, встраивая рекламные блоки. Они могут быть в начале контента, в конце и в середине. Если бы мы решали эту задачу, имея простой VAST, то нам пришлось бы отслеживать timestamp видео и в нужные промежутки запускать рекламу. Благо все это можно автоматизировать с помощью спецификации VMAP.  Насколько мне известно, рекламные сервисы обычно отдают VAST файлы, но никто не мешает упаковать их в VMAP. Этот блок можно пропустить, если вы уже знакомы с темплейтом VMAP-файлов, и сразу перейти к разделу с реализацией. Video Multiple Ad Playlist или VMAP — это единый стандарт для обмена наборами видеообъявлений. Это xml-файл, оформленный согласно спецификации, разрабатываемой компанией Interactive Advertising Bureau (IAB). На момент написания статьи имеет одну версию. Если проще, то VMAP — это расписание рекламных пауз для видеоплеера, воспроизводящего какой-либо контент.  Ниже представлена структура VMAP-файла *это пример файла. Мы не гарантируем, что внутри VAST будет содержаться ссылка на существующий видеофайл. AdBreak — рекламная пауза, обертка для отдельных VAST. Атрибуты:timeOffset — указывает, в какой момент воспроизведения основного видео начнется реклама. Допустимы значения для IMA SDK — “start”, “end” или указание времени до миллисекунд.breakType — задает тип рекламы. AdSource — хранит информацию о VAST, который будет воспроизводиться. В него может быть вложена ссылка на VAST (AdTagURI) или целый файл ((1*) VASTData).Атрибуты:allowMultipleAds — разрешает или запрещает наличие нескольких узлов Ad в VAST (для формирования рекламного блока), followRedirects — разрешает или запрещает наличие VAST с переадресацией на другой VAST. (например, Wrapper) (2*) Также можно подписаться на события в TrackingEvents В репозитории можно найти каталог exampleVmap. Все дальнейшие действия я буду описывать в нем. Я была бы не я, если бы не захостила для вас медиафайл и рабочий VMAP (ссылка на него). Откройте этот файл в браузере. В нем ссылки на 4 рекламных блока — в начале (preroll) и в конце (postroll) воспроизведения, а также два midroll на 5-й и 10-й секундах.  Здесь загружать рекламу мы будем уже не по кнопке, а при старте контентного видео.  После этих изменений рекламные паузы будут воспроизводиться по заданному расписанию, но вместе с основным видео, а также перекрывать кликабельные области после воспроизведения. Для таких сайд-эффектов в IMA предусмотрены два события CONTENT_PAUSE_REQUESTED и CONTENT_RESUME_REQUESTED. Они сообщают, когда рекламный блок планирует начать воспроизведение и когда заканчивает.  Класс backwards меняет z-index у элемента на -1. Обратите внимание, что в этом примере мы уже не запускаем рекламу на событие LOADED. Это происходит автоматически, так как в vast у нас заданы конкретные промежутки воспроизведения интеграций. Поэтому вызвать adsManager?.start() необходимо всего раз после инициализации. Причем Ima будет префетчить следующую рекламу перед воспроизведением для минимальной задержки. На этом этапе воспроизводятся все рекламы, кроме последней. Это происходит, потому что IMA не знает о том, когда контентное видео заканчивается. Давайте ей поможем. Проверим наличие postroll внутри VMAP и, если есть, сообщим об этом adsLoader.contentComplete() в момент окончания контентного видео. Вызовем эту функцию после получения adsManager в функции onAdsManagerLoaded. Но предположим, мы хотим начать загрузку заранее, чтобы не дожидаться медиафайла и при включении плеера уже иметь возможность ее отобразить. Этот пример в репозитории я расположила в каталоге advancedExampleVmap. В настройках adsLoader отключим автоматическое воспроизведение Теперь при готовности рекламы к воспроизведению мы будем получать ивент AD_BREAK_READY. И только после этого события запускать интеграцию. При начале воспроизведения основного видео будем вызывать метод imaManager.startAd(),  который будет стартовать рекламный блок,  либо ждать, пока он загрузится  P. S. Однако с этими настройками нужно глядеть в оба, если вы работаете не с обычным VAST-файлом, а с VPAID. В его коде может быть прописано время ожидания для запуска. Другими словами, после загрузки некоторые разработчики VPAID ставят timeout, через который мы обязаны вызвать команду adsManager.start(), иначе реклама упадет с ошибкой и IMA ее пропустит. Вы дошли до этого раздела? Поздравляю. А я как раз собиралась рассказать про VPAID. Давайте вспомним, как выглядит VAST. И сравните его с VPAID ниже *это пример файла. Мы не гарантируем, что внутри VAST будет содержаться ссылка на существующий видеофайл Я подскажу, различие лишь в одном — в MediaFile здесь лежит уже не медиафайл .mp4, а скрипт. Video Player-Ad Interface Definition (VPAID) — спецификация для интерактивной рекламы. В нее разработчики могут поместить любой скрипт, отрендерить в пределах контейнера любые элементы. По итогу это может быть даже не видеофайл, а баннер с опросом. Мой совет: никогда не пытайтесь обрабатывать поведение этой рекламы и оставляйте ее ровно такой, какой ее описал разработчик VPAID, а значит, по заветам рекламодателя. Наша задача — VPAID только запустить, и делается это добавлением всего одной строки  Узнать, что к вам пришел VPAID, можно после загрузки рекламы в событии LOADED Работа VPAID построена на описании методов для различных ивентов рекламы — они приведены в спецификации, которую можно найти тут. На этом я бы хотела закончить свой «небольшой» рассказ про рекламу. Но еще парочка моментов, которые могут быть вам полезны: автоплей VAST — это в конечном итоге медиафайл, и как для всех медиаэлементов в браузерах, на них действуют ограничения на воспроизведение. Если видео играет после действия пользователя, то реклама может воспроизводиться со звуком. Автоплеить же рекламу лучше без звука, иначе в некоторых браузерах она может не воспроизвестись. С VAST достаточно поставить  adsManager.setVolume(0) сразу после получения adsManager Однако здесь могут возникнуть проблемы с VPAID, потому что в его скрипте тоже могут переопределяться настройки звука. В таком случае идеально найти способ договориться с представителями рекламных сервисов настроить VPAID для воспроизведения без звука, если в их админках такое невозможно. фулскрин Можно взять готовые решения вместе с плеером — такие как video.js или plyr. Если же использовать IMA SDK, то обрабатывать fullscreen нужно на родителе videoElement и adContainer. перемотка При перемотке основного видео IMA воспроизводит рекламный блок, если он попал в интервал перемотки, но не больше одного. предзагрузка VAST IMA старается заранее подгружать васты перед началом рекламного блока внутри плеера. На этом точно все, можете снова включать AdBlock! Спасибо, что прочитали эту статью и, надеюсь, что со мной вам было интересно!