Skip to content

Commit 49278e6

Browse files
authored
Merge pull request #180 from shadielhajj/master
Improved recording capabilities and configurability.
2 parents 2da008a + a961281 commit 49278e6

9 files changed

+147
-5
lines changed

README.md

+16
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,14 @@ The extension also supports highlighting of compilation errors in the text edito
154154

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

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

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

177185
## Release Notes
178186

187+
### 0.11.4
188+
* Added `shader-toy.recordVideoContainer` (set video file container),
189+
* Added `shader-toy.recordVideoCodec` (set video codec),
190+
* Added `shader-toy.recordVideoBitRate` (set recording bit rate),
191+
* Added `shader-toy.recordMaxDuration` (set maximum recording duration),
192+
* Fixed the `shader-toy.recordTargetFramerate` setting,
193+
* Moved the Stats widget to the bottom left, so it doesn't overlap with the GUI.
194+
179195
### 0.11.3
180196
* Added option to reload on save,
181197
* fixed a bug where glslify would never find modules.

package.json

+21
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,27 @@
113113
"default": 30,
114114
"description": "Framerate that is targeted when using the record button."
115115
},
116+
"shader-toy.recordVideoContainer": {
117+
"type": "string",
118+
"default": "webm",
119+
"description": "Video file container. Currently only webm is supported, but mp4support is coming."
120+
},
121+
"shader-toy.recordVideoCodec": {
122+
"type": "string",
123+
"default": "vp8",
124+
"enum": ["vp8", "vp9", "h264", "avc1"],
125+
"description": "Video codec. vp8, vp9, h264 and avc1 are all supported. Default it vp8."
126+
},
127+
"shader-toy.recordVideoBitRate": {
128+
"type": "number",
129+
"default": 2500000,
130+
"description": "Video encoding bit rate in bits/seconds."
131+
},
132+
"shader-toy.recordMaxDuration": {
133+
"type": "number",
134+
"default": 0,
135+
"description": "Maximum recording duration in seconds. 0 (the default) will keep recording until the record button is pressed again."
136+
},
116137
"shader-toy.showPauseButton": {
117138
"type": "boolean",
118139
"default": true,

resources/webview_base.html

+27-4
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@
235235
let normalizedMouse = new THREE.Vector2(<!-- Start Normalized Mouse -->);
236236
let frameCounter = 0;
237237
let recorder = null;
238+
let recordingTimeout = null;
238239

239240
// Audio Init
240241
// Audio Resume
@@ -418,10 +419,14 @@
418419
}
419420
function computeSize() {
420421

422+
let forcedResolutions = [<!-- Forced Resolution -->];
423+
let zoom = forcedResolutions[0] > 0 ? 1.0/window.devicePixelRatio : 1.0;
424+
canvas.style.zoom = zoom;
425+
421426
// Compute forced aspect ratio and align canvas
422427
resolution = forceAspectRatio(window.innerWidth, window.innerHeight);
423-
canvas.style.left = `${(window.innerWidth - resolution.x) / 2}px`;
424-
canvas.style.top = `${(window.innerHeight - resolution.y) / 2}px`;
428+
canvas.style.left = `${(window.innerWidth - resolution.x*zoom) / 2}px`;
429+
canvas.style.top = `${(window.innerHeight - resolution.y*zoom) / 2}px`;
425430

426431
for (let buffer of buffers) {
427432
if (buffer.Target) {
@@ -478,22 +483,40 @@
478483
}
479484
function recordAction() {
480485
let recordButton = document.getElementById("record");
486+
if (recordingTimeout != null) {
487+
clearTimeout(recordingTimeout);
488+
recordingTimeout = null;
489+
}
481490
if (recorder == null) {
482491
recordButton.classList.add('recording');
483492

493+
let videoContainer = <!-- Record Video Container -->;
494+
let videoCodec = <!-- Record Video Codec -->;
495+
let maxDuration = <!-- Record Max Duration -->;
496+
484497
let stream = canvas.captureStream(<!-- Record Target Framerate -->);
485498
let recorderOptions = {
486-
mimeType: "video/webm"
499+
videoBitsPerSecond: <!-- Record Video Bit Rate -->,
500+
mimeType: `video/${videoContainer};codecs=${videoCodec}`
487501
};
488502
recorder = new MediaRecorder(stream, recorderOptions);
489503
recorder.start();
490504
recorder.ondataavailable = function(evt) {
491505
let a = document.createElement('a');
492506
let url = URL.createObjectURL(evt.data);
493507
a.href = url;
494-
a.download = 'shadertoy.webm';
508+
a.download = `shadertoy.${videoContainer}`;
495509
a.click();
496510
};
511+
512+
if (maxDuration > 0) {
513+
recordingTimeout = setTimeout(() => {
514+
recordButton.classList.remove('recording');
515+
recorder.stop();
516+
recorder = null;
517+
recordingTimeout = null;
518+
}, maxDuration*1000);
519+
}
497520
}
498521
else {
499522
recordButton.classList.remove('recording');

src/extensions/packages/stats_extension.ts

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ export class StatsExtension implements WebviewExtension {
3434
let stats = new Stats();
3535
compileTimePanel = stats.addPanel(new Stats.Panel('CT MS', '#ff8', '#221'));
3636
stats.showPanel(1);
37+
stats.${domElement}.style.removeProperty('top');
38+
stats.${domElement}.style.bottom = '0px';
3739
document.body.appendChild(stats.${domElement});
3840
requestAnimationFrame(function loop() {
3941
stats.update();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
'use strict';
2+
3+
import { WebviewExtension } from '../webview_extension';
4+
5+
export class RecordMaxDurationExtension implements WebviewExtension {
6+
private recordMaxDuration: number;
7+
8+
public constructor(recordMaxDuration: number) {
9+
this.recordMaxDuration = recordMaxDuration;
10+
}
11+
12+
public generateContent(): string {
13+
return `${this.recordMaxDuration}`;
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
'use strict';
2+
3+
import { WebviewExtension } from '../webview_extension';
4+
5+
export class RecordVideoBitRateExtension implements WebviewExtension {
6+
private recordVideoBitRate: number;
7+
8+
public constructor(recordVideoBitRate: number) {
9+
this.recordVideoBitRate = recordVideoBitRate;
10+
}
11+
12+
public generateContent(): string {
13+
return `${this.recordVideoBitRate}`;
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
'use strict';
2+
3+
import { WebviewExtension } from '../webview_extension';
4+
5+
export class RecordVideoCodecExtension implements WebviewExtension {
6+
private recordVideoCodec: string;
7+
8+
public constructor(recordVideoCodec: string) {
9+
this.recordVideoCodec = recordVideoCodec;
10+
}
11+
12+
public generateContent(): string {
13+
return `"${this.recordVideoCodec}"`;
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
'use strict';
2+
3+
import { WebviewExtension } from '../webview_extension';
4+
5+
export class RecordVideoContainerExtension implements WebviewExtension {
6+
private recordVideoContainer: string;
7+
8+
public constructor(recordVideoContainer: string) {
9+
this.recordVideoContainer = recordVideoContainer;
10+
}
11+
12+
public generateContent(): string {
13+
return `"${this.recordVideoContainer}"`;
14+
}
15+
}

src/webviewcontentprovider.ts

+21-1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ import { UniformsPreambleExtension } from './extensions/uniforms/uniforms_preamb
6767

6868
import { removeDuplicates } from './utility';
6969
import { RecordTargetFramerateExtension } from './extensions/user_interface/record_target_framerate_extension';
70+
import { RecordVideoContainerExtension } from './extensions/user_interface/record_video_container_extension';
71+
import { RecordVideoCodecExtension } from './extensions/user_interface/record_video_codec_extension';
72+
import { RecordVideoBitRateExtension } from './extensions/user_interface/record_video_bit_rate_extension';
73+
import { RecordMaxDurationExtension } from './extensions/user_interface/record_max_duration_extension';
7074

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

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

395+
const recordVideoContainer = this.context.getConfig<string>('recordVideoContainer') || "webm";
396+
const recordVideoContainerExtension = new RecordVideoContainerExtension(recordVideoContainer);
397+
this.webviewAssembler.addReplaceModule(recordVideoContainerExtension, 'let videoContainer = <!-- Record Video Container -->;', '<!-- Record Video Container -->');
398+
399+
const recordVideoCodec = this.context.getConfig<string>('recordVideoCodec') || "vp8";
400+
const recordVideoCodecExtension = new RecordVideoCodecExtension(recordVideoCodec);
401+
this.webviewAssembler.addReplaceModule(recordVideoCodecExtension, 'let videoCodec = <!-- Record Video Codec -->;', '<!-- Record Video Codec -->');
402+
403+
const recordVideoBitRate = this.context.getConfig<number>('recordVideoBitRate') || 2500000;
404+
const recordVideoBitRateExtension = new RecordVideoBitRateExtension(recordVideoBitRate);
405+
this.webviewAssembler.addReplaceModule(recordVideoBitRateExtension, 'videoBitsPerSecond: <!-- Record Video Bit Rate -->,', '<!-- Record Video Bit Rate -->');
406+
407+
const recordMaxDuration = this.context.getConfig<number>('recordMaxDuration') || 0;
408+
const recordMaxDurationExtension = new RecordMaxDurationExtension(recordMaxDuration);
409+
this.webviewAssembler.addReplaceModule(recordMaxDurationExtension, 'let maxDuration = <!-- Record Max Duration -->;', '<!-- Record Max Duration -->');
410+
391411
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
392412
// Reload Logic
393413
if (!generateStandalone) {

0 commit comments

Comments
 (0)