mirror of https://github.com/OpenVidu/openvidu.git
openvidu-browser LocalRecording API
parent
7d64b242f4
commit
36d5018507
|
@ -30,7 +30,7 @@
|
|||
"browserify-prod": "VERSION=${VERSION:-}; cd ts/OpenVidu && browserify --debug Main.ts -p [ tsify ] --exclude kurento-browser-extensions | uglifyjs --source-map content=inline --output ../../static/js/openvidu-browser-$VERSION.min.js",
|
||||
"prepublish": "cd ts/OpenViduInternal && tsc && cd ../OpenVidu && tsc && cd ../.. && tsc --declaration ts/OpenVidu/index.ts --outDir lib --sourceMap && tsc --declaration ts/OpenVidu/Main.ts --outDir lib --sourceMap",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"updatetsc": "cd ts/OpenViduInternal && tsc && cd ../OpenVidu && tsc && cd ../.. && tsc --declaration ts/OpenVidu/index.ts --outDir lib --sourceMap && tsc --declaration ts/OpenVidu/Main.ts --outDir lib --sourceMap"
|
||||
"updatetsc": "cd ts/OpenViduInternal && tsc && cd ../OpenVidu && tsc && cd ../.. && tsc --declaration ts/OpenVidu/index.ts --outDir lib --sourceMap --lib dom,es5,es2015.promise,scripthost && tsc --declaration ts/OpenVidu/Main.ts --outDir lib --sourceMap --lib dom,es5,es2015.promise,scripthost"
|
||||
},
|
||||
"types": "lib/OpenVidu/index.d.ts",
|
||||
"version": "1.7.0"
|
||||
|
|
|
@ -20,6 +20,8 @@ import { Session } from './Session';
|
|||
import { Publisher } from './Publisher';
|
||||
import { OpenViduError, OpenViduErrorName } from '../OpenViduInternal/OpenViduError';
|
||||
import { OutboundStreamOptions } from '../OpenViduInternal/index';
|
||||
import { Stream } from '../OpenViduInternal/Stream';
|
||||
import { LocalRecorder } from '../OpenViduInternal/LocalRecorder';
|
||||
|
||||
import * as adapter from 'webrtc-adapter';
|
||||
import * as screenSharing from '../ScreenSharing/Screen-Capturing.js';
|
||||
|
@ -269,4 +271,8 @@ export class OpenVidu {
|
|||
console.warn = function () { };
|
||||
}
|
||||
|
||||
initLocalRecorder(stream: Stream): LocalRecorder {
|
||||
return new LocalRecorder(stream);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,3 +4,4 @@ export * from './Publisher';
|
|||
export * from './Subscriber';
|
||||
export * from '../OpenViduInternal/Stream';
|
||||
export * from '../OpenViduInternal/Connection';
|
||||
export * from '../OpenViduInternal/LocalRecorder';
|
|
@ -21,7 +21,8 @@
|
|||
"outDir": "../../lib",
|
||||
"emitBOM": false,
|
||||
"preserveConstEnums": true,
|
||||
"sourceMap": true
|
||||
"sourceMap": true,
|
||||
"lib": ["dom","es5","es2015.promise","scripthost"]
|
||||
},
|
||||
//"buildOnSave": true,
|
||||
"compileOnSave": true
|
||||
|
|
|
@ -0,0 +1,258 @@
|
|||
import { Stream } from "./Stream";
|
||||
|
||||
declare var MediaRecorder: any;
|
||||
|
||||
export const enum LocalRecoderState {
|
||||
READY = 'READY',
|
||||
RECORDING = 'RECORDING',
|
||||
PAUSED = 'PAUSED',
|
||||
FINISHED = 'FINISHED'
|
||||
}
|
||||
|
||||
export class LocalRecorder {
|
||||
|
||||
state: LocalRecoderState;
|
||||
|
||||
private stream: Stream;
|
||||
|
||||
private mediaRecorder: any;
|
||||
private chunks: any[] = [];
|
||||
private blob: Blob;
|
||||
private count: number = 0;
|
||||
private id: string;
|
||||
|
||||
private videoPreviewSrc: string;
|
||||
private htmlParentElementId: string;
|
||||
private videoPreview: HTMLVideoElement;
|
||||
|
||||
constructor(stream: Stream) {
|
||||
this.stream = stream;
|
||||
this.id = this.stream.streamId + '_' + this.stream.connection.connectionId + '_localrecord';
|
||||
this.state = LocalRecoderState.READY;
|
||||
}
|
||||
|
||||
record() {
|
||||
if (typeof MediaRecorder === 'undefined') {
|
||||
console.error('MediaRecorder not supported on your browser. See compatibility in https://caniuse.com/#search=MediaRecorder');
|
||||
throw (Error('MediaRecorder not supported on your browser. See compatibility in https://caniuse.com/#search=MediaRecorder'));
|
||||
}
|
||||
if (this.state !== LocalRecoderState.READY) {
|
||||
throw (Error('\'LocalRecord.record()\' needs \'LocalRecord.state\' to be \'READY\' (current value: \'' + this.state + '\'). Call \'LocalRecorder.clean()\' or init a new LocalRecorder before'));
|
||||
}
|
||||
console.log("Starting local recording of stream '" + this.stream.streamId + "' of connection '" + this.stream.connection.connectionId + "'");
|
||||
if (typeof MediaRecorder.isTypeSupported == 'function') {
|
||||
let options;
|
||||
if (MediaRecorder.isTypeSupported('video/webm;codecs=vp9')) {
|
||||
options = { mimeType: 'video/webm;codecs=vp9' };
|
||||
} else if (MediaRecorder.isTypeSupported('video/webm;codecs=h264')) {
|
||||
options = { mimeType: 'video/webm;codecs=h264' };
|
||||
} else if (MediaRecorder.isTypeSupported('video/webm;codecs=vp8')) {
|
||||
options = { mimeType: 'video/webm;codecs=vp8' };
|
||||
}
|
||||
console.log('Using mimeType ' + options.mimeType);
|
||||
this.mediaRecorder = new MediaRecorder(this.stream.getMediaStream(), options);
|
||||
} else {
|
||||
console.warn('isTypeSupported is not supported, using default codecs for browser');
|
||||
this.mediaRecorder = new MediaRecorder(this.stream.getMediaStream());
|
||||
}
|
||||
|
||||
this.mediaRecorder.start(10);
|
||||
|
||||
this.mediaRecorder.ondataavailable = (e) => {
|
||||
this.chunks.push(e.data);
|
||||
};
|
||||
|
||||
this.mediaRecorder.onerror = (e) => {
|
||||
console.error('MediaRecorder error: ', e);
|
||||
};
|
||||
|
||||
|
||||
this.mediaRecorder.onstart = () => {
|
||||
console.log('MediaRecorder started (state=' + this.mediaRecorder.state + ")");
|
||||
};
|
||||
|
||||
this.mediaRecorder.onstop = () => {
|
||||
this.onStopDefault();
|
||||
};
|
||||
|
||||
this.mediaRecorder.onpause = () => {
|
||||
console.log('MediaRecorder paused (state=' + this.mediaRecorder.state + ")");
|
||||
}
|
||||
|
||||
this.mediaRecorder.onresume = () => {
|
||||
console.log('MediaRecorder resumed (state=' + this.mediaRecorder.state + ")");
|
||||
}
|
||||
|
||||
this.mediaRecorder.onwarning = (e) => {
|
||||
console.log('MediaRecorder warning: ' + e);
|
||||
};
|
||||
|
||||
this.state = LocalRecoderState.RECORDING;
|
||||
}
|
||||
|
||||
stop(): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
if (this.state === LocalRecoderState.READY || this.state === LocalRecoderState.FINISHED) {
|
||||
throw (Error('\'LocalRecord.stop()\' needs \'LocalRecord.state\' to be \'RECORDING\' or \'PAUSED\' (current value: \'' + this.state + '\'). Call \'LocalRecorder.start()\' before'));
|
||||
}
|
||||
this.mediaRecorder.onstop = () => {
|
||||
this.onStopDefault();
|
||||
resolve();
|
||||
}
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
try {
|
||||
this.mediaRecorder.stop();
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pause() {
|
||||
if (this.state !== LocalRecoderState.RECORDING) {
|
||||
throw (Error('\'LocalRecord.pause()\' needs \'LocalRecord.state\' to be \'RECORDING\' (current value: \'' + this.state + '\'). Call \'LocalRecorder.start()\' or \'LocalRecorder.resume()\' before'));
|
||||
}
|
||||
this.mediaRecorder.pause();
|
||||
this.state = LocalRecoderState.PAUSED;
|
||||
}
|
||||
|
||||
resume() {
|
||||
if (this.state !== LocalRecoderState.PAUSED) {
|
||||
throw (Error('\'LocalRecord.resume()\' needs \'LocalRecord.state\' to be \'PAUSED\' (current value: \'' + this.state + '\'). Call \'LocalRecorder.pause()\' before'));
|
||||
}
|
||||
this.mediaRecorder.resume();
|
||||
this.state = LocalRecoderState.RECORDING;
|
||||
}
|
||||
|
||||
preview(parentElement): HTMLVideoElement {
|
||||
|
||||
if (this.state !== LocalRecoderState.FINISHED) {
|
||||
throw (Error('\'LocalRecord.preview()\' needs \'LocalRecord.state\' to be \'FINISHED\' (current value: \'' + this.state + '\'). Call \'LocalRecorder.stop()\' before'));
|
||||
}
|
||||
|
||||
this.videoPreview = document.createElement('video');
|
||||
|
||||
this.videoPreview.id = this.id;
|
||||
this.videoPreview.autoplay = true;
|
||||
|
||||
if (typeof parentElement === "string") {
|
||||
this.htmlParentElementId = parentElement;
|
||||
|
||||
let parentElementDom = document.getElementById(parentElement);
|
||||
if (parentElementDom) {
|
||||
this.videoPreview = parentElementDom.appendChild(this.videoPreview);
|
||||
}
|
||||
} else {
|
||||
this.htmlParentElementId = parentElement.id;
|
||||
this.videoPreview = parentElement.appendChild(this.videoPreview);
|
||||
}
|
||||
|
||||
this.videoPreview.src = this.videoPreviewSrc;
|
||||
|
||||
return this.videoPreview;
|
||||
}
|
||||
|
||||
clean() {
|
||||
let f = () => {
|
||||
delete this.blob;
|
||||
this.chunks = [];
|
||||
this.count = 0;
|
||||
delete this.mediaRecorder;
|
||||
this.state = LocalRecoderState.READY;
|
||||
}
|
||||
if (this.state === LocalRecoderState.RECORDING || this.state === LocalRecoderState.PAUSED) {
|
||||
this.stop().then(() => f()).catch(() => f());
|
||||
} else {
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
download() {
|
||||
if (this.state !== LocalRecoderState.FINISHED) {
|
||||
throw (Error('\'LocalRecord.download()\' needs \'LocalRecord.state\' to be \'FINISHED\' (current value: \'' + this.state + '\'). Call \'LocalRecorder.stop()\' before'));
|
||||
} else {
|
||||
let a: HTMLAnchorElement = document.createElement("a");
|
||||
a.style.display = 'none';
|
||||
document.body.appendChild(a);
|
||||
|
||||
let url = window.URL.createObjectURL(this.blob);
|
||||
a.href = url;
|
||||
a.download = this.id + '.webm';
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
|
||||
document.body.removeChild(a);
|
||||
}
|
||||
}
|
||||
|
||||
getBlob(): Blob {
|
||||
if (this.state !== LocalRecoderState.FINISHED) {
|
||||
throw (Error('Call \'LocalRecord.stop()\' before getting Blob file'));
|
||||
} else {
|
||||
return this.blob;
|
||||
}
|
||||
}
|
||||
|
||||
uploadAsBinary(endpoint: string): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.state !== LocalRecoderState.FINISHED) {
|
||||
reject(Error('\'LocalRecord.uploadAsBinary()\' needs \'LocalRecord.state\' to be \'FINISHED\' (current value: \'' + this.state + '\'). Call \'LocalRecorder.stop()\' before'));
|
||||
} else {
|
||||
let http = new XMLHttpRequest();
|
||||
http.open("POST", endpoint, true);
|
||||
http.onreadystatechange = () => {
|
||||
if (http.readyState === 4) {
|
||||
if (http.status === 200) {
|
||||
resolve("File uploaded");
|
||||
} else {
|
||||
reject(Error("Upload error: " + http.status));
|
||||
}
|
||||
}
|
||||
}
|
||||
http.send(this.blob);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
uploadAsMultipartfile(endpoint: string): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.state !== LocalRecoderState.FINISHED) {
|
||||
reject(Error('\'LocalRecord.uploadAsMultipartfile()\' needs \'LocalRecord.state\' to be \'FINISHED\' (current value: \'' + this.state + '\'). Call \'LocalRecorder.stop()\' before'));
|
||||
} else {
|
||||
let http = new XMLHttpRequest();
|
||||
http.open("POST", endpoint, true);
|
||||
|
||||
let sendable = new FormData();
|
||||
sendable.append("name", this.id);
|
||||
sendable.append("file", this.blob);
|
||||
|
||||
http.onreadystatechange = () => {
|
||||
if (http.readyState === 4) {
|
||||
if (http.status === 200) {
|
||||
resolve("File uploaded");
|
||||
} else {
|
||||
reject(Error("Upload error: " + http.status));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
http.send(sendable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private onStopDefault() {
|
||||
console.log('MediaRecorder stopped (state=' + this.mediaRecorder.state + ")");
|
||||
|
||||
this.blob = new Blob(this.chunks, { type: "video/webm" });
|
||||
this.chunks = [];
|
||||
|
||||
this.videoPreviewSrc = window.URL.createObjectURL(this.blob);
|
||||
|
||||
this.state = LocalRecoderState.FINISHED;
|
||||
}
|
||||
|
||||
}
|
|
@ -693,7 +693,7 @@ export class Stream {
|
|||
}
|
||||
|
||||
private stopWebRtcStats() {
|
||||
if (this.webRtcStats.isEnabled()) {
|
||||
if (this.webRtcStats != null && this.webRtcStats.isEnabled()) {
|
||||
this.webRtcStats.stopWebRtcStats();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,4 +2,5 @@ export * from './OpenViduInternal';
|
|||
export * from './Connection';
|
||||
export * from './SessionInternal';
|
||||
export * from './Stream';
|
||||
export * from './LocalRecorder';
|
||||
export * from './OpenViduError';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"target": "es6",
|
||||
"target": "es5",
|
||||
"module": "commonjs",
|
||||
//"noImplicitAny": true,
|
||||
"noImplicitThis": true,
|
||||
|
@ -21,8 +21,9 @@
|
|||
"outDir": "../../lib/OpenViduInternal",
|
||||
"emitBOM": false,
|
||||
"preserveConstEnums": true,
|
||||
"sourceMap": true
|
||||
"sourceMap": true,
|
||||
"lib": ["dom","es5","es2015.promise","scripthost"]
|
||||
},
|
||||
//"buildOnSave": true,
|
||||
"compileOnSave":true
|
||||
"compileOnSave": true
|
||||
}
|
Loading…
Reference in New Issue