Twilio Video - Track enable/disable event not firing - twilio

For tracks that the localParticipant constructs and publishes manually, enabling/disabling those tracks apparently has no effect on remote participants. So for example, if a localParticipant wanted to mute their audio, the remote participant will continue to receive that localParticipant's audio.
I found at least 2 ways to reproduce this error:
When a participant first connects to a room, if the participant constructs their own tracks object and passes it in as a connect option like so:
Twilio.Video.connect(twilioToken, {
name: roomName,
tracks: [LocalTrack|MediaStreamTrack] // The tracks array is constructed by converting from a MediaStream object containing 1 audio and 1 video track
})
Those tracks will get published successfully but not register their enable/disable events.
However, if the participant connects to the room like so:
Twilio.Video.connect(twilioToken, {
name: roomName,
video: true,
audio: true,
})
the track enable/disable events fire for those tracks and the remote participant will be notified when the localPartipants disables any tracks.
When a localParticipant publishes a new track constructed manually, the new track's enable/disable events will not fire off to remoteParticipants.
const newLocalAudioTrack = new Twilio.Video.LocalAudioTrack(newAudioTrack); //newAudioTrack is a MediaStreamTrack object
localParticipant.publishTrack(newLocalAudioTrack); // track gets successfully published
// Trying to disable track after track has been published and registered by the remoteParticipant
localParticipant.audioTracks.forEach((publication) => { publication.track.disable(); }); // This will not work and remote Participant will continue to receive audio, no disable event is registered by any remoteParticipants
Project Environment: Node.js, Twilio-video#2.12.0, Twilio#3.56.0
Please let me know if you need any other details.

function roomJoined(room) {
$('#call-microphone').click(function ()
{
console.log('MICROPHONE MUTED');
if (microphone) {
room.localParticipant.audioTracks.forEach(function (audioTrack) {
console.log("audioTrack-- "+audioTrack);
audioTrack.disable();
});
microphone = false;
$('#call-microphone').html('<span class="fa fa-microphone"></span> ');
console.log('MICROPHONE MUTED', 'control', 'bg-warning');
} else {
room.localParticipant.audioTracks.forEach(function (audioTrack) {
console.log("audioTrack-- "+audioTrack);
audioTrack.enable();
});
microphone = true;
$('#call-microphone').html('<span class="fa fa-microphone-slash"></span> ');
console.log('MICROPHONE ON', 'control', 'bg-info');
}
});
}

Related

How can I restitue a raw audio data stream using WebAudio?

I use WebAudio API and basically my setup is fairly simple.
I use 1 AudioWorkletNode as an emitter and another one as a receiver
emitter:
process(inputs, outputs) {
inputs[ 0 ].length && this.port.postMessage( inputs[ 0 ] );
return ( true );
}
receiver:
inputs = [ new Float32Array(128), new Float32Array(128) ]
constructor() {
super();
// Create a message port to receive messages from the main thread
this.port.onmessage = (event) => {
this.inputs = event.data.inputs;
};
}
process( inputs, outputs) {
const output = outputs[0];
for (let channel = 0; channel < output.length; ++channel) {
output[ channel ].set( this.inputs[ channel ] );
}
return true;
}
on client side I have
//emitter
this.inputWorklet.port.onmessage = e => this.receiverWorklet.port.postMessage( { inputs: e.data } );
and for receiving the data I have connected the nodes together
this.receiverWorklet.connect( this.gainNode );
This works but my problem is that the sound is really glitchy
One thing I though of is there might be a delay between events also WebAudio is in a DOM context
Do you have any ideas How I could achieve a fluid stream restitution?
or maybe another technique?
The reason for the glitchy audio is that your code only works if everything always happens in the exact same order.
The input worklet's process() function needs to be called. It sends an event.
The event needs to pass through the main thread.
The event needs to arrive at the receiver worklet.
Only after that the receiver worklet's process() function needs to be called.
Since there is no buffer it always has to happen in the exact same order. If for some reason the main thread is busy and it can't process the events right away the receiver will continue playing the old audio.
I think you can almost keep the current implementation by buffering a few events in your receiver worklet before you start playing. It will of course also add some latency.
Another approach would be to use a SharedArrayBuffer instead of sending events. Your input worklet would write to the SharedArrayBuffer and your receiver worklet would read from it.

RTCPeerConnection replaceTrack only changing stream for the remote peer

