123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 |
- import { audioConstraints } from './audioConstraints';
- import { videoConstraints } from './videoConstraints';
- import { isSupport } from './isSupport';
- import { getVideo } from './videoElement';
- import { EventEmitter } from 'eventemitter3';
- export type ResolutionType = '1080p' | '2k' | '4k';
- export type PlatformType = 'web' | 'electron' | 'canvas';
- export interface PlatformConfigType {
- chromeMediaSourceId?: string | null;
- canvasId?: string;
- minWidth?: number;
- maxWidth?: number;
- minHeight?: number;
- maxHeight?: number;
- frameRate?: number;
- }
- export interface InitConfigType extends DisplayMediaStreamConstraints {
- uploadUrl?: string;
- resolution: ResolutionType;
- autoDownload?: boolean;
- isElectron?: boolean;
- platform?: PlatformType;
- config?: PlatformConfigType;
- debug?: boolean;
- }
- export enum RecorderStatusType {
- init = 0,
- start = 1,
- hold = 2,
- end = 3,
- }
- export class BasicSimaqRecorder extends EventEmitter {
- displayMediaStreamConstraints: DisplayMediaStreamConstraints = {
- video: videoConstraints.getValue(),
- audio: audioConstraints.getValue(),
- };
- private isStartRecoding = false;
- private stream: MediaStream;
- private audioInput: MediaStream;
- private mediaRecorder: MediaRecorder;
- public status: RecorderStatusType = 0;
- // public record = new BehaviorSubject<Blob>(null);
- private recordChunks: Blob[] = [];
- private autoDownload = false;
- private passiveEnd = false;
- private platform: string;
- private uploadUrl: string;
- private canvasId: string;
- private platformConfig: PlatformConfigType;
- private chromeMediaSourceId: string | null;
- constructor(arg: InitConfigType) {
- super();
- console.log('arg', arg);
- this.autoDownload = arg.autoDownload;
- this.platform = arg.platform;
- this.platformConfig = arg.config;
- this.uploadUrl = arg.uploadUrl;
- this.initParams(arg);
- videoConstraints.subscribe((value) => {
- console.log('subscribe', value);
- });
- }
- private sleep = (ms) => new Promise((r) => setTimeout(r, ms));
- private get isElectron(): boolean {
- return this.platform === 'electron';
- }
- private get isWeb(): boolean {
- return this.platform === 'web';
- }
- private get isCanvas(): boolean {
- return this.platform === 'canvas';
- }
- private get canvasElement(): HTMLCanvasElement {
- // return document.getElementById(this.canvasId);
- return document.querySelector(this.canvasId);
- }
- private set canvasElement(canvas) {
- this.canvasElement = canvas;
- }
- private initParams(arg: InitConfigType): void {
- switch (arg.platform) {
- case 'web':
- break;
- case 'electron':
- this.chromeMediaSourceId = arg.config.chromeMediaSourceId;
- break;
- case 'canvas':
- this.canvasId = arg.config.canvasId;
- break;
- default:
- break;
- }
- }
- public async startRecord(): Promise<void> {
- try {
- if (!this.isStartRecoding) {
- console.log('开始录屏!', isSupport());
- if (!isSupport()) {
- console.error('当前浏览器不支持录屏或不存在https环境');
- return;
- }
- // const media = this.isElectron
- // ? await this.getEletronDisplayMedia()
- // : await this.getDisplayMedia();
- const media = await this.getDefaultMedia();
- console.log('media', media);
- if (media) {
- this.emit('startRecord');
- this.isStartRecoding = true;
- this.status = RecorderStatusType.start;
- // console.log('media', media);
- const video: HTMLVideoElement = getVideo();
- if (video) {
- // console.log('video', video);
- video.srcObject = media;
- this.stream = media;
- this.createMediaRecoder();
- this.mediaRecorder.start();
- this.stream.getVideoTracks()[0].onended = () => {
- console.log('stop-share');
- this.endRecord();
- };
- }
- } else {
- this.streamStop();
- this.isStartRecoding = false;
- this.status = RecorderStatusType.end;
- this.emit('cancelRecord');
- }
- }
- } catch (error) {
- console.error('startRecord::', error);
- }
- }
- private getDefaultMedia(): Promise<MediaStream | null> {
- return new Promise(async (resolve) => {
- switch (this.platform) {
- case 'web':
- return resolve(await this.getDisplayMedia());
- case 'canvas':
- return resolve(await this.getCanvasSteam());
- case 'electron':
- return resolve(await this.getEletronDisplayMedia());
- default:
- return resolve(await this.getDisplayMedia());
- }
- });
- }
- private getCanvasSteam(): Promise<MediaStream | null> {
- return new Promise(async (resolve) => {
- try {
- const audioInput = await this.getDeaultAudio();
- if (audioInput) {
- this.audioInput = audioInput;
- }
- console.log('audioInput', audioInput);
- console.log('this.canvasElement', this.canvasElement);
- const stream = this.canvasElement.captureStream(30);
- if (stream) {
- return resolve(stream);
- }
- return resolve(null);
- } catch (error) {
- return resolve(null);
- }
- });
- }
- private getEletronDisplayMedia(): Promise<MediaStream | null> {
- return new Promise(async (resolve) => {
- try {
- const audioInput = await this.getEletronDeaultAudio();
- if (audioInput) {
- this.audioInput = audioInput;
- }
- console.log('eletron-audioInput', audioInput);
- if (navigator.mediaDevices.getDisplayMedia) {
- const videoConfig = {
- mandatory: {
- chromeMediaSource: 'desktop',
- chromeMediaSourceId: this.chromeMediaSourceId,
- minWidth: this.platformConfig.minWidth || 1280,
- maxWidth: this.platformConfig.maxWidth || 3840,
- minHeight: this.platformConfig.minHeight || 720,
- maxHeight: this.platformConfig.maxHeight || 2160,
- },
- };
- console.log('videoConfig', videoConfig);
- const res = await navigator.mediaDevices.getUserMedia({
- audio: false,
- video: videoConfig,
- } as any as MediaStreamConstraints);
- return resolve(res);
- }
- return resolve(null);
- } catch (error) {
- return resolve(null);
- }
- });
- }
- private getDisplayMedia(): Promise<MediaStream | null> {
- return new Promise(async (resolve) => {
- try {
- const audioInput = await this.getDeaultAudio();
- if (audioInput) {
- this.audioInput = audioInput;
- }
- console.log('audioInput', audioInput);
- if (navigator.mediaDevices.getDisplayMedia) {
- const res = await navigator.mediaDevices.getDisplayMedia(
- this.displayMediaStreamConstraints,
- );
- return resolve(res);
- }
- return resolve(null);
- } catch (error) {
- return resolve(null);
- }
- });
- }
- private async getDeaultAudio(): Promise<MediaStream> {
- return new Promise(async (resolve) => {
- try {
- if (navigator.mediaDevices.getUserMedia) {
- const res = await navigator.mediaDevices.getUserMedia({
- audio: true,
- video: false,
- });
- return resolve(res);
- }
- return resolve(null);
- } catch (error) {
- return resolve(null);
- }
- });
- }
- private async getEletronDeaultAudio(): Promise<MediaStream> {
- return new Promise(async (resolve) => {
- try {
- if (navigator.mediaDevices.getUserMedia) {
- const res = await navigator.mediaDevices.getUserMedia({
- video: false,
- audio: { deviceId: 'default' },
- } as any as MediaStreamConstraints);
- return resolve(res);
- }
- return resolve(null);
- } catch (error) {
- return resolve(null);
- }
- });
- }
- public holdRecord(): void {
- this.isStartRecoding = false;
- this.status = RecorderStatusType.hold;
- this.streamStop();
- }
- public async endRecord(): Promise<Blob[]> {
- return new Promise<Blob[]>(async (resolve) => {
- try {
- this.streamStop();
- await this.sleep(1000);
- this.isStartRecoding = false;
- this.status = RecorderStatusType.end;
- const blobs = this.recordChunks.slice();
- console.log('last-dump', blobs);
- if (this.autoDownload) {
- blobs?.length && this.handleAutoDownload(blobs);
- }
- this.emit('endRecord', blobs);
- this.passiveEnd = false;
- this.recordChunks = [];
- resolve(blobs);
- } catch (error) {
- resolve([]);
- }
- });
- }
- private streamStop(): void {
- if (this.stream) {
- this.stream.getTracks().forEach((track) => track.stop());
- }
- if (this.audioInput) {
- this.audioInput.getTracks().forEach((track) => track.stop());
- }
- if (this.mediaRecorder) {
- this.mediaRecorder.stop();
- }
- }
- private createMediaRecoder(): void {
- console.log('video-flag', videoConstraints.value);
- // let mergeSteam: MediaStream;
- let audioTrack: MediaStreamTrack, videoTrack: MediaStreamTrack;
- if (this.audioInput) {
- [videoTrack] = this.stream.getVideoTracks();
- [audioTrack] = this.audioInput.getAudioTracks();
- this.stream = new MediaStream([videoTrack, audioTrack]);
- }
- const mediaRecorder = new MediaRecorder(this.stream, {
- mimeType: 'video/webm;codecs=H264,opus',
- audioBitsPerSecond: videoConstraints.value.audioBitsPerSecond,
- videoBitsPerSecond: videoConstraints.value.videoBitsPerSecond,
- });
- this.mediaRecorder = mediaRecorder;
- this.mediaRecorder.ondataavailable = (event) => {
- this.recordChunks.push(event.data);
- this.emit(
- 'record',
- new Blob([event.data], {
- type: 'video/mp4; codecs=h264',
- }),
- );
- };
- this.mediaRecorder.stop = () => {
- // setTimeout(() => {
- // this.handleAutoDownload();
- // }, 1000);
- };
- }
- private handleAutoDownload(chunks: Blob[]): void {
- const downloadBlob = new Blob(chunks, {
- type: 'video/mp4; codecs=h264',
- });
- const url = URL.createObjectURL(downloadBlob);
- const a: HTMLAnchorElement = document.createElement('a');
- document.body.appendChild(a);
- a.style.display = 'none';
- a.href = url;
- a.download = 'test.mp4';
- a.click();
- window.URL.revokeObjectURL(url);
- }
- public updateCanvas(canvas: HTMLCanvasElement) {
- if (this.isCanvas) {
- this.canvasElement = canvas;
- }
- }
- private uploadToServer(): void { }
- }
|