To GIF or not to GIF?

Всем привет! Меня зовут Антон, я занимаюсь разработкой серверной части iFunny — мобильного юмористического сервиса. В этой статье я расскажу о том, как эволюционировал наш подход в работе с анимацией в проекте и какие профиты были получены в конечном итоге.

8f6775e27b49eea85bd199fda5002c3b


Известно, что юмор замедляет процессы старения, а смех продлевает жизнь, именно поэтому нам столь приятно делать iFunny, который ежедневно дарит улыбки и хорошее настроение более, чем 4 миллионам пользователей (в данный момент, к сожалению или счастью, по большей части из США). Мы стремимся стать самым удобным fun-браузером в мире, а также местом, где рождается лучший юмор. В сутки наши пользователи загружают более 250 000 работ.

До недавнего времени мы раздавали около 150TB статики в сутки, но с добавлением анимации в проект получили прирост количества загружаемого контента на 17% и увеличение ежесуточного трафика до 750TB.

Оптимизация GIF

Так сложилось, что в настоящее время в GIF кодируют самые интересные нарезки из юмористических (и не очень) видеороликов, что печально отражается на объеме результирующих файлов. По нашим ощущениям, средний размер GIF-анимации в современном интернете составляет порядка 5Mb. Логичный вопрос, которым мы задались при разработке — сможем ли мы как-то оптимизировать анимацию на серверной стороне, чтобы уменьшить её объём?

Проведя ряд экспериментов с различными анимированными файлами и алгоритмами оптимизации (Basic Frame Optimization, Transparency Optimization), мы пришли к выводу, что выигрыш от оптимизации для большей части изображений оказался совсем небольшим, порядка 10-20%. Более того, если GIF-анимация была уже неплохо оптимизирована её автором, то при автоматическом подходе мы могли получить противоположный эффект.

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

Релиз версии с GIF

Если с реализацией поддержки GIF на стороне сервера и в iOS-клиенте было всё довольно просто, то на Android-клиенте нам пришлось прибегнуть к инструментам бородатых джедаев — libgif + JNI, что только раззадорило нас.

Как только все было закончено, мы прошли проверку цензоров в App Store, залили приложение в Google Play и запустились… И сразу по итогам первого дня увидели резкий скачок просмотров контента и, соответственно, трафика. В первые 24 часа трафик вырос в 6 раз!

369e09c0e76983f0ac03a66c415ab99e

Суммарно за полмесяца объем раздаваемого трафика возрос на 200% и в июне, благодаря запуску анимации в iFunny, мы раздали 4.75 Петабайта! Увеличение активности аудитории, безусловно, не может не радовать, но так ли все оказалось хорошо в итоге?

Мы были готовы к определенному количеству GIF-анимации в iFunny, но та статистика, которую мы получили в первые несколько суток, дала нам понять, что мы недобитые оптимисты: реальное количество загружаемой GIF-анимации в сутки превышало ожидаемое в 5 раз. Вкупе с массивными 3-4-мегабайтными GIF, идущими через каждые 3-5 статичных изображений, мы получили сервис, которым можно было пользоваться только через WiFi. Даже пользователям с безлимитным 3G приходилось туго: анимация грузилась долго, а из-за активной работы 3G-модуля девайс сильно разогревался и убивалась батарейка.

Нужно что-то делать…

На фоне отрезвляющей статистики мы задались вопросом: насколько GIF (набор изображений, по сути) подходит для подобного использования? Нам видится, что если мы имеем дело с простой анимацией, содержащей небольшое количество фреймов, то использование GIF более чем оправдано. Однако, как я говорил выше, современный юмористический GIF — это нарезка интересных моментов из видеороликов…

Исходя из вышесказанного, мы решили попробовать отказаться от GIF в пользу какого-нибудь подходящего для воспроизведения коротких анимированных роликов видеоформата. В таком случае, необходимо было найти подходящий формат кодирования. Были рассмотрены стандарты кодирования h.263, h.264, а также видеокодек vp8 в контейнере WebM.

Провели тесты рекодирования GIF в видео, и замаячил свет в конце тоннеля: размер результирующих видеороликов получался в 2-10 раз меньше, чем GIF-исходник!

Осталось определиться с кодеком. Использование кодека-пенсионера — h.263 давало ужасную результирующую картинку и противоречило нашим моральным нормам, поэтому его мы отмели первым.
h.264 — хороший кодек, однако связываться с охраняемыми патентами технологиями нам не хотелось, да и результирующий размер файла оказывался довольно большим: на 20-40% больше, чем при кодировании того же фрагмента в vp8. Поэтому мы решили остановиться на vp8.

Дополнительным аргументом при выборе vp8 также послужило то, что Google активно внедряет и использует его на YouTube, и в настоящий момент разрабатывается улучшенный формат VP9, что позволяет судить об успешности и перспективности всего проекта в целом.

Для конвертации из GIF в WebM используем сборку ffmpeg с поддержкой GIF-декодера и vp8-энкодера. Сам процесс конвертации проходит без особых замечаний и довольно быстро, но сразу же сталкиваемся с проблемой в результирующем видеоролике. Возьмем такую GIF-анимацию, например:

16309580b0d354725bff2fdabd8d35b3

Обратите внимание, у последнего кадра есть задержка в 2 секунды, после чего анимация проигрывается заново. В полученном WebM-файле эта задержка куда-то волшебным образом исчезает, и последний кадр “проскакивает”. Анализ прочего контента показал, что задержка после последнего кадра у анимации убирается всегда, что для нас является довольно большой проблемой, ведь мы хотим, чтобы пользователи в приложении вообще не видели разницу между GIF и видеороликом. Проблему решили, но таким костылём, что до сих пор краснеем от стыда: к конвертируемой GIF добавляем дубликат последнего фрейма.

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

Релиз версии с WebM

После релиза версии, где пользователям вместо GIF-анимации мы стали проигрывать короткие видеоролики, стало ясно, что проделанная работа принесла свои плоды: расход трафика за одну сессию снизился с неадекватных 50-100Mb до вполне комфортных 7-20Mb, девайсы стали меньше греться и убивать батарейку, а самое главное, пользователи стали видеть контент чаще, чем его прелоадер.

Если говорить о нашем раздаваемом трафике, то картина получилась следующая:

20bd9f5c604cc1da08411d337a1eaa65

Переход с GIF на WebM позволил сократить объемы раздаваемого трафика на 30% и снизить CDN-расходы примерно на $50 000 в месяц.

Выводы

Формат GIF87, созданный более 20 лет назад, по задумке автора должен был использоваться для передачи растровых изображений по сети, а модификация GIF89 позволяла использовать изображения с прозрачностью и хранить набор простых слайдов с определенной задержкой — анимацию.

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

Например, все мы помним анимированные рекламные баннеры, которые, к сожалению, до сих пор используются в некоторых рекламных сетях. Да, сейчас воспоминания о мигающих и дёрганых баннерах больше ассоциируются с галлюцинациями от ЛСД, но 10-20 лет назад это была революция — интернет перестал быть статичным.

Сейчас GIF также делает революцию, способствуя лавинообразному распространению нового формата юмористического контента — коротких видеофрагментов. Однако, с высоты нашего опыта, можем сказать, что использование GIF для такого контента не является правильным по ряду перечисленных выше причин, и лишь один весомый аргумент заставляет пользователей распространять нарезки из видеороликов в GIF — это повсеместная его поддержка во всех браузерах. У нас не связаны руки возможностью веб-навигаторов, поэтому нам удалось «оживить» iFunny без минусов, присущих GIF.