How can I receive a notification/event when mediaStreamTrack.enabled is modified? - javascript

I am using getUserMedia to get access to web cam. I have a function which toggle on and off the video doing the following:
var videoTracks = this.stream.getVideoTracks();
if (videoTracks.length === 0) {
trace('No local video available.');
return;
}
trace('Toggling video mute state.');
for (var i = 0; i < videoTracks.length; ++i) {
videoTracks[i].enabled = !videoTracks[i].enabled;
}
trace('Video ' + (videoTracks[0].enabled ? 'unmuted.' : 'muted.'));
How can receive an event when the the value of enabled is changed? I tried to use Object.observe, but it doesn't work.

As far as I can tell there currently is no event fired/callback invoked when the enabled property changes.
From here:
Also, there is no "onmuted" and "onunmuted" event defined or fired in the WebRTC native implementations.
You might have to build this mechanism yourself:
Keep in mind that you're disabling a media track locally; it will not fire any event on target users side. If you disabled video track; then it will cause "blank-video" on target-users side.
You can manually fire events like "onmuted" or "onmediatrackdisabled" by using socket that was used for signaling. You can send/emit messages like:
yourSignalingSocket.send({
isMediaStreamTrackDisabled: true,
mediaStreamLabel: stream.label
});
According to the spec this should be part of the MediaStreamTrack interface eventually:
onmute of type EventHandler,
This event handler, of type mute, must be supported by all objects implementing the MediaStreamTrack interface.
I tried assigning a function to a track's onmute in Chrome (43) but it never got called (looks like this is not implemented yet).

Related

How to create a generic "joystick/gamepad event" in Javascript?

