Публикация была переведена автоматически. Исходный язык: Русский
Камеры пишут MJPEG-стримы в контейнер AVI. Проблема: AVI не хранит длительность в заголовке, нужно пройтись по всем кадрам и посчитать. ffprobe на 2-часовом файле
работает 10 минут.
Нужен быстрый способ узнать длительность не читая весь файл.
Способ 1: прочитать последний кадр
MJPEG — это последовательность JPEG-картинок. Каждый кадр имеет timestamp. Если прочитать временную метку последнего кадра и первого → разница = длительность.
Но как найти последний кадр не читая весь файл? Читаю с конца:
const fd = fs.openSync(filePath, 'r');
const fileSize = fs.statSync(filePath).size;
const buffer = Buffer.alloc(100000); // 100KB с конца
fs.readSync(fd, buffer, 0, 100000, fileSize - 100000);
fs.closeSync(fd);
// Ищем JPEG маркер FFD8 FFD9 в буфере
const lastFrameTimestamp = extractTimestamp(buffer);
const duration = lastFrameTimestamp - firstFrameTimestamp;
Работает в 95% случаев. Остальные 5% — файлы где последние 100KB битые, тогда fallback на полное чтение.
Способ 2: конвертация с остановкой
Запускаю FFmpeg на конвертацию, но убиваю процесс через 5 секунд. FFmpeg в логе пишет "frame=1234 time=01:45:32". Парсю лог, извлекаю время, экстраполирую на весь
файл.
const ffmpegProcess = spawn('ffmpeg', ['-i', filePath, '-f', 'null', '-']);
setTimeout(() => {
ffmpegProcess.kill('SIGTERM');
}, 5000);
// Парсим stderr FFmpeg
let duration = null;
ffmpegProcess.stderr.on('data', (data) => {
const match = data.toString().match(/time=(\d{2}):(\d{2}):(\d{2})/);
if (match) {
duration = `${match[1]}:${match[2]}:${match[3]}`;
}
});
Не точно (погрешность ±10 секунд на часовом файле), но быстро — 5 секунд против 10 минут.
Способ 3: по размеру файла
MJPEG имеет предсказуемый битрейт. Если камера пишет 15 FPS, 640x480, JPEG качество 80% → ~100KB на кадр → 1.5MB в секунду.
const fileSize = fs.statSync(filePath).size;
const estimatedDuration = fileSize / 1500000; // размер / битрейт
Очень неточно (погрешность ±20%), но мгновенно. Использую как первое приближение, потом уточняю одним из точных методов.
Комбинация всех трёх способов даёт точность 99% и скорость <10 секунд на любой файл.
Камеры пишут MJPEG-стримы в контейнер AVI. Проблема: AVI не хранит длительность в заголовке, нужно пройтись по всем кадрам и посчитать. ffprobe на 2-часовом файле
работает 10 минут.
Нужен быстрый способ узнать длительность не читая весь файл.
Способ 1: прочитать последний кадр
MJPEG — это последовательность JPEG-картинок. Каждый кадр имеет timestamp. Если прочитать временную метку последнего кадра и первого → разница = длительность.
Но как найти последний кадр не читая весь файл? Читаю с конца:
const fd = fs.openSync(filePath, 'r');
const fileSize = fs.statSync(filePath).size;
const buffer = Buffer.alloc(100000); // 100KB с конца
fs.readSync(fd, buffer, 0, 100000, fileSize - 100000);
fs.closeSync(fd);
// Ищем JPEG маркер FFD8 FFD9 в буфере
const lastFrameTimestamp = extractTimestamp(buffer);
const duration = lastFrameTimestamp - firstFrameTimestamp;
Работает в 95% случаев. Остальные 5% — файлы где последние 100KB битые, тогда fallback на полное чтение.
Способ 2: конвертация с остановкой
Запускаю FFmpeg на конвертацию, но убиваю процесс через 5 секунд. FFmpeg в логе пишет "frame=1234 time=01:45:32". Парсю лог, извлекаю время, экстраполирую на весь
файл.
const ffmpegProcess = spawn('ffmpeg', ['-i', filePath, '-f', 'null', '-']);
setTimeout(() => {
ffmpegProcess.kill('SIGTERM');
}, 5000);
// Парсим stderr FFmpeg
let duration = null;
ffmpegProcess.stderr.on('data', (data) => {
const match = data.toString().match(/time=(\d{2}):(\d{2}):(\d{2})/);
if (match) {
duration = `${match[1]}:${match[2]}:${match[3]}`;
}
});
Не точно (погрешность ±10 секунд на часовом файле), но быстро — 5 секунд против 10 минут.
Способ 3: по размеру файла
MJPEG имеет предсказуемый битрейт. Если камера пишет 15 FPS, 640x480, JPEG качество 80% → ~100KB на кадр → 1.5MB в секунду.
const fileSize = fs.statSync(filePath).size;
const estimatedDuration = fileSize / 1500000; // размер / битрейт
Очень неточно (погрешность ±20%), но мгновенно. Использую как первое приближение, потом уточняю одним из точных методов.
Комбинация всех трёх способов даёт точность 99% и скорость <10 секунд на любой файл.