diff --git a/super-reality-client/src/renderer/components/create-leson-detached/index.tsx b/super-reality-client/src/renderer/components/create-leson-detached/index.tsx index 9ef66a92..c4fcb2ca 100644 --- a/super-reality-client/src/renderer/components/create-leson-detached/index.tsx +++ b/super-reality-client/src/renderer/components/create-leson-detached/index.tsx @@ -148,7 +148,7 @@ export default function CreateLessonDetached(): JSX.Element { if (currentRecording) { try { const file = fs - .readFileSync(`${stepSnapshotPath}/${currentRecording}.webm.json`) + .readFileSync(`${stepSnapshotPath}/${currentRecording}.json`) .toString("utf8"); json = JSON.parse(file); } catch (e) { diff --git a/super-reality-client/src/renderer/components/create-leson-detached/lesson-utils/deleteSelectedRecording.ts b/super-reality-client/src/renderer/components/create-leson-detached/lesson-utils/deleteSelectedRecording.ts index dc2fe766..bc98f03a 100644 --- a/super-reality-client/src/renderer/components/create-leson-detached/lesson-utils/deleteSelectedRecording.ts +++ b/super-reality-client/src/renderer/components/create-leson-detached/lesson-utils/deleteSelectedRecording.ts @@ -10,11 +10,11 @@ export default function deleteSelectedRecording(): void { let errored = false; if (currentRecording) { try { - const recordingVideo = `${recordingPath}/vid-${currentRecording}.webm`; + const recordingVideo = `${recordingPath}/vid-${currentRecording}.mkv`; if (fs.existsSync(recordingVideo)) { fs.unlinkSync(recordingVideo); } - const recordingJson = `${stepSnapshotPath}/${currentRecording}.webm.json`; + const recordingJson = `${stepSnapshotPath}/${currentRecording}.json`; if (fs.existsSync(recordingJson)) { fs.unlinkSync(recordingJson); } diff --git a/super-reality-client/src/renderer/components/create-leson-detached/recorder/CVEditor/index.js b/super-reality-client/src/renderer/components/create-leson-detached/recorder/CVEditor/index.js index 1697ab83..e6d11c35 100644 --- a/super-reality-client/src/renderer/components/create-leson-detached/recorder/CVEditor/index.js +++ b/super-reality-client/src/renderer/components/create-leson-detached/recorder/CVEditor/index.js @@ -1,3 +1,6 @@ +import { exec } from "child_process"; +import shell from "any-shell-escape"; +import pathToFfmpeg from "../../../../../utils/files/pathToFfmpeg"; import reduxAction from "../../../../redux/reduxAction"; import store from "../../../../redux/stores/renderer"; @@ -50,6 +53,30 @@ export default class CVEditor { arg: null, }); } +<<<<<<< HEAD + + trimVideo(trimFrom, trimTo, width, height, x, y, src, dst){ + const ffmpegCommand = shell([ + pathToFfmpeg, + '-i', src, + '-filter:v', 'crop='+width+':'+height+':'+x+':'+y, + '-ss', trimFrom, + '-to', trimTo, + '-video_size', width+'x'+height, + '-async', '1', + dst + ]) + + exec(ffmpegCommand, (err) => { + if (err) { + console.error(err) + } else { + console.info('Video Trimmed!') + } + }) + } +======= +>>>>>>> 92996ad056f235deadc5ea7d4bcf027d64bb539a } export function getRawAudioData(pathToAudio) { @@ -73,3 +100,28 @@ export function getRawAudioData(pathToAudio) { } }); } + +export function trimVideo(trimFrom, trimTo, width, height, x, y, src, dst) { + const ffmpegCommand = shell([ + pathToFfmpeg(), + "-i", + src, + "-filter:v", + `crop=${width}:${height}:${x}:${y}, scale=${width}x${height},setdar=16:9`, + "-ss", + trimFrom, + "-to", + trimTo, + "-async", + "1", + dst, + ]); + + exec(ffmpegCommand, (err) => { + if (err) { + console.error(err); + } else { + console.info("Video Trimmed!"); + } + }); +} diff --git a/super-reality-client/src/renderer/components/create-leson-detached/recorder/CVRecorder/index.js b/super-reality-client/src/renderer/components/create-leson-detached/recorder/CVRecorder/index.js index a0e40653..ed605ab5 100644 --- a/super-reality-client/src/renderer/components/create-leson-detached/recorder/CVRecorder/index.js +++ b/super-reality-client/src/renderer/components/create-leson-detached/recorder/CVRecorder/index.js @@ -1,5 +1,7 @@ /* eslint-disable prefer-destructuring */ /* eslint-disable lines-between-class-members */ +import shell from "any-shell-escape"; +import { exec } from "child_process"; import getWebsiteUrlByTitle from "../../../../../utils/getWebsiteUrlByTitle"; import { voidFunction } from "../../../../constants"; import { @@ -8,9 +10,10 @@ import { stepSnapshotPath, } from "../../../../electron-constants"; import globalData from "../../../../globalData"; +import pathToFfmpeg from "../../../../../utils/files/pathToFfmpeg"; /* eslint-disable radix */ -const { desktopCapturer } = require("electron"); +const { desktopCapturer, remote } = require("electron"); const fs = require("fs"); const { Decoder, tools, Reader } = require("ts-ebml"); @@ -53,6 +56,9 @@ export default class CVRecorder { }); }; + this._child = null; + this._ffmpegCommand = null; + this.start = this.start.bind(this); this.extractClickedImages = this.extractClickedImages.bind(this); this.handleStop = this.handleStop.bind(this); @@ -72,6 +78,12 @@ export default class CVRecorder { this.delete = this.delete.bind(this); this.stop = this.stop.bind(this); this.finishCallback = this.finishCallback.bind(this); + this.startRecordingWithoutCursor = this.startRecordingWithoutCursor.bind( + this + ); + this.stopRecordingWithoutCursor = this.stopRecordingWithoutCursor.bind( + this + ); } set finishCallback(value) { @@ -201,6 +213,7 @@ export default class CVRecorder { let mainImage = cap.read(); const jsonMetaData = { + filename: `vid-${this._stepRecordingName}.mkv`, step_data: [], }; @@ -418,7 +431,7 @@ export default class CVRecorder { seekableVideoBlob.then((blob) => { blob.arrayBuffer().then((arrayBuffer) => { const buffer = Buffer.from(arrayBuffer); - this._recordingFullPath = `${this._recordingPath}vid-${this._stepRecordingName}`; + this._recordingFullPath = `${this._recordingPath}vid-${this._stepRecordingName}.webm`; console.log("recording path: ", this._recordingFullPath); if (this._recordingFullPath) { fs.writeFile( @@ -440,7 +453,7 @@ export default class CVRecorder { audioBlob.arrayBuffer().then((arrayBuffer) => { const buffer = Buffer.from(arrayBuffer); - this._audioRecordingFullPath = `${this._recordingPath}aud-${this._stepRecordingName}`; + this._audioRecordingFullPath = `${this._recordingPath}aud-${this._stepRecordingName}.webm`; this._fileNameAndExtension = this._audioRecordingFullPath.split("."); this._audioRecordingFullPath = `${this._fileNameAndExtension[0]}.webm`; if (this._audioRecordingFullPath) { @@ -570,12 +583,69 @@ export default class CVRecorder { clearInterval(this._started); } + startRecordingWithoutCursor() { + const display = remote.screen + .getAllDisplays() + .filter((d) => `${d.id}` == this._source.display_id)[0]; + console.log(display); + const displayXpos = display.bounds.x; + const displayYpos = display.bounds.y; + const displaySize = `${display.bounds.width}x${display.bounds.height}`; + + let command = [ + pathToFfmpeg(), + '-f', 'gdigrab', // grabs stream from screen + '-draw_mouse', '0', // 0 hides and 1 shows cursor + '-i', 'desktop', // grabs whole desktop title="window name" for a particular window + '-y', + '-r', '30', + '-g', '90', + '-quality', 'realtime', + '-speed', '7', + '-threads', '8 \ ', + '-row-mt', '1', + '-tile-columns', '2', + '-frame-parallel', '1', + '-qmin', '4', + '-qmax', '48', + '-b:v', '4500k', + '-c:v', 'vp9', + '-b:a', '128k', + '-c:a', 'libopus', + `${this._recordingPath}vid-${this._stepRecordingName}.mkv`, + ]; + + if (display) { + command = [ + ...command, + "-offset_x", + displayXpos, + "-offset_y", + displayYpos, + "-video_size", + displaySize, + ]; + } + + this._ffmpegCommand = shell(command); + this._child = exec(this._ffmpegCommand, (err) => { + if (err) { + console.error(err); + } + }); + } + + stopRecordingWithoutCursor() { + this._child.stdin.write("q"); + } + start(source) { this._source = source; this._titlesQueue = []; console.log("started video recording"); return this.selectSource(this._source).then(() => { - this._stepRecordingName = `${Date.now()}.webm`; + this._stepRecordingName = `${Date.now()}`; + this.startRecordingWithoutCursor(); this._mediaRecorder.start(); this._audioMediaRecorder.start(); this._recordingStarted = true; @@ -637,6 +707,7 @@ export default class CVRecorder { this._recordingStarted = false; this.stopTimer(); this.resetTimer(); + this.stopRecordingWithoutCursor(); this._mediaRecorder.stop(); this._audioMediaRecorder.stop(); } diff --git a/super-reality-client/src/renderer/components/create-leson-detached/recorder/types.ts b/super-reality-client/src/renderer/components/create-leson-detached/recorder/types.ts index 0caa45cd..1413e8a1 100644 --- a/super-reality-client/src/renderer/components/create-leson-detached/recorder/types.ts +++ b/super-reality-client/src/renderer/components/create-leson-detached/recorder/types.ts @@ -39,5 +39,6 @@ export interface StepData { export interface RecordingJson { anchor?: string; spectrum: number[]; + filename?: string; step_data: StepData[]; } diff --git a/super-reality-client/src/renderer/components/create-leson-detached/recordings-view/index.tsx b/super-reality-client/src/renderer/components/create-leson-detached/recordings-view/index.tsx index e6ee5a23..412ec258 100644 --- a/super-reality-client/src/renderer/components/create-leson-detached/recordings-view/index.tsx +++ b/super-reality-client/src/renderer/components/create-leson-detached/recordings-view/index.tsx @@ -42,8 +42,8 @@ export default function RecordingsView() { const newFiles: string[] = []; const files = fs.readdirSync(stepSnapshotPath); files - .filter((f) => f.indexOf(".webm.json") > -1) - .map((f) => f.replace(".webm.json", "")) + .filter((f) => f.indexOf(".json") > -1) + .map((f) => f.replace(".json", "")) .forEach((f) => newFiles.push(f)); setVideos(newFiles); }, [currentRecording]); diff --git a/super-reality-client/src/renderer/components/create-leson-detached/video-preview/index.tsx b/super-reality-client/src/renderer/components/create-leson-detached/video-preview/index.tsx index 67d61e1f..a74da979 100644 --- a/super-reality-client/src/renderer/components/create-leson-detached/video-preview/index.tsx +++ b/super-reality-client/src/renderer/components/create-leson-detached/video-preview/index.tsx @@ -148,7 +148,7 @@ export default function VideoPreview(): JSX.Element { canvasSource && videoHiddenRef.current ) { - const newSrc = `${recordingPath}/vid-${canvasSource}.webm`; + const newSrc = `${recordingPath}/vid-${canvasSource}.mkv`; if (videoHiddenRef.current.src !== newSrc) { videoHiddenRef.current.src = newSrc; videoHiddenRef.current.addEventListener("loadeddata", () => { diff --git a/super-reality-client/src/renderer/redux/utils/setCanvasSource.ts b/super-reality-client/src/renderer/redux/utils/setCanvasSource.ts index 3c88398b..fa52603d 100644 --- a/super-reality-client/src/renderer/redux/utils/setCanvasSource.ts +++ b/super-reality-client/src/renderer/redux/utils/setCanvasSource.ts @@ -17,7 +17,7 @@ export default function setCanvasSource( } if ( type == "recording" && - fs.existsSync(`${recordingPath}/vid-${source}.webm`) + fs.existsSync(`${recordingPath}/vid-${source}.mkv`) ) { ok = true; canvasSourceDesc = `recording ${source}`; diff --git a/super-reality-client/src/utils/files/pathToFfmpeg.ts b/super-reality-client/src/utils/files/pathToFfmpeg.ts new file mode 100644 index 00000000..144866c6 --- /dev/null +++ b/super-reality-client/src/utils/files/pathToFfmpeg.ts @@ -0,0 +1,6 @@ +import path from "path"; +import getPublicPath from "../electron/getPublicPath"; + +export default function pathToFfmpeg(): string { + return path.join(getPublicPath(), "extra", "ffmpeg.exe"); +} diff --git a/super-reality-client/src/utils/trimAudio.ts b/super-reality-client/src/utils/trimAudio.ts index 26c369d2..b568f23f 100644 --- a/super-reality-client/src/utils/trimAudio.ts +++ b/super-reality-client/src/utils/trimAudio.ts @@ -1,7 +1,6 @@ -import path from "path"; import shell from "any-shell-escape"; import { exec } from "child_process"; -import getPublicPath from "./electron/getPublicPath"; +import pathToFfmpeg from "./files/pathToFfmpeg"; export default function trimAudio( trimFrom: string, @@ -9,11 +8,9 @@ export default function trimAudio( src: string, dst: string ): Promise { - const pathToFfmpeg = path.join(getPublicPath(), "extra", "ffmpeg.exe"); - return new Promise((resolve, reject) => { const ffmpegCommand = shell([ - pathToFfmpeg, + pathToFfmpeg(), "-ss", trimFrom, "-i",