mirror of https://github.com/OpenVidu/openvidu.git
Merge branch 'master' of https://github.com/OpenVidu/openvidu
commit
3e777a9fbc
|
@ -202,6 +202,10 @@ export class LocalRecorder {
|
|||
this.videoPreview.id = this.id;
|
||||
this.videoPreview.autoplay = true;
|
||||
|
||||
if (platform.name === 'Safari' && platform.product === 'iPhone') {
|
||||
this.videoPreview.setAttribute('playsinline', 'true');
|
||||
}
|
||||
|
||||
if (typeof parentElement === 'string') {
|
||||
this.htmlParentElementId = parentElement;
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ export class OpenVidu {
|
|||
constructor() {
|
||||
console.info("'OpenVidu' initialized");
|
||||
|
||||
if (platform.name!!.toLowerCase().indexOf('mobile') !== -1) {
|
||||
if (platform.os!!.family === 'iOS' || platform.os!!.family === 'Android') {
|
||||
// Listen to orientationchange only on mobile browsers
|
||||
(<any>window).onorientationchange = () => {
|
||||
this.publishers.forEach(publisher => {
|
||||
|
@ -276,12 +276,23 @@ export class OpenVidu {
|
|||
*/
|
||||
checkSystemRequirements(): number {
|
||||
const browser = platform.name;
|
||||
const version = platform.version;
|
||||
const family = platform.os!!.family;
|
||||
const userAgent = !!platform.ua ? platform.ua : navigator.userAgent;
|
||||
|
||||
if ((browser !== 'Chrome') && (browser !== 'Chrome Mobile') &&
|
||||
(browser !== 'Firefox') && (browser !== 'Firefox Mobile') && (browser !== 'Firefox for iOS') &&
|
||||
// Reject iPhones and iPads if not Safari ('Safari' also covers Ionic for iOS)
|
||||
if (family === 'iOS' && (browser !== 'Safari' || userAgent.indexOf('CriOS') !== -1 || userAgent.indexOf('FxiOS') !== -1)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Accept: Chrome (desktop and Android), Firefox (desktop and Android), Opera (desktop and Android),
|
||||
// Safari (OSX and iOS), Ionic (Android and iOS)
|
||||
if (
|
||||
(browser !== 'Safari') &&
|
||||
(browser !== 'Chrome') && (browser !== 'Chrome Mobile') &&
|
||||
(browser !== 'Firefox') && (browser !== 'Firefox Mobile') &&
|
||||
(browser !== 'Opera') && (browser !== 'Opera Mobile') &&
|
||||
(browser !== 'Safari') && (browser !== 'Android Browser')) {
|
||||
(browser !== 'Android Browser')
|
||||
) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
|
@ -290,11 +301,18 @@ export class OpenVidu {
|
|||
|
||||
|
||||
/**
|
||||
* Checks if the browser supports screen-sharing. Chrome, Firefox and Opera support screen-sharing
|
||||
* Checks if the browser supports screen-sharing. Desktop Chrome, Firefox and Opera support screen-sharing
|
||||
* @returns 1 if the browser supports screen-sharing, 0 otherwise
|
||||
*/
|
||||
checkScreenSharingCapabilities(): number {
|
||||
const browser = platform.name;
|
||||
const family = platform.os!!.family;
|
||||
|
||||
// Reject mobile devices
|
||||
if (family === 'iOS' || family === 'Android' || family === 'Windows Phone') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((browser !== 'Chrome') && (browser !== 'Firefox') && (browser !== 'Opera')) {
|
||||
return 0;
|
||||
} else {
|
||||
|
@ -484,7 +502,7 @@ export class OpenVidu {
|
|||
(platform.name!.indexOf('Firefox') !== -1 && publisherProperties.videoSource === 'window')) {
|
||||
|
||||
if (platform.name !== 'Chrome' && platform.name!.indexOf('Firefox') === -1 && platform.name !== 'Opera') {
|
||||
const error = new OpenViduError(OpenViduErrorName.SCREEN_SHARING_NOT_SUPPORTED, 'You can only screen share in desktop Chrome and Firefox. Detected browser: ' + platform.name);
|
||||
const error = new OpenViduError(OpenViduErrorName.SCREEN_SHARING_NOT_SUPPORTED, 'You can only screen share in desktop Chrome, Firefox or Opera. Detected browser: ' + platform.name);
|
||||
console.error(error);
|
||||
reject(error);
|
||||
} else {
|
||||
|
|
|
@ -292,6 +292,11 @@ export class Publisher extends StreamManager {
|
|||
}
|
||||
|
||||
this.videoReference = document.createElement('video');
|
||||
|
||||
if (platform.name === 'Safari' && platform.product === 'iPhone') {
|
||||
this.videoReference.setAttribute('playsinline', 'true');
|
||||
}
|
||||
|
||||
this.videoReference.srcObject = mediaStream;
|
||||
|
||||
this.stream.setMediaStream(mediaStream);
|
||||
|
@ -337,8 +342,8 @@ export class Publisher extends StreamManager {
|
|||
};
|
||||
this.screenShareResizeInterval = setInterval(() => {
|
||||
const firefoxSettings = mediaStream.getVideoTracks()[0].getSettings();
|
||||
const newWidth = (platform.name === 'Chrome') ? this.videoReference.videoWidth : firefoxSettings.width;
|
||||
const newHeight = (platform.name === 'Chrome') ? this.videoReference.videoHeight : firefoxSettings.height;
|
||||
const newWidth = (platform.name === 'Chrome' || platform.name === 'Opera') ? this.videoReference.videoWidth : firefoxSettings.width;
|
||||
const newHeight = (platform.name === 'Chrome' || platform.name === 'Opera') ? this.videoReference.videoHeight : firefoxSettings.height;
|
||||
if (this.stream.isLocalStreamPublished &&
|
||||
(newWidth !== this.stream.videoDimensions.width ||
|
||||
newHeight !== this.stream.videoDimensions.height)) {
|
||||
|
|
|
@ -152,7 +152,7 @@ export class Session implements EventDispatcher {
|
|||
reject(error);
|
||||
});
|
||||
} else {
|
||||
reject(new OpenViduError(OpenViduErrorName.BROWSER_NOT_SUPPORTED, 'Browser ' + platform.name + ' ' + platform.version + ' is not supported in OpenVidu'));
|
||||
reject(new OpenViduError(OpenViduErrorName.BROWSER_NOT_SUPPORTED, 'Browser ' + platform.name + ' for ' + platform.os!!.family + ' is not supported in OpenVidu'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -112,6 +112,9 @@ export class StreamManager implements EventDispatcher {
|
|||
video: document.createElement('video'),
|
||||
id: ''
|
||||
};
|
||||
if (platform.name === 'Safari' && platform.product === 'iPhone') {
|
||||
this.firstVideoElement.video.setAttribute('playsinline', 'true');
|
||||
}
|
||||
this.targetElement = targEl;
|
||||
this.element = targEl;
|
||||
}
|
||||
|
@ -329,6 +332,11 @@ export class StreamManager implements EventDispatcher {
|
|||
}
|
||||
video.autoplay = true;
|
||||
video.controls = false;
|
||||
|
||||
if (platform.name === 'Safari' && platform.product === 'iPhone') {
|
||||
video.setAttribute('playsinline', 'true');
|
||||
}
|
||||
|
||||
if (!video.id) {
|
||||
video.id = (this.remote ? 'remote-' : 'local-') + 'video-' + this.stream.streamId;
|
||||
// DEPRECATED property: assign once the property id if the user provided a valid targetElement
|
||||
|
|
|
@ -29,7 +29,7 @@ export interface WebRtcPeerConfiguration {
|
|||
onicecandidate: (event) => void;
|
||||
iceServers: RTCIceServer[] | undefined;
|
||||
mediaStream?: MediaStream;
|
||||
mode?: string; // sendonly, reconly, sendrecv
|
||||
mode?: 'sendonly' | 'recvonly' | 'sendrecv';
|
||||
id?: string;
|
||||
}
|
||||
|
||||
|
@ -156,18 +156,52 @@ export class WebRtcPeer {
|
|||
|
||||
console.debug('RTCPeerConnection constraints: ' + JSON.stringify(constraints));
|
||||
|
||||
this.pc.createOffer(constraints).then(offer => {
|
||||
console.debug('Created SDP offer');
|
||||
return this.pc.setLocalDescription(offer);
|
||||
}).then(() => {
|
||||
const localDescription = this.pc.localDescription;
|
||||
if (!!localDescription) {
|
||||
console.debug('Local description set', localDescription.sdp);
|
||||
resolve(<string>localDescription.sdp);
|
||||
} else {
|
||||
reject('Local description is not defined');
|
||||
if (platform.name === 'Safari') {
|
||||
// Safari, at least on iOS just seems to support unified plan, whereas in other browsers is not yet ready and considered experimental
|
||||
if (offerAudio) {
|
||||
this.pc.addTransceiver('audio', {
|
||||
direction: this.configuration.mode,
|
||||
});
|
||||
}
|
||||
}).catch(error => reject(error));
|
||||
|
||||
if (offerVideo) {
|
||||
this.pc.addTransceiver('video', {
|
||||
direction: this.configuration.mode,
|
||||
});
|
||||
}
|
||||
|
||||
this.pc
|
||||
.createOffer()
|
||||
.then(offer => {
|
||||
console.debug('Created SDP offer');
|
||||
return this.pc.setLocalDescription(offer);
|
||||
})
|
||||
.then(() => {
|
||||
const localDescription = this.pc.localDescription;
|
||||
|
||||
if (!!localDescription) {
|
||||
console.debug('Local description set', localDescription.sdp);
|
||||
resolve(localDescription.sdp);
|
||||
} else {
|
||||
reject('Local description is not defined');
|
||||
}
|
||||
})
|
||||
.catch(error => reject(error));
|
||||
} else {
|
||||
this.pc.createOffer(constraints).then(offer => {
|
||||
console.debug('Created SDP offer');
|
||||
return this.pc.setLocalDescription(offer);
|
||||
}).then(() => {
|
||||
const localDescription = this.pc.localDescription;
|
||||
if (!!localDescription) {
|
||||
console.debug('Local description set', localDescription.sdp);
|
||||
resolve(localDescription.sdp);
|
||||
} else {
|
||||
reject('Local description is not defined');
|
||||
}
|
||||
})
|
||||
.catch(error => reject(error));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -451,7 +451,7 @@ public abstract class SessionManager {
|
|||
this.closeSessionAndEmptyCollections(session, reason);
|
||||
|
||||
if (recordingService.sessionIsBeingRecorded(session.getSessionId())) {
|
||||
recordingService.stopRecording(session, reason);
|
||||
recordingService.stopRecording(session, null, reason);
|
||||
}
|
||||
|
||||
return participants;
|
||||
|
|
|
@ -175,14 +175,21 @@ public class KurentoSessionManager extends SessionManager {
|
|||
showTokens();
|
||||
} else if (remainingParticipants.size() == 1 && openviduConfig.isRecordingModuleEnabled()
|
||||
&& MediaMode.ROUTED.equals(session.getSessionProperties().mediaMode())
|
||||
&& RecordingMode.ALWAYS.equals(session.getSessionProperties().recordingMode())
|
||||
&& ProtocolElements.RECORDER_PARTICIPANT_PUBLICID
|
||||
.equals(remainingParticipants.iterator().next().getParticipantPublicId())) {
|
||||
|
||||
log.info("Last participant left. Stopping recording for session {}", sessionId);
|
||||
recordingService.stopRecording(session, reason);
|
||||
evictParticipant(session.getParticipantByPublicId(ProtocolElements.RECORDER_PARTICIPANT_PUBLICID), null,
|
||||
null, "EVICT_RECORDER");
|
||||
if (RecordingMode.ALWAYS.equals(session.getSessionProperties().recordingMode())) {
|
||||
// Immediately stop recording when last real participant left if
|
||||
// RecordingMode.ALWAYS
|
||||
log.info("Last participant left. Stopping recording for session {}", sessionId);
|
||||
recordingService.stopRecording(session, null, reason);
|
||||
evictParticipant(session.getParticipantByPublicId(ProtocolElements.RECORDER_PARTICIPANT_PUBLICID), null,
|
||||
null, "EVICT_RECORDER");
|
||||
} else if (RecordingMode.MANUAL.equals(session.getSessionProperties().recordingMode())) {
|
||||
// Start countdown to stop recording if RecordingMode.MANUAL (will be aborted if
|
||||
// a Publisher starts before timeout)
|
||||
log.info("Last participant left. Starting countdown for stopping recording of session {}", sessionId);
|
||||
recordingService.initAutomaticRecordingStopThread(session.getSessionId());
|
||||
}
|
||||
}
|
||||
|
||||
// Finally close websocket session if required
|
||||
|
@ -206,14 +213,10 @@ public class KurentoSessionManager extends SessionManager {
|
|||
* the peer's request by sending it the SDP response (answer or updated offer)
|
||||
* generated by the WebRTC endpoint on the server.
|
||||
*
|
||||
* @param participant
|
||||
* Participant publishing video
|
||||
* @param MediaOptions
|
||||
* configuration of the stream to publish
|
||||
* @param transactionId
|
||||
* identifier of the Transaction
|
||||
* @throws OpenViduException
|
||||
* on error
|
||||
* @param participant Participant publishing video
|
||||
* @param MediaOptions configuration of the stream to publish
|
||||
* @param transactionId identifier of the Transaction
|
||||
* @throws OpenViduException on error
|
||||
*/
|
||||
@Override
|
||||
public void publishVideo(Participant participant, MediaOptions mediaOptions, Integer transactionId)
|
||||
|
@ -272,16 +275,29 @@ public class KurentoSessionManager extends SessionManager {
|
|||
|
||||
if (this.openviduConfig.isRecordingModuleEnabled()
|
||||
&& MediaMode.ROUTED.equals(session.getSessionProperties().mediaMode())
|
||||
&& RecordingMode.ALWAYS.equals(session.getSessionProperties().recordingMode())
|
||||
&& !recordingService.sessionIsBeingRecorded(session.getSessionId())
|
||||
&& session.getActivePublishers() == 0) {
|
||||
// Insecure session recording
|
||||
new Thread(() -> {
|
||||
recordingService.startRecording(session,
|
||||
new RecordingProperties.Builder().name("")
|
||||
.recordingLayout(session.getSessionProperties().defaultRecordingLayout())
|
||||
.customLayout(session.getSessionProperties().defaultCustomLayout()).build());
|
||||
}).start();
|
||||
if (RecordingMode.ALWAYS.equals(session.getSessionProperties().recordingMode())
|
||||
&& !recordingService.sessionIsBeingRecorded(session.getSessionId())) {
|
||||
// Insecure session recording
|
||||
new Thread(() -> {
|
||||
recordingService.startRecording(session,
|
||||
new RecordingProperties.Builder().name("")
|
||||
.recordingLayout(session.getSessionProperties().defaultRecordingLayout())
|
||||
.customLayout(session.getSessionProperties().defaultCustomLayout()).build());
|
||||
}).start();
|
||||
} else if (RecordingMode.MANUAL.equals(session.getSessionProperties().recordingMode())
|
||||
&& recordingService.sessionIsBeingRecorded(session.getSessionId())) {
|
||||
// Abort automatic recording stop (user published before timeout)
|
||||
log.info("Participant {} published before timeout finished. Aborting automatic recording stop",
|
||||
participant.getParticipantPublicId());
|
||||
boolean stopAborted = recordingService.abortAutomaticRecordingStopThread(session.getSessionId());
|
||||
if (stopAborted) {
|
||||
log.info("Automatic recording stopped succesfully aborted");
|
||||
} else {
|
||||
log.info("Automatic recording stopped couldn't be aborted. Recording of session {} has stopped",
|
||||
session.getSessionId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
session.newPublisher(participant);
|
||||
|
@ -458,12 +474,10 @@ public class KurentoSessionManager extends SessionManager {
|
|||
* Creates a session if it doesn't already exist. The session's id will be
|
||||
* indicated by the session info bean.
|
||||
*
|
||||
* @param kcSessionInfo
|
||||
* bean that will be passed to the {@link KurentoClientProvider} in
|
||||
* order to obtain the {@link KurentoClient} that will be used by the
|
||||
* room
|
||||
* @throws OpenViduException
|
||||
* in case of error while creating the session
|
||||
* @param kcSessionInfo bean that will be passed to the
|
||||
* {@link KurentoClientProvider} in order to obtain the
|
||||
* {@link KurentoClient} that will be used by the room
|
||||
* @throws OpenViduException in case of error while creating the session
|
||||
*/
|
||||
public void createSession(KurentoClientSessionInfo kcSessionInfo, SessionProperties sessionProperties)
|
||||
throws OpenViduException {
|
||||
|
|
|
@ -31,6 +31,8 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -87,6 +89,10 @@ public class ComposedRecordingService {
|
|||
private Map<String, Recording> startingRecordings = new ConcurrentHashMap<>();
|
||||
private Map<String, Recording> startedRecordings = new ConcurrentHashMap<>();
|
||||
private Map<String, Recording> sessionsRecordings = new ConcurrentHashMap<>();
|
||||
private final Map<String, ScheduledFuture<?>> automaticRecordingStopThreads = new ConcurrentHashMap<>();
|
||||
|
||||
private ScheduledThreadPoolExecutor automaticRecordingStopExecutor = new ScheduledThreadPoolExecutor(
|
||||
Runtime.getRuntime().availableProcessors());
|
||||
|
||||
private final String IMAGE_NAME = "openvidu/openvidu-recording";
|
||||
private String IMAGE_TAG;
|
||||
|
@ -155,10 +161,24 @@ public class ComposedRecordingService {
|
|||
return recording;
|
||||
}
|
||||
|
||||
public Recording stopRecording(Session session, String reason) {
|
||||
Recording recording = this.sessionsRecordings.remove(session.getSessionId());
|
||||
String containerId = this.sessionsContainers.remove(session.getSessionId());
|
||||
this.startedRecordings.remove(recording.getId());
|
||||
public Recording stopRecording(Session session, String recordingId, String reason) {
|
||||
Recording recording;
|
||||
String containerId;
|
||||
|
||||
if (session == null) {
|
||||
log.warn(
|
||||
"Existing recording {} does not have an active session associated. This usually means the recording"
|
||||
+ " layout did not join a recorded participant or the recording has been automatically"
|
||||
+ " stopped after last user left and timeout passed",
|
||||
recordingId);
|
||||
recording = this.startedRecordings.remove(recordingId);
|
||||
containerId = this.sessionsContainers.remove(recording.getSessionId());
|
||||
this.sessionsRecordings.remove(recording.getSessionId());
|
||||
} else {
|
||||
recording = this.sessionsRecordings.remove(session.getSessionId());
|
||||
containerId = this.sessionsContainers.remove(session.getSessionId());
|
||||
this.startedRecordings.remove(recording.getId());
|
||||
}
|
||||
|
||||
if (containerId == null) {
|
||||
|
||||
|
@ -250,7 +270,9 @@ public class ComposedRecordingService {
|
|||
throw new OpenViduException(Code.RECORDING_REPORT_ERROR_CODE,
|
||||
"There was an error generating the metadata report file for the recording");
|
||||
}
|
||||
this.sessionHandler.sendRecordingStoppedNotification(session, recording, reason);
|
||||
if (session != null) {
|
||||
this.sessionHandler.sendRecordingStoppedNotification(session, recording, reason);
|
||||
}
|
||||
}
|
||||
return recording;
|
||||
}
|
||||
|
@ -465,8 +487,7 @@ public class ComposedRecordingService {
|
|||
private boolean isFileFromRecording(File file, String recordingId, String recordingName) {
|
||||
return (((recordingId + ".info").equals(file.getName()))
|
||||
|| ((RECORDING_ENTITY_FILE + recordingId).equals(file.getName()))
|
||||
|| (recordingName + ".mp4").equals(file.getName())
|
||||
|| (recordingId + ".jpg").equals(file.getName()));
|
||||
|| (recordingName + ".mp4").equals(file.getName()) || (recordingId + ".jpg").equals(file.getName()));
|
||||
}
|
||||
|
||||
private String getFreeRecordingId(String sessionId, String shortSessionId) {
|
||||
|
@ -528,4 +549,19 @@ public class ComposedRecordingService {
|
|||
this.IMAGE_TAG = version;
|
||||
}
|
||||
|
||||
public void initAutomaticRecordingStopThread(String sessionId) {
|
||||
final String recordingId = this.sessionsRecordings.get(sessionId).getId();
|
||||
ScheduledFuture<?> future = this.automaticRecordingStopExecutor.schedule(() -> {
|
||||
log.info("Stopping recording {} after 2 minutes wait (no publisher published before timeout)", recordingId);
|
||||
this.stopRecording(null, recordingId, "lastParticipantLeft");
|
||||
this.automaticRecordingStopThreads.remove(sessionId);
|
||||
}, 2, TimeUnit.MINUTES);
|
||||
this.automaticRecordingStopThreads.putIfAbsent(sessionId, future);
|
||||
}
|
||||
|
||||
public boolean abortAutomaticRecordingStopThread(String sessionId) {
|
||||
ScheduledFuture<?> future = this.automaticRecordingStopThreads.remove(sessionId);
|
||||
return future.cancel(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -336,7 +336,9 @@ public class SessionRestController {
|
|||
recordingLayout = RecordingLayout.valueOf(recordingLayoutString);
|
||||
}
|
||||
|
||||
customLayout = (customLayout == null) ? session.getSessionProperties().defaultCustomLayout() : customLayout;
|
||||
customLayout = (customLayout == null || customLayout.isEmpty())
|
||||
? session.getSessionProperties().defaultCustomLayout()
|
||||
: customLayout;
|
||||
|
||||
Recording startedRecording = this.recordingService.startRecording(session, new RecordingProperties.Builder()
|
||||
.name(name).recordingLayout(recordingLayout).customLayout(customLayout).build());
|
||||
|
@ -368,11 +370,14 @@ public class SessionRestController {
|
|||
|
||||
Session session = sessionManager.getSession(recording.getSessionId());
|
||||
|
||||
Recording stoppedRecording = this.recordingService.stopRecording(session, "recordingStoppedByServer");
|
||||
Recording stoppedRecording = this.recordingService.stopRecording(session, recording.getId(),
|
||||
"recordingStoppedByServer");
|
||||
|
||||
sessionManager.evictParticipant(
|
||||
session.getParticipantByPublicId(ProtocolElements.RECORDER_PARTICIPANT_PUBLICID), null, null,
|
||||
"EVICT_RECORDER");
|
||||
if (session != null) {
|
||||
sessionManager.evictParticipant(
|
||||
session.getParticipantByPublicId(ProtocolElements.RECORDER_PARTICIPANT_PUBLICID), null, null,
|
||||
"EVICT_RECORDER");
|
||||
}
|
||||
|
||||
return new ResponseEntity<>(stoppedRecording.toJson().toString(), getResponseHeaders(), HttpStatus.OK);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import { OpenviduParamsService } from './services/openvidu-params.service';
|
|||
})
|
||||
export class AppComponent {
|
||||
|
||||
openviduURL = 'https://localhost:4443/';
|
||||
openviduURL = 'https://' + window.location.hostname + ':4443/';
|
||||
openviduSecret = 'MY_SECRET';
|
||||
|
||||
constructor(private router: Router, private openviduParamsService: OpenviduParamsService) { }
|
||||
|
|
|
@ -229,6 +229,7 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
|
|||
})
|
||||
.catch(error => {
|
||||
console.log('There was an error connecting to the session:', error.code, error.message);
|
||||
alert('Error connecting to the session: ' + error.message);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -10,10 +10,10 @@ export interface OpenviduParams {
|
|||
export class OpenviduParamsService {
|
||||
|
||||
params: OpenviduParams =
|
||||
{
|
||||
openviduUrl: 'https://localhost:4443/',
|
||||
openviduSecret: 'MY_SECRET'
|
||||
};
|
||||
{
|
||||
openviduUrl: 'https://' + window.location.hostname + ':4443/',
|
||||
openviduSecret: 'MY_SECRET'
|
||||
};
|
||||
|
||||
newParams$ = new Subject<OpenviduParams>();
|
||||
|
||||
|
|
Loading…
Reference in New Issue