2016-10-15 00:11:17 +02:00
|
|
|
/*
|
|
|
|
* options: name: XXX data: true (Maybe this is based on webrtc) audio: true,
|
|
|
|
* video: true, url: "file:///..." > Player screen: true > Desktop (implicit
|
|
|
|
* video:true, audio:false) audio: true, video: true > Webcam
|
|
|
|
*
|
|
|
|
* stream.hasAudio(); stream.hasVideo(); stream.hasData();
|
|
|
|
*/
|
2017-05-17 11:46:32 +02:00
|
|
|
import { Connection } from './Connection';
|
2017-05-10 10:55:31 +02:00
|
|
|
import { SessionInternal } from './SessionInternal';
|
|
|
|
import { OpenViduInternal, Callback } from './OpenViduInternal';
|
2016-10-16 23:16:58 +02:00
|
|
|
import EventEmitter = require('wolfy87-eventemitter');
|
2017-09-22 15:57:59 +02:00
|
|
|
import * as kurentoUtils from '../KurentoUtils/kurento-utils-js';
|
2016-10-16 23:16:58 +02:00
|
|
|
|
2017-03-23 13:15:45 +01:00
|
|
|
import * as adapter from 'webrtc-adapter';
|
2016-10-16 23:16:58 +02:00
|
|
|
declare var navigator: any;
|
2016-10-15 00:11:17 +02:00
|
|
|
declare var RTCSessionDescription: any;
|
|
|
|
|
2017-03-23 13:15:45 +01:00
|
|
|
if (window) {
|
|
|
|
window["adapter"] = adapter;
|
|
|
|
}
|
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
function jq(id: string): string {
|
2016-10-16 23:51:41 +02:00
|
|
|
return id.replace(/(@|:|\.|\[|\]|,)/g, "\\$1");
|
2016-10-16 23:16:58 +02:00
|
|
|
}
|
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
function show(id: string) {
|
2016-10-16 23:16:58 +02:00
|
|
|
document.getElementById(jq(id))!.style.display = 'block';
|
|
|
|
}
|
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
function hide(id: string) {
|
2016-10-16 23:16:58 +02:00
|
|
|
document.getElementById(jq(id))!.style.display = 'none';
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export interface StreamOptions {
|
|
|
|
id: string;
|
2017-05-22 22:30:08 +02:00
|
|
|
connection: Connection;
|
2017-09-29 20:00:00 +02:00
|
|
|
recvVideo: boolean;
|
|
|
|
recvAudio: boolean;
|
|
|
|
sendVideo: boolean;
|
|
|
|
sendAudio: boolean;
|
|
|
|
activeAudio: boolean;
|
|
|
|
activeVideo: boolean;
|
2016-10-15 00:11:17 +02:00
|
|
|
data: boolean;
|
2017-03-09 19:46:07 +01:00
|
|
|
mediaConstraints: any;
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export interface VideoOptions {
|
|
|
|
thumb: string;
|
|
|
|
video: HTMLVideoElement;
|
|
|
|
}
|
|
|
|
|
|
|
|
export class Stream {
|
|
|
|
|
2017-05-22 22:30:08 +02:00
|
|
|
public connection: Connection;
|
|
|
|
|
2016-10-15 00:11:17 +02:00
|
|
|
private ee = new EventEmitter();
|
|
|
|
private wrStream: any;
|
|
|
|
private wp: any;
|
|
|
|
private id: string;
|
|
|
|
private video: HTMLVideoElement;
|
|
|
|
private videoElements: VideoOptions[] = [];
|
|
|
|
private elements: HTMLDivElement[] = [];
|
|
|
|
private speechEvent: any;
|
2017-09-29 20:00:00 +02:00
|
|
|
private recvVideo: boolean;
|
|
|
|
private recvAudio: boolean;
|
2017-03-09 19:46:07 +01:00
|
|
|
private sendVideo: boolean;
|
|
|
|
private sendAudio: boolean;
|
|
|
|
private mediaConstraints: any;
|
2016-10-15 00:11:17 +02:00
|
|
|
private showMyRemote = false;
|
|
|
|
private localMirrored = false;
|
|
|
|
private chanId = 0;
|
|
|
|
private dataChannel: boolean;
|
|
|
|
private dataChannelOpened = false;
|
|
|
|
|
2017-09-29 20:00:00 +02:00
|
|
|
private activeAudio = true;
|
|
|
|
private activeVideo = true;
|
2017-08-21 20:12:13 +02:00
|
|
|
|
2017-09-22 15:57:59 +02:00
|
|
|
private videoSrcObject: MediaStream | null;
|
2017-04-17 16:23:30 +02:00
|
|
|
private parentId: string;
|
2017-04-12 00:54:35 +02:00
|
|
|
public isReady: boolean = false;
|
2017-07-19 17:56:06 +02:00
|
|
|
public isVideoELementCreated: boolean = false;
|
2017-05-31 17:31:26 +02:00
|
|
|
public accessIsAllowed: boolean = false;
|
|
|
|
public accessIsDenied: boolean = false;
|
2017-03-28 20:17:44 +02:00
|
|
|
|
2017-05-10 10:55:31 +02:00
|
|
|
constructor(private openVidu: OpenViduInternal, private local: boolean, private room: SessionInternal, options: StreamOptions) {
|
2016-10-15 00:11:17 +02:00
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
if (options.id) {
|
2016-10-15 00:11:17 +02:00
|
|
|
this.id = options.id;
|
|
|
|
} else {
|
|
|
|
this.id = "webcam";
|
|
|
|
}
|
|
|
|
|
2017-05-22 22:30:08 +02:00
|
|
|
this.connection = options.connection;
|
2017-09-29 20:00:00 +02:00
|
|
|
this.recvVideo = options.recvVideo || false;
|
|
|
|
this.recvAudio = options.recvAudio || false;
|
|
|
|
this.sendVideo = options.sendVideo;
|
|
|
|
this.sendAudio = options.sendAudio;
|
|
|
|
this.activeAudio = options.activeAudio;
|
|
|
|
this.activeVideo = options.activeVideo;
|
2016-10-15 00:11:17 +02:00
|
|
|
this.dataChannel = options.data || false;
|
2017-03-09 19:46:07 +01:00
|
|
|
this.mediaConstraints = options.mediaConstraints;
|
2017-03-28 20:17:44 +02:00
|
|
|
|
|
|
|
this.addEventListener('src-added', (srcEvent) => {
|
2017-09-22 15:57:59 +02:00
|
|
|
this.videoSrcObject = srcEvent.srcObject;
|
|
|
|
if (this.video) this.video.srcObject = srcEvent.srcObject;
|
|
|
|
console.debug("Video srcObject [" + srcEvent.srcObject + "] added to stream [" + this.getId() + "]");
|
2017-03-28 20:17:44 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
emitSrcEvent(wrstream) {
|
|
|
|
this.ee.emitEvent('src-added', [{
|
2017-09-22 15:57:59 +02:00
|
|
|
srcObject: wrstream
|
2017-03-28 20:17:44 +02:00
|
|
|
}]);
|
|
|
|
}
|
|
|
|
|
2017-04-17 16:23:30 +02:00
|
|
|
emitStreamReadyEvent() {
|
2017-04-12 00:54:35 +02:00
|
|
|
this.ee.emitEvent('stream-ready'), [{}];
|
|
|
|
}
|
|
|
|
|
2017-09-22 15:57:59 +02:00
|
|
|
getVideoSrcObject() {
|
|
|
|
return this.videoSrcObject;
|
2017-03-28 20:17:44 +02:00
|
|
|
}
|
|
|
|
|
2017-04-28 14:52:55 +02:00
|
|
|
removeVideo(parentElement: string);
|
|
|
|
removeVideo(parentElement: Element);
|
|
|
|
removeVideo();
|
|
|
|
|
2017-04-17 16:23:30 +02:00
|
|
|
removeVideo(parentElement?) {
|
2017-04-28 14:52:55 +02:00
|
|
|
if (typeof parentElement === "string") {
|
2017-04-17 16:23:30 +02:00
|
|
|
document.getElementById(parentElement)!.removeChild(this.video);
|
2017-04-28 14:52:55 +02:00
|
|
|
} else if (parentElement instanceof Element) {
|
|
|
|
parentElement.removeChild(this.video);
|
|
|
|
}
|
|
|
|
else if (!parentElement) {
|
2017-04-17 16:23:30 +02:00
|
|
|
if (document.getElementById(this.parentId)) {
|
|
|
|
document.getElementById(this.parentId)!.removeChild(this.video);
|
|
|
|
}
|
|
|
|
}
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
|
2017-04-28 14:52:55 +02:00
|
|
|
getVideoElement(): HTMLVideoElement {
|
|
|
|
return this.video;
|
|
|
|
}
|
|
|
|
|
|
|
|
setVideoElement(video: HTMLVideoElement) {
|
|
|
|
this.video = video;
|
|
|
|
}
|
2017-03-28 20:17:44 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-04-17 16:23:30 +02:00
|
|
|
|
|
|
|
|
2016-10-15 00:11:17 +02:00
|
|
|
getRecvVideo() {
|
|
|
|
return this.recvVideo;
|
|
|
|
}
|
|
|
|
|
|
|
|
getRecvAudio() {
|
|
|
|
return this.recvAudio;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
subscribeToMyRemote() {
|
|
|
|
this.showMyRemote = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
displayMyRemote() {
|
|
|
|
return this.showMyRemote;
|
|
|
|
}
|
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
mirrorLocalStream(wr) {
|
2016-10-15 00:11:17 +02:00
|
|
|
this.showMyRemote = true;
|
|
|
|
this.localMirrored = true;
|
2017-03-17 19:22:02 +01:00
|
|
|
if (wr) {
|
2016-10-15 00:11:17 +02:00
|
|
|
this.wrStream = wr;
|
2017-03-28 20:17:44 +02:00
|
|
|
this.emitSrcEvent(this.wrStream);
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
isLocalMirrored() {
|
|
|
|
return this.localMirrored;
|
|
|
|
}
|
|
|
|
|
|
|
|
getChannelName() {
|
2016-10-15 02:08:23 +02:00
|
|
|
return this.getId() + '_' + this.chanId++;
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
isDataChannelEnabled() {
|
|
|
|
return this.dataChannel;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
isDataChannelOpened() {
|
|
|
|
return this.dataChannelOpened;
|
|
|
|
}
|
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
onDataChannelOpen(event) {
|
2017-09-22 15:57:59 +02:00
|
|
|
console.debug('Data channel is opened');
|
2016-10-15 00:11:17 +02:00
|
|
|
this.dataChannelOpened = true;
|
|
|
|
}
|
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
onDataChannelClosed(event) {
|
2017-09-22 15:57:59 +02:00
|
|
|
console.debug('Data channel is closed');
|
2016-10-15 00:11:17 +02:00
|
|
|
this.dataChannelOpened = false;
|
|
|
|
}
|
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
sendData(data) {
|
|
|
|
if (this.wp === undefined) {
|
|
|
|
throw new Error('WebRTC peer has not been created yet');
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
2017-03-17 19:22:02 +01:00
|
|
|
if (!this.dataChannelOpened) {
|
|
|
|
throw new Error('Data channel is not opened');
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
2017-09-22 15:57:59 +02:00
|
|
|
console.info("Sending through data channel: " + data);
|
2017-03-17 19:22:02 +01:00
|
|
|
this.wp.send(data);
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
getWrStream() {
|
|
|
|
return this.wrStream;
|
|
|
|
}
|
|
|
|
|
|
|
|
getWebRtcPeer() {
|
|
|
|
return this.wp;
|
|
|
|
}
|
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
addEventListener(eventName: string, listener: any) {
|
|
|
|
this.ee.addListener(eventName, listener);
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
|
2017-04-28 14:52:55 +02:00
|
|
|
addOnceEventListener(eventName: string, listener: any) {
|
|
|
|
this.ee.addOnceListener(eventName, listener);
|
|
|
|
}
|
|
|
|
|
2017-06-26 10:15:58 +02:00
|
|
|
removeListener(eventName) {
|
2017-04-28 14:52:55 +02:00
|
|
|
this.ee.removeAllListeners(eventName);
|
|
|
|
}
|
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
showSpinner(spinnerParentId: string) {
|
|
|
|
let progress = document.createElement('div');
|
2016-10-15 02:08:23 +02:00
|
|
|
progress.id = 'progress-' + this.getId();
|
2016-10-15 00:11:17 +02:00
|
|
|
progress.style.background = "center transparent url('img/spinner.gif') no-repeat";
|
2017-03-17 19:22:02 +01:00
|
|
|
let spinnerParent = document.getElementById(spinnerParentId);
|
|
|
|
if (spinnerParent) {
|
|
|
|
spinnerParent.appendChild(progress);
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
hideSpinner(spinnerId?: string) {
|
|
|
|
spinnerId = (spinnerId === undefined) ? this.getId() : spinnerId;
|
|
|
|
hide('progress-' + spinnerId);
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
playOnlyVideo(parentElement, thumbnailId) {
|
2017-06-26 10:15:58 +02:00
|
|
|
|
|
|
|
// TO-DO: check somehow if the stream is audio only, so the element created is <audio> instead of <video>
|
2017-07-19 17:56:06 +02:00
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
this.video = document.createElement('video');
|
2016-10-15 00:11:17 +02:00
|
|
|
|
2017-09-22 15:57:59 +02:00
|
|
|
this.video.id = (this.local ? 'local-' : 'remote-') + 'video-' + this.getId();
|
2016-10-15 00:11:17 +02:00
|
|
|
this.video.autoplay = true;
|
|
|
|
this.video.controls = false;
|
2017-09-22 15:57:59 +02:00
|
|
|
this.video.srcObject = this.videoSrcObject;
|
2016-10-15 00:11:17 +02:00
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
this.videoElements.push({
|
2016-10-15 00:11:17 +02:00
|
|
|
thumb: thumbnailId,
|
|
|
|
video: this.video
|
|
|
|
});
|
|
|
|
|
2017-09-22 15:57:59 +02:00
|
|
|
if (this.local && !this.displayMyRemote()) {
|
2016-10-15 00:11:17 +02:00
|
|
|
this.video.muted = true;
|
2017-09-28 19:13:29 +02:00
|
|
|
this.video.onplaying = () => {
|
2017-09-22 15:57:59 +02:00
|
|
|
console.info("Local 'Stream' with id [" + this.getId() + "] video is now playing");
|
|
|
|
this.ee.emitEvent('video-is-playing', [{
|
|
|
|
element: this.video
|
|
|
|
}]);
|
|
|
|
};
|
2017-03-29 14:57:45 +02:00
|
|
|
} else {
|
|
|
|
this.video.title = this.getId();
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
if (typeof parentElement === "string") {
|
2017-04-17 16:23:30 +02:00
|
|
|
this.parentId = parentElement;
|
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
let parentElementDom = document.getElementById(parentElement);
|
|
|
|
if (parentElementDom) {
|
2017-03-28 20:17:44 +02:00
|
|
|
this.video = parentElementDom.appendChild(this.video);
|
2017-07-19 17:56:06 +02:00
|
|
|
this.ee.emitEvent('video-element-created-by-stream', [{
|
|
|
|
element: this.video
|
|
|
|
}]);
|
|
|
|
this.isVideoELementCreated = true;
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
} else {
|
2017-04-17 16:23:30 +02:00
|
|
|
this.parentId = parentElement.id;
|
2017-03-28 20:17:44 +02:00
|
|
|
this.video = parentElement.appendChild(this.video);
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
|
2017-05-31 17:31:26 +02:00
|
|
|
this.ee.emitEvent('stream-created-by-publisher');
|
|
|
|
|
2017-04-28 14:52:55 +02:00
|
|
|
this.isReady = true;
|
|
|
|
|
2016-10-15 00:11:17 +02:00
|
|
|
return this.video;
|
|
|
|
}
|
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
playThumbnail(thumbnailId) {
|
2016-10-15 00:11:17 +02:00
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
let container = document.createElement('div');
|
2016-10-15 00:11:17 +02:00
|
|
|
container.className = "participant";
|
2016-10-15 02:08:23 +02:00
|
|
|
container.id = this.getId();
|
2017-03-17 19:22:02 +01:00
|
|
|
let thumbnail = document.getElementById(thumbnailId);
|
|
|
|
if (thumbnail) {
|
|
|
|
thumbnail.appendChild(container);
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
this.elements.push(container);
|
2016-10-15 00:11:17 +02:00
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
let name = document.createElement('div');
|
|
|
|
container.appendChild(name);
|
|
|
|
let userName = this.getId().replace('_webcam', '');
|
|
|
|
if (userName.length >= 16) {
|
|
|
|
userName = userName.substring(0, 16) + "...";
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
2017-03-17 19:22:02 +01:00
|
|
|
name.appendChild(document.createTextNode(userName));
|
2016-10-15 02:08:23 +02:00
|
|
|
name.id = "name-" + this.getId();
|
2016-10-15 00:11:17 +02:00
|
|
|
name.className = "name";
|
2016-10-15 02:08:23 +02:00
|
|
|
name.title = this.getId();
|
2016-10-15 00:11:17 +02:00
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
this.showSpinner(thumbnailId);
|
2016-10-15 00:11:17 +02:00
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
return this.playOnlyVideo(container, thumbnailId);
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
|
2016-10-15 02:08:23 +02:00
|
|
|
getIdInParticipant() {
|
2016-10-15 00:11:17 +02:00
|
|
|
return this.id;
|
|
|
|
}
|
|
|
|
|
|
|
|
getParticipant() {
|
2017-05-22 22:30:08 +02:00
|
|
|
return this.connection;
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
|
2016-10-15 02:08:23 +02:00
|
|
|
getId() {
|
2017-09-22 15:57:59 +02:00
|
|
|
return this.connection.connectionId + "_" + this.id;
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
|
2017-03-23 13:15:45 +01:00
|
|
|
getRTCPeerConnection() {
|
2017-03-21 10:36:18 +01:00
|
|
|
return this.getWebRtcPeer().peerConnection;
|
|
|
|
}
|
|
|
|
|
2016-10-15 02:08:23 +02:00
|
|
|
requestCameraAccess(callback: Callback<Stream>) {
|
2016-10-15 00:11:17 +02:00
|
|
|
|
2017-05-22 22:30:08 +02:00
|
|
|
this.connection.addStream(this);
|
2016-10-15 00:11:17 +02:00
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
let constraints = this.mediaConstraints;
|
|
|
|
|
2017-06-26 10:15:58 +02:00
|
|
|
/*let constraints2 = {
|
2016-10-15 00:11:17 +02:00
|
|
|
audio: true,
|
|
|
|
video: {
|
|
|
|
width: {
|
|
|
|
ideal: 1280
|
|
|
|
},
|
|
|
|
frameRate: {
|
|
|
|
ideal: 15
|
|
|
|
}
|
|
|
|
}
|
2017-06-26 10:15:58 +02:00
|
|
|
};*/
|
|
|
|
|
|
|
|
this.userMediaHasVideo((hasVideo) => {
|
|
|
|
if (!hasVideo) {
|
|
|
|
constraints.video = false;
|
|
|
|
this.sendVideo = false;
|
|
|
|
this.requestCameraAccesAux(constraints, callback);
|
|
|
|
} else {
|
|
|
|
this.requestCameraAccesAux(constraints, callback);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2016-10-15 00:11:17 +02:00
|
|
|
|
2017-06-26 10:15:58 +02:00
|
|
|
private requestCameraAccesAux(constraints, callback) {
|
2017-04-02 23:09:04 +02:00
|
|
|
navigator.mediaDevices.getUserMedia(constraints)
|
2017-03-17 19:22:02 +01:00
|
|
|
.then(userStream => {
|
2017-08-22 12:09:44 +02:00
|
|
|
this.cameraAccessSuccess(userStream, callback);
|
|
|
|
})
|
|
|
|
.catch(error => {
|
2017-09-29 20:00:00 +02:00
|
|
|
/*// Try to ask for microphone only
|
2017-08-22 12:09:44 +02:00
|
|
|
navigator.mediaDevices.getUserMedia({ audio: true, video: false })
|
|
|
|
.then(userStream => {
|
|
|
|
constraints.video = false;
|
|
|
|
this.sendVideo = false;
|
2017-09-29 20:00:00 +02:00
|
|
|
this.sendAudio = true;
|
2017-08-22 12:09:44 +02:00
|
|
|
this.cameraAccessSuccess(userStream, callback);
|
|
|
|
})
|
2017-09-29 20:00:00 +02:00
|
|
|
.catch(error => {*/
|
|
|
|
this.accessIsDenied = true;
|
|
|
|
this.accessIsAllowed = false;
|
|
|
|
this.ee.emitEvent('access-denied-by-publisher');
|
|
|
|
|
|
|
|
console.error("Access denied", error);
|
|
|
|
callback(error, this);
|
2017-08-22 12:09:44 +02:00
|
|
|
});
|
|
|
|
}
|
2017-05-31 17:31:26 +02:00
|
|
|
|
2017-08-22 12:09:44 +02:00
|
|
|
private cameraAccessSuccess(userStream, callback) {
|
|
|
|
this.accessIsAllowed = true;
|
|
|
|
this.accessIsDenied = false;
|
|
|
|
this.ee.emitEvent('access-allowed-by-publisher');
|
2017-03-17 19:22:02 +01:00
|
|
|
|
2017-08-22 12:09:44 +02:00
|
|
|
if (userStream.getAudioTracks()[0] != null) {
|
2017-09-29 20:00:00 +02:00
|
|
|
userStream.getAudioTracks()[0].enabled = this.activeAudio;
|
2017-08-22 12:09:44 +02:00
|
|
|
}
|
|
|
|
if (userStream.getVideoTracks()[0] != null) {
|
2017-09-29 20:00:00 +02:00
|
|
|
userStream.getVideoTracks()[0].enabled = this.activeVideo;
|
2017-08-22 12:09:44 +02:00
|
|
|
}
|
2017-03-28 20:17:44 +02:00
|
|
|
|
2017-08-22 12:09:44 +02:00
|
|
|
this.wrStream = userStream;
|
|
|
|
this.emitSrcEvent(this.wrStream);
|
2017-05-31 17:31:26 +02:00
|
|
|
|
2017-08-22 12:09:44 +02:00
|
|
|
callback(undefined, this);
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
|
2017-06-26 10:15:58 +02:00
|
|
|
private userMediaHasVideo(callback) {
|
|
|
|
navigator.mediaDevices.enumerateDevices().then(function (mediaDevices) {
|
|
|
|
var videoInput = mediaDevices.filter(function (deviceInfo) {
|
|
|
|
return deviceInfo.kind === 'videoinput';
|
|
|
|
})[0];
|
|
|
|
callback(videoInput != null);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
publishVideoCallback(error, sdpOfferParam, wp) {
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
return console.error("(publish) SDP offer error: "
|
|
|
|
+ JSON.stringify(error));
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
|
2017-09-22 15:57:59 +02:00
|
|
|
console.debug("Sending SDP offer to publish as "
|
2017-03-17 19:22:02 +01:00
|
|
|
+ this.getId(), sdpOfferParam);
|
2016-10-15 00:11:17 +02:00
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
this.openVidu.sendRequest("publishVideo", {
|
2016-10-15 00:11:17 +02:00
|
|
|
sdpOffer: sdpOfferParam,
|
2017-08-21 20:12:13 +02:00
|
|
|
doLoopback: this.displayMyRemote() || false,
|
2017-09-29 20:00:00 +02:00
|
|
|
audioActive: this.sendAudio,
|
|
|
|
videoActive: this.sendVideo
|
2017-03-17 19:22:02 +01:00
|
|
|
}, (error, response) => {
|
|
|
|
if (error) {
|
|
|
|
console.error("Error on publishVideo: " + JSON.stringify(error));
|
2016-10-15 00:11:17 +02:00
|
|
|
} else {
|
2017-03-17 19:22:02 +01:00
|
|
|
this.processSdpAnswer(response.sdpAnswer);
|
2017-09-22 15:57:59 +02:00
|
|
|
console.info("'Publisher' succesfully published to session");
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
startVideoCallback(error, sdpOfferParam, wp) {
|
|
|
|
if (error) {
|
|
|
|
return console.error("(subscribe) SDP offer error: "
|
|
|
|
+ JSON.stringify(error));
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
2017-09-22 15:57:59 +02:00
|
|
|
console.debug("Sending SDP offer to subscribe to "
|
2017-03-17 19:22:02 +01:00
|
|
|
+ this.getId(), sdpOfferParam);
|
|
|
|
this.openVidu.sendRequest("receiveVideoFrom", {
|
2016-10-15 02:08:23 +02:00
|
|
|
sender: this.getId(),
|
2016-10-15 00:11:17 +02:00
|
|
|
sdpOffer: sdpOfferParam
|
2017-03-17 19:22:02 +01:00
|
|
|
}, (error, response) => {
|
|
|
|
if (error) {
|
|
|
|
console.error("Error on recvVideoFrom: " + JSON.stringify(error));
|
2016-10-15 00:11:17 +02:00
|
|
|
} else {
|
2017-03-17 19:22:02 +01:00
|
|
|
this.processSdpAnswer(response.sdpAnswer);
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
private initWebRtcPeer(sdpOfferCallback) {
|
|
|
|
if (this.local) {
|
2017-03-09 19:46:07 +01:00
|
|
|
|
|
|
|
let userMediaConstraints = {
|
2017-03-17 19:22:02 +01:00
|
|
|
audio: this.sendAudio,
|
|
|
|
video: this.sendVideo
|
2017-03-09 19:46:07 +01:00
|
|
|
}
|
2017-03-17 19:22:02 +01:00
|
|
|
|
2016-10-15 00:11:17 +02:00
|
|
|
let options: any = {
|
|
|
|
videoStream: this.wrStream,
|
2017-03-09 19:46:07 +01:00
|
|
|
mediaConstraints: userMediaConstraints,
|
2017-05-22 22:30:08 +02:00
|
|
|
onicecandidate: this.connection.sendIceCandidate.bind(this.connection),
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
2017-03-17 19:22:02 +01:00
|
|
|
|
|
|
|
if (this.dataChannel) {
|
2016-10-15 00:11:17 +02:00
|
|
|
options.dataChannelConfig = {
|
|
|
|
id: this.getChannelName(),
|
|
|
|
onopen: this.onDataChannelOpen,
|
|
|
|
onclose: this.onDataChannelClosed
|
|
|
|
};
|
|
|
|
options.dataChannels = true;
|
|
|
|
}
|
2017-03-17 19:22:02 +01:00
|
|
|
|
|
|
|
if (this.displayMyRemote()) {
|
2017-09-22 15:57:59 +02:00
|
|
|
this.wp = kurentoUtils.WebRtcPeer.WebRtcPeerSendrecv(options, error => {
|
2017-03-17 19:22:02 +01:00
|
|
|
if (error) {
|
|
|
|
return console.error(error);
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
2017-03-17 19:22:02 +01:00
|
|
|
this.wp.generateOffer(sdpOfferCallback.bind(this));
|
2016-10-15 00:11:17 +02:00
|
|
|
});
|
|
|
|
} else {
|
2017-09-22 15:57:59 +02:00
|
|
|
this.wp = kurentoUtils.WebRtcPeer.WebRtcPeerSendonly(options, error => {
|
2017-03-17 19:22:02 +01:00
|
|
|
if (error) {
|
|
|
|
return console.error(error);
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
2017-03-17 19:22:02 +01:00
|
|
|
this.wp.generateOffer(sdpOfferCallback.bind(this));
|
2016-10-15 00:11:17 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
let offerConstraints = {
|
2017-08-21 20:12:13 +02:00
|
|
|
audio: this.recvAudio,
|
2017-09-29 20:00:00 +02:00
|
|
|
video: this.recvVideo
|
2016-10-15 00:11:17 +02:00
|
|
|
};
|
2017-09-22 15:57:59 +02:00
|
|
|
console.debug("'Session.subscribe(Stream)' called. Constraints of generate SDP offer",
|
2017-03-17 19:22:02 +01:00
|
|
|
offerConstraints);
|
2016-10-15 00:11:17 +02:00
|
|
|
let options = {
|
2017-05-22 22:30:08 +02:00
|
|
|
onicecandidate: this.connection.sendIceCandidate.bind(this.connection),
|
2017-08-21 20:12:13 +02:00
|
|
|
mediaConstraints: offerConstraints
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
2017-09-22 15:57:59 +02:00
|
|
|
this.wp = kurentoUtils.WebRtcPeer.WebRtcPeerRecvonly(options, error => {
|
2017-03-17 19:22:02 +01:00
|
|
|
if (error) {
|
|
|
|
return console.error(error);
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
2017-03-17 19:22:02 +01:00
|
|
|
this.wp.generateOffer(sdpOfferCallback.bind(this));
|
2016-10-15 00:11:17 +02:00
|
|
|
});
|
|
|
|
}
|
2017-09-22 15:57:59 +02:00
|
|
|
console.debug("Waiting for SDP offer to be generated ("
|
|
|
|
+ (this.local ? "local" : "remote") + " 'Stream': " + this.getId() + ")");
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
publish() {
|
|
|
|
|
|
|
|
// FIXME: Throw error when stream is not local
|
2017-04-12 00:54:35 +02:00
|
|
|
if (this.isReady) {
|
|
|
|
this.initWebRtcPeer(this.publishVideoCallback);
|
|
|
|
} else {
|
2017-04-12 11:45:08 +02:00
|
|
|
this.ee.once('stream-ready', streamEvent => {
|
2017-04-12 00:54:35 +02:00
|
|
|
this.publish();
|
|
|
|
});
|
|
|
|
}
|
2016-10-15 00:11:17 +02:00
|
|
|
|
|
|
|
// FIXME: Now we have coupled connecting to a room and adding a
|
|
|
|
// stream to this room. But in the new API, there are two steps.
|
|
|
|
// This is the second step. For now, it do nothing.
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
subscribe() {
|
|
|
|
|
|
|
|
// FIXME: In the current implementation all participants are subscribed
|
|
|
|
// automatically to all other participants. We use this method only to
|
|
|
|
// negotiate SDP
|
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
this.initWebRtcPeer(this.startVideoCallback);
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
processSdpAnswer(sdpAnswer) {
|
2016-10-15 00:11:17 +02:00
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
let answer = new RTCSessionDescription({
|
2016-10-15 00:11:17 +02:00
|
|
|
type: 'answer',
|
|
|
|
sdp: sdpAnswer,
|
|
|
|
});
|
2017-09-22 15:57:59 +02:00
|
|
|
console.debug(this.getId() + ": set peer connection with recvd SDP answer",
|
2017-03-17 19:22:02 +01:00
|
|
|
sdpAnswer);
|
2016-10-15 02:08:23 +02:00
|
|
|
let participantId = this.getId();
|
2016-10-15 00:11:17 +02:00
|
|
|
let pc = this.wp.peerConnection;
|
2017-03-17 19:22:02 +01:00
|
|
|
pc.setRemoteDescription(answer, () => {
|
2016-10-15 00:11:17 +02:00
|
|
|
// Avoids to subscribe to your own stream remotely
|
|
|
|
// except when showMyRemote is true
|
2017-03-17 19:22:02 +01:00
|
|
|
if (!this.local || this.displayMyRemote()) {
|
2016-10-15 00:11:17 +02:00
|
|
|
this.wrStream = pc.getRemoteStreams()[0];
|
2017-09-22 15:57:59 +02:00
|
|
|
console.debug("Peer remote stream", this.wrStream);
|
2017-03-17 19:22:02 +01:00
|
|
|
|
|
|
|
if (this.wrStream != undefined) {
|
|
|
|
|
2017-03-28 20:17:44 +02:00
|
|
|
this.emitSrcEvent(this.wrStream);
|
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
this.speechEvent = kurentoUtils.WebRtcPeer.hark(this.wrStream, { threshold: this.room.thresholdSpeaker });
|
|
|
|
|
|
|
|
this.speechEvent.on('speaking', () => {
|
|
|
|
this.room.addParticipantSpeaking(participantId);
|
|
|
|
this.room.emitEvent('stream-speaking', [{
|
2016-10-15 00:11:17 +02:00
|
|
|
participantId: participantId
|
2017-03-17 19:22:02 +01:00
|
|
|
}]);
|
2016-10-15 00:11:17 +02:00
|
|
|
});
|
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
this.speechEvent.on('stopped_speaking', () => {
|
|
|
|
this.room.removeParticipantSpeaking(participantId);
|
|
|
|
this.room.emitEvent('stream-stopped-speaking', [{
|
2016-10-15 00:11:17 +02:00
|
|
|
participantId: participantId
|
2017-03-17 19:22:02 +01:00
|
|
|
}]);
|
2016-10-15 00:11:17 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
for (let videoElement of this.videoElements) {
|
|
|
|
let thumbnailId = videoElement.thumb;
|
|
|
|
let video = videoElement.video;
|
2017-09-22 15:57:59 +02:00
|
|
|
video.srcObject = this.wrStream;
|
2017-09-28 19:13:29 +02:00
|
|
|
video.onplaying = () => {
|
2017-09-22 15:57:59 +02:00
|
|
|
if (this.local && this.displayMyRemote()) {
|
|
|
|
console.info("Your own remote 'Stream' with id [" + this.getId() + "] video is now playing");
|
|
|
|
this.ee.emitEvent('remote-video-is-playing', [{
|
|
|
|
element: this.video
|
|
|
|
}]);
|
|
|
|
} else if (!this.local && !this.displayMyRemote()) {
|
|
|
|
console.info("Remote 'Stream' with id [" + this.getId() + "] video is now playing");
|
|
|
|
this.ee.emitEvent('video-is-playing', [{
|
|
|
|
element: this.video
|
|
|
|
}]);
|
|
|
|
}
|
2017-03-28 20:17:44 +02:00
|
|
|
//show(thumbnailId);
|
|
|
|
//this.hideSpinner(this.getId());
|
2016-10-15 00:11:17 +02:00
|
|
|
};
|
|
|
|
}
|
2017-03-17 19:22:02 +01:00
|
|
|
this.room.emitEvent('stream-subscribed', [{
|
2016-10-15 00:11:17 +02:00
|
|
|
stream: this
|
2017-03-17 19:22:02 +01:00
|
|
|
}]);
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
}, error => {
|
2017-03-17 19:22:02 +01:00
|
|
|
console.error(this.getId() + ": Error setting SDP to the peer connection: "
|
|
|
|
+ JSON.stringify(error));
|
2016-10-15 00:11:17 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
unpublish() {
|
2017-03-17 19:22:02 +01:00
|
|
|
if (this.wp) {
|
2016-10-15 00:11:17 +02:00
|
|
|
this.wp.dispose();
|
|
|
|
} else {
|
2017-03-17 19:22:02 +01:00
|
|
|
if (this.wrStream) {
|
|
|
|
this.wrStream.getAudioTracks().forEach(function (track) {
|
2016-10-15 00:11:17 +02:00
|
|
|
track.stop && track.stop()
|
|
|
|
})
|
2017-03-17 19:22:02 +01:00
|
|
|
this.wrStream.getVideoTracks().forEach(function (track) {
|
2016-10-15 00:11:17 +02:00
|
|
|
track.stop && track.stop()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
if (this.speechEvent) {
|
2016-10-15 00:11:17 +02:00
|
|
|
this.speechEvent.stop();
|
|
|
|
}
|
|
|
|
|
2017-09-22 15:57:59 +02:00
|
|
|
console.info(this.getId() + ": Stream '" + this.id + "' unpublished");
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
dispose() {
|
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
function disposeElement(element) {
|
|
|
|
if (element && element.parentNode) {
|
|
|
|
element.parentNode.removeChild(element);
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
this.elements.forEach(e => disposeElement(e));
|
2016-10-15 00:11:17 +02:00
|
|
|
|
2017-08-29 17:31:34 +02:00
|
|
|
//this.videoElements.forEach(ve => disposeElement(ve.video));
|
2016-10-15 00:11:17 +02:00
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
disposeElement("progress-" + this.getId());
|
2016-10-15 00:11:17 +02:00
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
if (this.wp) {
|
2016-10-15 00:11:17 +02:00
|
|
|
this.wp.dispose();
|
|
|
|
} else {
|
2017-03-17 19:22:02 +01:00
|
|
|
if (this.wrStream) {
|
|
|
|
this.wrStream.getAudioTracks().forEach(function (track) {
|
2016-10-15 00:11:17 +02:00
|
|
|
track.stop && track.stop()
|
|
|
|
})
|
2017-03-17 19:22:02 +01:00
|
|
|
this.wrStream.getVideoTracks().forEach(function (track) {
|
2016-10-15 00:11:17 +02:00
|
|
|
track.stop && track.stop()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-17 19:22:02 +01:00
|
|
|
if (this.speechEvent) {
|
2016-10-15 00:11:17 +02:00
|
|
|
this.speechEvent.stop();
|
|
|
|
}
|
|
|
|
|
2017-09-22 15:57:59 +02:00
|
|
|
console.info((this.local ? "Local " : "Remote ") + "'Stream' with id [" + this.getId() + "]' has been succesfully disposed");
|
2016-10-15 00:11:17 +02:00
|
|
|
}
|
|
|
|
}
|