I am uploading a audio file recorded in Chrome to an S3 bucket that I am then attempting to play with a TwiML verb.
The error code I get is 12300 Failed to retrieve Play url, e.g.
https:// xxxx.s3.amazonaws.com/recordings/dev/prompt-26-87.mp3
With content type video/webm
I suspect the problem is the content type. video/webm is one of the Twilio accepted MIME types, but I suspect it is not accepted by the verb.
I can play the recording in the browser just fine, so there is nothing wrong with the URL or file upload.
I am uploading the file to S3 using 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) {
});
}
}
In the PHP server I am using the AWS Flysystem adaptor to write the uploaded audio file to AWS
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()]);
}
}
However when I query the file with Postman, the content type always comes back as video/webm.
The recording is made in the browser with 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);
}
}
The TwiML file looks something like this...
<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
I have tried a few other formats besides audio/mpeg: audio/ogg and audio/mp4 and codecs: opus and mp4. All with the same result: 12300 error in Twilio with content type of video/webm.
So I am wondering what is going on.
- Are my format specifications just being ignored?
- Is S3 returning content type based on the file contents?
- Is the content type of video/webm the real issue with the verb.
- Is there a way to set the mime type in Flysystem? I see I can set the mime type using the AWS console, but how it is set on upload?
There are a few questions along this same line on SO. (e.g. Play audio file from S3, Audio is not playing for AWS s3 bucket video played through PHP Streaming file) but none of them seem to shed light on this problem.
UPDATE:
After a lot of fooling around I have discovered that the TwiML PLAY verb only plays .WAV and .MP3 files. The documentation is completely wrong on the supported formats, at least for the PLAY verb.
I tried having Chrome record an MP3 file and this didn't work, but I have discovered the MP3 file that is created in Chrome through MediaRecorder is not a true MP3 file but an MPEG container without the video track. The PLAY verb does not play this either, only true MP3 files.
So now I am working on getting Chrome to save a .WAV or a true .MP3 file. There is a bunch of example code floating around and I'll post an answer when I get something working.