Про 200 webpack-плагінів для перформансу на конференції JavaScript fwdays'20 | 14 березня
×Закрыть

Переупаковка контейнера mpeg4 налету

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

В общем есть контейнер с видео и есть контейнер с аудио и задача сервера при запросе к нему например от html5 плеера — налету перепаковать возвращаемый контейнер который подменит в видео аудио-дорожку ну и понятно подкорректирует заголовки.

Все файлы заранее перепаковывать и сохранять на диске не имеет смысла так как файлов очень много и переупакованный файл как правило понадобится всего один раз а вернуть в плеер его надо сразу.
Знаю когда то очень давно читал блог mail.ru как у них получилось налету конвертировать видео-файлы в веб версии их облака, но сейчас не найду эту статью да и врядли там будет полезная информация, пока только остается надежда возможности решить это с технической стороны.

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

LinkedIn
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

www.wowza.com — эта хрень умеет налету паковать в запрошенный формат

Для отдачи клиенту файла целиком проблем нет — ffmpeg’ом читаем из оригинала видео пакеты, читаем из другого источника аудио пакеты и пишем это в новый файл. Только этот файл «виртуальный», буфер, просто небольшой кусок в памяти. С одного конца в него пишем, а с другого шлем клиенту по http (chunked). Переданные куски очищаем из буфера.
Не понятно только как с перемоткой будет. Когда клиент запросил какой-то произвольный кусок, ffmpeg должен по сути создать заново тот «виртуальный» файл и записать медиа пакеты с запрошенного места из оригинала. Во-первых, так как это новый файл, то у него будет в начале (или в конце) заголовки, а плеер их не ожидает в этом месте, как он себя поведет хз. Во-вторых, DTS и PTS у пакетов наверное для клиента будут неправильными, с этим тоже нужно что-то решать.

Я делал так. Делил файл на отрезки по 1-5 минут(виртуально), перепаковывал аудио через amazon lambda в нужный формат(я вообще и видео перепаковывал) используя ffmpeg, потом менял дорожку используя VLC и собирал все обратно им же. Операция перекодировки секунд 10-15(сильная паралельность), а вот собрать 4Гб файл обратно уже дольше. Но можно в принципе начать его отдавать до конца сборки(общий размер то и данные уже есть), ну или спросить у экспертов по FS как быстро собрать файл из других файлов.

p.s. Решение задачи подойдет именно перепаковка на лету, все другие варианты ffmpeg hls и всякие альтернативы рассматривались в первую очередь, возможно кто существует данными знаниями и есть желание подзаработать приличную суму денег, с удовольствием обсужу данный вариант

Почему вы решили, что ffmpeg не может перепаковывать «на лету»?

ffmpeg работает со стримом, по хттп тоже файл передается в потоковом режиме, а не «оп и всё».

Если взять, например, ноду, то ваша задача решается в 1 модуль (простая обертка над ффмпегом) + несколько строк кода:

const http = require('http');
const ffmpeg = require('fluent-ffmpeg');
const requestHandler = (request, response) => {
  ffmpeg('/path/to/file.avi')
  // тут делаем что надо, перепаковываем, перекодируем, все что угодно
  .pipe(res, { end: true });
}
const server = http.createServer(requestHandler)ж
server.listen(8080, () => {})

При необходимости можно одновременно писать и в хттп респонс, и в файл (в другой папке, например)

я тестировал с подобным вариантом только на го, ни чем не отличается от вашего примера. Все равно не подходит данный вариант либо я что то упустил в мануале. Попробую пересмотреть мануал может правда что то упустил. Прикол в том что надо поддержать http заголовок range и пересчитать возвращаемый вес контейнера может это где то возращает библиотека я не рассмотрел в мануале плюс еще прикол в том что сам файл берется по http

Что конкретно не работает-то? Медленно? Файл не перепаковывается? Ошибки вываливаются?

Я не разобрался как работать с range: bytes

для того чтоб был заголовок range (предусматривающий докачку или закачку в несколько потоков) — нужен как минимум готовый файл с известным размером, просчитать конечный размер видео-файла с перепаковкой на-лету не получится с вероятностью 99.99(9)%. Как вариант — писать свой плейер, который умеет ориентироваться по готовому видеопотоку (заголовок range обеспечен) и параллельно (вторым потоком) подгружать нужное аудио (но тут непонятно как синхронизировать)...

Для поддержки заголовка

range

готовый файл не нужен. Это заголовок клиента и там может быть все что угодно. Если нужный рендж «попадает» в файл — то ффмпегом проматываем на нужный момент времени (которой считается из исходного файла) или просто модулем для работы с файловой системы отрезаем кусок и отдаем его опять в ffmpeg. Если не попадает — то возвращаем ошибку и до свидания.

