Fail to read audio from blob url in mobile Chrome - javascript

I want to use mp3 playing in my mobile web app, so I wrote a test app, I used this solution but blob URL seems to be broken and empty while file is rightly readed.
<input type="file" accept=".mp3" onchange="autoplay()">
<script>
var file, url, audio, source;
function autoplay(){
window.URL = window.URL || window.webkitURL;
file = document.querySelector("input[type=file]").files[0];
url = decodeURIComponent(window.URL.createObjectURL(file));
audio = document.createElement("audio");
source = document.createElement("source");
source.src = url;
audio.appendChild(source);
document.body.appendChild(audio);
audio.play();
}
</script>
Blob size is wrong, I've put bigger file
EDIT:
I used older version with FileReader, maybe this is not good choice but it works...

Don't use decodeURIComponent just do URL.createObjectURL
window.URL = window.URL || window.webkitURL;
function autoplay(evt) {
var file = document.querySelector("input[type=file]").files[0];
var url = URL.createObjectURL(file);
var audio = new Audio();
audio.src = url;
audio.play().catch(function(err){
console.error(err) // could not play audio
});
audio.controls = true;
document.body.appendChild(audio);
}
document.querySelector("input[type=file]").onchange = autoplay
<input type="file" accept=".mp3">
btw, I get problem playing it directly, it's weird since the event.isTrusted is true, that's why i appended the audio to the DOM

Related

cannot download file from BlobUrl (using MediaReader)

I have the following code:
let self = this;
this.chunks = [];
const canvas2 = document.getElementById("self-canvas");
let recordStream = canvas2.captureStream(1);
var options;
options = {mimeType: 'video/webm; codecs=vp9'};
this.recorder = new MediaRecorder(recordStream, options);
this.recorder.ondataavailable = function(evt) {
self.chunks.push(evt.data);
};
this.recorder.onstop = function(evt) {
console.log("recorder stopping");
const link = document.createElement('a');
const videoBlob = new Blob(self.chunks, { type: "video/webm" });
console.log("file size: " + videoBlob.size);
const url = URL.createObjectURL(videoBlob);
link.href = url;
link.download = "sample.webm";
document.body.append(link);
link.click(); //if I comment out here I can see the video
};
console.log("finished setting controller")
console.log("recorder starting");
this.recorder.start(10000);
// the recorder.stop is called somewhere else
What it is supposed to do is pretty simple:
I have the element with id "self-canvas" that is showing my camera.
Now I am trying to record the camera and download the video from the browser using MediaRecorder, but for some reason I am unable to download the file.
I am sure that the file is being recorded, and console.log("file size: " + videoBlob.size); does not return empty.
But when I let the code run, instead of downloading the file, it tries to open it on the same window, and I cannot even see the video because the previous window disappears with the data of the recording.
However if I comment out the link.click(); I am able to see the video by opening the link on a new page (without closing the previous one). But it still doesn't download...
I used this as example, what am I doing wrong?
For heaven's sake...
I just added target blank and it worked.
link.href = url;
link.download = "sample.webm";
link.target = '_blank';
Probably because the resources are lost if it tries to open on the same page, and because it doesn't actually download the file if it is not a link "click".
Still, I never saw anyone having to add target blank in their examples like this one.
So I wonder why this is the case only for me...

JavaScript screen record is playing in video tag but not after downloading

