thedeemon: (Default)
[personal profile] thedeemon
Давайте я сразу покажу картинку, а потом немного расскажу о чем это:



Есть у меня свой видеокодек - ScreenPressor - изначально 100% беспотерьный кодек для записи и/или передачи экрана, но также умеющий опционально немного потерять качества (нижние биты цветов), если попросят. Для него в стародавние времена был сделан плеер на Flash'e. Года три назад я экспериментировал с идеей перевода его на JS, Dart или ASM.js, портировал на них кусочек, померял скорость, показал клиентам, те сказали "пока не надо, флэша хватает". А в этом году, когда Flash стал уже совсем не комильфо, и бразуеры его стали выключать, клиенты опомнились и говорят "хотим JS! И скорости!". А у меня как раз поспела новая 3-я версия кодека, где вместо арифметического кодера модный ANS, и сжатие параллелизовано получше, на ряде тестов кодирование+декодирование раза в 2 ускорилось.
Dart к этому времени уже давно отказался от своей ВМ в браузере. ASM.js по-прежнему очень жирный, и не везде он хорошую скорость показывал, да и WebAssembly его вытесняет. Но WebAssembly еще рановато использовать, когда у тебя корпоративные клиенты с не самыми новыми браузерами. Флэшовый плеер был написан на Haxe, который, как известно, умеет и JS генерить. Взял я тогда исходники флэшовой версии плеера и задействовал OpenFL - это библиотека на Haxe, которая дает API Flash'a и позволяет их использовать на других таргет платформах, в моем случае JS.
И шо я вам скажу за OpenFL. В принципе, она работает. Берешь код, что был заточен на Flash, раз-два, и вот уже готов один толстый .js файл и рядом парочка маленьких (две библиотечки-зависимости, одна из которых - Howler.js, звукоигралка). И он даже работает. Но плохо и медленно. И тут начинается долгая работа по адаптации, исправлению косяков и обходу граблей.
Во Flash'e обычные массивы были медленные, но был отдельный волшебный API для работы с быстрым типизированным массивом чисел (вроде typed array и ArrayBuffer в JS), но только одним за раз. Соответственно, в моей флэшовой реализации кодека все его данные жили в этом одном массиве. Его реализация в OpenFL мягко говоря не очень шустрая, потому надо все переписать на использование всяких родных Uint8Array.
Потом обнаружилась подстава с загрузкой данных. Казалось бы, элементарная вещь - запросить по HTTP какой-то файл, и по мере поступления бинарных данных как-то их разбирать и обрабатывать. Во Flash'e это отлично делалось стандартным Loader'ом с колбэком. OpenFL его реализует через XMLHttpRequest (а что ж еще?), но в нем обычно колбэки о получении данных срабатывают лишь когда все данные уже получены, а не в процессе. Точнее, есть заднепроходный способ сделать так, чтобы он колбэки дергал в процессе, но OpenFL его не использует, пришлось переписать работу с XHR самому.
Или, например, в какой-то момент оказалось, что больше половины времени уходит на то, чтобы декодированный кадр оказался в нужном битмапе (BitmapData.setPixels()). Сколько есть способов разместить значения R,G,B в RGB32? И это сказывается. Кодек подразумевает один порядок байтов в RGB32, Flash - другой, битмап DOMа браузера - третий. Получилось, что там данные дважды конвертировались, причем делали это весьма неоптимальным способом: в исходниках OpenFL для работы со всякими RGB32 и BGR32 есть красивые классы и проперти, но как посмотришь, что за JS код получается в итоге, сразу волосы шевелятся, столько лишних действий ради перестановки пары байт. Убрал все эти лишние конвертации, сразу полегчало, а то они около 60 мс на кадр съедали.
Ну и используемый для звука howler.js не мог не подложить свинью. Звук в видеофайле же раскидан кусочками, надо уметь эти кусочки в памяти собрать, декодировать, потом когда надо проиграть. А howler сделан как обычно для упрощения и унификации, в итоге он только целиком аудиофайл может загружать, а так чтобы из памяти - хрен. Пришлось напрямую WebAudio API использовать, благо это просто, все заработало, но только не в Internet Explorer'e.
В общем, [insert expletive] на дворе конец 2017 года, Flash, в котором все просто работало, причем одинаково во всех браузерах, выкинули, а HTML5, который давно считается годной заменой, на деле на 90% состоит из плохо работающих костылей и подпорок.
С размером тоже смешно вышло - весь плеер на Flash'e весил около 30 КБ. Вариант на JS же весит больше мегабайта, ибо включает много ненужных частей из OpenFL, которые Closure Compiler не смог выкинуть.
Отдельное удовольствие - отладка. Вот есть у нас массив Uint8Array на 100 элементов. Что будет, если мы попробуем записать в 103-й? Ничего. Вообще ничего. Ни исключения, ни изменения его длины, тишина. А что будет, если прочитать 103-й элемент? Просто undefined, опять никаких сообщений о вылете за пределы. Удачи в поиске ошибок!
Тем не менее, в итоге все получилось. Новый pure JS player работает, и видимо даже быстрее флэшового. Примеры: маленькое видео со звуком, побольше и без звука.
На картинке в начале поста результаты замеров, когда сначала делался переход на 0 кадр, а затем на 490-й (при том что следующий ключевой кадр - 500-й). После перехода на нулевой кадр плеер автоматически декодирует несколько следующих, заполняя свой буфер, поэтому переход потом на 490-й кадр означает в данном случае декодирование 480 кадров. Эту операцию и мерял, на видео 1364х768 (1 миллион точек, 4 МБ данных RGB32 на один кадр). Там в среднем в одном кадре не так много изменений, и большая часть кадра получается копированием из предыдущего (в том числе при скроллах и движении окошек), но все равно, способность JavaScript реализации ворочать такие 4 МБ мешки по 150-200 штук в секунду (на ноуте с Core i5) впечатляет. Нативный же код, где компилятор умеет в векторизацию, в 2-4 раза быстрее. На других видео цифры могут быть другими, тут многое зависит от декодируемого контента, его сложности.
А, еще что занятно: 3-я нативная версия кодека заметно быстрее 2-й. А вот их JS реализации оказались наоборот, третья несколько медленнее. Что хорошо для нативного кода в плане оптимизаций, не всегда хорошо для интерпретируемого.

Date: 2017-11-25 10:50 am (UTC)
From: [personal profile] jamhed
Могу свои 5 копеек добавить: flash выкинули, но на момент выкидывания это был единственный способ видео в браузере показывать близко к real time. Альтернативы в виде html5 hls принципиально работали только с буферизацией секунд в 30. Сейчас для этого можно модный WebRTC использовать.

Date: 2017-11-25 02:27 pm (UTC)
From: [personal profile] sassa_nf
same CPU utilization everywhere?

Date: 2017-11-25 03:32 pm (UTC)
mtve: (Default)
From: [personal profile] mtve
круто!

Date: 2017-11-25 09:59 pm (UTC)
juan_gandhi: (Default)
From: [personal profile] juan_gandhi
Ничего себе приключения и достижения!!!

Profile

thedeemon: (Default)
Dmitry Popov

April 2025

S M T W T F S
  1234 5
6789101112
13141516171819
20212223242526
27282930   

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Apr. 23rd, 2025 05:29 pm
Powered by Dreamwidth Studios