Bug 198015 - Cannot play audio created from blob
Summary: Cannot play audio created from blob
Status: NEW
Alias: None
Product: WebKit
Classification: Unclassified
Component: Web Audio (show other bugs)
Version: Safari 12
Hardware: iPhone / iPad Unspecified
: P2 Blocker
Assignee: Nobody
URL:
Keywords: InRadar
: 245056 (view as bug list)
Depends on:
Blocks:
 
Reported: 2019-05-18 15:21 PDT by jordan.michael.last
Modified: 2022-09-17 03:46 PDT (History)
5 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description jordan.michael.last 2019-05-18 15:21:09 PDT
I am downloading audio files and storing them in IndexedDB as ArrayBuffers. When it comes time for the user to play the audio files, I pull them out IndexedDB, create a blob, and use window.URL.createObjectURL to create the url for the src attribute of an audio element. This works fine in the Chromium browsers that I have tested. It works in none of the iOS devices that I've tested. Because of this, it does not seem possible to play audio files that have been stored in IndexedDB.

There have been various discussions about this on StackOverflow, and there does not seem to be a solution:

https://stackoverflow.com/questions/4201576/html5-audio-files-fail-to-load-in-safari

https://stackoverflow.com/questions/13231915/loading-audio-via-a-blob-url-fails-in-safari

https://stackoverflow.com/questions/43620594/audio-blob-not-working-in-ios-safari
Comment 1 Radar WebKit Bug Importer 2019-05-20 17:17:44 PDT
<rdar://problem/50967575>
Comment 2 Jer Noble 2019-05-21 06:35:48 PDT
Do the same audio files play correctly when you load them via the src attribute as regular URLs?
Comment 3 Jer Noble 2019-05-21 06:40:36 PDT
Aha, one of those stackoverflow links contains a hint. 

Try doing something like:

<audio>
  <source src=“blob-url” type=“audio/mp3”>
</audio>

If that works, it’s because the file parser can’t determine the type of the file in the absence of a Content-Type HTTP header and a lack of any file extension on the URL. Adding a “type=“ attribute let’s us give the parser a hint about the files contents.
Comment 4 jordan.michael.last 2019-05-24 10:26:04 PDT
I've put up a very simple test site here: https://lastmjs.github.io/safari-object-url-test

If after you hit the button there is an audio element and you can play it, then it worked.

I don't have access to Safari right now, but I'll try it on some devices later. Even if this works, it would be nice to not have to create a source element with an explicit type. Apparently it is possible to do this, because Chrome does it without an explicit type. The inconvenience comes from the fact that I'm storing the audio as an arraybuffer in IndexedDB. I would have to create another field to store the type information in, which would be inconvenient and extra complexity.
Comment 5 Jer Noble 2019-05-24 10:47:14 PDT
(In reply to jordan.michael.last from comment #4)
> I've put up a very simple test site here:
> https://lastmjs.github.io/safari-object-url-test
> 
> If after you hit the button there is an audio element and you can play it,
> then it worked.
> 
> I don't have access to Safari right now, but I'll try it on some devices
> later. Even if this works, it would be nice to not have to create a source
> element with an explicit type. Apparently it is possible to do this, because
> Chrome does it without an explicit type. The inconvenience comes from the
> fact that I'm storing the audio as an arraybuffer in IndexedDB. I would have
> to create another field to store the type information in, which would be
> inconvenient and extra complexity.

Absent any type information from the server, any filename information, and any context at all, all the UA has to go on is file inspection, which is error prone. The fact that you found files which don't error out on one browser doesn't mean all files will play without error on that browser. In other words, there's a reason servers send Content-Type headers. You should store them and give that information to the UA.
Comment 6 jordan.michael.last 2019-05-27 10:57:20 PDT
The example I created above indeed works on all of the iOS devices I have tested. Ideally I wouldn't need to use a <source> element to specify the mime type, and indeed it is possible to create a blob and pass the mime type in. For example, if you store an mp3 file as an arraybuffer, and want to create an object URL from it:

// arrayBuffer is a variable that has already been defined, which holds mp3 audio

const blob = new Blob([arrayBuffer], { type: 'audio/mpeg' });

const objectURL = window.URL.createObjectURL(blob);

You can then set the src of an audio element to objectURL. This works on the two iOS devices that I have tested. I believe it is a solution to the StackOverflow questions.
Comment 7 Jer Noble 2019-05-28 09:09:10 PDT
(In reply to jordan.michael.last from comment #6)
> The example I created above indeed works on all of the iOS devices I have
> tested. Ideally I wouldn't need to use a <source> element to specify the
> mime type, and indeed it is possible to create a blob and pass the mime type
> in. For example, if you store an mp3 file as an arraybuffer, and want to
> create an object URL from it:
> 
> // arrayBuffer is a variable that has already been defined, which holds mp3
> audio
> 
> const blob = new Blob([arrayBuffer], { type: 'audio/mpeg' });
> 
> const objectURL = window.URL.createObjectURL(blob);
> 
> You can then set the src of an audio element to objectURL. This works on the
> two iOS devices that I have tested. I believe it is a solution to the
> StackOverflow questions.

Aha, I learned something today! I wasn't aware that the Blob constructor took a MIME type.
Comment 8 Alexey Proskuryakov 2022-09-16 19:10:14 PDT
*** Bug 245056 has been marked as a duplicate of this bug. ***
Comment 9 Ted Stresen-Reuter 2022-09-17 03:46:26 PDT
Thanks for marking Bug 245056 as a duplicate. As I read the comments in this bug, I don't see a clear definition of the failing behavior but rather, the symptoms. Does this acceptance criteria help?

Given you create a Blob object and the mimetype is not specified
When the code reproduces the media
Then the media plays properly

Assuming this is the case, what's the point of the mimetype attribute? Does it have a priority over any headers or hints that might be in the first few bytes of the file?

TIA and for taking the time to get this working.