Skip to content

Instantly share code, notes, and snippets.

@westonruter
Created December 10, 2009 07:13
  • Star 15 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save westonruter/253174 to your computer and use it in GitHub Desktop.
HTML5 MP3 Audio detection
/**
* Detect if the browser can play MP3 audio using native HTML5 Audio.
* Invokes the callack function with first parameter is the boolean success
* value; if that value is false, a second error parameter is passed. This error
* is either HTMLMediaError or some other DOMException or Error object.
* Note the callback is likely to be invoked asynchronously!
* @param {function(boolean, Object|undefined)} callback
*/
function canPlayAudioMP3(callback){
try {
var audio = new Audio();
//Shortcut which doesn't work in Chrome (always returns ""); pass through
// if "maybe" to do asynchronous check by loading MP3 data: URI
if(audio.canPlayType('audio/mpeg') == "probably")
callback(true);
//If this event fires, then MP3s can be played
audio.addEventListener('canplaythrough', function(e){
callback(true);
}, false);
//If this is fired, then client can't play MP3s
audio.addEventListener('error', function(e){
callback(false, this.error)
}, false);
//Smallest base64-encoded MP3 I could come up with (<0.000001 seconds long)
audio.src = "data:audio/mpeg;base64,/+MYxAAAAANIAAAAAExBTUUzLjk4LjIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
audio.load();
}
catch(e){
callback(false, e);
}
}
@TooTallNate
Copy link

So do you think you can make some more base64 URIs for some other formats? Namely Ogg Vorbis and AAC? Maybe even for some video formats? I'm using your current MP3 compatibly check in my http://github.com/TooTallNate/HtmlMedia project.

@westonruter
Copy link
Author

@TooTallNate:
I don't have time right now to make other base64 samples, but it is easy to do. All you have to do is create the shorted possible media file with the highest possible compression to result in the smallest possible file; then you just base64-encode the file and there you have it.

@TooTallNate
Copy link

Thanks for the reply. Do you use Audacity to create the clip?

@westonruter
Copy link
Author

@TooTallNate: I'm pretty sure. It was either Audacity or GoldWave, but pretty sure it was Audacity.

@TooTallNate
Copy link

It also just occurred to me that you could optimize this check a little bit by detecting the loadedmetadata event, rather than the canplaythrough event, or possibly even both! According to the HTML5 spec:
Once enough of the media data has been fetched to determine the duration of the media resource,
its dimensions, and other metadata, this indicates that the resource is usable. The user agent must...
5. Queue a task to fire a simple event named loadedmetadata at the element.

@westonruter
Copy link
Author

@TooTallNate: Nice catch! I pinged Modernizr about it: http://twitter.com/westonruter/status/11436680165

Let me know if it actually works as advertised in browsers today!

@TooTallNate
Copy link

In doing some testing, the only error I've noticed so far is with Safari (4.0.4) on Snow Leopard. While it should asynchronously load correctly and fire the loadedmetadata/canplaythrough event, instead an error event is thrown, and the Audio#error property contains a MediaError object with code 4. In other words, it acts like Firefox; as if it doesn't support MP3s at all, which is incorrect.

For now I've been trying out using: 'audio/mpeg; codecs="MP3"' instead of 'audio/mpeg' for the mime-type check, which returns "probably" instead of "maybe" in Safari, and thus will synchronously call the callback with true. You also need to return from the canPlayAudioMP3 function after that "probably" test passes and the callback is invoked, otherwise it continues, and the event listeners are added, etc, and the callback is invoked twice.

@scottschiller
Copy link

The encoded audio data is a wonderful little trick. Is it expected to be common that playback may fail, despite canPlayType('audio/mpeg') returning a "maybe" or "probably" response? .. If so, I may need to look carefully at something like this. :)

@westonruter
Copy link
Author

@scott: The reason why I came up with this technique is that Chrome was falsely reporting that it could not play MP3s via canPlayType('audio/mpeg'), but if I actually tried playing one, it would work.

@scottschiller
Copy link

Interesting. Opera 10.52 currently returns "maybe" for (new Audio().canPlayType('audio/mp3')) also, FWIW, when it does not actually work (audio/mpeg seems to return null?) - so it may make sense to actually test support with real sounds where possible and get a more solid idea of support.

@pantulis
Copy link

pantulis commented Jun 2, 2010

I'm consistently getting the 'error' event callback with exception MediaError, no matter what browser (tried with Firefox 3.6, recent Webkit and even Chrome 5.0.375.55...

Any ideas?

@scottschiller
Copy link