Issue:
In the current implementation of modern browsers, (like Firefox or Chrome), there are only two joystick/gamepad events:
gameadConnected
gamepadDisconnected
Since it appears that the original idea behind implementing joystick/gamepad support in the browser was to allow for in-browser games, the joystick was made dependents on the requestAnimationFrame() call to create a game-loop sync'd with v_sync.
However, in other use cases, for example where the joystick is being used to control something remotely over a network or wireless connection, the best case is to only send data when there is something useful to say - did something happen?  Using requestAnimationFrame() floods the interface with potentially useless data.
Unfortunately, there is currently no established interface for triggering gamepad events.&nbsp: (Note, there is some discussion of this very issue over on the Mozilla and W3C forums, so this may, eventually, change.)
Since flooding an industrial device or remote controlled system with useless messages isn't a "best practice" - the question becomes how to generate the equivalent of a gamepad event without flooding the network or stalling the browser in a wait-loop.
Webworkers was a thought, but they cannot be used because they don't have access to the window.event context and cannot interface with the joystick/gamepad.  At least not directly.
In order to handle this efficiently, some method of triggering an "event" that allows data to be sent, only when something of interest happens.
For the benefit of those who may be confronting this same issue, here is the solution I eventually implemented:
=======================
My solution:
This solution is based on the fact that the gamepad's time_stamp attribute only changes when something happens.  (i.e. A button was pressed or a joystick axis was moved,)
Keep track of the gamepad's time_stamp attribute and capture it on the initial gamepad connected event.
Provide a "gateway" condition that surrounds the routine that actually sends the data to the receiving device.
I implemented this in two steps as noted above:
First:
When the gamepad connects, I immediately capture the time_stamp attribute and store it in a variable (old_time).
window.addEventListener("gamepadconnected", (event) => {
js = event.gamepad;
gamepad_connected(); // Gamepad is now connected
old_time = gopigo3_joystick.time_stamp // Capture the initial value of the time_stamp
send_data(gopigo3_joystick) // send it to the robot
Then I do whatever looping and processing of data I need to do.
As a part of that loop, I periodically attempt to send data to the server device with the following code:
function is_something_happening(old_time, gopigo3_joystick) {
if (gopigo3_joystick.trigger_1 == 1 || gopigo3_joystick.head_enable == 1) {
if (old_time != Number.parseFloat(jsdata.timestamp).toFixed()) {
send_data(gopigo3_joystick)
old_time = gopigo3_joystick.time_stamp
}
}
return;
}
function send_data(gpg_data) {
// this sends a gamepad data frame to the robot for interpreting.
[code goes here];
return;
}
The first function, is_something_happening, tests for two qualifying conditions:
A specific joystick button press.  The robot is not allowed to move without a trigger being pressed so no data is sent.&nbsp "head_enable" is another condition that allows messages for head pan-and-tilt commands to be sent.
A change in the time_stamp value.  If the time_stamp value has not changed, nothing of interest has happened.
Both conditions must be satisfied, otherwise the test falls through and immediately returns.
Only if both conditions are met does the send_data() function get called.
This results in a stable interface that always gets called if something of interest has happened, but only if something of interest has happened.
Note:  There are keyboard commands that can be sent, but since they have active events, they can call send_data() by themselves as they only fire when a key is pressed.

How to do screen sharing with simple-peer webRTC SDK

I'm trying to implement webrtc & simple peer to my chat. Everything works but I would like to add screen sharing option. For that I tried that:
$("#callScreenShare").click(async function(){
if(captureStream != null){
p.removeStream(captureStream)
p.addStream(videoStream)
captureStreamTrack.stop()
captureStreamTrack =captureStream= null
$("#callVideo")[0].srcObject = videoStream
$(this).text("screen_share")
}else{
captureStream = await navigator.mediaDevices.getDisplayMedia({video:true, audio:true})
captureStreamTrack = captureStream.getTracks()[0]
$("#callVideo")[0].srcObject = captureStream
p.removeStream(videoStream)
console.log(p)
p.addStream(captureStream)
$(this).text("stop_screen_share")
}
})
But I stop the camera and after that doesn't do anything and my video stream on my peer's computer is blocked. No errors, nothing only that.
I've put a console.log when the event stream is fired. The first time it fires but when I call the addStream method, it doesn't
If someone could help me it would be really helpful.
What I do is replacing the track. So instead of removing and adding the stream:
p.streams[0].getVideoTracks()[0].stop()
p.streams[0].replaceTrack(p.streams[0].getVideoTracks()[0], captureStreamTrack, p.streams[0])
This will replace the video track from the stream with the one of the display.
simple-peer docs
The below function will do the trick. Simply call the replaceTrack function, passing it the new track and the remote peer instance.
function replaceTrack(stream, recipientPeer ) {
recipientPeer.replaceTrack(
recipientPeer.streams[0].getVideoTracks()[0],
stream,
recipientPeer.streams[0]
)
}

How to listen volume changes of youtube iframe?

Here I found an example how I can listen Play\Pause button of youtube iframe.
player.addEventListener('onStateChange', function(e) {
console.log('State is:', e.data);
});
Now I need to listen the volume changes.
In the youtube documentation and here I found a method player.getVolume(), but I have no idea how this method can be implemented if I want to be informed about volume changes from iframe side, instead of ask iframe from my side.
On YouTube Player Demo page such functionality exists (when I change the volume of a player, I see appropriate changes in the row Volume, (0-100) [current level: **]), but neither in the doc nor in internet I can not find how to implement it.
I also tried to use the above mentioned code with onApiChange event (it is not clear for me what this event actually does), like:
player.addEventListener('onApiChange', function(e) {
console.log('onApiChange is:', e.data);
});
but console shows nothing new.
player.getOptions(); shows Promise {<resolved>: Array(0)}.
Could anyone show an example?
See this question.
You can listen to postMessage events emitted by the IFrame and react only to the volume change ones:
// Instantiate the Player.
function onYouTubeIframeAPIReady() {
var player = new YT.Player("player", {
height: "390",
width: "640",
videoId: "dQw4w9WgXcQ"
});
// This is the source "window" that will emit the events.
var iframeWindow = player.getIframe().contentWindow;
// Listen to events triggered by postMessage.
window.addEventListener("message", function(event) {
// Check that the event was sent from the YouTube IFrame.
if (event.source === iframeWindow) {
var data = JSON.parse(event.data);
// The "infoDelivery" event is used by YT to transmit any
// kind of information change in the player,
// such as the current time or a volume change.
if (
data.event === "infoDelivery" &&
data.info &&
data.info.volume
) {
console.log(data.info.volume); // there's also data.info.muted (a boolean)
}
}
});
}
See it live.
Note that this relies on a private API that may change at anytime without previous notice.
I inspected the code of YouTube Player Demo page and found that the html line which shows the current YouTube volume (<span id="volume">**</span>) constantly blinking (~ 2 times per 1 sec), so I can assume this demo page uses something like this:
// YouTube returns Promise, but we need actual data
self = this
setInterval(function () { self.player.getVolume().then(data => { self.volumeLv = data }) }, 250)
Possibly not the best method, but it seems there is no other option (I also tried to listen changes in the appropriate style of the volume bar, but no luck due to the cross-origin problem).
So, this let us 'listen' volume changes of youtube.
Just in case, if someone wants to set youtube volume, you need to use [this.]player.setVolume(volume_from_0_to_100)

WebRTC continue video stream when webcam is reconnected

I've got simple video stream working via getUserMedia, but I would like to handle case when webCam what i'm streaming from becomes disconnected or unavailable. So I've found oninactive event on stream object passed to successCallback function. Also I would like to restart video stream when exactly same webcam/mediaDevice will be plugged in.
Code example:
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
navigator.getUserMedia(constrains, function successCallback(stream) {
this.video.src = URL.createObjectURL(stream);
stream.oninactive = function (error) {
//this handler runs when device becomes unavailable.
this.onStreamInactive(error, stream);
}.bind(this);
}.bind(this), function errorCallback () {});
Based on the example above how i can:
Detect recently connected media device
Check is it the same device what I was streaming from
A better way would be to use MediaDevices.ondevicechange() as mentioned in the other answer in this thread, but it is still behind a flag on Chrome. Instead of using ondevicechange() to enumerate devices, poll MediaDevices.enumerateDevices() at regular interval when you start the call, at end of every poll interval compare the list of devices you get from the devices in the previous poll. This way you can know the new devices added/remove during the call.
A little late to answer, but it looks like you can use MediaDevices.ondevicechange to attach an event handler, and then in the event handler you can query MediaDevices.enumerateDevices() to get the full list. Then you inspect the list of devices, identify the one that was recently added by comparing by a cached list you have, and comparing properties to a record you kept of the properties of the current device. The links have more thorough examples.
Adapted from the ondevicechange reference page
navigator.mediaDevices.ondevicechange = function(event) {
navigator.mediaDevices.enumerateDevices()
.then(function(devices) {
devices.forEach(function(device) {
console.log(device);
// check if this is the device that was disconnected
});
});
}
Note that the type of the device objects returned by enumerateDevices is described here
Browser Support
It looks like it's pretty patchy as of writing this. See this related question: Audio devices plugin and plugout event on chrome browser for further discussion, but the short story is for Chrome you'll need to enable the "Experimental Web Platform features" flag.

Javascript request fullscreen is unreliable

I'm trying to use the JavaScript FullScreen API, using workarounds for current non-standard implementations from here:
https://developer.mozilla.org/en/DOM/Using_full-screen_mode#AutoCompatibilityTable
Sadly, it behaves very erratically. I only care about Chrome (using v17), but since I was having problems I did some tests in Firefox 10 for comparison, results are similar.
The code below attempts to set the browser to fullscreen, sometimes it works, sometimes not. It ALWAYS calls the alert to indicate it is requesting fullscreen. Here's what I've found:
It USUALLY sets fullscreen. It can get to a state where this stops working, but the alert still happens, i.e. it is still requesting FullScreen, but it doesn't work.
It can work if called from a keypress handler (document.onkeypress), but not when called on page loading (window.onload).
My code is as follows:
function DoFullScreen() {
var isInFullScreen = (document.fullScreenElement && document.fullScreenElement !== null) || // alternative standard method
(document.mozFullScreen || document.webkitIsFullScreen);
var docElm = document.documentElement;
if (!isInFullScreen) {
if (docElm.requestFullscreen) {
docElm.requestFullscreen();
}
else if (docElm.mozRequestFullScreen) {
docElm.mozRequestFullScreen();
alert("Mozilla entering fullscreen!");
}
else if (docElm.webkitRequestFullScreen) {
docElm.webkitRequestFullScreen();
alert("Webkit entering fullscreen!");
}
}
}
requestFullscreen() can not be called automatically is because of security reasons (at least in Chrome). Therefore it can only be called by a user action such as:
click (button, link...)
key (keydown, keypress...)
And if your document is contained in a frame:
allowfullscreen needs to be present on the <iframe> element*
* W3 Spec:
"...To prevent embedded content from going fullscreen only embedded content specifically allowed via the allowfullscreen attribute of the HTML iframe element will be able to go fullscreen. This prevents untrusted content from going fullscreen..."
Read more: W3 Spec on Fullscreen
Also mentioned by #abergmeier, on Firefox your fullscreen request must be executed within 1 second after the user-generated event was fired.
I know this is quite an old question but it is still the top result in Google when searching for FireFox's error message when calling mozRequestFullScreen() from code that wasn't triggered by any user interaction.
Request for full-screen was denied because
Element.mozRequestFullScreen() was not called from inside a short
running user-generated event handler.
As already discussed this is a security setting and therefore is the correct behaviour in normal browser environment (end user machine).
But I am writting an HTML5-based digital signage application which runs under a controlled environment without any user interaction intended. It is vital for my apllication to be able to switch to fullscreen automatically.
Luckily FireFox offers a possibilty to remove this restriction on the browser, which is rather hard to find. I will write it here as future reference for everybody finding this page via the Google search as I did
On the about:config page search for the following key and set it to false
full-screen-api.allow-trusted-requests-only
For my digital signage application I also removed the prompt the browser shows when entering fullscren:
full-screen-api.approval-required
Hopefully this might save someone the hours I wasted to find these settings.
You have nothing wrong with your function. In Firefox, if you call that function directly, it will prevent to for full-screen. As you know, Request for full-screen was denied because docElm.mozRequestFullScreen(); was not called from inside a short running user-generated event handler. So, You have to call the function on event such as onClick in Firefox.
Full Screen Mode
Another unexpected issue with requestFullscreen() is that parent frames need to have the allowfullscreen attribute, otherwise Firefox outputs the following error:
Request for fullscreen was denied because at least one of the document’s containing elements is not an iframe or does not have an “allowfullscreen” attribute.
Aside from iframes, this can be caused by your page being within a frameset frame. Because frameset is deprecated, there is no support for the HTML5 allowfullscreen attribute, and the requestFullscreen() call fails.
The Firefox documentation explicitly states this on MDN, but I think it bears reiterating here, for developers who might not read the documentation first.... ahem
Only elements in the top-level document or in an with the allowfullscreen attribute can be displayed full-screen. This means that elements inside a frame or an object can't.
I realize this is an old post, but in case someone else finds this I'd like to add a few suggestions and sample code.
To help avoid this error...
Failed to execute 'requestFullscreen' on 'Element': API can only be
initiated by a user gesture.
Don't test for the existence of requestFullscreen(), which is a method. Instead, test for the existence of a property like document.fullscreenEnabled.
Also consider the following...
Move your fullscreen check into its own function so you can reuse it.
Make DoFullScreen() reusable by passing the element you want to affect as a parameter.
Use a guard clause at the top of DoFullScreen() to exit out of the function immediately if the window is already in fullscreen mode. This also simplifies the logic.
Set a default value for your DoFullScreen() element parameter to ensure the requestFullscreen() method is always called on an existing element. Defaulting to document.documentElement will probably save you some keystrokes.
// Move your fullscreen check into its own function
function isFullScreen() {
return Boolean(
document.fullscreenElement ||
document.webkitFullscreenElement ||
document.mozFullScreenElement ||
document.msFullscreenElement
);
}
// Make DoFullScreen() reusable by passing the element as a parameter
function DoFullScreen(el) {
// Use a guard clause to exit out of the function immediately
if (isFullScreen()) return false;
// Set a default value for your element parameter
if (el === undefined) el = document.documentElement;
// Test for the existence of document.fullscreenEnabled instead of requestFullscreen()
if (document.fullscreenEnabled) {
el.requestFullscreen();
} else if (document.webkitFullscreenEnabled) {
el.webkitRequestFullscreen();
} else if (document.mozFullScreenEnabled) {
el.mozRequestFullScreen();
} else if (document.msFullscreenEnabled) {
el.msRequestFullscreen();
}
}
(function () {
const btnFullscreenContent = document.querySelector(".request-fullscreen-content");
const el = document.querySelector(".fullscreen-content");
// Request the .fullscreen-content element go into fullscreen mode
btnFullscreenContent .addEventListener("click", function (){ DoFullScreen(el) }, false);
const btnFullscreenDocument = document.querySelector(".request-fullscreen-document");
// Request the document.documentElement go into fullscreen mode by not passing element
btnFullscreenDocument .addEventListener("click", function (){ requestFullscreen() }, false);
})();

Categories