Hey I am using WEBRTC for screen share. But I am stuck at point where i need user entire screen, but browser is providing user with more options like application and browser tabs, so i want to check what option is user selecting from the popup produce by the browser if its not entire screen then i can generate a error message to user, Please see this popup image
const constraints = {
audio: false,
video: {
width: { max: 1920 },
height: { max: 1080 },
frameRate: { max: 10 }
}
}
const stream = await navigator.mediaDevices.getDisplayMedia(constraints);
After searching for a long time I found something that works for me. if it works for you that's great.
const isFirefox = typeof InstallTrigger !== 'undefined';
const isChrome = !!window.chrome && (!!window.chrome.webstore || !!window.chrome.runtime);
const videoTrack = mediaStream.getVideoTracks()[0];
if (isFirefox) {
if(videoTrack.label === "Primary Monitor"){
return true;
} else {
return false;
}
} else if (isChrome) {
const videoSetting = videoTrack.getSettings();
if (videoSetting && videoSetting.displaySurface !== "monitor") {
return false;
} else {
return true;
}
}
you can check like this
const videoTrack = stream.getVideoTracks()[0];
console.log(videoTrack.getSettings());
In console, you will see 'displaySurface' property.
Also this MDN document will help you :)
Firefox multiple screens maybe:
if (isFirefox) {
const videoTrack = stream.getVideoTracks()[0];
return (videoTrack.label === "Primary Monitor" || -1 !== videoTrack.label.indexOf('Screen'))
} # else ...
Related
I used the code from Nikolay answer https://jsfiddle.net/makhalin/nzw5tv1q/ on my Ionic - Angular PWA (I put it on a custom.js file and imported it on angular.json). It's working great if I open it in Chrome or Edge on Android but if I install it as a PWA it works the first time, then stops working.
Is there anything I must do to make it work as a PWA?
//have a console on mobile
const consoleOutput = document.getElementById("console");
const log = function (msg) {
consoleOutput.innerText = `${consoleOutput.innerText}\n${msg}`;
console.log(msg);
}
//Test browser support
const SUPPORTS_MEDIA_DEVICES = 'mediaDevices' in navigator;
if (SUPPORTS_MEDIA_DEVICES) {
//Get the environment camera (usually the second one)
navigator.mediaDevices.enumerateDevices().then(devices => {
const cameras = devices.filter((device) => device.kind === 'videoinput');
if (cameras.length === 0) {
log('No camera found on this device.');
}
// Create stream and get video track
navigator.mediaDevices.getUserMedia({
video: {
facingMode: 'environment',
}
}).then(stream => {
const track = stream.getVideoTracks()[0];
//Create image capture object and get camera capabilities
const imageCapture = new ImageCapture(track)
imageCapture.getPhotoCapabilities().then(capabilities => {
//let there be light!
const btn = document.querySelector('.switch');
const torchSupported = !!capabilities.torch || (
'fillLightMode' in capabilities &&
capabilities.fillLightMode.length != 0 &&
capabilities.fillLightMode != 'none'
);
if (torchSupported) {
let torch = false;
btn.addEventListener('click', function (e) {
try {
track.applyConstraints({
advanced: [{
torch: (torch = !torch)
}]
});
} catch (err) {
log(err);
}
});
} else {
log("No torch found");
}
}).catch(log);
}).catch(log);
}).catch(log);
//The light will be on as long the track exists
}
I am writing a custom module for quilljs. It is a simple text macro replacement tool.
ie type ".hi", it will replace it with "hello".
Originally I was calling quill.setSelection(...) within the text-change event. It wasn't working Codepen Original. Eventually I found this in the docs
Changes to text may cause changes to the selection (ex. typing advances the cursor), however during the text-change handler, the selection is not yet updated, and native browser behavior may place it in an inconsistent state. Use selection-change or editor-change for reliable selection updates." Quill setSelection API docs
So I reworked the code Codepen Revised. It works, but it sure is ugly and cursor updating after the text insertion looks weird. I cannot believe there isn't a better/idiomatic way to do this.
Quill.register("modules/quicktext", function (quill, options) {
let cache = "";
let finalcaret = null;
quill.on("editor-change", (eventName, ...args) => {
if (eventName === "text-change" && args[2] === "user") {
let [delta, oldDelta, source] = args;
console.log(source, delta);
let lastkey = delta.ops[delta.ops.length - 1]?.insert || "";
if (delta.ops[delta.ops.length - 1]?.delete) {
// handle delete key
cache = cache.slice(0, -1);
return;
}
if (lastkey && lastkey !== " ") {
cache += lastkey;
console.log("cache", cache, "lastkey", lastkey);
} else if (cache) {
// avoid initial call
console.log("cache", cache, "lastkey", lastkey);
reps.forEach((rep) => {
console.log("rep check", cache, rep[cache]);
if (rep[cache]) {
console.log("triggered");
let caret = quill.getSelection().index;
let start = caret - cache.length - 1;
quill.deleteText(start, cache.length, "api");
quill.insertText(start, rep[cache], "api");
//quill.update("api");
finalcaret = caret + rep[cache].length - cache.length;
console.log(
`caret at ${caret}, moving forward ${
rep[cache].length - cache.length
} spaces, to position ${
caret + rep[cache].length - cache.length
}.`
);
console.log("done trigger");
}
});
cache = "";
}
} else if (eventName === "selection-change") {
if (finalcaret) {
quill.setSelection(finalcaret);
finalcaret = "";
}
}
});
});
let reps = [
{ ".hi": "hello" },
{ ".bye": "goodbye" },
{ ".brb": "be right back" }
];
// We can now initialize Quill with something like this:
var quill = new Quill("#editor", {
modules: {
quicktext: {
reps: reps
}
}
});
I was able to figure out a solution based upon this issue. Not sure why this is necessary, but works but it does.
// quill.setSelection(finalcaret, 0) // this fails
setTimeout(() => quill.setSelection(finalcaret, 0), 1); // this works
Codepen FINAL
I have been looking up how to play sound with node.js all day, I can't use "document.getElementById" and I can't use "new Audio" either. I want it to be able to play sound when I do #everyone in my chatroom. The audio file is name "ping.mp3" and is in the same path as my main node.js file. I need some recommendations or code snippets. Thanks!
This is a code snippet of where the ping code is.
function highlight(message){
if(message == "") {
return message
}
let mentions = message.match(/#\b([A-Za-z0-9]+)\b/g)
let urlCheck1 = message.split(` `)
if (mentions === null ) { return message }
for (i = 0; i < mentions.length; i++) {
let urlCheck = urlCheck1[i].includes(`http`)
let mention = mentions[i].substring(1)
if(sesskx.has(mention) && !urlCheck) {
message = message.replace(mentions[i], `<span class="name-color">#${mention}</span>`)
} else if (mention == 'everyone') {
ping.play();
message = message.replace(mentions[i], `<span class="name-color">#${mention}</span>`)
} else if (mention == 'here') {
ping.play();
message = message.replace(mentions[i], `<span class="name-color">#${mention}</span>`)
}
else {
return message;
}
}
return message
};
I want "ping.play();" to make the sound.
This is possible but kind of tricky. You can use play-sound to take care of it for you:
var player = require('play-sound')(opts = {})
player.play('foo.mp3', function(err){
if (err) throw err
});
What it basically does is look for sound players in the environment and try to use one of them to play the mp3:
const { spawn, execSync } = require('child_process');
// List of players used
const players = [
'mplayer',
'afplay',
'mpg123',
'mpg321',
'play',
'omxplayer',
'aplay',
'cmdmp3'
];
// find a player by seeing what command doesn't error
let player;
for(const p of players) {
if (isExec(p)) {
player = p;
break;
}
}
function isExec(command) {
try{
execSync(command)
return true
}
catch {
return false
}
}
spawn(player, ['ping.mp3']);
If you are creating a node.js server, node.js is back-end/server side, so users won't be able to hear anything that happens if a sound is "played" there. So you'll have to do it client side using "new Audio()", when the client receives a message including "#everyone" (which I don't understand why you cant use, you can send the mp3 file to the client with the page.).
I also made an example for you on repl.
I am writing a front end to a game engine in Javascript. The engine runs on the server and sends pictures and sounds to the web browser through 'SignalR'. I am using the React framework.
As the game runs the server sends small sound samples in WAVE format, passed into this component through the AudioPlayerProps.
I am having two main issues with the sound. The first is that the sound sounds 'disjointed'.
And the second is that after a time the sound just stops playing. I can see sound being queued in the audio queue, but the 'playNextAudioTrack' method is not being called. There are no errors in the console to explain this.
If this is not the best way to provide sound for a game front end, please let me know.
Also if you want to see any more code please let me know. This is a huge multi tiered project so I am only showing what I think you need to see.
Right now I am testing in Chrome. At this stage I need to turn on the DEV tools to get past the 'user didn't interact with page so you can't play any sound issue'. I will sort that issue out in due course.
import * as React from "react";
import { useEffect, useState } from "react";
export interface AudioPlayerProps {
data: string;
}
export const AudioPlayer = function (props: AudioPlayerProps): JSX.Element {
const [audioQueue, setAudioQueue] = useState<string[]>([])
useEffect(
() => {
if (props.data != undefined) {
audioQueue.push(props.data);
}
}, [props.data]);
const playNextAudioTrack = () => {
if (audioQueue.length > 0) {
const audioBase64 = audioQueue.pop();
const newAudio = new Audio(`data:audio/wav;base64,${audioBase64}`)
newAudio.play().then(playNextAudioTrack).catch(
(error) => {
setTimeout(playNextAudioTrack, 10);
}
)
}
else {
setTimeout(playNextAudioTrack, 10);
}
}
useEffect(playNextAudioTrack, []);
return null;
}
I solved my own problem. Here is the typescript class I wrote to handle chunked audio in JavaScript.
I am not a JavaScript expert and therefore there may be faults.
EDIT: After running it in 15 minute lots several times, it failed a couple of times at about the 10 minute mark. Still needs some work.
// mostly from https://gist.github.com/revolunet/e620e2c532b7144c62768a36b8b96da2
// Modified to play chunked audio for games
import { setInterval } from "timers";
//
const MaxScheduled = 10;
const MaxQueueLength = 2000;
const MinScheduledToStopDraining = 5;
export class WebAudioStreamer {
constructor() {
this.isDraining = false;
this.isWorking = false;
this.audioStack = [];
this.nextTime = 0;
this.numberScheduled = 0;
setInterval(() => {
if (this.audioStack.length && !this.isWorking) {
this.scheduleBuffers(this);
}
}, 0);
}
context: AudioContext;
audioStack: AudioBuffer[];
nextTime: number;
numberScheduled: number;
isDraining: boolean;
isWorking: boolean;
pushOntoAudioStack(encodedBytes: number[]) {
if (this.context == undefined) {
this.context = new (window.AudioContext)();
}
const encodedBuffer = new Uint8ClampedArray(encodedBytes).buffer;
const streamer: WebAudioStreamer = this;
if (this.audioStack.length > MaxQueueLength) {
this.audioStack = [];
}
streamer.context.decodeAudioData(encodedBuffer, function (decodedBuffer) {
streamer.audioStack.push(decodedBuffer);
}
);
}
scheduleBuffers(streamer: WebAudioStreamer) {
streamer.isWorking = true;
if (streamer.context == undefined) {
streamer.context = new (window.AudioContext)();
}
if (streamer.isDraining && streamer.numberScheduled <= MinScheduledToStopDraining) {
streamer.isDraining = false;
}
while (streamer.audioStack.length && !streamer.isDraining) {
var buffer = streamer.audioStack.shift();
var source = streamer.context.createBufferSource();
source.buffer = buffer;
source.connect(streamer.context.destination);
if (streamer.nextTime == 0)
streamer.nextTime = streamer.context.currentTime + 0.01; /// add 50ms latency to work well across systems - tune this if you like
source.start(streamer.nextTime);
streamer.nextTime += source.buffer.duration; // Make the next buffer wait the length of the last buffer before being played
streamer.numberScheduled++;
source.onended = function () {
streamer.numberScheduled--;
}
if (streamer.numberScheduled == MaxScheduled) {
streamer.isDraining = true;
}
};
streamer.isWorking = false;
}
}
One of my jquery plugins is having issues, and the issue occurs when private browsing is turned on in ios.
Is there a way to check this?
In private mode user can't use local storage try this:
var storageTestKey = 'sTest',
storage = window.sessionStorage;
try {
storage.setItem(storageTestKey, 'test');
storage.removeItem(storageTestKey);
} catch (e) {
if (e.code === DOMException.QUOTA_EXCEEDED_ERR && storage.length === 0) {
// private mode
} else {
throw e;
}
}
I've found an Answear at GitHub and tested it: Working on IOS 11!
var isPrivate = false;
try {
window.openDatabase(null, null, null, null);
} catch (_) {
isPrivate = true;
}
alert((isPrivate ? 'You\'re' : 'You aren\'t') + ' in private browsing mode');