It may be the browser doesn't like base64-encoded data, I'm not quite certain. I tried a few different 1-sample MP3s at bitrates between 8 and 128 kbps, and wasn't able to get Chrome on OS X to fire play or canplaythrough events in any case. Chrome on Windows may have been better in some cases, but frankly I forget how it all ended up working out with all the different browsers.

Base64 stuff seemed to completely fail on the iPhone and iPad also, for what it's worth.

In the end, I just made a small array of canPlayType() tests, associating multiple mime strings with 'mp3', for example, and taking any "probably" responses as positive for support. Chrome/OSX still returns 'maybe' at best for the MP3 tests, but OGG and WAV are fine. I pulled the types together from wikipedia notes on MP3 and HTML 5 examples in the wild which looked to work.

eg.
'mp3': ['audio/mpeg; codecs="mp3"','audio/mpeg','audio/mp3','audio/MPA','audio/mpa-robust']

Relevant bit(s):
http://github.com/scottschiller/SoundManager2/blob/master/script/soundmanager2.js#L42

@mudcube
Copy link

mudcube commented Dec 20, 2011

I found this URL to work in Safari (it might be that Safari requires the audio to be more than 0.00001 seconds long);

data:audio/mpeg;base64,/+MYxAAAAANIAUAAAASEEB/jwOFM/0MM/90b/+RhST//w4NFwOjf///PZu////9lns5GFDv//l9GlUIEEIAAAgIg8Ir/JGq3/+MYxDsLIj5QMYcoAP0dv9HIjUcH//yYSg+CIbkGP//8w0bLVjUP///3Z0x5QCAv/yLjwtGKTEFNRTMuOTeqqqqqqqqqqqqq/+MYxEkNmdJkUYc4AKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq

@mudcube
Copy link

mudcube commented Dec 20, 2011

The OGG data url is much longer, because the minimum bitrate is 32;

