I am trying to play a video using javascript mediasource to hide video URL from the users. But I get an error as,
Uncaught InvalidStateError: Failed to execute 'endOfStream' on 'MediaSource': The MediaSource's readyState is not 'open'. And it would also be helpful if the video type and codes to be passed dynamically.
Below is my code,
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<video controls></video>
<script>
var video = document.querySelector('video');
var assetURL = 'adsss.mp4';
// Need to be specific for Blink regarding codecs
// ./mp4info frag_bunny.mp4 | grep Codec
var mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';
if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) {
var mediaSource = new MediaSource;
//console.log(mediaSource.readyState); // closed
video.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', sourceOpen);
} else {
console.error('Unsupported MIME type or codec: ', mimeCodec);
}
function sourceOpen (_) {
console.log(this.readyState); // open
var mediaSource = this;
console.log(mediaSource);
var sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
console.log(sourceBuffer);
fetchAB(assetURL, function (buf) {
sourceBuffer.addEventListener('updateend', function (_) {
mediaSource.endOfStream();
video.play();
console.log(mediaSource.readyState); // ended
});
sourceBuffer.appendBuffer(buf);
});
};
function fetchAB (url, cb) {
console.log(url);
var xhr = new XMLHttpRequest;
xhr.open('get', url);
console.log(xhr);
xhr.responseType = 'arraybuffer';
xhr.onload = function () {
cb(xhr.response);
console.log(xhr.response);
};
xhr.send();
};
</script>
The reason why the above code was not working is that, only fragmented mp4 videos with moov(movie header box) moved at the beginning will be played using the Media source.
Refer the below links,
1) Unable to get MediaSource working with mp4 format in chrome
2) What exactly is Fragmented mp4(fMP4)? How is it different from normal mp4?
You can fragment your videos using MP4box tool with the following command,
MP4Box -dash 1000 -rap -frag-rap test.mp4
Related
I'm trying to record audio from the microphone and then add a download link for it.
This is my current code:
<!DOCTYPE html>
<html>
<head>
<title>Weird Problem</title>
<script>
function microphone() {
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then(function(stream) {
var recorder = new MediaRecorder(stream);
var chunks = [];
recorder.ondataavailable = function(e) {
chunks.push(e.data);
}
recorder.onstop = function() {
stream.getTracks()[0].stop();
var blob = new Blob(chunks, { type: "audio/ogg; codecs=opus" });
var url = window.URL.createObjectURL(blob);
document.getElementById("player").src = url;
document.getElementById("upload").href = url;
document.getElementById("upload").download = "test.ogg";
}
recorder.start();
setTimeout(function() {
recorder.stop();
}, 5000);
}).catch(function(error) {
console.log(error);
});
}
}
</script>
</head>
<body>
<input type="button" value="Record Microphone" onclick="microphone();">
<br>
<audio controls id="player"></audio>
<br>
<a id="upload">Download Microphone Recording</a>
</body>
</html>
I'm on Chrome, and for me it plays the microphone audio correctly, but when I try to download it, the file won't play or even open.
I get various errors that the file contains data in an unknown format, so I think it's something to do with the audio headers.
Any help would be highly appreciated!
It turns out this issue is a bit more complicated than I expected.
I'm now adapting code from Recorder.js.
The online demo does exactly what I need.
I am working on media source extension to play the video in a seamless loop without any delay. I have done an extensive r&d about it and also done different things. Now i am working on this code
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<video controls></video>
<script>
var video = document.querySelector('video');
var assetURL = 'test1.mp4';
// Need to be specific for Blink regarding codecs
// ./mp4info frag_bunny.mp4 | grep Codec
var mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';
if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) {
var mediaSource = new MediaSource;
//console.log(mediaSource.readyState); // closed
video.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', sourceOpen);
} else {
console.error('Unsupported MIME type or codec: ', mimeCodec);
}
function sourceOpen (_) {
//console.log(this.readyState); // open
var mediaSource = this;
var sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
fetchAB(assetURL, function (buf) {
sourceBuffer.addEventListener('updateend', function (_) {
mediaSource.endOfStream();
video.play();
//console.log(mediaSource.readyState); // ended
});
sourceBuffer.appendBuffer(buf);
});
};
function fetchAB (url, cb) {
console.log(url);
var xhr = new XMLHttpRequest;
xhr.open('get', url);
xhr.responseType = 'arraybuffer';
xhr.onload = function () {
cb(xhr.response);
};
xhr.send();
};
</script>
</body>
</html>
It is working fine but the video is playing again with a slight delay. I want to play the video without any delay and I know that it is possible with media source extensions but after spending a lot of time still didn't get any idea that how to do it. Any help would be really appreciated. Thanks
Setting the mode value to sequence worked for me, it ensures continuous media playback, no matter if the media segment timestamps were discontinuous.
sourceBuffer.mode = 'sequence';
May be this code solve Your Problem ....play the video in a seamless loop without any delay
for (let chunk of media) {
await new Promise(resolve => {
sourceBuffer.appendBuffer(chunk.mediaBuffer);
sourceBuffer.onupdateend = e => {
sourceBuffer.onupdateend = null;
sourceBuffer.timestampOffset += chunk.mediaDuration;
console.log(mediaSource.duration);
resolve()
}
})
}
if you need more information visit this link.....
http://www.esjay.org/2020/01/01/videos-play-without-buffering-in-javascript/
You can't really do that because it really depends on how fast your PC runs. Its delay should only be there if you have a slow PC but if not it shouldn't have a noticeable delay. Maybe the video that you have is big or it just delays inside the video because only thing I could tell you is autoplay loop inside the videos element as a attribute unless you find a way to replay the video before the video ends but even if it doesn't delay for you it may delay big or just play over the video for others
#CoolOppo
The simplest thing to do is to just rewind the video when it reaches it's end time (duration).
See demo here: https://vc-lut.000webhostapp.com/demos/public/mse_loop_example1.html
(to play video just click the picture part, since no controls for looping background)
The code:
<!DOCTYPE html>
<html>
<head> <meta charset="utf-8"/> </head>
<body>
<div>
<video id="video_mse" > </video>
</div>
<script>
var assetURL = "testloop.mp4";
//# use correct mime/codecs or else Error is "source not open"...
var mimeCodec = 'video/mp4; codecs="avc1.42C01E"';
var mediaSource; var sourceBuffer;
var video = document.getElementById( "video_mse" );
video.onclick = vid_togglePlay; //# for playing/pausing
video.ontimeupdate = vid_checkTime; //# for looping
//# handle play/pause (because controls are hidden)
function vid_togglePlay()
{
if( video.paused == true ) { video.play(); }
else { video.pause(); }
}
//# handle looping...
function vid_checkTime()
{
if( video.currentTime == video.duration)
{
video.currentTime = 0;
video.play();
}
}
if ( 'MediaSource' in window && MediaSource.isTypeSupported(mimeCodec) )
{
mediaSource = new MediaSource;
video.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', sourceOpen);
}
else
{ console.error('Unsupported MIME type or codec: ', mimeCodec); }
function sourceOpen()
{
//console.log(this.readyState); // open
var mediaSource = this;
sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
fetchAB(assetURL, function (buf) {
sourceBuffer.addEventListener('updateend', function() {
mediaSource.endOfStream();
video.play();
});
sourceBuffer.appendBuffer(buf);
});
};
function fetchAB (url, callbackfunc )
{
console.log("loading file: " + url);
var xhr = new XMLHttpRequest;
xhr.open('get', url);
xhr.responseType = 'arraybuffer';
xhr.onload = function() { callbackfunc( xhr.response ); }
xhr.send();
}
</script>
</body>
</html>
I have assumed (possibly incorrectly?), due to the asynchronous nature of HTML, that the timeline of page loading is as follows:
... etc loading html into DOM
encounter <audio> tag
preload is specified as "auto"; trigger buffering
continue loading html into DOM... etc
fire window.onload event callbacks
asynchronously some time later: audio resource is found, begin buffering, or server error is returned; fire readystatechange event callbacks
Whereas what I'm hoping for is that the preload attribute with a value of "auto" will delay the window.onload event from firing or delay tag-to-DOM processing until the audio resource is found and has had begun buffering, or a server error is returned and loading is canceled.
I can't imagine withholding window.onload for an audio resource, but then I have seen page processing come to a halt for flash resource loading or tracking script loading in the past.
TLDR: What is the exact timeline of window.onload with regard to resource loading--specifically the audio tag?
window.onload event appears to be called before the media src is fully loaded. Using approaches described at How do you check if a HTML5 audio element is loaded? ; and including .webkitAudioDecodedByteCount
<!DOCTYPE html>
<html>
<head>
<script>
window.addEventListener("load", function() {
var media = document.querySelector("audio");
console.log("window onload event"
, media.webkitAudioDecodedByteCount
, media.readyState)
})
function myOnCanPlayFunction() {
console.log("Can play", event.target.webkitAudioDecodedByteCount
, event.target.seekable.start(0)
, event.target.seekable.end(0));
}
function myOnCanPlayThroughFunction() {
console.log("Can play through", event.target.webkitAudioDecodedByteCount
, event.target.seekable.start(0)
, event.target.seekable.end(0));
}
function myOnLoadedData() {
console.log("Loaded data", event.target.webkitAudioDecodedByteCount
, event.target.seekable.start(0)
, event.target.seekable.end(0));
}
</script>
</head>
<body>
<audio oncanplay="myOnCanPlayFunction()"
oncanplaythrough="myOnCanPlayThroughFunction()"
onloadeddata="myOnLoadedData()"
src="/path/to/audio/file"
preload autoplay buffered controls></audio>
</body>
</html>
plnkr version 1 http://plnkr.co/edit/zIIDDLZeVU7NHdfAtFka?p=preview
An alternative approach using XMLHttpRequest , onended event of AudioContext; Promise; recursion to request, play array of files in sequence. See AudioContext.decodeAudioData()
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<script>
var sources = ["/path/to/audio/src/1"
, "/path/to/audio/src/2"];
var src = sources.slice(0); // copy original array
function getAudio(url) {
return new Promise(function(resolve, reject) {
var audioCtx = new(window.AudioContext || window.webkitAudioContext)();
var source = audioCtx.createBufferSource();
var request = new XMLHttpRequest();
request.open("GET", url, true);
request.responseType = "arraybuffer";
request.onload = function() {
var audioData = request.response;
audioCtx.decodeAudioData(audioData).then(function(decodedData) {
source.buffer = decodedData;
source.connect(audioCtx.destination);
console.log(source, decodedData);
// do stuff when current audio has ended
source.onended = function() {
console.log("onended:", url);
if (src.length)
resolve(src)
else resolve("complete")
}
source.start(0);
});
}
request.send();
})
}
var audio = (function tracks(s) {
return getAudio(s.shift())
.then(function(data) {
if (Array.isArray(data) && data.length) return tracks(data)
else return data
})
}(src));
// do stuff when all `src` have been requested, played, ended
audio.then(function(msg) {
console.log(msg)
})
</script>
</head>
<body>
</body>
</html>
plnkr version 2 http://plnkr.co/edit/zIIDDLZeVU7NHdfAtFka?p=preview
I'd like to record a video in the browser and then play it back for the user. I'm currently using the MediaRecorder API (only available in Firefox for now) to do this. It works fine for videos shorter than a few seconds, but otherwise the video doesn't show up at all.
There are no errors in the console or similar, and I couldn't find anything about any file size limitations in the documentation.
Here's the code I'm using (Firefox only):
index.html:
<!DOCTYPE html>
<html>
<body>
<button id="start">Start</button>
<button id="stop">Stop</button>
<video id="player" src="" width="300" height="300" autoplay></video>
<script src="script.js"></script>
</body>
</html>
script.js:
var record = document.getElementById('start');
var stop = document.getElementById('stop');
var video = document.getElementById('player');
var constraints = {video: true};
var onSuccess = function(stream) {
var mediaRecorder = new MediaRecorder(stream);
record.onclick = function() {
mediaRecorder.start();
}
stop.onclick = function() {
mediaRecorder.stop();
}
mediaRecorder.ondataavailable = function(e) {
video.src = window.URL.createObjectURL(e.data);
}
};
var onError = function(err) {
console.log('The following error occured: ' + err);
}
navigator.mozGetUserMedia(constraints, onSuccess, onError);
On Codepen: http://codepen.io/anon/pen/xGqKgE
Is this a bug/browser issue? Are there any limitations to this API that I'm not aware of?
after a couple of hours of struggling here I am. I have the following code, which apparently should just start my webcam and prompt the stream on the webpage:
<!doctype html>
<html>
<head>
<title>HTML5 Webcam Test</title>
</head>
<body>
<video id="sourcevid" autoplay>Put your fallback message here.</video>
<div id="errorMessage"></div>
<script>
video = document.getElementById('sourcevid');
navigator.getUserMedia = navigator.webkitGetUserMedia || navigator.getUserMedia;
window.URL = window.URL || window.webkitURL;
function gotStream(stream) {
if (window.URL) {
video.src = window.URL.createObjectURL(stream);
} else {
video.src = stream; // Opera.
}
video.onerror = function(e) {
stream.stop();
};
stream.onended = noStream;
}
function noStream(e) {
var msg = 'No camera available.';
if (e.code == 1) {
msg = 'User denied access to use camera.';
}
document.getElementById('errorMessage').textContent = msg;
}
navigator.webkitGetUserMedia({video: true}, gotStream, noStream);
</script>
</body>
</html>
No errors in the console, but no webcam stream either. Just the "User denied access to use camera.".
I tried another example, too long to show, but again apparently as soon as I try to run the page the stream falls into the .onended function:
function gotStream(stream) {
video.src = URL.createObjectURL(stream);
video.onerror = function () {
stream.stop();
};
stream.onended = noStream;
[...]
Where noStream is a simple function that prints something:
function noStream() {
document.getElementById('errorMessage').textContent = 'No camera available.';
}
So basically when I'm running the second example I'm shown the "No camera available" on the webpage.
I'm running on Chrome Version 22.0.1229.94, I saw somewhere that I needed to enable some flags, but I couldn't find them in my chrome://flags; the flags' name were Enable MediaStream and Enable PeerConnection, but in my version I only have the second one, which I enabled.
Any thoughts? Is the API I'm using old by any means? Can somebody point me to some working example?
Thanks
According to http://www.webrtc.org/running-the-demos the getUserMedia API is available on stable version as of Chrome 21 without the need to use any flag.
I think the error happens because you are trying to instantiate the stream without to define the url stream properly. Consider that you need to access the url stream differently in Chrome and Opera.
I would create the structure of your code as something like below:
function gotStream(stream) {
if (window.URL) {
video.src = window.URL.createObjectURL(stream) || stream;
video.play();
} else {
video.src = stream || stream; // Opera.
video.play();
}
video.onerror = function(e) {
stream.stop();
};
stream.onended = noStream;
}
function noStream(e) {
var msg = 'No camera available.';
if (e.code == 1) {
msg = 'User denied access to use camera.';
}
document.getElementById('errorMessage').textContent = msg;
}
var options = {video: true, toString: function(){return 'video';}};
navigator.getUserMedia(options, gotStream, noStream);
EDIT:
You need to replace the source video element with the media stream. Edited the code above.
video = document.getElementById('sourcevid');
I recommend for reading these two articles:
http://www.html5rocks.com/en/tutorials/getusermedia/intro/
http://dev.opera.com/articles/view/playing-with-html5-video-and-getusermedia-support/