Запишите 5-секундные фрагменты звука с помощью MediaRecorder, а затем загрузите на сервер

Я хочу записать 5-секундные сегменты микрофона пользователя и загрузить каждый на сервер. Я попытался использовать MediaRecorder и вызвал методы start () и stop () с интервалом в 5 секунд, но когда я объединяю эти записи, между ними появляется звук «пропуска». Итак, я попытался записать 5-секундные сегменты, используя параметр временного интервала метода start ():

navigator.mediaDevices.getUserMedia({ audio: { channelCount: 2, volume: 1.0, echoCancellation: false, noiseSuppression: false } }).then(function(stream) {
  const Recorder = new MediaRecorder(stream, { audioBitsPerSecond: 128000, mimeType: "audio/ogg; codecs=opus" });
  Recorder.start(5000); 
  Recorder.addEventListener("dataavailable", function(event) {
    const audioBlob = new Blob([event.data], { type: 'audio/ogg' });
    upload(audioBlob);
  });
});

Но можно играть только в первом сегменте. Что я могу сделать или как сделать все капли доступными для игры? Я ДОЛЖЕН записать, а затем загрузить каждый сегмент. Я НЕ МОГУ создать массив блобов (потому что пользователь может записывать данные в течение 24 часов или даже больше, и данные должны быть загружены на сервер, пока пользователь записывает - с задержкой в ​​5 секунд).

Спасибо!


person MM PP    schedule 13.07.2018    source источник


Ответы (1)


Вы должны понимать, как создаются медиафайлы.
Это не только необработанные данные, которые можно напрямую преобразовать в аудио или видео.

Это будет зависеть от выбранного формата, но в основном у вас есть так называемые метаданные, которые похожи на словарь, описывающий структуру файла.

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

API MediaRecorder здесь находится в странном положении, поскольку он должен иметь возможность одновременно записывать эти метаданные, а также добавлять неопределенные данные (это живые рекордер).

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

Теперь то, что вы получаете в datavailableEvent.data, - это только часть создаваемого файла.
Первый, как правило, будет содержать метаданные и некоторые другие данные, в зависимости от того, когда событие было приказано запустить, но следующие части не обязательно будут содержать какие-либо метаданные.

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


Итак, к вашей проблеме у вас есть разные возможные подходы:

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

    const recorder = new MediaRecorder(stream);
    const chunks = [];
    recorder.ondataavailable = e => chunks.push(e.data);
    recorder.start(); // you don't need the timeslice argument
    setInterval(()=>{
      // here we both empty the 'chunks' array, and send its content to the server
      sendToServer(new Blob(chunks.splice(0,chunks.length)))
    }, 5000);
    

    А на стороне сервера вы добавляете вновь отправленные данные в записываемый файл.

  • Другой способ - создать множество небольших автономных файлов, и для этого вы можете просто сгенерировать новый MediaRecorder через определенный интервал:

    function record_and_send(stream) {
       const recorder = new MediaRecorder(stream);
       const chunks = [];
       recorder.ondataavailable = e => chunks.push(e.data);
       recorder.onstop = e => sendToServer(new Blob(chunks));
       setTimeout(()=> recorder.stop(), 5000); // we'll have a 5s media file
       recorder.start();
    }
    // generate a new file every 5s
    setInterval(record_and_send, 5000);
    

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

person Kaiido    schedule 16.07.2018
comment
Большое спасибо за ответ! Да, мне нужно, чтобы можно было воспроизводить каждый аудиосегмент. Я использовал первый метод, но отдельные сегменты не воспроизводятся. Если я использую второй метод, и я запускаю и останавливаю рекордер, каждый сегмент можно воспроизводить, так что это хорошо, но когда мне нужно присоединиться к ним, тогда между каждым из сегментов будет звук drop, clack, pac. Хммм ... Есть ли возможность использовать первый метод и скопировать и вставить метаданные (или что-то еще) из первого файла во все остальные сегменты? Большое тебе спасибо! - person MM PP; 16.07.2018
comment
@MMPP не совсем нет. Третий способ, который я не включил в ответ, - это одновременное выполнение обоих: запустить один цикл, который будет записывать короткие последовательности, и более крупный, который создаст полную удлиненную запись. (Отправляйте только куски из полного на ваш сервер) - person Kaiido; 16.07.2018
comment
Раньше я думал назначить награду, если не получу ответа (потому что это было срочно). Итак, поскольку вы ответили на вопрос до того, как я смог начать награду, я хочу предложить вам 50 единиц моей репутации (примерно за 23 часа). Спасибо! - person MM PP; 16.07.2018
comment
Третий способ действительно хороший вариант! Действительно хорошая идея! Большое спасибо! - person MM PP; 16.07.2018
comment
@MMPP Вы можете посмотреть stackoverflow.com/questions/52817966/. Хотя приведенное здесь решение устарело, можно использовать новый API AudioWorklet. - person Sadanand Upase; 06.06.2019
comment
@MMPP, могу я узнать, как вы решили эту проблему? В настоящее время я столкнулся с той же проблемой ... не могли бы вы предложить решение? - person Akbar Basha; 15.09.2020
comment
@AkbarBasha Я не решил эту проблему этими методами. Вы можете попробовать использовать WebRTC. Лично я создал некоторое компьютерное программное обеспечение на языке C, используя FFMPEG, чтобы решить эту проблему быстро, то есть без веб-браузера. - person MM PP; 21.09.2020
comment
просто хочу упомянуть, что вы можете использовать mediaRecorder.requestData() для запуска ondataavailable для обработки нового блока данных. - person Buntel; 17.03.2021
comment
Спасибо! это работает. - person Abhay Bh; 25.05.2021