Skip to content

Commit

Permalink
Gif Encoder (#7)
Browse files Browse the repository at this point in the history
* Initial GIF Encoder implementation

* Add GifEncoder to EncoderDropdown
  • Loading branch information
chrisgervang authored Jun 8, 2020
1 parent 5cb40fd commit 4f731d1
Show file tree
Hide file tree
Showing 11 changed files with 113 additions and 12 deletions.
3 changes: 3 additions & 0 deletions examples/minimal/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ const encoderSettings = {
},
jpeg: {
quality: 0.8
},
gif: {
sampleInterval: 1000
}
};

Expand Down
3 changes: 3 additions & 0 deletions examples/terrain/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ const encoderSettings = {
},
jpeg: {
quality: 0.8
},
gif: {
sampleInterval: 1000
}
};

Expand Down
2 changes: 2 additions & 0 deletions modules/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
"build-bundle": "webpack --display=minimal --config ../../scripts/bundle.config.js"
},
"dependencies": {
"@loaders.gl/core": "^2.1.6",
"@loaders.gl/video": "2.2.0-alpha.1",
"@luma.gl/engine": "^8.1.2",
"downloadjs": "^1.4.7",
"popmotion": "^8.6.10",
Expand Down
4 changes: 4 additions & 0 deletions modules/core/src/capture/video-capture.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export class VideoCapture {
*/
render(Encoder, encoderSettings, sceneLengthMs, onStop = undefined) {
if (!this.isRecording()) {
console.time('render');
encoderSettings = this.parseEncoderSettings(encoderSettings, sceneLengthMs);

console.log(`Starting recording for ${this.durationMs}ms.`);
Expand Down Expand Up @@ -169,17 +170,20 @@ export class VideoCapture {
* @param {{ (blob: Blob): boolean }} [callback]
*/
save(callback) {
console.timeEnd('render');
if (!callback) {
/**
* @param {Blob} blob
*/
callback = blob => {
console.timeEnd('save');
if (blob) {
download(blob, this.filename + this.encoder.extension, this.encoder.mimeType);
}
return false;
};
}
console.time('save');
this.encoder.save().then(callback);
}

Expand Down
1 change: 1 addition & 0 deletions modules/core/src/encoders/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ export {default as WebMEncoder} from './video/webm-encoder';
export {default as StreamEncoder} from './video/stream-encoder';
export {default as FrameEncoder} from './frame-encoder';
export {default as PreviewEncoder} from './utils/preview-encoder';
export {default as GifEncoder} from './video/gif-encoder';
49 changes: 49 additions & 0 deletions modules/core/src/encoders/video/gif-encoder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import {GIFBuilder} from '@loaders.gl/video';
import FrameEncoder from '../frame-encoder';

export default class GifEncoder extends FrameEncoder {
/** @param {import('types').FrameEncoderSettings} settings */
constructor(settings) {
super(settings);
this.mimeType = 'image/gif';
this.extension = '.gif';
this.gifBuilder = null;
this.options = {};

if (settings.gif) {
this.options = settings.gif;
}

this.options.width = this.options.width || 720;
this.options.height = this.options.height || 480;
this.options.numWorkers = this.options.numWorkers || 4;
this.options.sampleInterval = this.options.sampleInterval || 10;

// this.source = settings.source
this.source = 'images';

this.start = this.start.bind(this);
this.add = this.add.bind(this);
this.save = this.save.bind(this);
}

start() {
this.gifBuilder = new GIFBuilder({
source: this.source,
...this.options,
interval: 1 / this.framerate
});
}

/** @param {HTMLCanvasElement} canvas */
async add(canvas) {
if (this.source === 'images') {
const dataUrl = canvas.toDataURL('image/jpeg', 0.8);
await this.gifBuilder.add(dataUrl);
}
}

async save() {
return this.gifBuilder.build();
}
}
3 changes: 2 additions & 1 deletion modules/core/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ export {
PNGEncoder,
WebMEncoder,
FrameEncoder,
PreviewEncoder
PreviewEncoder,
GifEncoder
} from './encoders';

export {
Expand Down
14 changes: 6 additions & 8 deletions modules/core/src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,12 @@ interface EncoderSettings {
webm: {
quality: number
}
// gif: {
// numWorkers: number,
// sampleInterval: number,
// resize: {
// width: number,
// height: number
// }
// },
gif: {
numWorkers: number,
sampleInterval: number,
width: number,
height: number
},
}

interface DeckSceneParams {
Expand Down
7 changes: 5 additions & 2 deletions modules/react/src/components/basic-controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@ import {
WebMEncoder,
JPEGSequenceEncoder,
PNGSequenceEncoder,
PreviewEncoder
PreviewEncoder,
GifEncoder
} from '@hubble.gl/core';
import EncoderDropdown from './encoder-dropdown';

export default function BasicControls({adapter, busy, setBusy, encoderSettings}) {
const [encoder, setEncoder] = useState('webm');
const [encoder, setEncoder] = useState('gif');

const onRender = () => {
if (encoder === 'preview') {
Expand All @@ -39,6 +40,8 @@ export default function BasicControls({adapter, busy, setBusy, encoderSettings})
adapter.render(JPEGSequenceEncoder, encoderSettings, () => setBusy(false));
} else if (encoder === 'png') {
adapter.render(PNGSequenceEncoder, encoderSettings, () => setBusy(false));
} else if (encoder === 'gif') {
adapter.render(GifEncoder, encoderSettings, () => setBusy(false));
}

setBusy(true);
Expand Down
1 change: 1 addition & 0 deletions modules/react/src/components/encoder-dropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export default function EncoderDropdown({disabled, encoder, setEncoder}) {
<option value="webm">WebM</option>
<option value="jpeg">JPEG Sequence</option>
<option value="png">PNG Sequence</option>
<option value="gif">GIF</option>
</select>
);
}
38 changes: 37 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,13 @@
dependencies:
regenerator-runtime "^0.13.4"

"@babel/runtime@^7.3.1":
version "7.10.2"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.2.tgz#d103f21f2602497d38348a32e008637d506db839"
integrity sha512-6sF3uQw2ivImfVIl62RZ7MXhO2tap69WeWK57vAaimT6AZbE4FbqjdEJIN1UqoD6wI6B+1n9UiagafH1sxjOtg==
dependencies:
regenerator-runtime "^0.13.4"

"@babel/template@^7.4.0", "@babel/template@^7.8.3", "@babel/template@^7.8.6":
version "7.8.6"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b"
Expand Down Expand Up @@ -1645,6 +1652,30 @@
npmlog "^4.1.2"
write-file-atomic "^2.3.0"

"@loaders.gl/core@^2.1.6":
version "2.1.6"
resolved "https://registry.yarnpkg.com/@loaders.gl/core/-/core-2.1.6.tgz#9c3e0cf5a1aebb672c50876ab75fd35033b8672c"
integrity sha512-1uxlSZ3dKbraWmvsOsBeSfJxR/+mXjDCsDLkl8N9QzXeN1nIer3siDPLqckox4MMsPxPo5uo1Be8iwORAUo3VQ==
dependencies:
"@babel/runtime" "^7.3.1"
"@loaders.gl/loader-utils" "2.1.6"

"@loaders.gl/[email protected]", "@loaders.gl/loader-utils@^2.1.3":
version "2.1.6"
resolved "https://registry.yarnpkg.com/@loaders.gl/loader-utils/-/loader-utils-2.1.6.tgz#8eb5a9a2215b3c94c3bb548767cbc276e3701a5c"
integrity sha512-V9ZJH60HwSmSIw2jqNDMFpDqZVfy02WZ1/1e2ENw3PODE8ubNqs1bQ8nFj9omqwVMPF7fBVLvzoeoGrpeW0gfQ==
dependencies:
"@babel/runtime" "^7.3.1"
"@probe.gl/stats" "^3.2.1"

"@loaders.gl/[email protected]":
version "2.2.0-alpha.1"
resolved "https://registry.yarnpkg.com/@loaders.gl/video/-/video-2.2.0-alpha.1.tgz#1bb09e9e507605a72c6e8a13b34926b69c717191"
integrity sha512-4NDe3fNPOTS4MvPxlxrbqr/kKgkB9tOVY2in/s/oYBo0uiDK+WSU0AztnyKxF1b/EsHzTEIzhOEhjvSwbgRQgQ==
dependencies:
"@loaders.gl/loader-utils" "^2.1.3"
gifshot "^0.4.5"

"@luma.gl/[email protected]":
version "8.1.2"
resolved "https://registry.yarnpkg.com/@luma.gl/constants/-/constants-8.1.2.tgz#729fb10ba6dc6946ccb2745f4b4d22c2f4b021d7"
Expand Down Expand Up @@ -1837,7 +1868,7 @@
"@babel/runtime" "^7.0.0"
probe.gl "3.2.1"

"@probe.gl/[email protected]":
"@probe.gl/[email protected]", "@probe.gl/stats@^3.2.1":
version "3.2.1"
resolved "https://registry.yarnpkg.com/@probe.gl/stats/-/stats-3.2.1.tgz#4e95c75513229ebb01384045e89524a466c022eb"
integrity sha512-tXe5krgbodxtVdUVWG4oIicMoCHNGp7QYkaHSDrUeTfJVqYuZz99T6r7qmn0bCo4wQlzn936jJ+QiAltHxatig==
Expand Down Expand Up @@ -4848,6 +4879,11 @@ getpass@^0.1.1:
dependencies:
assert-plus "^1.0.0"

gifshot@^0.4.5:
version "0.4.5"
resolved "https://registry.yarnpkg.com/gifshot/-/gifshot-0.4.5.tgz#e3cb570203a3b139ff3069d7578098a29c03b0f8"
integrity sha1-48tXAgOjsTn/MGnXV4CYopwDsPg=

[email protected]:
version "2.0.0"
resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-2.0.0.tgz#d92addf74440c14bcc5c83ecce3fb7f8a79118b5"
Expand Down

0 comments on commit 4f731d1

Please sign in to comment.