I'm using JavaScript to record the user screen and afterward playing it in a video tag of the page and downloading it.
Now it's playing fine the browser video tag but not playing once it's downloaded
Here's my code
let btn = document.querySelector('button')
btn.addEventListener('click', async function (){
let stream = await navigator.mediaDevices.getDisplayMedia({
video: true
})
let mediaRecorder = new MediaRecorder(stream)
let chunks = []
mediaRecorder.addEventListener('dataavailable', function (e) {
chunks.push(e.data)
})
mediaRecorder.addEventListener('stop', function () {
let blob = new Blob(chunks, { 'type': 'video/webm' })
// Working fine
let video = document.querySelector('video')
video.src = URL.createObjectURL(blob)
// Downloaded video is not playing
let a = document.createElement('a')
a.href = URL.createObjectURL(blob)
a.download = 'video.webm'
a.click()
})
mediaRecorder.start()
})```
It might be worth you trying this experiment, run this code:
click
<script>
const a = document.querySelector('a');
a.click();
</script>
(doesn't work as a stack snippet, need to run direct on your system/browser).
Does the video start automatically? If not, are you seeing any warning, for example at the top right of your browser window, that popups are blocked? If so you could try unblocking them and see what happens.
Note: I'm not sure what you mean by 'download' and its setting in the anchor element.

javascript + html5 audio player cors not play dynamic source

Sorry for my English, I'm using a translator.
The question is the following: passing the source of an audio file hosted on Google Drive doesn't work.
But if I pass a source of a local file it works.
Also if the mp3 files are in my domain, it works as well.
Example:
function _playDrive() { //dont work
let audio = new Audio('http://docs.google.com/uc?export=download&id=ejemplo'); //is mp3
audio.controls = true;
audio.crossOrigin = 'anonymous';
document.body.appendChild(audio);
audio.play();
}
function _playLocal() { //work
let audio = new Audio('./mysong.mp3');
audio.controls = true;
audio.crossOrigin = 'anonymous';
document.body.appendChild(audio);
audio.play();
}
I would appreciate a solution with no dependencies, thx.
You need to get the file id which is a 20+ alphanumeric located after id= in the url.
Base
https://drive.google.com/uc?export=download&confirm=no_antivirus&id=
File_ID
0B1c82XNzuQ5ZU3RYSmZENERBQjQ
SOLUTION
var player = new Audio(Base + File_ID )
Demo
var player = new Audio('https://drive.google.com/uc?export=download&confirm=no_antivirus&id=0B1c82XNzuQ5ZU3RYSmZENERBQjQ');
player.controls = true;
document.body.appendChild(player);
player.play();

How to save a jpg image/video captured with webcam in the local hard drive with HTML5

The problem appears simple, although I cannot find a suitable solution
because of my lack of knowledge of HTML and Javascript.
The task is simply to design a webpage where a button will activate the webcam and store either a still image or a video (preferable) in the local hard drive. No upload/download required for the time being.
After some attempts, I am able to use the getusermedia() api to activate the webcam and render the video in the browser window, but unable to save it. This is what my code looks like.
if (navigator.getUserMedia) {
navigator.getUserMedia({video: true}, handleVideo, videoError);
}
function handleVideo(stream) {
video.src = window.URL.createObjectURL(stream);
}
So any idea on how to save either a still image or a video in the hard drive captured the same way?
First, the navigator.getUserMedia API is being deprecated, you should now use the navigator.mediaDevices.getUserMedia method.
Then to take a still image, you can indeed use a canvas which can draw a video element.
const vid = document.querySelector('video');
navigator.mediaDevices.getUserMedia({video: true}) // request cam
.then(stream => {
vid.srcObject = stream; // don't use createObjectURL(MediaStream)
return vid.play(); // returns a Promise
})
.then(()=>{ // enable the button
const btn = document.querySelector('button');
btn.disabled = false;
btn.onclick = e => {
takeASnap()
.then(download);
};
})
.catch(e=>console.log('please use the fiddle instead'));
function takeASnap(){
const canvas = document.createElement('canvas'); // create a canvas
const ctx = canvas.getContext('2d'); // get its context
canvas.width = vid.videoWidth; // set its size to the one of the video
canvas.height = vid.videoHeight;
ctx.drawImage(vid, 0,0); // the video
return new Promise((res, rej)=>{
canvas.toBlob(res, 'image/jpeg'); // request a Blob from the canvas
});
}
function download(blob){
// uses the <a download> to download a Blob
let a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = 'screenshot.jpg';
document.body.appendChild(a);
a.click();
}
<button>take a snapshot</button>
<video id="vid"></video>
As a fiddle since Stacksnippets may block gUM requests...
And to save as a video, you can use the [MediaRecorder API](https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorderà, which will allow you to save a MediaStream as webm:
const vid = document.querySelector('video');
navigator.mediaDevices.getUserMedia({video: true}) // request cam
.then(stream => {
vid.srcObject = stream; // don't use createObjectURL(MediaStream)
return vid.play(); // returns a Promise
})
.then(()=>{ // enable the button
const btn = document.querySelector('button');
btn.disabled = false;
btn.onclick = startRecording;
})
.catch(e=>console.log('please use the fiddle instead'));
function startRecording(){
// switch button's behavior
const btn = this;
btn.textContent = 'stop recording';
btn.onclick = stopRecording;
const chunks = []; // here we will save all video data
const rec = new MediaRecorder(vid.srcObject);
// this event contains our data
rec.ondataavailable = e => chunks.push(e.data);
// when done, concatenate our chunks in a single Blob
rec.onstop = e => download(new Blob(chunks));
rec.start();
function stopRecording(){
rec.stop();
// switch button's behavior
btn.textContent = 'start recording';
btn.onclick = startRecording;
}
}
function download(blob){
// uses the <a download> to download a Blob
let a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = 'recorded.webm';
document.body.appendChild(a);
a.click();
}
<button disabled>start recording</button>
<video></video>
And as a fiddle
Notes:
The MediaRecorder API is still a quite new API and there are still some bugs in the [little set of browser implementations.
What you did is a good start. You can now 'paint' the webcam picture into a canvas and then create an image from the canvas information. You can then use some tricks to prevent the browser from displaying the image and get it to download it instead.

When is it safe to call URL.revokeObjectURL?

If I understand correctly URL.createObjectURL creates a URL that represents a file or a blob. Because the URL is just a string the browser has no way to know when you're finished with the resource that URL represents so there's a provided URL.revokeObjectURL function.
MDN shows this example:
var canvas = document.getElementById("canvas");
canvas.toBlob(function(blob) {
var newImg = document.createElement("img");
var url = URL.createObjectURL(blob);
newImg.onload = function() {
// no longer need to read the blob so it's revoked
URL.revokeObjectURL(url);
};
newImg.src = url;
document.body.appendChild(newImg);
});
So some questions
Would it be safe to change the code to revoke the URL immediately after assigning newImg.src?
var canvas = document.getElementById("canvas");
canvas.toBlob(function(blob) {
var newImg = document.createElement("img");
var url = URL.createObjectURL(blob);
newImg.src = url;
// no longer need to read the blob as it's assigned to newImg.src
URL.revokeObjectURL(url);
document.body.appendChild(newImg);
});
I'm guessing the answer is "no" because potentially nothing has started on newImg.src = url;. It's still just a string at that point and will remain so until the current JavaScript event exits. Or is it?
Would it be valid/legal/correct to revoke the URL but still use it knowing it's referenced by other objects?
var canvas = document.getElementById("canvas");
canvas.toBlob(function(blob) {
var newImg = document.createElement("img");
var url = URL.createObjectURL(blob);
newImg.onload = function() {
// no longer need to read the blob so it's revoked
URL.revokeObjectURL(url);
// Use the URL again even though it's revoked
var newImg2 = new Image():
newImg2.src = url;
};
newImg.src = url;
document.body.appendChild(newImg);
});
In this case I'm assigning newImg2.src = url even though I've already revoked the URL. The idea being that newImg is still referencing the blob URL so it would seem valid to be able to say
someImage.src = someOtherImage.src
at any time. Is it?
Okay, well, following #adeneo's advice I tested this
$('#test').on('change', function(e) {
var newImg = document.createElement("img");
var url = URL.createObjectURL( e.target.files[0] )
console.log(url);
newImg.src = url;
URL.revokeObjectURL(url);
document.body.appendChild(newImg);
console.log(url);
});
$('#test3').on('change', function(e) {
var newImg = document.createElement("img");
var url = URL.createObjectURL( e.target.files[0] )
console.log(url);
newImg.src = url;
newImg.onload = function() {
URL.revokeObjectURL(url);
document.body.appendChild(newImg);
var i = new Image();
i.src= newImg.src;
document.body.appendChild(i);
setTimeout(function() {
var g = new Image();
g.src = newImg.src;
document.body.appendChild(g);
}, 3000);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>test revoke before use</p>
<input type="file" id="test"/>
<br />
<br />
<br />
<p>test use revoke use</p>
<input type="file" id="test3" />
At least in Firefox, once URL.revokeObjectURL is called the URL can no longer be used, even though other things are accessing it.
So both #1 and #2 in the question fail and, even though in #2 newImg.src still has the URL that URL won't work anywhere else once URL.revokeObjectURL has been called.
In React, there is a hook called useEffect, and one of the things that it does, is what action would you like to take right before you're leaving the component(function), and there you release the existing object URL which was previously created by calling URL.createObjectURL().
Releasing an existing object URL using URL.revokeObjectURL:
useEffect(() => () => URL.revokeObjectURL(file as string), [file])
const onChangeFile = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files[0]) {
setFile(URL.createObjectURL(e.target.files[0]))
}
}

Categories