Что то мне кажется что было бы намного проще и производительнее просто самому курировать данными. И не приходилось бы проматывать данные а просто в потоке по пересчетам сразу берешь необходимые данные.
По поводу перемотки это будет долго по времени намного быстрее будет работать seak ну в случае с http это тот же range.
Просто речь идет не о маленьком проекте. Сейчас у меня десятки серверов каждых из них обслуживает десятки тысяч клиентов одновременно. По этому надо искать максимально быстрое решение. Все больше склоняюсь к тому что надо перечитывать ISO 14496
Сам же проект заключается как раз в том что бы загружать файлы которые хранятся в на ютубе и который падло возвращает отдельно дорожку аудио и отдельно видео в 1080p а последнее время замечаю он начал отказываться от переменной url_encoded_fmt_stream_map и и возвращает даже 720 разбитым на видео и аудио. Да и замечаю что ютуб значительно пытается сейчас перейти на HLS и последний месяц ежедневно вносит изменения в плеер.
Как бы вообще не завалился мой проект из-за этого

Ну в таком слечае надо разбивать файл на чанки сразу и отдавать Partial Content. Но в итоге вы изобретете hls.

Этот заголовок должно понимать то, что будет стоять на раздаче контента. Классические веб-серверы умеют понимать байты а не тайм-метки:
developer.mozilla.org/...​TTP/Headers/Accept-Ranges
tools.ietf.org/html/rfc7233#section-3.1
поэтому нужно пилить свой плейер и свой стриммер

Почему не получится? Если заране знать размер каждого медиа, атомов и прочего использовать кеши вообще проблем не вижу расcчитать с вероятностью 100%

Скажите, что у вас файл больше размером, чем он есть
делов то. все работает.

Пересчитывать возвращаемый вес контейнера не надо. Если у вас таки

запросе к нему например от html5 плеера

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

Еще я подозреваю у вас mp4 как результирующий контейнер. Если так, то можно от него отказаться и посмотреть в сторону ts, mkv.

Не совсем понятна проблема. Если задача в том, чтобы динамично подменять аудио-дорожку для видео (например, с разными языками перевода), то почему не воспользоваться одной из технологий адаптивного стриминга? Например HLS или DASH.
Исходя из того, что у вас заранее есть контейнер с видео и контейнер с аудио, то я понимаю, что речь идет не о Live Streaming, а о Video on Demand.
В случае с HLS, вы легко можете нарезать свои контейнеры на фрагменты и разбить их на плейлисты. Ваша кастомная аудио-дорожка будет одним из под-плейлистов, на который клиент сможет в любой момент переключиться (или вы можете зафорсить ее).
По DASH точно не подскажу, но там идея такая же.
Для HTML5 есть отличный HLS клиент www.github.com/video-dev/hls.js

Дока по созданию HLS плейлиста. Там же где-то будет ссылка на консольную тулзу для нарезки.
developer.apple.com/...​and_playlist_construction

Если таки нужно перепаковывать контейнер, то смотреть стоит в сторону ffmpeg, как тут уже упоминали. Но я бы сперва убедился, что именно в этом задача.

Забыл добавить что сам контейнер надо именно целиком так как файл не только в плеере используется, по этому HLS не подходит хотя его я тоже рассматривал.

Дорого получится. И небезопасно. В том смысле что распространённые кодеки = распространённые уязвимости. А кодек-то пашет из-под пользователя операционки, которая зачастую сама дырявая, так что рута/админа смогут получить просто подсунув «правильное» видео. Так что возможно придётся кодек отдельной виртуалкой поднимать, что ещё дороже и продукт может не оправдать затрат.

Мой совет — освой поиск по конкретному домену и таки найди статью, что сделали до тебя. Просто чтобы убедиться, что ничего хорошего у них не вышло.

Проблема перепаковать контейнеры как по мне не серьёзная: если речь не о перекодировании, а просто о перепаковке пакета — то не проще ли поднять на JavaScript непосредтсвенно на клиенте? Другими словами, добыть какой-нить ПЛЕЕР в исходниках, почитать исходнички до степени понимания на какой стороне ловить распакованный материал — и если цель просто воспроизвести — то вообще не перепаковывая в серьёзные контейнеры отдать твоему клиенту два РАЗНЫХ потока [аудио и видео], как это делает Ютуб, и уже в сыром формате, не требующем дальнейших раскодирований.

Как правило все файлы на сервере перекодированы одним кодеком.
По поводу виртуалки вообще не парюсь арендую сервера по 1000$/мес.
Сам продукт оправдает себя я готов за него любые деньги заплатить
По поводу js был такой плеер это гемморойно подгонять это все по таймингу и куча багов была, хотя не доработано было и заброшенно, как раз от примера ютуба и отталкивался.
Но в моем случае не только html5 плеер нужен. Для некоторых клиентов файл нужен целиком, не то что для некоторых примерно 50%

