【问题标题】:Live-recorded base64-encoded audio from MediaRecorder is broken来自 MediaRecorder 的实时录制的 base64 编码音频已损坏
【发布时间】:2022-01-24 08:21:29
【问题描述】:

我在 js 和 php 上写了一个简单的页面来实时录制麦克风的音频。逻辑很简单:

JS

  1. 从麦克风获取数据块;
  2. Base64 编码和 urlencode;
  3. 通过 POST 请求发送;

PHP

  1. Base64 解码数据;
  2. (重新)写入 .ogg 文件;
  3. 延迟后重复 1。

数据已成功写入文件,但当我尝试播放时,播放器说文件已损坏。

Mozilla guide 的 blob 解决方案适合我,我想要的正是 PHP 解决方案,可以保存(重写)到文件。

下面的完整代码,我做错了什么?

<?php
if(isset($_POST["data"]))
{
file_put_contents("r.ogg", base64_decode($_POST["data"]));
exit;   
}
?>

<script>
var mediaRecorder = null;
let chunks = [];

if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
   console.log('getUserMedia supported.');
   navigator.mediaDevices.getUserMedia (
      {
         audio: true
      })
      .then(function(stream) {
        mediaRecorder = new MediaRecorder(stream);
        mediaRecorder.start(2000);
        
        mediaRecorder.ondataavailable = function(e) {
            chunks.push(e.data);
            const blob = new Blob(chunks, { 'type' : 'audio/ogg; codecs=opus' });
            chunks = [];
            var reader = new FileReader();
            reader.readAsDataURL(blob); 
            reader.onloadend = function() {
            var data = reader.result.split(";base64,")[1]; 
            requestp2("a.php", "data="+encodeURIComponent(data));
            }
}
      })
      .catch(function(err) {
         console.log('The following getUserMedia error occurred: ' + err);
      }
   );
} else {
   console.log('getUserMedia not supported on your browser!');
}

function requestp2(path, data)
{
var http = new XMLHttpRequest();
http.open('POST', path, true);
http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
http.send(data);
}
</script>

【问题讨论】:

    标签: javascript php base64 web-mediarecorder


    【解决方案1】:

    您正在尝试通过在 Blob 构造函数中指定媒体类型来选择音频格式和编解码器。 You Can't Do That™。

    改为在您的 MediaRecorder constructor 中指定媒体类型。像这样的东西,没有调试!

            ...
            /* here's the media type definition vvv */
            mrOptions = {mimeType: 'audio/ogg; codecs=opus'}
            mediaRecorder = new MediaRecorder(stream, mrOptions)
            mediaRecorder.start(2000)
            
            mediaRecorder.ondataavailable = function(e) {
                ...
                const blob = new Blob(chunks, { type : mediaRecorder.mimeType })
                ...
    

    您在处理 MediaRecorder 传递给您的 ondataavailable 处理程序的连续 e.data 块时也可能存在一些问题。要制作有效的可播放文件,您必须将所有这些块连接到文件中。如果你不能让它工作,请再问一个问题。

    【讨论】:

    • 感谢您的反馈。似乎只有第一个卡盘可以玩。我认为,每次我想发送数据时都需要停止然后重新启动
    • 不,这是不对的。如果你连接块,它们肯定是可玩的。 (我有一个广泛部署的应用程序,它依赖于这种行为。)但是以正确的顺序连接它们非常重要。
    【解决方案2】:

    问题是似乎只有第一个块有签名标签或其他东西,因此可以播放。所以我不得不添加一个间隔来停止并每次播放mediaRecorder

    而且文件的格式不是ogg,而是webm

    完整的工作代码:

    <?php
    if(isset($_POST["data"]))
    {
    file_put_contents("r.webm", base64_decode($_POST["data"]));
    exit;   
    }
    ?>
    
    <script>
    var mediaRecorder = null;
    var delay = 2000;
    
    if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
       console.log('getUserMedia supported.');
       navigator.mediaDevices.getUserMedia (
          {
             audio: true
          })
          .then(function(stream) {
            mediaRecorder = new MediaRecorder(stream, {mimeType: 'audio/webm; codecs=opus'});
            mediaRecorder.start(); 
            mediaRecorder.ondataavailable = function(e) {
                var blob = e.data;
                var reader = new FileReader();
                reader.readAsDataURL(blob); 
                reader.onloadend = function() {
                var data = reader.result.split(";base64,")[1]; 
                requestp2("a.php", "data="+encodeURIComponent(data));
                }
    }
    setInterval(function(){
                    mediaRecorder.stop();
                    mediaRecorder.start();
                    }, delay);
          })
          .catch(function(err) {
             console.log('The following getUserMedia error occurred: ' + err);
          }
       );
    } else {
       console.log('getUserMedia not supported on your browser!');
    }
    function requestp2(path, data)
    {
    var http = new XMLHttpRequest();
    http.open('POST', path, true);
    http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
    http.send(data);
    }
    </script>
    

    【讨论】:

      猜你喜欢
      • 2013-04-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多