Feature screen sharing
- Send stream of screen sharing in peer connexion - Add button for share your screen
This commit is contained in:
parent
e1989c1c21
commit
208b91e52a
11
front/dist/index.html
vendored
11
front/dist/index.html
vendored
@ -77,6 +77,10 @@
|
|||||||
<img id="cinema" src="resources/logos/cinema.svg">
|
<img id="cinema" src="resources/logos/cinema.svg">
|
||||||
<img id="cinema-close" src="resources/logos/cinema-close.svg">
|
<img id="cinema-close" src="resources/logos/cinema-close.svg">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="btn-monitor">
|
||||||
|
<img id="monitor" src="resources/logos/monitor.svg">
|
||||||
|
<img id="monitor-close" src="resources/logos/monitor-close.svg">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -100,6 +104,13 @@
|
|||||||
<img id="cinema" src="resources/logos/cinema.svg">
|
<img id="cinema" src="resources/logos/cinema.svg">
|
||||||
<img id="cinema-close" src="resources/logos/cinema-close.svg">
|
<img id="cinema-close" src="resources/logos/cinema-close.svg">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="btn-monitor">
|
||||||
|
<img id="monitor" src="resources/logos/monitor.svg">
|
||||||
|
<img id="monitor-close" src="resources/logos/monitor-close.svg">
|
||||||
|
</div>
|
||||||
|
<!--<div class="btn-call">
|
||||||
|
<img src="resources/logos/phone.svg">
|
||||||
|
</div>-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
-->
|
-->
|
||||||
|
44
front/dist/resources/logos/monitor-close.svg
vendored
Normal file
44
front/dist/resources/logos/monitor-close.svg
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 512 480" style="enable-background:new 0 0 512 480;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#FFFFFF;}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<path class="st0" d="M411.1,384.2c-12.2,0-24.3,0-36.5,0C259.6,257.6,144.5,130.9,29.5,4.3c11.4-0.8,22.9-1.6,34.3-2.4
|
||||||
|
C179.6,129.3,295.3,256.8,411.1,384.2z"/>
|
||||||
|
<g>
|
||||||
|
<path class="st0" d="M352,152.5c-8.8-8.7-34.2-31.6-74.5-38.1c-32.3-5.2-58.1,2.7-70,7.3C170.9,81.5,134.3,41.3,97.8,1
|
||||||
|
C220.4,1,343,1,465.6,1C427.7,51.5,389.8,102,352,152.5z"/>
|
||||||
|
<path class="st0" d="M511.5,338.3c0,4.7-0.8,12.2-4.7,20.2c-1.2,2.4-3.4,6.3-7.1,10.5c-4,4.4-7.9,7.1-10.2,8.5
|
||||||
|
c-5.6,3.5-10.7,4.9-13.5,5.6c-3.8,0.9-6.7,1-10.2,1.2c-3.6,0.2-5.3,0-13.1,0c-3,0-5.4,0-7,0C414.5,307,383.2,229.8,352,152.5
|
||||||
|
C402.9,62.7,448.7,1,465.6,1c7.5,0,14.3,2.3,14.3,2.3c14.5,4.8,22.3,15.8,23.6,17.8c7.4,10.8,8,21.7,8,25.9
|
||||||
|
C511.5,144.1,511.5,241.2,511.5,338.3z"/>
|
||||||
|
<path class="st0" d="M312.5,192c-5.2-5.2-15.6-14.1-31.4-19.4c-12.8-4.2-24-4.3-30.9-3.8c-6.2-7.1-12.4-14.2-18.6-21.3
|
||||||
|
c10.3-2.4,36.5-6.8,65.1,5.3c15.3,6.5,26.1,15.5,32.7,22.2C323.7,180.8,318.1,186.4,312.5,192z"/>
|
||||||
|
<path class="st0" d="M329.4,175.1c38.8,69.7,77.6,139.4,116.4,209.1c-50.3-55.4-100.6-110.8-151-166.2c6.9,2.9,14.9,0.7,19.2-5.2
|
||||||
|
c4.6-6.2,4-15.1-1.6-20.8C318.1,186.4,323.7,180.8,329.4,175.1z"/>
|
||||||
|
<path class="st0" d="M445.8,384.2L445.8,384.2c-38.8-69.7-77.6-139.4-116.4-209.1c5.3,4.9,12.9,6,18.9,2.7
|
||||||
|
c7.8-4.2,8.3-13.4,8.3-13.8C386.4,237.4,416.1,310.8,445.8,384.2z"/>
|
||||||
|
</g>
|
||||||
|
<path class="st0" d="M162.2,150.4C108.3,213,54.4,275.7,0.5,338.3c0-97.1,0-194.3,0-291.4c0-4,0.6-15.1,8.1-26
|
||||||
|
C16,10.2,25.7,5.8,29.5,4.3C73.7,53,118,101.7,162.2,150.4z"/>
|
||||||
|
<path class="st0" d="M199.5,192c-5.3-6-10.6-12-15.8-18C122.6,228.8,61.6,283.6,0.5,338.3c0,4.1,0.6,15.5,8.6,26.7
|
||||||
|
c1.7,2.4,9.6,12.9,24.1,17.2c5.3,1.6,10,1.9,13.1,1.9C97.5,320.2,148.5,256.1,199.5,192z"/>
|
||||||
|
<path class="st0" d="M84.7,384.2c-12.7,0-25.5,0-38.2,0c58.2-56.2,116.5-112.5,174.7-168.7c8.3,9.1,16.6,18.2,24.9,27.2
|
||||||
|
c-2.2,1.1-5.5,3-8.4,6.4c-3.1,3.7-4.2,7.3-4.4,7.8C231.3,262.4,194.5,295.2,84.7,384.2z"/>
|
||||||
|
<path class="st0" d="M46.4,384.2c-15.3-15.3-30.6-30.6-45.9-45.9C52.8,277.5,105.1,216.8,157.4,156c-3.7,6.7-2.2,15.1,3.5,20
|
||||||
|
c5.4,4.6,13.3,5.1,19.4,1C135.7,246.1,91.1,315.1,46.4,384.2z"/>
|
||||||
|
<path class="st0" d="M49.6,384.3c-1.1,0-2.1,0-3.2-0.1c50.1-62.9,100.2-125.8,150.3-188.7c-3.5,6.6-2.1,14.8,3.4,19.7
|
||||||
|
c5.8,5.2,14.8,5.3,21,0.3C164,271.7,106.8,328,49.6,384.3z"/>
|
||||||
|
<path class="st0" d="M374.6,384.2c-96.6,0-193.3,0-289.9,0C16.5,308,1.3,223,28.5,194.5C57,164.7,150.6,177,233.3,256.9
|
||||||
|
c-3.9,11.8,2,24.8,13.3,29.6c11,4.7,24,0.4,30.2-10C309.3,312.4,342,348.3,374.6,384.2z"/>
|
||||||
|
<path class="st0" d="M219.7,226.7"/>
|
||||||
|
<path class="st0" d="M368.9,480c-74.9,0-149.8,0-224.7,0c-8.8,0-16-7.2-16-16c0-8.8,7.2-16,16-16c74.9,0,149.9,0,224.8,0.1
|
||||||
|
c8.3,0.7,14.7,7.6,14.7,15.9C383.7,472.3,377.2,479.3,368.9,480z"/>
|
||||||
|
<rect x="208.1" y="384.2" class="st0" width="31.9" height="63.9"/>
|
||||||
|
<rect x="272" y="384.2" class="st0" width="32" height="63.9"/>
|
||||||
|
<path class="st0" d="M410.3,395.5"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.2 KiB |
15
front/dist/resources/logos/monitor.svg
vendored
Normal file
15
front/dist/resources/logos/monitor.svg
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 24.1.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 512 480" style="enable-background:new 0 0 512 480;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#FFFFFF;}
|
||||||
|
</style>
|
||||||
|
<path class="st0" d="M466,0H46C20.6,0,0,20.6,0,46v292c0,25.4,20.6,46,46,46h162v64h-64c-8.8,0-16,7.2-16,16s7.2,16,16,16h224
|
||||||
|
c8.8,0,16-7.2,16-16s-7.2-16-16-16h-64v-64h162c25.4,0,46-20.6,46-46V46C512,20.6,491.4,0,466,0z M232,264c0-13.3,10.7-24,24-24
|
||||||
|
c13.3,0,24,10.7,24,24s-10.7,24-24,24C242.7,288,232,277.3,232,264z M272,448h-32v-64h32V448z M312.6,214.1
|
||||||
|
c-6.2,6.2-16.4,6.2-22.6,0c-18.7-18.8-49.1-18.8-67.9,0c0,0,0,0,0,0c-6.4,6.1-16.5,5.8-22.6-0.6c-5.9-6.2-5.9-15.9,0-22
|
||||||
|
c31.2-31.2,81.9-31.2,113.1,0c0,0,0,0,0,0C318.8,197.7,318.8,207.8,312.6,214.1z M352.2,174.5c-6.2,6.2-16.4,6.3-22.6,0c0,0,0,0,0,0
|
||||||
|
c-40.6-40.6-106.4-40.6-147.1,0c-6.2,6.3-16.4,6.3-22.6,0c-6.3-6.2-6.3-16.4,0-22.6c53.1-53.1,139.2-53.1,192.3,0c0,0,0,0,0,0
|
||||||
|
C358.4,158.1,358.4,168.2,352.2,174.5C352.2,174.5,352.2,174.5,352.2,174.5L352.2,174.5z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
4
front/dist/resources/style/style.css
vendored
4
front/dist/resources/style/style.css
vendored
@ -109,6 +109,10 @@ video#myCamVideo{
|
|||||||
transition: all .2s;
|
transition: all .2s;
|
||||||
right: 134px;
|
right: 134px;
|
||||||
}
|
}
|
||||||
|
.btn-monitor{
|
||||||
|
transition: all .2s;
|
||||||
|
right: 224px;
|
||||||
|
}
|
||||||
/*.btn-call{
|
/*.btn-call{
|
||||||
transition: all .1s;
|
transition: all .1s;
|
||||||
left: 0px;
|
left: 0px;
|
||||||
|
@ -5,6 +5,9 @@ const videoConstraint: boolean|MediaTrackConstraints = {
|
|||||||
height: { ideal: 720 },
|
height: { ideal: 720 },
|
||||||
facingMode: "user"
|
facingMode: "user"
|
||||||
};
|
};
|
||||||
|
interface MediaServiceInterface extends MediaDevices{
|
||||||
|
getDisplayMedia(constrain: any) : Promise<any>;
|
||||||
|
}
|
||||||
|
|
||||||
type UpdatedLocalStreamCallback = (media: MediaStream) => void;
|
type UpdatedLocalStreamCallback = (media: MediaStream) => void;
|
||||||
|
|
||||||
@ -12,10 +15,13 @@ type UpdatedLocalStreamCallback = (media: MediaStream) => void;
|
|||||||
// TODO: verify that microphone event listeners are not triggered plenty of time NOW (since MediaManager is created many times!!!!)
|
// TODO: verify that microphone event listeners are not triggered plenty of time NOW (since MediaManager is created many times!!!!)
|
||||||
export class MediaManager {
|
export class MediaManager {
|
||||||
localStream: MediaStream|null = null;
|
localStream: MediaStream|null = null;
|
||||||
|
localScreenCapture: MediaStream|null = null;
|
||||||
private remoteVideo: Map<string, HTMLVideoElement> = new Map<string, HTMLVideoElement>();
|
private remoteVideo: Map<string, HTMLVideoElement> = new Map<string, HTMLVideoElement>();
|
||||||
myCamVideo: HTMLVideoElement;
|
myCamVideo: HTMLVideoElement;
|
||||||
cinemaClose: HTMLImageElement;
|
cinemaClose: HTMLImageElement;
|
||||||
cinema: HTMLImageElement;
|
cinema: HTMLImageElement;
|
||||||
|
monitorClose: HTMLImageElement;
|
||||||
|
monitor: HTMLImageElement;
|
||||||
microphoneClose: HTMLImageElement;
|
microphoneClose: HTMLImageElement;
|
||||||
microphone: HTMLImageElement;
|
microphone: HTMLImageElement;
|
||||||
webrtcInAudio: HTMLAudioElement;
|
webrtcInAudio: HTMLAudioElement;
|
||||||
@ -57,6 +63,21 @@ export class MediaManager {
|
|||||||
this.disabledCamera();
|
this.disabledCamera();
|
||||||
//update tracking
|
//update tracking
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.monitorClose = document.getElementById('monitor-close');
|
||||||
|
this.monitorClose.style.display = "block";
|
||||||
|
this.monitorClose.addEventListener('click', (e: any) => {
|
||||||
|
e.preventDefault();
|
||||||
|
this.enabledMonitor();
|
||||||
|
//update tracking
|
||||||
|
});
|
||||||
|
this.monitor = document.getElementById('monitor');
|
||||||
|
this.monitor.style.display = "none";
|
||||||
|
this.monitor.addEventListener('click', (e: any) => {
|
||||||
|
e.preventDefault();
|
||||||
|
this.disabledMonitor();
|
||||||
|
//update tracking
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onUpdateLocalStream(callback: UpdatedLocalStreamCallback): void {
|
onUpdateLocalStream(callback: UpdatedLocalStreamCallback): void {
|
||||||
@ -126,6 +147,58 @@ export class MediaManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enabledMonitor() {
|
||||||
|
this.monitorClose.style.display = "none";
|
||||||
|
this.monitor.style.display = "block";
|
||||||
|
this.getScreenMedia().then((stream) => {
|
||||||
|
this.updatedLocalStreamCallBack(stream);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
disabledMonitor() {
|
||||||
|
this.monitorClose.style.display = "block";
|
||||||
|
this.monitor.style.display = "none";
|
||||||
|
this.localScreenCapture?.getTracks().forEach((track: MediaStreamTrack) => {
|
||||||
|
track.stop();
|
||||||
|
});
|
||||||
|
this.localScreenCapture = null;
|
||||||
|
this.getCamera().then((stream) => {
|
||||||
|
this.updatedLocalStreamCallBack(stream);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//get screen
|
||||||
|
getScreenMedia() : Promise<MediaStream>{
|
||||||
|
try {
|
||||||
|
return this._startScreenCapture()
|
||||||
|
.then((stream: MediaStream) => {
|
||||||
|
this.localScreenCapture = stream;
|
||||||
|
return stream;
|
||||||
|
})
|
||||||
|
.catch((err: any) => {
|
||||||
|
console.error("Error => getScreenMedia => " + err);
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
}catch (err) {
|
||||||
|
return new Promise((resolve, reject) => { // eslint-disable-line no-unused-vars
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _startScreenCapture() {
|
||||||
|
if ((navigator as any).getDisplayMedia) {
|
||||||
|
return (navigator as any).getDisplayMedia({video: true});
|
||||||
|
} else if ((navigator.mediaDevices as any).getDisplayMedia) {
|
||||||
|
return (navigator.mediaDevices as any).getDisplayMedia({video: true});
|
||||||
|
} else {
|
||||||
|
//return navigator.mediaDevices.getUserMedia(({video: {mediaSource: 'screen'}} as any));
|
||||||
|
return new Promise((resolve, reject) => { // eslint-disable-line no-unused-vars
|
||||||
|
reject("error sharing screen");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//get camera
|
//get camera
|
||||||
async getCamera(): Promise<MediaStream> {
|
async getCamera(): Promise<MediaStream> {
|
||||||
if (navigator.mediaDevices === undefined) {
|
if (navigator.mediaDevices === undefined) {
|
||||||
|
@ -156,22 +156,11 @@ export class SimplePeer {
|
|||||||
videoActive = true;
|
videoActive = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if(microphoneActive){
|
|
||||||
mediaManager.enabledMicrophoneByUserId(user.userId);
|
|
||||||
}else{
|
|
||||||
mediaManager.disabledMicrophoneByUserId(user.userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(videoActive){
|
|
||||||
mediaManager.enabledVideoByUserId(user.userId);
|
|
||||||
}else{
|
|
||||||
mediaManager.disabledVideoByUserId(user.userId);
|
|
||||||
}
|
|
||||||
this.stream(user.userId, stream);
|
this.stream(user.userId, stream);
|
||||||
});
|
});
|
||||||
|
|
||||||
/*peer.on('track', (track: MediaStreamTrack, stream: MediaStream) => {
|
/*peer.on('track', (track: MediaStreamTrack, stream: MediaStream) => {
|
||||||
this.stream(user.userId, stream);
|
|
||||||
});*/
|
});*/
|
||||||
|
|
||||||
peer.on('close', () => {
|
peer.on('close', () => {
|
||||||
@ -190,9 +179,19 @@ export class SimplePeer {
|
|||||||
});
|
});
|
||||||
|
|
||||||
peer.on('data', (chunk: Buffer) => {
|
peer.on('data', (chunk: Buffer) => {
|
||||||
const data = JSON.parse(chunk.toString('utf8'));
|
let constraint = JSON.parse(chunk.toString('utf8'));
|
||||||
if(data.type === "stream"){
|
|
||||||
this.stream(user.userId, data.stream);
|
if (constraint.audio) {
|
||||||
|
mediaManager.enabledMicrophoneByUserId(user.userId);
|
||||||
|
} else {
|
||||||
|
mediaManager.disabledMicrophoneByUserId(user.userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (constraint.video) {
|
||||||
|
mediaManager.enabledVideoByUserId(user.userId);
|
||||||
|
} else {
|
||||||
|
this.stream(user.userId);
|
||||||
|
mediaManager.disabledVideoByUserId(user.userId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -279,7 +278,7 @@ export class SimplePeer {
|
|||||||
* @param userId
|
* @param userId
|
||||||
* @param stream
|
* @param stream
|
||||||
*/
|
*/
|
||||||
private stream(userId : string, stream: MediaStream) {
|
private stream(userId : string, stream?: MediaStream) {
|
||||||
if(!stream){
|
if(!stream){
|
||||||
mediaManager.disabledVideoByUserId(userId);
|
mediaManager.disabledVideoByUserId(userId);
|
||||||
mediaManager.disabledMicrophoneByUserId(userId);
|
mediaManager.disabledMicrophoneByUserId(userId);
|
||||||
@ -294,24 +293,21 @@ export class SimplePeer {
|
|||||||
*/
|
*/
|
||||||
private addMedia (userId : string) {
|
private addMedia (userId : string) {
|
||||||
try {
|
try {
|
||||||
const localStream: MediaStream|null = mediaManager.localStream;
|
let localStream: MediaStream | null = mediaManager.localStream;
|
||||||
const peer = this.PeerConnectionArray.get(userId);
|
let localScreenCapture: MediaStream | null = mediaManager.localScreenCapture;
|
||||||
if(localStream === null) {
|
let peer = this.PeerConnectionArray.get(userId);
|
||||||
//send fake signal
|
|
||||||
if(peer === undefined){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
peer.write(new Buffer(JSON.stringify({
|
|
||||||
type: "stream",
|
|
||||||
stream: null
|
|
||||||
})));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (peer === undefined) {
|
if (peer === undefined) {
|
||||||
throw new Error('While adding media, cannot find user with ID '+userId);
|
throw new Error('While adding media, cannot find user with ID ' + userId);
|
||||||
}
|
}
|
||||||
for (const track of localStream.getTracks()) {
|
peer.write(new Buffer(JSON.stringify(mediaManager.constraintsMedia)));
|
||||||
peer.addTrack(track, localStream);
|
if (localScreenCapture !== null) {
|
||||||
|
for (const track of localScreenCapture.getTracks()) {
|
||||||
|
peer.addTrack(track, localScreenCapture);
|
||||||
|
}
|
||||||
|
} else if (localStream) {
|
||||||
|
for (const track of localStream.getTracks()) {
|
||||||
|
peer.addTrack(track, localStream);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}catch (e) {
|
}catch (e) {
|
||||||
console.error(`addMedia => addMedia => ${userId}`, e);
|
console.error(`addMedia => addMedia => ${userId}`, e);
|
||||||
|
Loading…
Reference in New Issue
Block a user