data:audio/ogg;base64,T2dnUwACAAAAAAAAAADqnjMlAAAAAOyyzPIBHgF2b3JiaXMAAAAAAUAfAABAHwAAQB8AAEAfAACZAU9nZ1MAAAAAAAAAAAAA6p4zJQEAAAANJGeqCj3//////////5ADdm9yYmlzLQAAAFhpcGguT3JnIGxpYlZvcmJpcyBJIDIwMTAxMTAxIChTY2hhdWZlbnVnZ2V0KQAAAAABBXZvcmJpcw9CQ1YBAAABAAxSFCElGVNKYwiVUlIpBR1jUFtHHWPUOUYhZBBTiEkZpXtPKpVYSsgRUlgpRR1TTFNJlVKWKUUdYxRTSCFT1jFloXMUS4ZJCSVsTa50FkvomWOWMUYdY85aSp1j1jFFHWNSUkmhcxg6ZiVkFDpGxehifDA6laJCKL7H3lLpLYWKW4q91xpT6y2EGEtpwQhhc+211dxKasUYY4wxxsXiUyiC0JBVAAABAABABAFCQ1YBAAoAAMJQDEVRgNCQVQBABgCAABRFcRTHcRxHkiTLAkJDVgEAQAAAAgAAKI7hKJIjSZJkWZZlWZameZaouaov+64u667t6roOhIasBACAAAAYRqF1TCqDEEPKQ4QUY9AzoxBDDEzGHGNONKQMMogzxZAyiFssLqgQBKEhKwKAKAAAwBjEGGIMOeekZFIi55iUTkoDnaPUUcoolRRLjBmlEluJMYLOUeooZZRCjKXFjFKJscRUAABAgAMAQICFUGjIigAgCgCAMAYphZRCjCnmFHOIMeUcgwwxxiBkzinoGJNOSuWck85JiRhjzjEHlXNOSuekctBJyaQTAAAQ4AAAEGAhFBqyIgCIEwAwSJKmWZomipamiaJniqrqiaKqWp5nmp5pqqpnmqpqqqrrmqrqypbnmaZnmqrqmaaqiqbquqaquq6nqrZsuqoum65q267s+rZru77uqapsm6or66bqyrrqyrbuurbtS56nqqKquq5nqq6ruq5uq65r25pqyq6purJtuq4tu7Js664s67pmqq5suqotm64s667s2rYqy7ovuq5uq7Ks+6os+75s67ru2rrwi65r66os674qy74x27bwy7ouHJMnqqqnqq7rmarrqq5r26rr2rqmmq5suq4tm6or26os67Yry7aumaosm64r26bryrIqy77vyrJui67r66Ys67oqy8Lu6roxzLat+6Lr6roqy7qvyrKuu7ru+7JuC7umqrpuyrKvm7Ks+7auC8us27oxuq7vq7It/KosC7+u+8Iy6z5jdF1fV21ZGFbZ9n3d95Vj1nVhWW1b+V1bZ7y+bgy7bvzKrQvLstq2scy6rSyvrxvDLux8W/iVmqratum6um7Ksq/Lui60dd1XRtf1fdW2fV+VZd+3hV9pG8OwjK6r+6os68Jry8ov67qw7MIvLKttK7+r68ow27qw3L6wLL/uC8uq277v6rrStXVluX2fsSu38QsAABhwAAAIMKEMFBqyIgCIEwBAEHIOKQahYgpCCKGkEEIqFWNSMuakZM5JKaWUFEpJrWJMSuaclMwxKaGUlkopqYRSWiqlxBRKaS2l1mJKqcVQSmulpNZKSa2llGJMrcUYMSYlc05K5pyUklJrJZXWMucoZQ5K6iCklEoqraTUYuacpA46Kx2E1EoqMZWUYgupxFZKaq2kFGMrMdXUWo4hpRhLSrGVlFptMdXWWqs1YkxK5pyUzDkqJaXWSiqtZc5J6iC01DkoqaTUYiopxco5SR2ElDLIqJSUWiupxBJSia20FGMpqcXUYq4pxRZDSS2WlFosqcTWYoy1tVRTJ6XFklKMJZUYW6y5ttZqDKXEVkqLsaSUW2sx1xZjjqGkFksrsZWUWmy15dhayzW1VGNKrdYWY40x5ZRrrT2n1mJNMdXaWqy51ZZbzLXnTkprpZQWS0oxttZijTHmHEppraQUWykpxtZara3FXEMpsZXSWiypxNhirLXFVmNqrcYWW62ltVprrb3GVlsurdXcYqw9tZRrrLXmWFNtBQAADDgAAASYUAYKDVkJAEQBAADGMMYYhEYpx5yT0ijlnHNSKucghJBS5hyEEFLKnINQSkuZcxBKSSmUklJqrYVSUmqttQIAAAocAAACbNCUWByg0JCVAEAqAIDBcTRNFFXVdX1fsSxRVFXXlW3jVyxNFFVVdm1b+DVRVFXXtW3bFn5NFFVVdmXZtoWiqrqybduybgvDqKqua9uybeuorqvbuq3bui9UXVmWbVu3dR3XtnXd9nVd+Bmzbeu2buu+8CMMR9/4IeTj+3RCCAAAT3AAACqwYXWEk6KxwEJDVgIAGQAAgDFKGYUYM0gxphhjTDHGmAAAgAEHAIAAE8pAoSErAoAoAADAOeecc84555xzzjnnnHPOOeecc44xxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY0wAwE6EA8BOhIVQaMhKACAcAABACCEpKaWUUkoRU85BSSmllFKqFIOMSkoppZRSpBR1lFJKKaWUIqWgpJJSSimllElJKaWUUkoppYw6SimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaVUSimllFJKKaWUUkoppRQAYPLgAACVYOMMK0lnhaPBhYasBAByAwAAhRiDEEJpraRUUkolVc5BKCWUlEpKKZWUUqqYgxBKKqmlklJKKbXSQSihlFBKKSWUUkooJYQQSgmhlFRCK6mEUkoHoYQSQimhhFRKKSWUzkEoIYUOQkmllNRCSB10VFIpIZVSSiklpZQ6CKGUklJLLZVSWkqpdBJSKamV1FJqqbWSUgmhpFZKSSWl0lpJJbUSSkklpZRSSymFVFJJJYSSUioltZZaSqm11lJIqZWUUkqppdRSSiWlkEpKqZSSUmollZRSaiGVlEpJKaTUSimlpFRCSamlUlpKLbWUSkmptFRSSaWUlEpJKaVSSksppRJKSqmllFpJKYWSUkoplZJSSyW1VEoKJaWUUkmptJRSSymVklIBAEAHDgAAAUZUWoidZlx5BI4oZJiAAgAAQABAgAkgMEBQMApBgDACAQAAAADAAAAfAABHARAR0ZzBAUKCwgJDg8MDAAAAAAAAAAAAAACAT2dnUwAEAAAAAAAAAADqnjMlAgAAADzQPmcBAQA=

@andylizi
Copy link

andylizi commented Sep 3, 2021

The original smallest MP3 no longer work (DEMUXER_ERROR_COULD_NOT_OPEN: FFmpegDemuxer: open context failed), this is a new one I came up with:

data:audio/mpeg;base64,/+MYwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+MYwA==

It can be decoded correctly and trigger canplaythrough (tested on Chrome 94 and ffmpeg avformat 59.4.101)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment