Я как-то
писал про то, как читать mp4 контейнер. Структура сложная, поэтому когда я реализовывал этот код пару лет назад, я решил сделать такую архитектуру:
- читаем заголовок каждого трека
- строим для каждого трека один большой бинарник в котором закодированы данные по кадрам:
<> - строим общий список всех фреймов, сортированных по DTS:
<> - когда нужно извлечь кадры, перебираем по второму индексу пока не найдем нужный номер с нужным треком
- потом вытаскиваем все кадры из нужных треков
Это работало с RTMP, который покадрово вычитывает файл и шлет в сеть.
Однако прошло время и стало ясно, что это абсолютно нерабочая ситуация:
- Во-первых нельзя слать файл покадрово. Нужно только блоками, лучше всего сразу целый GOP
- во-вторых на 12-гигабайтных файлах открытие занимает по 20 секунд, т.е. пользователь 20 секунд ждет начала просмотра
- в-третьих считывание gop по такому алгоритму занимает до 3 секунд. При том, что сегмент сам длится 2 секунды, просмотр становится невозможным
Решить проблему получилось крайне быстро и без извратов и без переписывания кусков кода на C
Получилось снизить время открытия файла с 20 секунд до 40 мс, расход памяти с 250 мегабайт до 5 мегабайт, чтение сегмента с 3 секунд до 20 мс.
- Во-первых пришлось отказался от парсинга всех таблиц в moov атоме. Я решил оставить данные сжатыми, а учитывая что доступ к файлам через mmap, эффект от этого был крайне положительный.
- Во-вторых пришлось перейти сразу на блочное считывание сегментов. Больше никакого покадрового доступа. Вся компрессия в moov атоме - это дельта-компрессия, т.е. зная таймстемп одного кадра, следующий вычисляется простым паттерн-матчингом бинарника.
Запрогать получилось за один день на январских праздниках, а мораль такова, что высокоуровневая оптимизация позволяет ускориться в 100 раз в то время, как низкоуровневый дроч с NIF-ами зачастую только в 1,5-2 раза.