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);
}
Related
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.
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');
}
});
}
I created a Youtube player and added a onStateChange listener, it works for the first video.
var o = new Object;
o.videoId = 'videoid1';
player = new YT.Player('div1', o);
player.addEventListener("onStateChange", "stateChangeListener", false);
function stateChangeListener(e) {
console.log(e.data);
}
But after load a new video, onStateChange event doesn't fire anymore.
function nextVideo() {
player.cueVideoById('div2');
}
Even if I added a new listener to it.
function nextVideo() {
player.cueVideoById('div2');
player.addEventListener("onStateChange", "stateChangeListener", false);
}
What's the problem? How to fire onStateChange event if there're many videos on one page?
I think the problem is it doesn't attach the event handler to the correct Youtube player.
<iframe id="video" src="http://www.youtube.com/embed/XXXX"></iframe>
<a href="http://www.youtube.com/embed/XXXX" />
I have the following code, where I hoped to be able to console.log out the list of videos in the playlist:
function onYouTubePlayerReady(playerId) {
ytplayer = document.getElementById("myytplayer");
ytplayer.cuePlaylist({
list:"PLf71xE2jRgTXB_LeUJkXxFwCc4r1z5if3"
});
console.log(ytplayer.getPlaylist());
}
But the getPlayList() method just returns an empty array.
When playing around with it if I call getPlaylist() from outside of this function it does return the correct array. Why is this method returning an empty array here? And how can I get the playlist array?
Many thanks.
Just stumbled across this problem myself. The issue seems to be a quirk with the player, and how YouTube (google) have defined "ready"
It seems that the onReady function of the player fires when the "chrome" for the player has finished rendering (i.e. it's tied the HTML document). The actual player data (video or playlist) is loaded asynchronously, so is ready at some arbitrary point after.
I put a simple work-around in place that looks at the state-change event of the player
var firstLoad = true;
function onPlayerStateChange(e) {
if (firstLoad && e.data == YT.PlayerState.CUED) {
//First Cued event - playlist is now available
firstLoad = false
myPlaylistFunction(player.getPlaylist());
}
}
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;
}