openvidu-testapp local recording

pull/30/head
pabloFuente 2018-03-01 11:25:25 +01:00
parent 36d5018507
commit bcf636b507
7 changed files with 400 additions and 19 deletions

View File

@ -11,9 +11,12 @@ import { TestSessionsComponent } from './components/test-sessions/test-sessions.
import { TestApirestComponent } from './components/test-apirest/test-apirest.component'; import { TestApirestComponent } from './components/test-apirest/test-apirest.component';
import { OpenviduInstanceComponent } from './components/openvidu-instance/openvidu-instance.component'; import { OpenviduInstanceComponent } from './components/openvidu-instance/openvidu-instance.component';
import { ExtensionDialogComponent } from './components/openvidu-instance/extension-dialog.component'; import { ExtensionDialogComponent } from './components/openvidu-instance/extension-dialog.component';
import { LocalRecordingDialogComponent } from './components/test-sessions/local-recording-dialog.component';
import { OpenviduRestService } from './services/openvidu-rest.service'; import { OpenviduRestService } from './services/openvidu-rest.service';
import { OpenviduParamsService } from './services/openvidu-params.service'; import { OpenviduParamsService } from './services/openvidu-params.service';
import { TestFeedService } from './services/test-feed.service'; import { TestFeedService } from './services/test-feed.service';
import { MuteSubscribersService } from './services/mute-subscribers.service';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -21,7 +24,8 @@ import { TestFeedService } from './services/test-feed.service';
OpenviduInstanceComponent, OpenviduInstanceComponent,
TestSessionsComponent, TestSessionsComponent,
TestApirestComponent, TestApirestComponent,
ExtensionDialogComponent ExtensionDialogComponent,
LocalRecordingDialogComponent
], ],
imports: [ imports: [
BrowserModule, BrowserModule,
@ -34,9 +38,13 @@ import { TestFeedService } from './services/test-feed.service';
providers: [ providers: [
OpenviduRestService, OpenviduRestService,
OpenviduParamsService, OpenviduParamsService,
TestFeedService TestFeedService,
MuteSubscribersService
],
entryComponents: [
ExtensionDialogComponent,
LocalRecordingDialogComponent
], ],
entryComponents: [ExtensionDialogComponent],
bootstrap: [AppComponent] bootstrap: [AppComponent]
}) })
export class AppModule { } export class AppModule { }

View File

