Skip to content

Commit df63896

Browse files
authored
Merge pull request #21221 from mrdoob/editor
Editor: Added ffmpeg.wasm video renderer
2 parents e5139eb + 612e38f commit df63896

File tree

6 files changed

+188
-4
lines changed

6 files changed

+188
-4
lines changed

editor/index.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
<body>
1212
<link rel="stylesheet" href="css/main.css">
1313

14+
<script src="https://unpkg.com/@ffmpeg/[email protected]/dist/ffmpeg.min.js" defer></script>
15+
1416
<script src="../examples/js/libs/draco/draco_encoder.js"></script>
1517

1618
<link rel="stylesheet" href="js/libs/codemirror/codemirror.css">

editor/js/Sidebar.Project.Video.js

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import { UIBreak, UIButton, UIInteger, UIPanel, UIProgress, UIRow, UIText } from './libs/ui.js';
2+
3+
import { APP } from './libs/app.js';
4+
5+
function SidebarProjectVideo( editor ) {
6+
7+
var container = new UIPanel();
8+
container.setId( 'render' );
9+
10+
// Video
11+
12+
container.add( new UIText( 'Video' ).setTextTransform( 'uppercase' ) );
13+
container.add( new UIBreak(), new UIBreak() );
14+
15+
// Resolution
16+
17+
var resolutionRow = new UIRow();
18+
container.add( resolutionRow );
19+
20+
resolutionRow.add( new UIText( 'Resolution' ).setWidth( '90px' ) );
21+
22+
var videoWidth = new UIInteger( 600 ).setWidth( '28px' );
23+
resolutionRow.add( videoWidth );
24+
25+
resolutionRow.add( new UIText( '×' ).setFontSize( '12px' ).setWidth( '14px' ) );
26+
27+
var videoHeight = new UIInteger( 600 ).setWidth( '28px' );
28+
resolutionRow.add( videoHeight );
29+
30+
var videoFPS = new UIInteger( 30 ).setTextAlign( 'center' ).setWidth( '20px' );
31+
resolutionRow.add( videoFPS );
32+
33+
resolutionRow.add( new UIText( 'fps' ).setFontSize( '12px' ) );
34+
35+
// Duration
36+
37+
var videoDurationRow = new UIRow();
38+
videoDurationRow.add( new UIText( 'Duration' ).setWidth( '90px' ) );
39+
40+
var videoDuration = new UIInteger( 10 );
41+
videoDurationRow.add( videoDuration );
42+
43+
container.add( videoDurationRow );
44+
45+
// Render
46+
47+
container.add( new UIText( '' ).setWidth( '90px' ) );
48+
49+
const progress = new UIProgress( 0 );
50+
progress.setDisplay( 'none' );
51+
progress.setWidth( '170px' );
52+
container.add( progress );
53+
54+
const renderButton = new UIButton( 'RENDER' );
55+
renderButton.setWidth( '170px' );
56+
renderButton.onClick( async () => {
57+
58+
renderButton.setDisplay( 'none' );
59+
progress.setDisplay( '' );
60+
progress.setValue( 0 );
61+
62+
const player = new APP.Player();
63+
player.load( editor.toJSON() );
64+
player.setPixelRatio( 1 );
65+
player.setSize( videoWidth.getValue(), videoHeight.getValue() );
66+
67+
const canvas = player.dom.firstElementChild;
68+
69+
//
70+
71+
const { createFFmpeg, fetchFile } = FFmpeg; // eslint-disable-line no-undef
72+
const ffmpeg = createFFmpeg( { log: true } );
73+
74+
await ffmpeg.load();
75+
76+
ffmpeg.setProgress( ( { ratio } ) => {
77+
78+
progress.setValue( ratio );
79+
80+
} );
81+
82+
const fps = videoFPS.getValue();
83+
const duration = videoDuration.getValue();
84+
const frames = duration * fps;
85+
86+
let currentTime = 0;
87+
88+
for ( let i = 0; i < frames; i ++ ) {
89+
90+
player.render( currentTime );
91+
92+
const num = i.toString().padStart( 5, '0' );
93+
ffmpeg.FS( 'writeFile', `tmp.${num}.png`, await fetchFile( canvas.toDataURL() ) );
94+
currentTime += 1 / fps;
95+
96+
progress.setValue( i / frames );
97+
98+
}
99+
100+
await ffmpeg.run( '-framerate', String( fps ), '-pattern_type', 'glob', '-i', '*.png', '-c:v', 'libx264', '-pix_fmt', 'yuv420p', '-preset', 'slow', '-crf', String( 6 ), 'out.mp4' );
101+
102+
const data = ffmpeg.FS( 'readFile', 'out.mp4' );
103+
104+
for ( let i = 0; i < frames; i ++ ) {
105+
106+
const num = i.toString().padStart( 5, '0' );
107+
ffmpeg.FS( 'unlink', `tmp.${num}.png` );
108+
109+
}
110+
111+
save( new Blob( [ data.buffer ], { type: 'video/mp4' } ), 'out.mp4' );
112+
113+
player.dispose();
114+
115+
renderButton.setDisplay( '' );
116+
progress.setDisplay( 'none' );
117+
118+
} );
119+
container.add( renderButton );
120+
121+
// SAVE
122+
123+
const link = document.createElement( 'a' );
124+
125+
function save( blob, filename ) {
126+
127+
link.href = URL.createObjectURL( blob );
128+
link.download = filename;
129+
link.dispatchEvent( new MouseEvent( 'click' ) );
130+
131+
// URL.revokeObjectURL( url ); breaks Firefox...
132+
133+
}
134+
135+
//
136+
137+
return container;
138+
139+
}
140+
141+
export { SidebarProjectVideo };

