Here' my complete code for displaying and live recording audio and video (and later uploading the blob chunk into the server):
$(function () {
var handleSuccess = function(stream) {
var player = document.querySelector("#vid-user");
player.srcObject = stream;
console.log("Starting media recording")
var options = {mimeType: 'video/webm'};
var recordedChunks = [];
var mediaRecorder = new MediaRecorder(stream, options);
mediaRecorder.ondataavailable = function(e) {
console.log("Data available")
if (e.data.size > 0) {
recordedChunks.push(e.data);
var url = URL.createObjectURL(new Blob(recordedChunks));
console.log("URL: " + url)
}
}
mediaRecorder.start();
};
navigator.mediaDevices.getUserMedia({ audio: true, video: true })
.then(handleSuccess)
})
The video playback works, but the problem is that the mediaRecorder.ondataavailable is not triggered/called. What could be the problem here?
The start() method takes an optional parameter called timeslice. Unless you specify that parameter the dataavailable event only fires after you call stop() on the MediaRecorder.
https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/start
The solution is to set
mediaRecorder.start(1000); // where 1000 is the interval
Solution according to the documentation (https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder): Use MediaRecorder.start("time in milliseconds") to trigger 'dataavailable' event at the specified interval AND/OR use setTimeout(MediaRecorder.requestData(), "time in milliseconds"). I have a jsFiddle example here (not complete code, check the console messages only).
Related
I am using recordJs library for recording voice of client and send it to the server. In firefox and other browsers, it works well without any error. when i try to run it in chrome, it starts recording the voice, but when it calls stopRecording function, it faces following error:
Uncaught TypeError: Cannot read property 'stop' of undefined
at stopRecording (توانایی-پرسش-سن-از-افراد:1209)
at startTimer (توانایی-پرسش-سن-از-افراد:1364)
Here is my JS codes:
<script type="text/javascript">
'use strict';
//webkitURL is deprecated but nevertheless
URL = window.URL || window.webkitURL;
let gumStream; //stream from getUserMedia()
let rec; //Recorder.js object
let input; //MediaStreamAudioSourceNode we'll be recording
// shim for AudioContext when it's not avb.
let AudioContext = window.AudioContext || window.webkitAudioContext;
let audioContext //audio context to help us record
function startRecording() {
console.log("recordButton clicked");
/*
Simple constraints object, for more advanced audio features see
https://addpipe.com/blog/audio-constraints-getusermedia/
*/
var constraints = { audio: true, video:false }
/*
Disable the record button until we get a success or fail from getUserMedia()
*/
/*
We're using the standard promise based getUserMedia()
https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
*/
navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
console.log("getUserMedia() success, stream created, initializing Recorder.js ...");
/*
create an audio context after getUserMedia is called
sampleRate might change after getUserMedia is called, like it does on macOS when recording through AirPods
the sampleRate defaults to the one set in your OS for your playback device
*/
audioContext = new AudioContext();
//update the format
// document.getElementById("formats").innerHTML="Format: 1 channel pcm # "+audioContext.sampleRate/1000+"kHz"
/* assign to gumStream for later use */
gumStream = stream;
/* use the stream */
input = audioContext.createMediaStreamSource(stream);
/*
Create the Recorder object and configure to record mono sound (1 channel)
Recording 2 channels will double the file size
*/
rec = new Recorder(input,{numChannels:1});
//start the recording process
rec.record();
console.log("Recording started");
}).catch(function(err) {
});
}
function pauseRecording(){
console.log("pauseButton clicked rec.recording=",rec.recording );
if (rec.recording){
//pause
rec.stop();
}else{
rec.record();
}
}
function stopRecording() {
//tell the recorder to stop the recording
rec.stop();
//stop microphone access
gumStream.getAudioTracks()[0].stop();
//create the wav blob and pass it on to createDownloadLink
rec.exportWAV(setUserVoice);
}
function setUserVoice(blob)
{
let formData = new FormData
formData.append('userVoice', blob)
$.ajax({
type: 'POST',
headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')},
url: '{{ route('user.mockTest.participation.saveUserVoice') }}',
data: formData,
processData: false,
contentType: false,
success: function (data) {
if (data['result'] == 'success')
{
$('#recordUserVoice').prop('disabled', true);
}
else
{
Swal.fire(
'{{__('Error')}}',
'{{__('An error occurred')}}',
'error'
);
}
},
error: function (err) {
console.log(err);
}
});
}
function createDownloadLink(blob) {
var url = URL.createObjectURL(blob);
var au = document.createElement('audio');
var li = document.createElement('li');
var link = document.createElement('a');
//name of .wav file to use during upload and download (without extendion)
var filename = new Date().toISOString();
//add controls to the <audio> element
au.controls = true;
au.src = url;
//save to disk link
link.href = url;
link.download = filename+".wav"; //download forces the browser to donwload the file using the filename
link.innerHTML = "Save to disk";
//add the new audio element to li
li.appendChild(au);
//add the filename to the li
li.appendChild(document.createTextNode(filename+".wav "))
//add the save to disk link to li
li.appendChild(link);
//upload link
var upload = document.createElement('a');
upload.href="#";
upload.innerHTML = "Upload";
upload.addEventListener("click", function(event){
var xhr=new XMLHttpRequest();
xhr.onload=function(e) {
if(this.readyState === 4) {
console.log("Server returned: ",e.target.responseText);
}
};
var fd=new FormData();
fd.append("audio_data",blob, filename);
xhr.open("POST","upload.php",true);
xhr.send(fd);
})
li.appendChild(document.createTextNode (" "))//add a space in between
li.appendChild(upload)//add the upload link to li
//add the li element to the ol
recordingsList.appendChild(li);
}
document.getElementById('timer').innerHTML =
'00' + ":" + '00';
function startRecord()
{
startRecording();
startTimer();
}
function startTimer() {
$('#recordTextHolder').addClass('d-none');
$('#timer').removeClass('d-none');
var presentTime = document.getElementById('timer').innerHTML;
var timeArray = presentTime.split(/[:]+/);
var m = timeArray[0];
console.log(timeArray[1])
var s = checkSecond((parseInt(timeArray[1]) + 1));
if(parseInt(s) == 5)
{
m = '0'+(parseInt(m)+1)
s = '00'
}
if(m == 2 && s == 1){
stopRecording()
shake()
return
}
document.getElementById('timer').innerHTML =
m + ":" + s;
console.log(m)
setTimeout(startTimer, 1000);
}
function checkSecond(sec) {
if (sec < 10 && sec >= 0) {sec = "0" + sec}; // add zero in front of numbers <10
if (sec < 0) {sec = "59"};
return sec;
</script>
I would be grateful, if someone guide me to handle this problem.
It looks like you are setting the value of rec here:
rec = new Recorder(input,{numChannels:1});
And the error message is presumably from here:
function stopRecording() {
//tell the recorder to stop the recording
rec.stop();
Can you try adding a console.log?
function stopRecording() {
console.log("rec:", rec)
rec.stop();
Report back what rec contains at that time.
Thanks for reporting that it says "undefined".
Now ask yourself: how can 'rec' be undefined at that time?
I assume that your console is showing the "recordButton clicked"?
And the "getUserMedia() success..."?
How about the "Recording started" message?
I suggest getting rid of the following block:
.catch(function(err) {
});
What that block does is silently "swallow" any error messages that you would otherwise see. In general, don't put in empty catch blocks unless you genuinely do not want to know about errors occurring there.
We have a laptop with one built-in webcam and 2 external USB webcams. I would like to receive images from all three webcams at the same time. I know that since 2018 this is possible.
I am using the following code to work with one camera, but how to display the image from three cameras at once?
<script>
var video = null;
var canvas = null;
var canvasContext = null;
var webimage = null;
var statusLabel = null;
function initVideo() {
video = document.getElementById("monitor");
statusLabel = document.getElementById("LBLSTATUS");
navigator.webkitGetUserMedia({video:true}, gotStream, noStream);
}
function setStatus(aStatus) {
statusLabel.innerHTML = aStatus;
}
function gotStream(stream) {
video.onerror = function () {
stream.stop();
streamError();
};
video.srcObject = stream;
//video.src = webkitURL.createObjectURL(stream); -> Deprecated
}
function noStream() {
setStatus('No camera available.');
}
function streamError() {
setStatus('Camera error.');
}
</script>
You need to get all 3 cameras deviceIDs. Once you get those, make 3 calls to getUserMedia, each with the respective camera ids.
navigator.mediaDevices.getUserMedia({
video: {
deviceId:{
exact: videoSource
},
},
}).then(function( video ) {
const localVidElem = document.getElementById( 'localVideo1' );
localVidElem.srcObject = video;
})
<video id="localVideo1"></video>
Make sure that you have the correct DeviceID and then assign it a srcObject. For more info about that, please read the official WebRTC docs.
I tried MediaSource API but it's not working correctly due to the latest browser does not support some of the functions from MediaSource API for example webkitMediaSourceURL.
https://wwwhtml5rockscom.readthedocs.io/en/latest/content/tutorials/streaming/multimedia/en/
I'm looking for open source one javascript.
I tried another example but There is a problem with seamless playback, a gap between two chunks is noticeable.
var player = document.querySelector('video');
getBlob('webm-chunk1')
.then(function (chunk1) {
var url = URL.createObjectURL(chunk1);
player.src = url;
player.play();
return getBlob('webm-chunk2');
})
.then(function (chunk2) {
var url = URL.createObjectURL(chunk2);
player.addEventListener('ended', handler, false);
function handler() {
player.removeEventListener('ended', handler);
player.src = url;
player.load();
player.play();
}
});
The javascript error is: Unhandled Promise Rejection: NotAllowedError: The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.
My setup works across other browsers, desktop and mobile.
The way it works is:
have a flag first_audio_played = false;
add a touch event listener that plays some audio, and sets first_audio_played = true; (then removes the touch listener)
all subsequent audio checks if(first_audio_played) some_other_audio.play();
this way, only the first audio played requires direct user input. after that, all audio is free to be triggered by in-game events, timing, etc...
this appears to be the "rule" for audio across most browsers. is the iOS "rule" that every audio needs to be triggered by user input? or is there some other step I'm missing?
For my javascript game, sounds stopped working on iOS recently. They all have readyState=4, but only the sound I played on tap works, the others won't play. Maybe you could play all the sounds on the first tap. But the solution I found that works for now for me is to load all the sounds from ajax arraybuffers and use decodeAudioData(). Then once you've played 1 sound from user tap (on not the body), they all play whenever.
Here is my working code where the second way of doing it is on bottom. When I tap to play sound2, sound1 starts working also.
<html>
<body>
<div id=all style='font-size:160%;background:#DDD' onclick="log('clicked');playSound(myAudio)">
Sound1 should be playing every couple seconds.
<br />Tap here to play sound1.
</div>
<div id=debug style='font-size:120%;' onclick="playSound(myAudio2)">
Tap here to play the sound2.
</div>
<script>
var url = "http://curtastic.com/drum.wav"
var url2 = "http://curtastic.com/gold.wav"
var myAudio, myAudio2
if(0)
{
var playSound = function(sound)
{
log("playSound() readyState="+sound.readyState)
log("gold readyState="+myAudio2.readyState)
sound.play()
}
var loadSound = function(url, callback)
{
var audio = new Audio(url)
audio.addEventListener('canplaythrough', function()
{
log('canplaythrough');
if(callback)
callback()
}, false)
audio.load()
if(audio.readyState > 3)
{
log('audio.readyState > 3');
if(callback)
callback()
}
return audio
}
myAudio = loadSound(url, startInterval)
myAudio2 = loadSound(url2)
}
else
{
var playSound = function(sound)
{
log("playSound()")
var source = audioContext.createBufferSource()
if(source)
{
source.buffer = sound
if(!source.start)
source.start = source.noteOn
if(source.start)
{
var gain = audioContext.createGain()
source.connect(gain)
gain.connect(audioContext.destination)
source.start()
}
}
}
var loadSound = function(url, callback)
{
log("start loading sound "+url)
var ajax = new XMLHttpRequest()
ajax.open("GET", url, true)
ajax.responseType = "arraybuffer"
ajax.onload = function()
{
audioContext.decodeAudioData(
ajax.response,
function(buffer)
{
log("loaded sound "+url)
log(buffer)
callback(buffer)
},
function(error)
{
log(error)
}
)
}
ajax.send()
}
var AudioContext = window.AudioContext || window.webkitAudioContext
var audioContext = new AudioContext()
loadSound(url, function(r) {myAudio = r; startInterval()})
loadSound(url2, function(r) {myAudio2 = r})
}
function startInterval()
{
log("startInterval()")
setInterval(function()
{
playSound(myAudio)
}, 2000)
}
function log(m)
{
console.log(m)
debug.innerHTML += m+"<br />"
}
</script>
</body>
</html>
You can use either [WKWebViewConfiguration setMediaTypesRequiringUserActionForPlayback:WKAudiovisualMediaTypeNone] or [UIWebView setMediaPlaybackRequiresUserAction:NO] depending on your WebView class (or Swift equivalent).
I opened a webcam by using the following JavaScript code:
const stream = await navigator.mediaDevices.getUserMedia({ /* ... */ });
Is there any JavaScript code to stop or close the webcam?
Since this answer has been originally posted the browser API has changed.
.stop() is no longer available on the stream that gets passed to the callback.
The developer will have to access the tracks that make up the stream (audio or video) and stop each of them individually.
More info here: https://developers.google.com/web/updates/2015/07/mediastream-deprecations?hl=en#stop-ended-and-active
Example (from the link above):
stream.getTracks().forEach(function(track) {
track.stop();
});
Browser support may differ.
Previously, navigator.getUserMedia provided you with a stream in the success callback, you could call .stop() on that stream to stop the recording (at least in Chrome, seems FF doesn't like it)
Use any of these functions:
// stop both mic and camera
function stopBothVideoAndAudio(stream) {
stream.getTracks().forEach(function(track) {
if (track.readyState == 'live') {
track.stop();
}
});
}
// stop only camera
function stopVideoOnly(stream) {
stream.getTracks().forEach(function(track) {
if (track.readyState == 'live' && track.kind === 'video') {
track.stop();
}
});
}
// stop only mic
function stopAudioOnly(stream) {
stream.getTracks().forEach(function(track) {
if (track.readyState == 'live' && track.kind === 'audio') {
track.stop();
}
});
}
Don't use stream.stop(), it's deprecated
MediaStream Deprecations
Use stream.getTracks().forEach(track => track.stop())
FF, Chrome and Opera has started exposing getUserMedia via navigator.mediaDevices as standard now (Might change :)
online demo
navigator.mediaDevices.getUserMedia({audio:true,video:true})
.then(stream => {
window.localStream = stream;
})
.catch( (err) =>{
console.log(err);
});
// later you can do below
// stop both video and audio
localStream.getTracks().forEach( (track) => {
track.stop();
});
// stop only audio
localStream.getAudioTracks()[0].stop();
// stop only video
localStream.getVideoTracks()[0].stop();
Suppose we have streaming in video tag and id is video - <video id="video"></video> then we should have following code -
var videoEl = document.getElementById('video');
// now get the steam
stream = videoEl.srcObject;
// now get all tracks
tracks = stream.getTracks();
// now close each track by having forEach loop
tracks.forEach(function(track) {
// stopping every track
track.stop();
});
// assign null to srcObject of video
videoEl.srcObject = null;
Starting Webcam Video with different browsers
For Opera 12
window.navigator.getUserMedia(param, function(stream) {
video.src =window.URL.createObjectURL(stream);
}, videoError );
For Firefox Nightly 18.0
window.navigator.mozGetUserMedia(param, function(stream) {
video.mozSrcObject = stream;
}, videoError );
For Chrome 22
window.navigator.webkitGetUserMedia(param, function(stream) {
video.src =window.webkitURL.createObjectURL(stream);
}, videoError );
Stopping Webcam Video with different browsers
For Opera 12
video.pause();
video.src=null;
For Firefox Nightly 18.0
video.pause();
video.mozSrcObject=null;
For Chrome 22
video.pause();
video.src="";
With this the Webcam light go down everytime...
Try method below:
var mediaStream = null;
navigator.getUserMedia(
{
audio: true,
video: true
},
function (stream) {
mediaStream = stream;
mediaStream.stop = function () {
this.getAudioTracks().forEach(function (track) {
track.stop();
});
this.getVideoTracks().forEach(function (track) { //in case... :)
track.stop();
});
};
/*
* Rest of your code.....
* */
});
/*
* somewhere insdie your code you call
* */
mediaStream.stop();
You can end the stream directly using the stream object returned in the success handler to getUserMedia. e.g.
localMediaStream.stop()
video.src="" or null would just remove the source from video tag. It wont release the hardware.
Since you need the tracks to close the streaming, and you need the stream boject to get to the tracks, the code I have used with the help of the Muaz Khan's answer above is as follows:
if (navigator.getUserMedia) {
navigator.getUserMedia(constraints, function (stream) {
videoEl.src = stream;
videoEl.play();
document.getElementById('close').addEventListener('click', function () {
stopStream(stream);
});
}, errBack);
function stopStream(stream) {
console.log('stop called');
stream.getVideoTracks().forEach(function (track) {
track.stop();
});
Of course this will close all the active video tracks. If you have multiple, you should select accordingly.
If the .stop() is deprecated then I don't think we should re-add it like #MuazKhan dose. It's a reason as to why things get deprecated and should not be used anymore. Just create a helper function instead... Here is a more es6 version
function stopStream (stream) {
for (let track of stream.getTracks()) {
track.stop()
}
}
You need to stop all tracks (from webcam, microphone):
localStream.getTracks().forEach(track => track.stop());
Start and Stop Web Camera,(Update 2020 React es6 )
Start Web Camera
stopWebCamera =()=>
//Start Web Came
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
//use WebCam
navigator.mediaDevices.getUserMedia({ video: true }).then(stream => {
this.localStream = stream;
this.video.srcObject = stream;
this.video.play();
});
}
}
Stop Web Camera or Video playback in general
stopVideo =()=>
{
this.video.pause();
this.video.src = "";
this.video.srcObject = null;
// As per new API stop all streams
if (this.localStream)
this.localStream.getTracks().forEach(track => track.stop());
}
Stop Web Camera function works even with video streams:
this.video.src = this.state.videoToTest;
this.video.play();
Using .stop() on the stream works on chrome when connected via http. It does not work when using ssl (https).
Please check this: https://jsfiddle.net/wazb1jks/3/
navigator.getUserMedia(mediaConstraints, function(stream) {
window.streamReference = stream;
}, onMediaError);
Stop Recording
function stopStream() {
if (!window.streamReference) return;
window.streamReference.getAudioTracks().forEach(function(track) {
track.stop();
});
window.streamReference.getVideoTracks().forEach(function(track) {
track.stop();
});
window.streamReference = null;
}
The following code worked for me:
public vidOff() {
let stream = this.video.nativeElement.srcObject;
let tracks = stream.getTracks();
tracks.forEach(function (track) {
track.stop();
});
this.video.nativeElement.srcObject = null;
this.video.nativeElement.stop();
}
Have a reference of stream form successHandle
var streamRef;
var handleVideo = function (stream) {
streamRef = stream;
}
//this will stop video and audio both track
streamRef.getTracks().map(function (val) {
val.stop();
});