[Перевод] Каждый браузер видит цвета видео по-разному
Большинство людей знает основы теории цвета. Сочетая яркости нескольких основных цветов, можно воссоздать любой видимый человеку цвет. Многие люди знают, что отдельные цвета — это просто длины волн электромагнитного спектра. Но чего многие не осознают, так это того, насколько сложной становится ситуация, когда мы стремимся точным образом записать и воспроизвести цвет.
В преобразовании значения RGB-триплета в конкретную длину волны света задействовано множество систем. Это преобразование должно быть стандартизовано, чтобы всё ПО, все декодеры видео, видеокарты и мониторы (даже изготовленные разными производителями в разные десятилетия) могли создавать одинаковые результаты по одинаковым входным данным. Для решения этой задачи были разработаны цветовые стандарты. Однако со временем дисплеи и другие технологии развивались. Телевидение стало цифровым, начали применять сжатие, а мы отказались от ЭЛТ в пользу LCD и OLED. Новое оборудование было способно отображать больше цветов при большей яркости, но получаемые им сигналы по-прежнему были адаптированы под возможности старых дисплеев.
Решение этой проблемы заключалось в создании нового «цветового пространства». Новый HD-контент можно производить в новом цветовом пространстве, а новые HD-дисплеи могут его отображать. И если старое цветовое пространство будет перед отображением преобразовываться в новое, то старый контент не утратит совместимости.
Это работающее решение, но оно усложняет процесс создания видео. На каждом этапе процесса захвата, записи, редактирования, кодирования, декодирования, композитинга и отображения необходимо учитывать используемое цветовое пространство. И если хотя бы на одном этапе процесса используется старое оборудование, или есть баг ПО, из-за которого не учитывается цветовое пространство, то в результате может получиться некорректное изображение. Видео всё равно можно будет смотреть, однако цвета могут оказаться слишком тёмными или бледными.
Сегодня двумя самыми популярными цветовыми пространствами являются BT.601 (также называемое smpte170m; в статье я буду использовать оба этих названия), которое стало стандартом для SD-контента, и BT.709, которое стало стандартом HD-контента. Существует также BT.2020, которое становится популярнее благодаря HDR- и UHD-контенту. Стоит заметить, что разделение на HD/SD здесь немного ошибочно. Технические ограничения отсутствуют, это просто традиционный подход. HD-контент можно кодировать в BT.601, а SD-контент — в BT.709. Если взять видеофайл с разрешением 1080p и уменьшить его до 480p, то цветовое пространство не изменится автоматически. Смена цветового пространства — это дополнительный этап, который выполняется как часть процесса.
Что же происходит, если процесс выполняется неправильно? Давайте проведём эксперимент.
Программа ffmpeg — настоящее чудо современности. Это очень популярный инструмент для работы с цифровым видео, и вся отрасль многим обязана её разработчикам. В своём эксперименте я воспользуюсь этим инструментом для создания и изменения видеофайлов.
Для начала я создам с помощью ffmpeg простой тестовый файл:
ffmpeg -f rawvideo -s 320x240 -pix_fmt yuv420p -t 1 -i /dev/zero -an -vcodec libx264 -profile:v baseline -crf 18 -preset:v placebo -color_range pc -y 601.mp4
Краткое объяснение этой команды:
ffmpeg
— исполняемый файл
-f rawvideo
— сообщает программе ffmpeg, что я передаю ей сырые пиксельные данные, а не видеофайл.
-s 320x240
— разрешение выходных данных. Можно использовать любое значение, потому что все входящие значения будут одинаковыми. Но благодаря маленькому размеру мы ускоряем процесс.
-pix_fmt yuv420p
— указываем формат пикселей входящих данных. Yuv420p — это способ описания пикселей. Здесь важно отметить, что значение yuv (0,0,0) представляет собой оттенок зелёного. Я использую этот формат в противовес RGB, поскольку он является самым популярным форматом, используемым в цифровом видео.
-t 1
— ограничиваем входящие данные 1 секундой
-i /dev/zero
— это файл входящих данных. /dev/zero — это виртуальный файл, существующий на всех компьютерах mac. Это бесконечно длинный файл, состоящий из одних нулей.
-an
— обозначает, что выходные данные не должны содержать звука.
-vcodec libx264
— используем для сжатия видео потрясающую библиотеку libx264.
-profile:v baseline
— используем базовый профиль h.264. Он отключает некоторые расширенные возможности h.264, но в этом тесте они нам не понадобятся.
-preset:v placebo
— сообщаем библиотеке libx264, что она может потратить дополнительные ресурсы процессора на кодирование видео с повышенным качеством. В реальной ситуации эту опцию выбирать не стоит, потому что кодирование занимает КУЧУ времени и обеспечивает минимальное улучшение качества. Нам она подходит, потому что у меня мало входящих данных.
-color_range pc
— один компьютерный байт может иметь значения от 0 до 255. При оцифровке аналогового видео используется интервал 16–235. Он был выбран из-за того, как телевизор интерпретирует очень тёмные и очень яркие сигналы. Так как мы используем цифровой источник, я выбрал значениеpc
, а неtv
.
-crf 18
— опция постоянного коэффициента потока (constant rate factor) сообщает libx264, что нужно создать высококачественный видеофайл и использовать любое количество бит, необходимое для обеспечения качества18
. Чем меньше число, тем выше качество. 18 — это очень высокое качество.
-y
— даёт ffmpeg разрешение на перезапись файла, если он существует.
601.mp4
— имя получившегося файла.
Эта команда создаёт файл 601.mp4 длительностью 1 секунду, который можно открывать и воспроизводить. После выполнения этой команды мы можем проверить, что ffmpeg не исказил значения пикселей, выполнив следующую команду и изучив выходные данные:
ffmpeg -i 601.mp4 -f rawvideo - | xxd
00000000: 0000 0000 0000 0000 0000 0000 0000 0000
00000010: 0000 0000 0000 0000 0000 0000 0000 0000
00000020: 0000 0000 0000 0000 0000 0000 0000 0000
00000030: 0000 0000 0000 0000 0000 0000 0000 0000
00000040: 0000 0000 0000 0000 0000 0000 0000 0000
00000050: 0000 0000 0000 0000 0000 0000 0000 0000
...
...
Эти данные в шестнадцатеричном виде показывают, что все значения пикселей после декодирования равны нулю.
При рендеринге видео в Safari мы получаем такой скриншот:
Возникает вопрос: что это за цветовое пространство? Я назвал файл 601.mp4, но нигде в команде я не указывал цветового пространства, так как же Safari узнал, какой оттенок зелёного нужно рендерить? Откуда браузер знает, что yuv (0,0,0) должно быть равно rgb (0,135,0)? Очевидно, что существует алгоритм для вычисления этих значений. На самом деле, это простое матричное умножение. (Примечание: в некоторых форматах пикселей, в том числе и в yuv420p, для преобразования требуется этап пре- и постпроцессинга, но в этой демонстрации мы опустим такие тонкости). Для каждого цветового пространства имеется собственная матрица. Так как мы не задавали матрицу цветового пространства при кодировании видео, Safari просто делает предположение. Мы можем перебрать все матрицы, умножить все значения RGB на обратные матрицы и посмотреть, чему же они соответствуют, но давайте попробуем использовать более визуальный подход и посмотреть, удастся ли нам разобраться, что делает Safari.
Для этого я вставлю в файл метаданные, чтобы Safari знал, какое цветовое пространство используется, и не был вынужден угадывать.
ffmpeg -f rawvideo -s 320x240 -pix_fmt yuv420p -t 1 -i /dev/zero -an -vcodec libx264 -profile:v baseline -crf 18 -preset:v placebo -colorspace smpte170m -color_primaries smpte170m -color_trc smpte170m -color_range pc -y 601vui.mp4
Команда ffmpeg осталась практически такой же, но я добавил следующее:
-color_trc smpte170m
-colorspace smpte170m
-color_primaries smpte170m
Это метаданные цветового пространства, в который будет кодироваться файл. Я не буду объяснять различия между этими опциями, потому что для этого понадобится ещё одна статья. Пока мы просто задаём всем им нужное нам цветовое пространство. smpte170m — это то же самое, что и BT.601.
Указание цветового пространства не влияет на способ кодирования файла, значения пикселей по-прежнему кодируются как yuv (0,0,0). Чтобы убедиться в этом, мы можем выполнить для нового файла команду
ffmpeg -i zero.mp4 -f rawvideo - | xxd
. Флаги цветового пространства не игнорируются, однако просто записываются в несколько битов внутри раздела «video usability information» (VUI) в заголовке видеопотока. Теперь декодер будет искать VUI и использовать его для загрузки нужной матрицы.А вот результат:
И с VUI, и без него видео рендерятся с одинаковым цветом. Давайте попробуем файл BT.709:
ffmpeg -i 601vui.mp4 -an -vcodec libx264 -profile:v baseline -crf 18 -preset:v placebo -vf "colorspace=range=pc:all=bt709" -y 709.mp4
Новые опции:
-i 601vui.mp4
— используем в качестве источника видео прежний файл 601vui.mp4
-vf "colorspace=all=BT.709"
— сообщаем ffmpeg, что нужно использовать видеофильтр цветового пространства для изменения значений пикселей. Это похоже на умножение матрицы для преобразования из yuv в rgb, но матрица имеет другие коэффициенты. «all» — это сокращение для одновременного задания color_primaries, colorspace и color_trc.
Здесь мы берём видео 601vui.mp4 и используем фильтр цветового пространства для преобразования в BT.709. Фильтр цветового пространства может считать из vui файла 601vui.mp4 цветовое пространство входящих данных, поэтому нам достаточно только указать цветовое пространство, которое мы хотим получить на выходе.
Выполнив для этого файла команду ffmpeg -i 709.mp4 -f rawvideo - | xxd
, мы получаем после преобразования цветового пространства значения пикселей yuv (93,67,68). Однако при рендеринге файла он должен выглядеть так же. Стоит заметить, что окончательные результаты могут и не быть идентичными, потому что мы продолжаем использовать 24 бита для кодирования каждого пикселя, а BT.709 имеет чуть больший диапазон цветов. Следовательно, некоторые цвета в BT.709 не сопоставляются точно с BT.601, и наоборот.
Посмотрев на результат, можно чётко заметить, что что-то не так. Новый файл рендерится со значениями rgb, равными 0,157,0 — гораздо ярче, чем входящий файл.
Давайте внимательно изучим свойства файла при помощи приложения ffprobe:
ffprobe 601vui.mp4:
Stream #0:0(und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuvj420p(pc, smpte170m), 320x240, 9 kb/s, 25 fps, 25 tbr, 12800 tbn, 50 tbc (default)
И
ffprobe 709.mp4:
Stream #0:0(und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuvj420p(pc), 320x240, 5 kb/s, 25 fps, 25 tbr, 12800 tbn, 50 tbc (default)
Основная часть информации здесь для нас неважна, но мы заметим, что 601vui.mp4 имеет формат пикселей «yuvj420p (pc, smpte170m)». Так мы понимаем, что файл имеет правильный VUI. Но 709.mp4 содержит только «yuvj420p (pc)». Похоже, метаданные цветового пространства не были включены в выходной файл. Даже несмотря на то, что фильтр цветового пространства смог прочитать исходное цветовое пространство, и мы явным образом указали новое пространство, программа ffmpeg не записала правильный vui в конечный файл.
Это плохо… Ffmpeg — самый популярный инструмент для преобразования видео. И он упускает информацию о цвете. Вероятно, по этой причине многие видео не содержат метаданные цветового пространства, и поэтому многие видео рендерятся с рассогласованными цветами. В защиту ffmpeg можно сказать, что это сложная проблема. Чтобы инициализировать энкодер, нужно заранее знать цветовое пространство. Кроме того, цветовое пространство может быть изменено видеофильтром. Это сложная для решения в коде проблема, но всё равно печально, что такое поведение является стандартным.
Обойти её можно, добавив метаданные цветового пространства вручную:
ffmpeg -i 601vui.mp4 -an -vcodec libx264 -profile:v baseline -crf 18 -preset:v placebo -vf "colorspace=range=pc:all=bt709" -colorspace bt709 -color_primaries bt709 -color_trc bt709 -color_range pc -y 709vui.mp4
В результате значение цвета в 709vui.mp4 будет равно rgb (0,132,0). Яркость зелёного канала чуть меньше, чем в 601vui.mp4, но поскольку преобразование цветового пространства происходит с потерями, а результат меня устраивает, то назовём это успехом.
Из этого мы можем прийти к заключению, что когда в файле не указано цветовое пространство, Safari считает, что это BT.601. И со стороны Safari это очень хорошее допущение. Но как сказано выше, BT.601 — это стандарт SD-видео, а BT.709 — стандарт для HD. Давайте проверим HD-видео с VUI и без него, и посмотрим, как их рендерит Safari. Я использовал те же команды ffmpeg, только изменил разрешение на 1920×1080.
И в SD, и в HD цвет рендерится одинаково. Делая предположение о цветовом пространстве, Safari не учитывает разрешение. Apple уже давно работает в пространстве медиа и издательского дела, поэтому я ожидал, что продукт этой компании обеспечит достойные результаты. Но если даже в Safari всё устроено настолько хитро, то интересно, как обстоит ситуация в других браузерах.
Chrome:
Chrome рендерит видео 601 чуть более тёмным, чем Safari, но 709 выглядит так же. Подозреваю, что разница вызвана оптимизацией скорости в вычислениях с плавающей запятой, но для нашего теста это несущественно.
По опыту я знаю, что когда в настройках отключено аппаратное ускорение, Chrome выполняет рендеринг иначе:
Изучив этот результат, мы увидим, что значения в 601 рендерятся очень похожими на предыдущие, но файлы 709 рендерятся так, как будто в них нет VUI. Из этого мы можем заключить, что Chrome при отключенном аппаратном ускорении просто игнорирует VUI и рендерит все файлы, как будто они имеют формат 601. Это означает, что все файлы 709 будут воспроизводиться некорректно.
И наконец, давайте изучим Firefox:
Здесь нужно разобрать многое. Так как 709.mp4 и 709vui.mp4 выглядят одинаково, можно прийти к заключению, что при отсутствии VUI браузер Firefox предполагает формат BT.709. Правильный рендеринг 601vui.mp4 означает, что для контента BT.601 раздел VUI учитывается. Однако когда файл BT.601 без VUI рендерится как 709, то становится очень тёмным. Очевидно, что невозможно отрендерить картинку правильно без всей необходимой информации, однако выбранный Firefox способ искажает цвет сильнее, чем выбранные браузерами Safari и Chrome.
Как мы видим, ситуация с рендерингом цветов видео по-прежнему представляет собой Дикий Запад. К сожалению, мы рассмотрели только часть проблемы. Мы ещё не изучали Windows. Давайте сделаем это.
Microsoft Edge:
Похоже, что Edge (по крайней мере, на моём компьютере) просто игнорирует VUI и рендерит всё как 601.
Chrome (со включенным аппаратным ускорением):
Ситуация не очень отличается от Mac. При наличии VUI он обрабатывается правильно, но если его нет, для SD-контента предполагается формат BT.601, а для HD-контента — BT.709. Это единственный браузер, в котором я такое видел, но в этом есть определённая логика. Так как рендеринг выполняется иначе, чем на Mac, то подозреваю, что дело в ОС или, что более вероятно, в чём-то на уровне драйверов видеокарты, и этот выбор сделан не командой разработчиков Chrome.
Firefox ведёт себя так же, как и на Mac.
Что касается Linux, iOS, Android, Roku, Fire TV, смарт-телевизоров, игровых консолей и т.д., то я оставлю это в качестве упражнения для читателя.
Чему же мы научились? Самое главное: всегда указывайте в своих видео метаданные цветового пространства. Если вы пользуетесь ffmpeg и не задаёте флаги цвета, то вы работаете неправильно. Во-вторых, хотя ffmpeg и является потрясающей программой, её популярность, простота использования и неудачно выбранные стандартные параметры сослужили плохую службу. Никогда не стоит допускать, что ПО достаточно умно, чтобы разобраться в этом самостоятельно. Руководителям проектов Ffmpeg, Google, Mozilla, Microsoft (и, вероятно, Nvidia и AMD) нужно собраться и вместе выбрать единый способ. Я понимаю, что здесь нет хорошего решения, но плохое и предсказуемое лучше, чем плохое и случайное. Лично я рекомендую всегда предполагать формат BT.601, если раздел VUI отсутствует. Это создаёт наименьшую степень искажений. Можно выбрать для согласования этого стандарта FOMS, или даже AOM, поскольку эти организации имеют довольно неплохое представительство.
И напоследок: если у вас есть видео без информации о цвете и вам нужно его преобразовать или отрендерить, то удачи!
На правах рекламы
VDSina предлагает недорогие серверы с посуточной оплатой. Интернет-канал для каждого сервера — 500 Мегабит, защита от DDoS-атак включена в тариф, возможность установить Windows, Linux или вообще ОС со своего образа, а ещё очень удобная панель управления серверами собственной разработки. Давно пора попробовать ;)
Присоединяйтесь к нашему чату в Telegram.