editor/js/Sidebar.Project.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { UIPanel, UIRow, UIInput, UICheckbox, UIText, UISpan } from './libs/ui.j
22

33
/* import { SidebarProjectMaterials } from './Sidebar.Project.Materials.js'; */
44
import { SidebarProjectRenderer } from './Sidebar.Project.Renderer.js';
5+
import { SidebarProjectVideo } from './Sidebar.Project.Video.js';
56

67
function SidebarProject( editor ) {
78

@@ -61,6 +62,7 @@ function SidebarProject( editor ) {
6162

6263
/* container.add( new SidebarProjectMaterials( editor ) ); */
6364
container.add( new SidebarProjectRenderer( editor ) );
65+
container.add( new SidebarProjectVideo( editor ) );
6466

6567
return container;
6668

editor/js/libs/app.js

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
1-
21
var APP = {
32

43
Player: function () {
54

65
var renderer = new THREE.WebGLRenderer( { antialias: true } );
7-
renderer.setPixelRatio( window.devicePixelRatio );
6+
renderer.setPixelRatio( window.devicePixelRatio ); // TODO: Use player.setPixelRatio()
87
renderer.outputEncoding = THREE.sRGBEncoding;
98

109
var loader = new THREE.ObjectLoader();
1110
var camera, scene;
1211

13-
var vrButton = VRButton.createButton( renderer );
12+
var vrButton = VRButton.createButton( renderer ); // eslint-disable-line no-undef
1413

1514
var events = {};
1615

@@ -116,6 +115,12 @@ var APP = {
116115

117116
};
118117

118+
this.setPixelRatio = function ( pixelRatio ) {
119+
120+
renderer.setPixelRatio( pixelRatio );
121+
122+
};
123+
119124
this.setSize = function ( width, height ) {
120125

121126
this.width = width;
@@ -202,6 +207,14 @@ var APP = {
202207

203208
};
204209

210+
this.render = function ( time ) {
211+
212+
dispatch( events.update, { time: time * 1000, delta: 0 /* TODO */ } );
213+
214+
renderer.render( scene, camera );
215+
216+
};
217+
205218
this.dispose = function () {
206219

207220
renderer.dispose();

editor/js/libs/ui.js

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1069,6 +1069,31 @@ UIButton.prototype.setLabel = function ( value ) {
10691069
};
10701070

10711071

1072+
// UIProgress
1073+
1074+
function UIProgress( value ) {
1075+
1076+
UIElement.call( this );
1077+
1078+
var dom = document.createElement( 'progress' );
1079+
1080+
this.dom = dom;
1081+
this.dom.value = value;
1082+
1083+
return this;
1084+
1085+
}
1086+
1087+
UIProgress.prototype = Object.create( UIElement.prototype );
1088+
UIProgress.prototype.constructor = UIProgress;
1089+
1090+
UIProgress.prototype.setValue = function ( value ) {
1091+
1092+
this.dom.value = value;
1093+
1094+
};
1095+
1096+
10721097
// UITabbedPanel
10731098

10741099
function UITabbedPanel( ) {
@@ -1351,4 +1376,4 @@ UIListbox.ListboxItem = function ( parent ) {
13511376
UIListbox.ListboxItem.prototype = Object.create( UIElement.prototype );
13521377
UIListbox.ListboxItem.prototype.constructor = UIListbox.ListboxItem;
13531378

1354-
export { UIElement, UISpan, UIDiv, UIRow, UIPanel, UIText, UIInput, UITextArea, UISelect, UICheckbox, UIColor, UINumber, UIInteger, UIBreak, UIHorizontalRule, UIButton, UITabbedPanel, UIListbox };
1379+
export { UIElement, UISpan, UIDiv, UIRow, UIPanel, UIText, UIInput, UITextArea, UISelect, UICheckbox, UIColor, UINumber, UIInteger, UIBreak, UIHorizontalRule, UIButton, UIProgress, UITabbedPanel, UIListbox };

editor/sw.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ const assets = [
134134
'./js/Sidebar.Project.js',
135135
'./js/Sidebar.Project.Materials.js',
136136
'./js/Sidebar.Project.Renderer.js',
137+
'./js/Sidebar.Project.Video.js',
137138
'./js/Sidebar.Settings.js',
138139
'./js/Sidebar.Settings.History.js',
139140
'./js/Sidebar.Settings.Shortcuts.js',

0 commit comments

Comments
 (0)