2016-12-12 10 views
3

В Chrome. Я использую MediaRecorder и canvas.captureStream() для создания webm-файла холста.Как редактировать .webm Blobs, захваченные Chrome MediaRecorder

let recorder = new MediaRecorder(document.querySelector('canvas').captureStream(), {mimeType: 'video/webm'}); 
let chunks = []; 
let blob; 

recorder.ondataavailable = function(event) { 
    if (event.data.size > 0) { 
    chunks.push(event.data); 
    } 
}; 
recorder.onstop = function() { 
    blob = new Blob(chunks, {type: 'video/webm'}); 
    let url = URL.createObjectURL(blob); 
    let a = document.createElement('a'); 
    document.body.appendChild(a); 
    a.href = url; 
    a.download = Date.now()+'.webm'; 
    a.click(); 
    window.URL.revokeObjectURL(url); 
    a.parentNode.removeChild(a); 
} 
recorder.onstart = function() { 
    chunks = []; 
} 

Это основная запись и загрузка кода вместе с вызовом recorder.start(), чтобы начать запись и recorder.stop() до конца.

Выходной файл webm в порядке, проблема, с которой я сталкиваюсь, состоит в том, что из-за дерьмового компьютера/накладных расходов я не всегда могу сделать холст достаточно быстрым, чтобы сделать его полным 60 кадров в секунду. На самом холсте я не возражаю против более низкой частоты кадров, но отставание при рисовании на холсте переходит в веб-интерфейс, и у меня остается видео с частотой x0.9.

Я попытался исправить это, используя canvas.captureStream(0), чтобы фиксировать только один кадр за раз и сопоставлять это с каждым рендерингом холста. Но это терпит неудачу, потому что я не могу указать продолжительность, в которой должен длиться каждый кадр, и размер файла становится огромным, поскольку каждый отдельный фрейм имеет всю информацию заголовка.

В моем блоке blob видно, что первые 131 капли постоянны, а blob 132 имеет очень большой объем данных. После этого обычно имеется ~ 7 спейсерных блоков с 1 байтом каждый, а затем один блок, содержащий некоторое большее количество данных. Я знаю, что первые 132 капли - это информация заголовка + мой первый кадр. И я думаю, что капли с большим количеством данных - это каждый кадр. Я также предполагаю, что 1-байтовые спейсерные капли имеют какое-то отношение к длительности кадра или паузе в течение установленного промежутка времени.

Что я хотел бы сделать, так это иметь возможность изменять эти пробелы spacer, чтобы указать точную продолжительность кадра. Я попытался сделать это вручную, скопировав 7 спейсеров между 2 кадрами, где я знал, что частота кадров идеальна, а затем удаляет все остальные разделители и вставляет в эти идеальные спейсерные капли между каждым фреймом, но выходной файл не воспроизводится.

Я не понимаю данные о блобе? Есть ли способ вручную указать продолжительность, в которой длится кадр, изменяя данные о блобе, или я придерживаюсь любой частоты кадров, которую я могу нарисовать на холсте?

+0

Не уверен, что то, что я собираюсь сказать сейчас, на 100% верно, но я думаю, что вы не можете контролировать кадр записанного видео. Параметр fps предназначен только для captureStream, но это не настройка для MediaRecorder. Единственный вариант, который у вас есть: 'videoBitsPerSecond' в [MediaRecorder Constructor] (https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/MediaRecorder). Вы могли бы попытаться сыграть с ним. – Kaiido

+0

@ Kaiido К сожалению, это не поможет. У MediaRecorder на самом деле есть временный листок, который я могу разместить в MediaRecorder.start (timeslice), который, если установлен в 1000/fps, приведет к захвату кадров в fps, который я хочу. Я также могу вручную .requestData() после рисования кадра. Проблема в том, что узким местом является источник видео (например, обновление холста). Представьте, если бы я рисовал свой холст раз в секунду, поэтому моя анимация составляла 1 fps.Я хочу, чтобы иметь возможность записывать этот холст, а затем генерировать .webm, где каждый кадр этой анимации холста длится по моему выбору, независимо от холста 1 fps. – Bobby

+0

Как я уже сказал, я не был уверен в предыдущем комментарии. У меня не было много времени, чтобы провести тесты, и мой VLC никогда не возвращает метаданные FPS из видеороликов, записанных в браузере ... Но у моего FF, похоже, одинаковое количество «mozDecodedFrames», чем fps, установленное в captureStream, когда установлено до <30 кадров в секунду. (10 кадров для 10-секундного видео с частотой 1 кадра в секунду и 297 кадров для видео с разрешением 10 сек при скорости 60 кадров в секунду). Не уверен, что он надежен. – Kaiido

ответ

1

я был в состоянии определить, частота кадров отдельно от частоты обновления холста путем приостановки и возобновления рекордер тайм-аут, и запрашивает кадр перед паузой снова:

let recorder = new MediaRecorder(canvas.captureStream(), {mimeType: 'video/webm'}); 
recorder.start(); 
recorder.pause(); 

function draw() { 
    context.drawImage(...); 
    recorder.resume(); 
    setTimeout(function() { 
    recorder.requestData(); 
    recorder.pause(); 

    //update progress bars or any laggy overhead stuff at this point 

    requestAnimationFrame(draw); 
    }, 1000/fps); 
} 
requestAnimationFrame(draw); 

Таким образом, любое отставание фактического холста рисование или обновление индикаторов выполнения и т. д. не повлияет на сбор кадров в рекордере. recorder.requestData(), по-видимому, не является необходимым, но также, похоже, не имеет недостатков. Он включен здесь для ясности.

Я не проверял подробно, но в начале может быть двойной кадр в зависимости от того, собирает ли или нет recorder.start() исходный кадр, и ваш холст не пуст.

+0

Хорошая идея: пауза/резюме. Но цикл анимации ... не знаю, почему вы делаете setTimeout в цикле rAF, rAF будет стрелять примерно на 60 кадров в секунду, но если вы делаете setTimeout для запуска следующего вызова, тогда вы можете потерять свою частоту кадров. – Kaiido

+0

SetTimeout - это то, что контролирует частоту кадров рекордера. Мы рисуем на холсте, а затем начинаем запись, а затем после таймаута 1000/fps мы прекращаем запись (поэтому мы будем пересматривать один кадр, который длится 1000/fps мс), а затем мы обрабатываем любые накладные расходы и переходим к рисованию следующий кадр. – Bobby

+0

Я не слишком беспокоюсь о частоте кадров на холсте, просто в рекордере. rAF частично присутствует, потому что я привык использовать его при рисовании на холсте, а также в случае, если вкладка заканчивается в фоновом режиме, тогда rAF будет ждать, пока стрелка снова не нажмет фокус. Я запустил его с добавленными таймерами и разницу между тем, где я приостанавливаю рекордер в таймаут и где функция запускается после вызова внутри rAF, составляла ~ 5 мс, тогда как мой fps равнялся 30. И это с обновлением индикатора выполнения и некоторых другие очень мелкие вещи. – Bobby