@ -2,10 +2,14 @@ import {
Component, Input, HostListener, ChangeDetectorRef, SimpleChanges, ElementRef, ViewChild, Component, Input, HostListener, ChangeDetectorRef, SimpleChanges, ElementRef, ViewChild,
OnInit, OnDestroy, OnChanges OnInit, OnDestroy, OnChanges
} from '@angular/core'; } from '@angular/core';
import { OpenVidu, Session, Subscriber, Publisher, Stream, Connection } from 'openvidu-browser'; import { Subscription } from 'rxjs/Subscription';
import { MatDialog } from '@angular/material';
import { OpenVidu, Session, Subscriber, Publisher, Stream, Connection, LocalRecorder } from 'openvidu-browser';
import { MatDialog, MatDialogRef } from '@angular/material';
import { ExtensionDialogComponent } from './extension-dialog.component'; import { ExtensionDialogComponent } from './extension-dialog.component';
import { LocalRecordingDialogComponent } from '../test-sessions/local-recording-dialog.component';
import { TestFeedService } from '../../services/test-feed.service'; import { TestFeedService } from '../../services/test-feed.service';
import { MuteSubscribersService } from '../../services/mute-subscribers.service';
declare var $: any; declare var $: any;
@ -94,10 +98,17 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
openviduError: any; openviduError: any;
private publisherRecorder: LocalRecorder;
private publisherRecording = false;
private publisherPaused = false;
private muteSubscribersSubscription: Subscription;
constructor( constructor(
private changeDetector: ChangeDetectorRef, private changeDetector: ChangeDetectorRef,
private extensionDialog: MatDialog, private extensionDialog: MatDialog,
private testFeedService: TestFeedService private recordDialog: MatDialog,
private testFeedService: TestFeedService,
private muteSubscribersService: MuteSubscribersService,
) { ) {
this.generateSessionInfo(); this.generateSessionInfo();
} }
@ -116,6 +127,13 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
if (this.sessionConf.startSession) { if (this.sessionConf.startSession) {
this.joinSession(); this.joinSession();
} }
this.muteSubscribersSubscription = this.muteSubscribersService.mutedEvent$.subscribe(
muteOrUnmute => {
Object.keys(this.subscribers).forEach((key) => {
this.subscribers[key].videoElement.muted = muteOrUnmute;
});
});
} }
ngOnChanges(changes: SimpleChanges) { ngOnChanges(changes: SimpleChanges) {
@ -128,6 +146,7 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
} }
ngOnDestroy() { ngOnDestroy() {
if (!!this.muteSubscribersSubscription) { this.muteSubscribersSubscription.unsubscribe(); }
this.leaveSession(); this.leaveSession();
} }
@ -259,18 +278,53 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
this.unpublished ? this.publishIcon = 'play_arrow' : this.publishIcon = 'stop'; this.unpublished ? this.publishIcon = 'play_arrow' : this.publishIcon = 'stop';
} }
private appendUserData(videoElement, connection): void { private appendSubscriberData(videoElement: HTMLVideoElement, connection: Connection): void {
const dataNode = document.createElement('div'); const dataNode = document.createElement('div');
dataNode.className = 'data-node'; dataNode.className = 'data-node';
dataNode.id = 'data-' + this.session.connection.connectionId + '-' + connection.connectionId; dataNode.id = 'data-' + this.session.connection.connectionId + '-' + connection.connectionId;
dataNode.innerHTML = '<p class="name">' + connection.data + '</p>' + dataNode.innerHTML = '<p class="name">' + connection.data + '</p>' +
'<button id="sub-btn-' + this.session.connection.connectionId + '-' + connection.connectionId + '" class="sub-btn">' + '<button id="sub-btn-' + this.session.connection.connectionId + '-' + connection.connectionId +
'<mat-icon id="icon-' + this.session.connection.connectionId + '-' + connection.connectionId + '" class="sub-btn" title="Subscribe/Unsubscribe"><mat-icon id="icon-' + this.session.connection.connectionId +
'" aria-label="Subscribe or unsubscribe" class="mat-icon material-icons" role="img"' + '-' + connection.connectionId + '" aria-label="Subscribe or unsubscribe" class="mat-icon material-icons" role="img"' +
'aria-hidden="true">notifications</mat-icon></button>'; 'aria-hidden="true">notifications</mat-icon></button>' +
'<button id="record-btn-' + this.session.connection.connectionId + '-' + connection.connectionId +
'" class="sub-btn rec-btn" title="Record"><mat-icon id="record-icon-' +
this.session.connection.connectionId + '-' + connection.connectionId +
'" aria-label="Start/Stop recording" class="mat-icon material-icons" role="img"' +
'aria-hidden="true">fiber_manual_record</mat-icon></button>' +
'<button style="display:none" id="pause-btn-' + this.session.connection.connectionId + '-' + connection.connectionId +
'" class="sub-btn rec-btn" title="Pause/Resume"><mat-icon id="pause-icon-' +
this.session.connection.connectionId + '-' + connection.connectionId +
'" aria-label="Pause/Resume recording" class="mat-icon material-icons" role="img"' +
'aria-hidden="true">pause</mat-icon></button>';
videoElement.parentNode.insertBefore(dataNode, videoElement.nextSibling); videoElement.parentNode.insertBefore(dataNode, videoElement.nextSibling);
document.getElementById('sub-btn-' + this.session.connection.connectionId + '-' + connection.connectionId).addEventListener('click', document.getElementById('sub-btn-' + this.session.connection.connectionId + '-' + connection.connectionId).addEventListener('click',
this.subUnsubFromSubscriber.bind(this, connection.connectionId)); this.subUnsubFromSubscriber.bind(this, connection.connectionId));
document.getElementById('record-btn-' + this.session.connection.connectionId + '-' + connection.connectionId).addEventListener('click',
this.recordSubscriber.bind(this, connection.connectionId));
document.getElementById('pause-btn-' + this.session.connection.connectionId + '-' + connection.connectionId).addEventListener('click',
this.pauseSubscriber.bind(this, connection.connectionId));
}
private appendPublisherData(videoElement: HTMLVideoElement): void {
const dataNode = document.createElement('div');
dataNode.className = 'data-node';
dataNode.id = 'data-' + this.session.connection.connectionId + '-' + this.session.connection.connectionId;
dataNode.innerHTML =
'<button id="local-record-btn-' + this.session.connection.connectionId +
'" class="sub-btn rec-btn publisher-rec-btn" title="Record"><mat-icon id="local-record-icon-' + this.session.connection.connectionId +
'" aria-label="Start/Stop local recording" class="mat-icon material-icons" role="img" aria-hidden="true">' +
'fiber_manual_record</mat-icon></button>' +
'<button style="display:none" id="local-pause-btn-' + this.session.connection.connectionId +
'" class="sub-btn rec-btn publisher-rec-btn" title="Pause/Resume">' +
'<mat-icon id="local-pause-icon-' + this.session.connection.connectionId +
'" aria-label="Pause/Resume local recording" class="mat-icon material-icons" role="img" aria-hidden="true">' +
'pause</mat-icon></button>';
videoElement.parentNode.insertBefore(dataNode, videoElement.nextSibling);
document.getElementById('local-record-btn-' + this.session.connection.connectionId).addEventListener('click',
this.recordPublisher.bind(this));
document.getElementById('local-pause-btn-' + this.session.connection.connectionId).addEventListener('click',
this.pausePublisher.bind(this));
} }
private removeUserData(connectionId: string): void { private removeUserData(connectionId: string): void {
@ -369,11 +423,108 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
}); });
} }
recordPublisher(): void {
if (!this.publisherRecording) {
this.publisherRecorder = this.OV.initLocalRecorder(this.publisher.stream);
this.publisherRecorder.record();
this.publisherRecording = true;
document.getElementById('local-record-icon-' + this.session.connection.connectionId).innerHTML = 'stop';
document.getElementById('local-pause-btn-' + this.session.connection.connectionId).style.display = 'block';
} else {
this.publisherRecorder.stop()
.then(() => {
let dialogRef: MatDialogRef<LocalRecordingDialogComponent>;
dialogRef = this.recordDialog.open(LocalRecordingDialogComponent, {
disableClose: true,
data: {
recorder: this.publisherRecorder
}
});
dialogRef.componentInstance.myReference = dialogRef;
dialogRef.afterOpen().subscribe(() => {
this.afterOpenPreview(this.publisherRecorder);
});
dialogRef.afterClosed().subscribe(() => {
this.afterClosePreview(this.publisherRecorder);
});
})
.catch((error) => {
console.error('Error stopping LocalRecorder: ' + error);
});
this.restartPublisherRecord();
}
}
pausePublisher(): void {
if (!this.publisherPaused) {
this.publisherRecorder.pause();
document.getElementById('local-pause-icon-' + this.session.connection.connectionId).innerHTML = 'play_arrow';
} else {
this.publisherRecorder.resume();
document.getElementById('local-pause-icon-' + this.session.connection.connectionId).innerHTML = 'pause';
}
this.publisherPaused = !this.publisherPaused;
}
recordSubscriber(connectionId: string): void {
const subscriber: Subscriber = this.subscribers[connectionId].subscriber;
const recording = this.subscribers[connectionId].recording;
if (!recording) {
this.subscribers[connectionId].recorder = this.OV.initLocalRecorder(subscriber.stream);
this.subscribers[connectionId].recorder.record();
this.subscribers[connectionId].recording = true;
document.getElementById('record-icon-' + this.session.connection.connectionId + '-' + connectionId).innerHTML = 'stop';
document.getElementById('pause-btn-' + this.session.connection.connectionId + '-' + connectionId).style.display = 'block';
} else {
this.subscribers[connectionId].recorder.stop()
.then(() => {
let dialogRef: MatDialogRef<LocalRecordingDialogComponent>;
dialogRef = this.recordDialog.open(LocalRecordingDialogComponent, {
disableClose: true,
data: {
recorder: this.subscribers[connectionId].recorder
}
});
dialogRef.componentInstance.myReference = dialogRef;
dialogRef.afterOpen().subscribe(() => {
this.afterOpenPreview(this.subscribers[connectionId].recorder);
});
dialogRef.afterClosed().subscribe(() => {
this.afterClosePreview(this.subscribers[connectionId].recorder);
});
})
.catch((error) => {
console.error('Error stopping LocalRecorder: ' + error);
});
this.restartSubscriberRecord(connectionId);
}
}
pauseSubscriber(connectionId: string): void {
const subscriber: Subscriber = this.subscribers[connectionId].subscriber;
const subscriberPaused = this.subscribers[connectionId].paused;
if (!subscriberPaused) {
this.subscribers[connectionId].recorder.pause();
document.getElementById('pause-icon-' + this.session.connection.connectionId + '-' + connectionId).innerHTML = 'play_arrow';
} else {
this.subscribers[connectionId].recorder.resume();
document.getElementById('pause-icon-' + this.session.connection.connectionId + '-' + connectionId).innerHTML = 'pause';
}
this.subscribers[connectionId].paused = !this.subscribers[connectionId].paused;
}
publishUnpublish(): void { publishUnpublish(): void {
if (this.unpublished) { if (this.unpublished) {
this.session.publish(this.publisher); this.session.publish(this.publisher);
} else { } else {
if (!!this.publisherRecorder && this.publisherRecording) {
this.publisherRecorder.clean();
}
this.session.unpublish(this.publisher); this.session.unpublish(this.publisher);
this.removeUserData(this.session.connection.connectionId);
this.restartPublisherRecord();
} }
this.unpublished = !this.unpublished; this.unpublished = !this.unpublished;
this.updatePublishIcon(); this.updatePublishIcon();
@ -383,6 +534,7 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
if (!this.unpublished) { if (!this.unpublished) {
this.session.unpublish(this.publisher); this.session.unpublish(this.publisher);
this.removeUserData(this.session.connection.connectionId);
} }
let screenChange; let screenChange;
@ -448,9 +600,15 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
subUnsubFromSubscriber(connectionId: string) { subUnsubFromSubscriber(connectionId: string) {
let subscriber: Subscriber = this.subscribers[connectionId].subscriber; let subscriber: Subscriber = this.subscribers[connectionId].subscriber;
if (this.subscribers[connectionId].subbed) { if (this.subscribers[connectionId].subbed) {
if (!!this.subscribers[connectionId].recorder && this.subscribers[connectionId].recording) {
this.subscribers[connectionId].recorder.clean();
}
this.session.unsubscribe(subscriber); this.session.unsubscribe(subscriber);
this.restartSubscriberRecord(connectionId);
document.getElementById('data-' + this.session.connection.connectionId + '-' + connectionId).style.marginLeft = '0'; document.getElementById('data-' + this.session.connection.connectionId + '-' + connectionId).style.marginLeft = '0';
document.getElementById('icon-' + this.session.connection.connectionId + '-' + connectionId).innerHTML = 'notifications_off'; document.getElementById('icon-' + this.session.connection.connectionId + '-' + connectionId).innerHTML = 'notifications_off';
document.getElementById('record-btn-' + this.session.connection.connectionId + '-' + connectionId).remove();
document.getElementById('pause-btn-' + this.session.connection.connectionId + '-' + connectionId).remove();
} else { } else {
subscriber = this.session.subscribe(subscriber.stream, 'remote-vid-' + this.session.connection.connectionId); subscriber = this.session.subscribe(subscriber.stream, 'remote-vid-' + this.session.connection.connectionId);
this.subscribers[connectionId].subscriber = subscriber; this.subscribers[connectionId].subscriber = subscriber;
@ -459,11 +617,12 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
$(e.element).css({ 'background-color': '#4d4d4d' }); $(e.element).css({ 'background-color': '#4d4d4d' });
$(e.element).attr('poster', 'assets/images/volume.png'); $(e.element).attr('poster', 'assets/images/volume.png');
} }
this.removeUserData(connectionId); this.subscribers[connectionId].videoElement = e.element;
this.appendUserData(e.element, subscriber.stream.connection);
this.updateEventList('videoElementCreated', e.element.id); this.updateEventList('videoElementCreated', e.element.id);
}); });
subscriber.on('videoPlaying', (e) => { subscriber.on('videoPlaying', (e) => {
this.removeUserData(connectionId);
this.appendSubscriberData(e.element, subscriber.stream.connection);
this.updateEventList('videoPlaying', e.element.id); this.updateEventList('videoPlaying', e.element.id);
}); });
} }
@ -477,21 +636,29 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
if (this.subscribeTo) { if (this.subscribeTo) {
const subscriber: Subscriber = session.subscribe(event.stream, 'remote-vid-' + session.connection.connectionId); const subscriber: Subscriber = session.subscribe(event.stream, 'remote-vid-' + session.connection.connectionId);
this.subscribers[subscriber.stream.connection.connectionId] = {
'subscriber': subscriber,
'subbed': true,
'recorder': undefined,
'recording': false,
'paused': false,
'videoElement': undefined
};
subscriber.on('videoElementCreated', (e) => { subscriber.on('videoElementCreated', (e) => {
if (!event.stream.getRecvVideo()) { if (!event.stream.getRecvVideo()) {
$(e.element).css({ 'background-color': '#4d4d4d' }); $(e.element).css({ 'background-color': '#4d4d4d' });
$(e.element).attr('poster', 'assets/images/volume.png'); $(e.element).attr('poster', 'assets/images/volume.png');
} }
this.appendUserData(e.element, subscriber.stream.connection); this.subscribers[subscriber.stream.connection.connectionId].videoElement = e.element;
this.updateEventList('videoElementCreated', e.element.id); this.updateEventList('videoElementCreated', e.element.id);
}); });
subscriber.on('videoPlaying', (e) => { subscriber.on('videoPlaying', (e) => {
this.appendSubscriberData(e.element, subscriber.stream.connection);
this.updateEventList('videoPlaying', e.element.id); this.updateEventList('videoPlaying', e.element.id);
}); });
subscriber.on('videoElementDestroyed', (e) => { subscriber.on('videoElementDestroyed', (e) => {
this.updateEventList('videoElementDestroyed', '(Subscriber)'); this.updateEventList('videoElementDestroyed', '(Subscriber)');
}); });
this.subscribers[subscriber.stream.connection.connectionId] = { 'subscriber': subscriber, 'subbed': true };
} }
this.updateEventList('streamCreated', event.stream.connection.connectionId); this.updateEventList('streamCreated', event.stream.connection.connectionId);
}); });
@ -546,12 +713,13 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
this.updateEventList('accessDenied', ''); this.updateEventList('accessDenied', '');
}); });
publisher.on('videoPlaying', (e) => { publisher.on('videoPlaying', (e) => {
this.appendPublisherData(e.element);
this.updateEventList('videoPlaying', e.element.id); this.updateEventList('videoPlaying', e.element.id);
}); });
publisher.on('remoteVideoPlaying', (e) => { publisher.on('remoteVideoPlaying', (e) => {
this.appendPublisherData(e.element);
this.updateEventList('remoteVideoPlaying', e.element.id); this.updateEventList('remoteVideoPlaying', e.element.id);
}); });
@ -568,4 +736,48 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
}); });
} }
private afterOpenPreview(recorder: LocalRecorder): void {
this.muteSubscribersService.updateMuted(true);
recorder.preview('local-recorder-preview').controls = true;
}
private afterClosePreview(recorder: LocalRecorder): void {
this.muteSubscribersService.updateMuted(false);
recorder.clean();
}
private restartPublisherRecord(): void {
let el: HTMLElement = document.getElementById('local-record-icon-' + this.session.connection.connectionId);
if (!!el) {
el.innerHTML = 'fiber_manual_record';
}
el = document.getElementById('local-pause-icon-' + this.session.connection.connectionId);
if (!!el) {
el.innerHTML = 'pause';
}
el = document.getElementById('local-pause-btn-' + this.session.connection.connectionId);
if (!!el) {
el.style.display = 'none';
}
this.publisherPaused = false;
this.publisherRecording = false;
}
private restartSubscriberRecord(connectionId: string): void {
let el: HTMLElement = document.getElementById('record-icon-' + this.session.connection.connectionId + '-' + connectionId);
if (!!el) {
el.innerHTML = 'fiber_manual_record';
}
el = document.getElementById('pause-icon-' + this.session.connection.connectionId + '-' + connectionId);
if (!!el) {
el.innerHTML = 'pause';
}
el = document.getElementById('pause-btn-' + this.session.connection.connectionId + '-' + connectionId);
if (!!el) {
el.style.display = 'none';
}
this.subscribers[connectionId].recording = false;
this.subscribers[connectionId].paused = false;
}
} }

