Javascript compatibility on Safari
slice
Array.prototype.slice
A simple method even through your are a newbie. It had two parameters start and end, but in so many using cases we ignore them.
let arr = [1,2,3,4,5];
let arrCopy = arr.slice();
Similar one
Arraybuffer.prototype.slice
var arr = [1,2,3,4,5,6]
var arrCopy = arr.slice();
var u8 = new Uint8Array(arr)
var u8Copy = u8.buffer.slice(); // slice without any param
The code above will working on chrome but safari cause chrome fixed that for us.
Look up the MDN and tc39 you will notice that the first param is non-ignoreable.
Its easy to mix up them. To make that correctly, just pass in a 0 to slice.
var u8Copy = u8.buffer.slice(0);
DecodeAudioData
This method returns a promise but for some older browsers they aren’t. spec.
// determine if safari
const yourAudioContext = new (window.AudioContext || window.webkitAudioContext)();const DecodeAudioData = (buffer) => {
if(isAppleSafari){
return new Promise((resolve, reject) => {
yourAudioContext.decodeAudioData(buffer,(audioBuffer)=>{
resolve(audioBuffer);
});
})
} return yourAudioContext.decodeAudioData(buffer);
};
For a part of files that metadata contain invalid character(in my testing they are chinese characters). They can’t be decode because the Safari thrown a null err.
You need to determine metadata.
Use the ffmpeg.
ffmpeg -i file.mp3 -vn -codec:a copy -map_metadata -1 out.mp3
Or just javascript to modify its bit data.
CreateBuffer
This method returns a AudioBuffer. Then you can called it through an AudioBufferSourceNode.
// it receive three params: numOfChannels, length, sampleRate.
let AB = audioContext.createBuffer(2, 1, 1);
that code will working on chrome but not on safari.
look up spec
… support sample rates in at least the range 8000 to 96000.
and the stack answer
… Safari’s Web Audio implementation does only support AudioBuffers with 22050 Hz and more.
So you just set it as
// it receive three params: numOfChannels, length, sampleRate.
let AB = yourAudioContext.createBuffer(2, 1, 22050);
Blob
If your creating a multiple media (video and audio) blob url and play it.
// mp3
const blob1 = new Blob([aAudioArrayBuffer])
const url1 = URL.createObjectURL(blob1);aAudioDom.src = url1;// mp4
const blob2 = new Blob([aVideoArrayBuffer])
const url2 = URL.createObjectURL(blob2);aVideoDom.src = url2;
that will be working on chrome but not on safari. There are two problems.
First one, you have to pass a mime type in while you get a instance of the Blob
const blob1 = new Blob([aAudioArrayBuffer],{ type : 'audio/mp3'});
const blob2 = new Blob([aVideoArrayBuffer],{ type : 'video/mp4'});
Another one is after you set the src of multiple media you need to invoke a function to update it.
aAudioDom.load();
aVideoDom.load();
ImageData
you can never delete a read-only property
const imageData = new ImageData(data, width, height);// this will thrown a error on safari
// but on chrome, it will be ignored.
delete imageData.data
InvalidStateError
If you are using AudioBufferSourceNode relative API and got a error and it said: InvalidStateError: The object is in an invalid state on the safari. you may need check your code those invokes like stop/start method.
Since the …AudioBufferSourceNode can only be played once. you have to write your owner status-controller to manage all your media buffer datas. try wrap it as a component and set a status for it while the user interact with it.
But for the simple using or temporarily fixing, you just need put them inside a try-catch pair statement.
try{
anABS.stop();
anABS.disconnect();
}catch(e){
// console.log(e);
}
Wechat video events
actually this also happened on some of android devices.
Sometimes we need to packback some videos from arraybuffer-blob-url, and render it to canvas.
generally you may watch the event oncanplay or other shits events to get video base info: such as width/height.In Wechat browser, these events will never been triggerd by video it self, but after user click play.
// onloadstart ondurationchange onloadedmetadata onloadeddata onprogress oncanplay oncanplaythrough
to approach that, you have to avoid use those events or create interval/timeout func to get video’s base info.