Skip to content

Commit

Permalink
Merge pull request #180 from shadielhajj/master
Browse files Browse the repository at this point in the history
Improved recording capabilities and configurability.
  • Loading branch information
Malacath-92 committed Jun 1, 2024
2 parents 2da008a + a961281 commit 49278e6
Show file tree
Hide file tree
Showing 9 changed files with 147 additions and 5 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,14 @@ The extension also supports highlighting of compilation errors in the text edito

![error example](https://github.com/raw/stevensona/shader-toy/master/images/example3.png)

## Recording Capabilities
The following settings allow to configure recording quality
* `shader-toy.recordVideoContainer`: Set the video file container. Currently only `webm` is supported, but `mp4`support is coming [soon](https://chromestatus.com/feature/5163469011943424).
* `shader-toy.recordVideoCodec`: Set video codec. `vp8`, `vp9`, `h264` and `avc1` are all supported. Default it `vp8`.
* `shader-toy.recordVideoBitRate`: Set recording bit rate in bits/second. Default is 2500000.
* `shader-toy.recordTargetFramerate`: Set recording target frame-rate. Default is 30fps.
* `shader-toy.recordMaxDuration`: Maximum recording duration in seconds. 0 (the default) will keep recording until the record button is pressed again.

## Requirements

* A graphics card supporting WebGL.
Expand All @@ -176,6 +184,14 @@ Contributions of any kind are welcome and encouraged.

## Release Notes

### 0.11.4
* Added `shader-toy.recordVideoContainer` (set video file container),
* Added `shader-toy.recordVideoCodec` (set video codec),
* Added `shader-toy.recordVideoBitRate` (set recording bit rate),
* Added `shader-toy.recordMaxDuration` (set maximum recording duration),
* Fixed the `shader-toy.recordTargetFramerate` setting,
* Moved the Stats widget to the bottom left, so it doesn't overlap with the GUI.

### 0.11.3
* Added option to reload on save,
* fixed a bug where glslify would never find modules.
Expand Down
21 changes: 21 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,27 @@
"default": 30,
"description": "Framerate that is targeted when using the record button."
},
"shader-toy.recordVideoContainer": {
"type": "string",
"default": "webm",
"description": "Video file container. Currently only webm is supported, but mp4support is coming."
},
"shader-toy.recordVideoCodec": {
"type": "string",
"default": "vp8",
"enum": ["vp8", "vp9", "h264", "avc1"],
"description": "Video codec. vp8, vp9, h264 and avc1 are all supported. Default it vp8."
},
"shader-toy.recordVideoBitRate": {
"type": "number",
"default": 2500000,
"description": "Video encoding bit rate in bits/seconds."
},
"shader-toy.recordMaxDuration": {
"type": "number",
"default": 0,
"description": "Maximum recording duration in seconds. 0 (the default) will keep recording until the record button is pressed again."
},
"shader-toy.showPauseButton": {
"type": "boolean",
"default": true,
Expand Down
31 changes: 27 additions & 4 deletions resources/webview_base.html
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@
let normalizedMouse = new THREE.Vector2(<!-- Start Normalized Mouse -->);
let frameCounter = 0;
let recorder = null;
let recordingTimeout = null;

// Audio Init
// Audio Resume
Expand Down Expand Up @@ -418,10 +419,14 @@
}
function computeSize() {

let forcedResolutions = [<!-- Forced Resolution -->];
let zoom = forcedResolutions[0] > 0 ? 1.0/window.devicePixelRatio : 1.0;
canvas.style.zoom = zoom;

// Compute forced aspect ratio and align canvas
resolution = forceAspectRatio(window.innerWidth, window.innerHeight);
canvas.style.left = `${(window.innerWidth - resolution.x) / 2}px`;
canvas.style.top = `${(window.innerHeight - resolution.y) / 2}px`;
canvas.style.left = `${(window.innerWidth - resolution.x*zoom) / 2}px`;
canvas.style.top = `${(window.innerHeight - resolution.y*zoom) / 2}px`;

for (let buffer of buffers) {
if (buffer.Target) {
Expand Down Expand Up @@ -478,22 +483,40 @@
}
function recordAction() {
let recordButton = document.getElementById("record");
if (recordingTimeout != null) {
clearTimeout(recordingTimeout);
recordingTimeout = null;
}
if (recorder == null) {
recordButton.classList.add('recording');

let videoContainer = <!-- Record Video Container -->;
let videoCodec = <!-- Record Video Codec -->;
let maxDuration = <!-- Record Max Duration -->;

let stream = canvas.captureStream(<!-- Record Target Framerate -->);
let recorderOptions = {
mimeType: "video/webm"
videoBitsPerSecond: <!-- Record Video Bit Rate -->,
mimeType: `video/${videoContainer};codecs=${videoCodec}`
};
recorder = new MediaRecorder(stream, recorderOptions);
recorder.start();
recorder.ondataavailable = function(evt) {
let a = document.createElement('a');
let url = URL.createObjectURL(evt.data);
a.href = url;
a.download = 'shadertoy.webm';
a.download = `shadertoy.${videoContainer}`;
a.click();
};

if (maxDuration > 0) {
recordingTimeout = setTimeout(() => {
recordButton.classList.remove('recording');
recorder.stop();
recorder = null;
recordingTimeout = null;
}, maxDuration*1000);
}
}
else {
recordButton.classList.remove('recording');
Expand Down
2 changes: 2 additions & 0 deletions src/extensions/packages/stats_extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ export class StatsExtension implements WebviewExtension {
let stats = new Stats();
compileTimePanel = stats.addPanel(new Stats.Panel('CT MS', '#ff8', '#221'));
stats.showPanel(1);
stats.${domElement}.style.removeProperty('top');
stats.${domElement}.style.bottom = '0px';
document.body.appendChild(stats.${domElement});
requestAnimationFrame(function loop() {
stats.update();
Expand Down
15 changes: 15 additions & 0 deletions src/extensions/user_interface/record_max_duration_extension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
'use strict';

import { WebviewExtension } from '../webview_extension';

export class RecordMaxDurationExtension implements WebviewExtension {
private recordMaxDuration: number;

public constructor(recordMaxDuration: number) {
this.recordMaxDuration = recordMaxDuration;
}

public generateContent(): string {
return `${this.recordMaxDuration}`;
}
}
15 changes: 15 additions & 0 deletions src/extensions/user_interface/record_video_bit_rate_extension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
'use strict';

import { WebviewExtension } from '../webview_extension';

export class RecordVideoBitRateExtension implements WebviewExtension {
private recordVideoBitRate: number;

public constructor(recordVideoBitRate: number) {
this.recordVideoBitRate = recordVideoBitRate;
}

public generateContent(): string {
return `${this.recordVideoBitRate}`;
}
}
15 changes: 15 additions & 0 deletions src/extensions/user_interface/record_video_codec_extension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
'use strict';

import { WebviewExtension } from '../webview_extension';

export class RecordVideoCodecExtension implements WebviewExtension {
private recordVideoCodec: string;

public constructor(recordVideoCodec: string) {
this.recordVideoCodec = recordVideoCodec;
}

public generateContent(): string {
return `"${this.recordVideoCodec}"`;
}
}
15 changes: 15 additions & 0 deletions src/extensions/user_interface/record_video_container_extension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
'use strict';

import { WebviewExtension } from '../webview_extension';

export class RecordVideoContainerExtension implements WebviewExtension {
private recordVideoContainer: string;

public constructor(recordVideoContainer: string) {
this.recordVideoContainer = recordVideoContainer;
}

public generateContent(): string {
return `"${this.recordVideoContainer}"`;
}
}
22 changes: 21 additions & 1 deletion src/webviewcontentprovider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ import { UniformsPreambleExtension } from './extensions/uniforms/uniforms_preamb

import { removeDuplicates } from './utility';
import { RecordTargetFramerateExtension } from './extensions/user_interface/record_target_framerate_extension';
import { RecordVideoContainerExtension } from './extensions/user_interface/record_video_container_extension';
import { RecordVideoCodecExtension } from './extensions/user_interface/record_video_codec_extension';
import { RecordVideoBitRateExtension } from './extensions/user_interface/record_video_bit_rate_extension';
import { RecordMaxDurationExtension } from './extensions/user_interface/record_max_duration_extension';

export class WebviewContentProvider {
private context: Context;
Expand Down Expand Up @@ -384,10 +388,26 @@ export class WebviewContentProvider {
const forcedScreenshotResolutionExtension = new ForcedScreenshotResolutionExtension(forcedScreenshotResolution);
this.webviewAssembler.addReplaceModule(forcedScreenshotResolutionExtension, 'let forcedScreenshotResolution = [<!-- Forced Screenshot Resolution -->];', '<!-- Forced Screenshot Resolution -->');

const recordTargetFramerate = this.context.getConfig<number>('shader-toy.recordTargetFramerate') || 30;
const recordTargetFramerate = this.context.getConfig<number>('recordTargetFramerate') || 30;
const recordTargetFramerateExtension = new RecordTargetFramerateExtension(recordTargetFramerate);
this.webviewAssembler.addReplaceModule(recordTargetFramerateExtension, 'let stream = canvas.captureStream(<!-- Record Target Framerate -->);', '<!-- Record Target Framerate -->');

const recordVideoContainer = this.context.getConfig<string>('recordVideoContainer') || "webm";
const recordVideoContainerExtension = new RecordVideoContainerExtension(recordVideoContainer);
this.webviewAssembler.addReplaceModule(recordVideoContainerExtension, 'let videoContainer = <!-- Record Video Container -->;', '<!-- Record Video Container -->');

const recordVideoCodec = this.context.getConfig<string>('recordVideoCodec') || "vp8";
const recordVideoCodecExtension = new RecordVideoCodecExtension(recordVideoCodec);
this.webviewAssembler.addReplaceModule(recordVideoCodecExtension, 'let videoCodec = <!-- Record Video Codec -->;', '<!-- Record Video Codec -->');

const recordVideoBitRate = this.context.getConfig<number>('recordVideoBitRate') || 2500000;
const recordVideoBitRateExtension = new RecordVideoBitRateExtension(recordVideoBitRate);
this.webviewAssembler.addReplaceModule(recordVideoBitRateExtension, 'videoBitsPerSecond: <!-- Record Video Bit Rate -->,', '<!-- Record Video Bit Rate -->');

const recordMaxDuration = this.context.getConfig<number>('recordMaxDuration') || 0;
const recordMaxDurationExtension = new RecordMaxDurationExtension(recordMaxDuration);
this.webviewAssembler.addReplaceModule(recordMaxDurationExtension, 'let maxDuration = <!-- Record Max Duration -->;', '<!-- Record Max Duration -->');

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Reload Logic
if (!generateStandalone) {
Expand Down

0 comments on commit 49278e6

Please sign in to comment.