View File

@ -53,7 +53,7 @@ export class TestApirestComponent implements OnInit, OnDestroy {
} }
ngOnDestroy() { ngOnDestroy() {
this.paramsSubscription.unsubscribe(); if (!!this.paramsSubscription) { this.paramsSubscription.unsubscribe(); }
} }
private getSessionId() { private getSessionId() {

View File

@ -0,0 +1,65 @@
import { Component, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
import { LocalRecorder } from 'openvidu-browser';
@Component({
selector: 'app-local-recording-dialog',
template: `
<div mat-dialog-content>
<div id="local-recorder-preview"></div>
</div>
<div mat-dialog-actions>
<button mat-button mat-dialog-close>Close</button>
<button mat-button (click)="recorder.download()">Download</button>
<button mat-button [disabled]="endpoint == ''" (click)="uploadFile()">Upload to</button>
<mat-form-field [style.font-size]="'14px'" [style.width]="'170px'">
<input matInput name="endpoint" [(ngModel)]="endpoint">
</mat-form-field>
<mat-icon *ngIf="uploading" [ngClass]="iconClass"
[style.margin-top]="'10px'" [style.color]="iconColor">{{uploadIcon}}</mat-icon>
</div>
`,
styles: [`
#quality-div {
margin-top: 20px;
}
`],
})
export class LocalRecordingDialogComponent {
public myReference: MatDialogRef<LocalRecordingDialogComponent>;
private recorder: LocalRecorder;
private uploading = false;
private endpoint = '';
private uploadIcon: string;
private iconColor: string;
private iconClass = '';
constructor(@Inject(MAT_DIALOG_DATA) public data: any) {
this.recorder = data.recorder;
}
close() {
this.myReference.close();
}
uploadFile() {
this.iconColor = 'black';
this.iconClass = 'rotating';
this.uploadIcon = 'cached';
this.uploading = true;
this.recorder.uploadAsBinary(this.endpoint)
.then(() => {
this.iconColor = 'green';
this.uploadIcon = 'done';
this.iconClass = '';
})
.catch((e) => {
this.iconColor = 'red';
this.uploadIcon = 'clear';
this.iconClass = '';
});
}
}

View File

@ -0,0 +1,21 @@
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';
@Injectable()
export class MuteSubscribersService {
muted = false;
mutedEvent$ = new Subject<boolean>();
constructor() { }
getMuted() {
return this.muted;
}
updateMuted(muted: boolean) {
this.muted = muted;
this.mutedEvent$.next(this.muted);
}
}

View File

@ -66,7 +66,7 @@ button {
.video-container div.data-node .sub-btn { .video-container div.data-node .sub-btn {
outline: 0; outline: 0;
border: none; border: none;
background: white; background: rgba(255, 255, 255, 0.75);
cursor: pointer; cursor: pointer;
padding: 0; padding: 0;
margin-top: 40px; margin-top: 40px;
@ -77,10 +77,26 @@ button {
color: #4d4d4d; color: #4d4d4d;
} }
.video-container div.data-node .rec-btn {
float: right;
border-top-right-radius: 0;
border-top-left-radius: 2px;
color: #ac0000;
}
.video-container div.data-node .rec-btn:hover {
color: #ac000082;
}
.video-container div.data-node .rec-btn.publisher-rec-btn {
margin-top: 70px;
}
.video-container div.data-node .material-icons { .video-container div.data-node .material-icons {
font-size: 17px; font-size: 17px;
width: 17px; width: 17px;
height: 17px; height: 17px;
line-height: 20px;
} }
.mat-expansion-panel-body { .mat-expansion-panel-body {
@ -105,3 +121,62 @@ button {
.mat-expansion-panel-spacing { .mat-expansion-panel-spacing {
margin: 3px 0 !important; margin: 3px 0 !important;
} }
#local-recorder-preview video {
width: 500px;
height: inherit;
}
/*Rotating animation*/
@-webkit-keyframes rotating
/* Safari and Chrome */
{
from {
-ms-transform: rotate(360deg);
-moz-transform: rotate(360deg);
-webkit-transform: rotate(360deg);
-o-transform: rotate(360deg);
transform: rotate(360deg);
}
to {
-ms-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-webkit-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
}
}
@keyframes rotating {
from {
-ms-transform: rotate(360deg);
-moz-transform: rotate(360deg);
-webkit-transform: rotate(360deg);
-o-transform: rotate(360deg);
transform: rotate(360deg);
}
to {
-ms-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-webkit-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
}
}
.rotating {
-webkit-animation: rotating 1s linear infinite;
-moz-animation: rotating 1s linear infinite;
-ms-animation: rotating 1s linear infinite;
-o-animation: rotating 1s linear infinite;
animation: rotating 1s linear infinite;
cursor: default !important;
}
.rotating:hover {
color: inherit !important;
}
/*End rotating animation*/