You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Twilio无法播放AWS S3中Chrome录制音频的问题排查

问题:TwiML 播放S3存储的Chrome录制音频报错12300

将Chrome录制的音频上传到AWS S3后,使用TwiML <Play>动词播放时触发错误码12300(无法获取Play URL),示例URL为https://xxxx.s3.amazonaws.com/recordings/dev/prompt-26-87.mp3,文件的Content-Type始终为video/webm。

文件可在浏览器正常播放,说明URL和上传流程无问题,但怀疑Content-Type或音频格式本身不被<Play>支持。


前端Ajax上传代码

this.uploadAudio = function(fname) {
    if(self.audioBlobs[fname]) {
        let url = $('#upload-recording-url').val();
        let fd = new FormData();
        fd.append('fname', fname);
        fd.append('data', self.audioBlobs[fname]);
        $.ajax({
            type: 'POST',
            url: url,
            data: fd,
            processData: false,
            contentType: 'audio/mpeg'
        }).done(function(data) {
        });
    }
}

PHP后端(Flysystem上传到S3)代码

public function uploadRecording(Request $request,
    LoggerInterface $callcenterLogger
): Response
{
    try {
        $fname = $request->request->get('fname');
        $data = $request->files->get('data');
        $tempFile = $_FILES['data']['tmp_name'];
        $blob = file_get_contents($tempFile);
        $repo = $this->getFileRepositories()->getFileRepo('recordings');
        $root = $repo->getAbsoluteWebRoot();
        $env = $_ENV['APP_ENV'];
        $path = $env.'/'.$fname;
        $repoName = $repo->write($path, $blob);
        return new JsonResponse(['success' => true, 'repo_name' => $repoName]);
    } catch(\Exception $ex) {
        return new JsonResponse(['success' => false, 'message' => $ex->getMessage()]);
    }
}

Chrome MediaRecorder录制代码

this.recordMicrophone = function(stream) {
    self.mediaRecorder = new MediaRecorder(stream);

    self.mediaRecorder.onstop = function(e) {
        self.$audio.attr('controls',true);
        let audioBlob = new Blob(self.chunks, { 'type' : 'audio/mpeg; codecs=mp3' });
        self.chunks = [];
        let audioURL = window.URL.createObjectURL(audioBlob);
        self.$audio.attr('src', audioURL);
        let recordingName = self.getRecordingName(self.$audio)
        self.audioBlobs[recordingName] = audioBlob;
    }

    self.mediaRecorder.ondataavailable = function(e) {
        self.chunks.push(e.data);
    }
}

TwiML示例

<Response> 
  <Play>https://xxxxx.s3.amazonaws.com/recordings/dev/prompt-26-87.mp3</Play>
 <Gather action="/anon/voice.xml/menu-option" input="dtmf speech" method="GET" numDigits="1">
 </Gather>
</Response>

疑问

  • 录制时指定的格式规格是否被浏览器忽略?
  • S3是否会根据文件内容自动识别并返回Content-Type?
  • video/webm是否是<Play>报错的真正原因?
  • 能否通过Flysystem上传时直接设置文件的MIME类型?(目前已知可通过AWS控制台手动修改)

更新

经过大量测试确认,TwiML <Play>动词仅支持播放真实的WAV和MP3文件,官方文档关于支持格式的描述存在不准确。Chrome的MediaRecorder生成的MP3并非标准MP3,而是无视频轨道的MPEG容器,同样无法被<Play>识别。目前正在研究如何让Chrome录制并保存标准MP3或WAV文件。

内容的提问来源于stack exchange,提问作者AQuirky

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 14:53:21