I am new to RTCPeerConnection (WEbRTC), so please bear with me.
So far I am able to get to the point where I can replace tracks on the run by switching camera or screen sharing in my app. But I noticed it in 2 browser tabs that newly replaced track stream is captured in partner/remote peer only, not on initiator's tab. It just keep showing old stream even though stream has been replaced.
It should've been nice if initiator can also see what he/she is sharing. I tried but no luck so far. Looking for some assistance.
My code looks like:
function screenShare(){
(async () => {
try {
await navigator.mediaDevices.getDisplayMedia(
{
cursor: true
}).then(stream => {
// localStream = stream;
let videoTrack = stream.getVideoTracks()[0];
var sender = senders.find(function(s) {
return s.track.kind == videoTrack.kind;
});
sender.replaceTrack(videoTrack);
videoTrack.onended = function(){
sender.replaceTrack(localStream.getTracks()[1]);
}
});
} catch (err) {
console.log('(async () =>: ' + err);
}
})();
}
Thanks in advance.
By design replaceTrack replaces the stream on the RTCPeerConnection. This does not affect the local video object. Reset the srcObject on the local video element to change it.

Youtube API, make sure a video was watched till end?

I would like to use the YouTube API to make sure the video was watched till end, maybe an event or so?
You need to use onStateChange event, which will give you 0 when ended. This event fires whenever the player's state changes. The value that the API passess to your event listener function will specify an integer that corresponds to the new player state.
Here's a sample code snippet to get notified when the player's state changes:
player.addEventListener("onStateChange", function(state){
if(state === 0){
// the video is end, do something here.
}
});
Here's another way to get notified when state changes:
function onYouTubePlayerReady(playerId) {
ytplayer = document.getElementById("myytplayer");
ytplayer.addEventListener("onStateChange", "onytplayerStateChange");
}
function onytplayerStateChange(newState) {
alert("Player's new state: " + newState);
}

Play Youtube videos one after the other

Is there any way to play one youtube video after another, or better yet, play a sequence of videos one after another with no breaks inbetween? I'm unsure how to do this using the youtube API, but I know it can be done somehow.
The easiest way is using Youtube API method to specify a playlist (check the docs). The harder way is to play the videos yourself, listening for the Video End event, then playing the next one.
About no breaks, that depends on a lot of things:
The speed of the internet connection
wether the videos have ads or not
If the videos have no ads, and you are not satisfied with the breaks you are getting, you might try to cue the next video say 5 seconds before the former video ends, then when you get the Video Finish event, just send a play to the next video. To do this, you have to sequence the videos yourself.
This is simple. I am assuming you have an array of video IDs you want to play. In your onStageChange function, listen for video ended state and once that happens, pick next video from array and start playing it. To play the next video, you need to call loadVideoById function followed by playVideo() call. Hope this helped.
And as #rupps said, whether your videos will run without any breaks one after another depends on factors like internet speed. You cannot buffer all videos of a playlist beforehand.
Visit this post to view the sample - Play a 2nd youtube video after first one has finished playing
I am having an array of video IDs that want to play. In your onStageChange function, on the video ended state, pick next video from array and start playing it. At the end of all array videos it will start again from initial.
<script>
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
var videoId = ['gCFpK_z0Y1o', '2XXzYCfBiAg', 'H-N2SynCSEs', '6PDBRoRFYvo'];
var player; currentVideoId = 0;
function onYouTubeIframeAPIReady() {
player = new YT.Player('player', {
playerVars: {
autoplay: 1,
// loop: 1,
// rel: 0,
// showinfo: 0,
// autohide: 0,
// modestbranding: 1,
},
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
function onPlayerReady(event) {
event.target.loadVideoById(videoId[currentVideoId]);
}
function onPlayerStateChange(event) {
if (event.data == YT.PlayerState.ENDED) {
currentVideoId++;
if (currentVideoId < videoId.length) {
player.loadVideoById(videoId[currentVideoId]);
} else {
currentVideoId = 0;
event.target.loadVideoById(videoId[currentVideoId]);
}
}
}

Counting YouTube player clicks that go to YouTube

I embed the YouTube player on my site. Is there a way using JS or the YouTube API to know when a user has clicked on the video itself, which takes him to YouTube? I can't find a good way to differentiate Flash clicks that play/stop the video vs. clicks that cause the user to go to YouTube.
If you suscribe to the onStateChange event then you can be notified when the player state changes.
function onYouTubePlayerReady(playerId) {
var player = document.getElementById("YTplayer");
player.addEventListener("onStateChange", "onplayerStateChange");
}
Then in the onplayerStateChange You can check for a "5" value which is issued when the video is ready to be "played" for the first time or when the user clicks the video to open a new Youtube window. You should save the old value of the player state to distinguish the two cases.
function onplayerStateChange(newState) {
if (newState == 5) {
if (oldState == -1) {
// First time the video loads
} else {
// User has just opened a new youtube window
}
}
oldState = newState;
}

Resources