Подгонять по таймингу обычно проблем нет, если это дорожки одинакового тайминга. Просто управление идёт не 1 потоком, а двумя. Подгонки тут не нужно, нужно вменяемое воспроизведение с синхронизацией, при котором 2 кодека семафорят софту что мол добрался до такой-то секунды, и если параллельный поток НЕ добрался [например, на слабом устройстве], там уже решаешь, либо пропустить кусок видео, либо задержать и подождать воспроизведения звука.
-------------------------------------
Файл целиком — проще перекодировать на сервере нативными утилитами, и уже тогда отдать клиенту ссылку. Нужно поймать КОГДА файл уже начал писаться, чтобы дать возможность его начать качать. И соответственно разрулить права доступа, чтобы когда файл пишется, он и читаться мог. КАК ПРАВИЛО, твоя задача отдавать контент ОЧЕНЬ медленно, по ≈0.1кБ в секунду, чтобы поток не прервался, но и не обгонял.

Проблема: не всё кодек способен проглотить, будут исключительные ситуации, будут зависания, и за этим делом надо следить в породившей его задаче.

Проблема если нужен контейнер, у которого индекс в начале файла, а не в конце. Соответственно пока весь не перекодировался, не начнёшь качать.

ВЕРОЯТНОЕ РЕШЕНИЕ: иметь дорожки хранимыми отдельно, а совмещать в контейнер уже онлайново. Проблема: не будет возможности докачки, не будет возможности назвать размер контента. Решение: кеширование данных на диск. Но это должны быть РАЗНЫЕ диски, потому что читать и писать одновременно с одного HDD — редкостное тормозилово.
Акцент: не RAID, а чистый винт, можно даже Seagate. Надёжность данных тебе не интересна, ну сбойнёт пару сотен байт, ну и хрен бы с ними. Можно несколько винтов, если тебе нужно параллелить процесс.
Опять же, решение сомнительное, если у тебя основной контейнер предсказуем. То есть возвращение к твоей задаче — перекодировать онлайн.
Я вижу такое решение, но это требует понимания кодека: хранить не весь контент, а исключительно ИНДЕКС, контейнера. По крайней мере это должно сработать с mp4, у которого индекс тупо в конце.
Вероятное решение: хранить видео уже нарезанными кусочками, как это делает vimeo, а кодеком совмещать это дело в контейнер. Но как раз это требует знать кодек. Судя по твоим объёмам, как раз это таки и придётся делать — нанимать человека, знающего конкретный кодек, и ставить задачу определить организацию хранения, ну и написание на С++ соответственного плагина под NGINX.

Почему нативные утилиты лучше: в разы быстрее любых онлайн-прибамбасов. В первую очередь из-за доступа к памяти и конвейерным командам процессора. Так что если у тебя полноценный сервак, лучше как раз таки перегонять видео на честном серваке утилитами.

Я бы, честно говоря, заплатил МЕСТОМ, чем производительностью. То есть когда видос закачался — он перегонялся фоново, как только есть возможность по нагрузке. А далее уже файлохранилище контролировалось как кеш, постфактум. Никто не скачал за месяц -> досвидос. Захотели скачать — вот тогда уже ждём онлайн.

Есть ОЧЕНЬ СКОТСКОЕ решение для того чтобы выиграть время: надо добавлять в начало видео фрагмент рекламы. Соответственно этот кусок лежит перекодированным, и пока он медленно качается — идёт перекодировка остального контента.

АКЦЕНТ: процессу перегонки видео нужно понижать приоритет ввода-вывода, если на Линуксах. Уж очень они умеют выжрать доступ к жёсткому диску.

ОЧЕНЬ СКОТСКОЕ

именно так сейчас и реализовано)) 15 секунд принудительной рекламы)

Решения сходу не знаю, но я бы в ffmpeg поковырялся — возможно там подобное уже реализовано.

Вторая подобная хрень гстример. Но он чаще глючит, чем работает.

ага, сам не делал, но о схожем опыте слышал как раз с ffmpeg.
как я понимаю, склеить как раз ваще не проблема stackoverflow.com/...​into-a-video-using-ffmpeg
вот разве что отдача потоком имеет дофига вариантов, не разобрался, какой подходит под ситуацию

Решение именно на ffmeg. Автору — гуглить ffmeg+nginx. Сразу скажу, малость ресурсоемкая задача.

ffхуег еще апач предложил бы. На ffmpeg это легко решается тремя параметрами но в моем случае кто понимает как работает ffmpeg поймут что данная библиотека не подходит для решения задачи.

Ну почему же не подходит?
Во первых, hls не обязателен. Я его выбирал, к примеру, из-за поддержки устройств. И то, были моменты с моб.
Во вторых — да, пусть ну не прям в реальности на лету, но с ограниченным кешем все можно делать. Если это не задача стриминга, где модуль nginx не подходит — что мешает управлять каким-нибудь небольшим скриптом в запускаемой потоке отдачи?

Подписаться на комментарии