Skip to content

[feat] seek video and update frame #98

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/worldview/src/features/renderer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export {
signalRendering,
signalPreviewing,
stopVideo,
seekTime,
filenameChange,
formatChange,
formatConfigsChange,
Expand Down
16 changes: 16 additions & 0 deletions examples/worldview/src/features/renderer/rendererMiddleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
PreviewEncoder,
GifEncoder
} from '@hubble.gl/core';
import {updateFrame} from '../timeline/timelineSlice';

import {
setupRenderer,
Expand All @@ -14,6 +15,7 @@ import {
signalRendering,
signalPreviewing,
stopVideo,
seekTime,
encoderSettingsSelector,
busySelector,
durationSelector
Expand Down Expand Up @@ -99,6 +101,20 @@ export const rendererMiddleware = store => {
}
break;
}
case seekTime.type: {
if (!busySelector(store.getState()) && adapter && adapter.scene) {
adapter.seek(action.payload);
const sceneFrame = Object.entries(adapter.scene.keyframes).reduce(
(frame, [key, keyframe]) => {
frame[key] = keyframe.getFrame();
return frame;
},
{}
);
store.dispatch(updateFrame(sceneFrame));
}
break;
}
default: {
break;
}
Expand Down
3 changes: 3 additions & 0 deletions examples/worldview/src/features/renderer/rendererSlice.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ export const previewVideo = createAction('renderer/previewVideo');
/** @param payload: getCameraKeyframes, onStop */
export const renderVideo = createAction('renderer/renderVideo');

/** @param payload: timeMs, getCameraKeyframes, getKeyframes */
export const seekTime = createAction('renderer/seekTime');

/** @param payload: boolean */
export const signalRendering = createAction('renderer/signalRendering', busy => {
if (busy) return {payload: 'rendering'};
Expand Down
10 changes: 7 additions & 3 deletions examples/worldview/src/features/timeline/timelineSlice.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import {createSlice} from '@reduxjs/toolkit';
const initialState = {
cameraKeyframes: undefined, // keyframe object
filterKeyframes: undefined, // keyframe object
layerKeyframes: {} // name: keyframe object
layerKeyframes: {}, // name: keyframe object
frame: {}
};

const timelineSlice = createSlice({
Expand All @@ -14,14 +15,16 @@ const timelineSlice = createSlice({
updateCameraKeyframes: (state, action) => void (state.cameraKeyframes = action.payload),
updateFilterKeyframes: (state, action) => void (state.filterKeyframes = action.payload),
updateLayerKeyframes: (state, action) =>
void (state.layerKeyframes = {...state.layerKeyframes, ...action.payload})
void (state.layerKeyframes = {...state.layerKeyframes, ...action.payload}),
updateFrame: (state, action) => void (state.frame = action.payload)
}
});

export const {
updateCameraKeyframes,
updateFilterKeyframes,
updateLayerKeyframes
updateLayerKeyframes,
updateFrame
} = timelineSlice.actions;

export default timelineSlice.reducer;
Expand All @@ -33,3 +36,4 @@ export default timelineSlice.reducer;
export const cameraKeyframeSelector = state => state.hubbleGl.timeline.cameraKeyframes;
export const filterKeyframeSelector = state => state.hubbleGl.timeline.filterKeyframes;
export const layerKeyframeSelector = state => state.hubbleGl.timeline.layerKeyframes;
export const frameSelector = state => state.hubbleGl.timeline.frame;
26 changes: 21 additions & 5 deletions modules/core/docs/deck-adapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,14 @@ Start rendering.

Parameters:

* **`getCameraKeyframes` (`() => CameraKeyframes`, Optional).**
* **`getCameraKeyframes` (`() => CameraKeyframes`, Optional) - Default: `undefined`.**

This function is used to access the camera's keyframes, and is called just prior to rendering.

* **`getKeyframes` (`() => Object<string, Keyframes>`, Optional) - Default: `undefined`.**

This function is called after the last frame is rendered and a file is created for download. It does not get called when a render is interrupted with `stop()`.

* **`Encoder` (`typeof FrameEncoder`, Optional) - Default: `PreviewEncoder`.**

Provide a FrameEncoder class for capturing deck canvas. See [Encoders Overview](/modules/core/docs/encoder) for options.
Expand All @@ -61,10 +65,6 @@ See [FrameEncoder](/modules/core/docs/encoder/frame-encoder#constructor-1) for i

* **`onStop` (`() => void`, Optional) - Default: `undefined`.**

* **`getKeyframes` (`() => Object<string, Keyframes>`, Optional) - Default: `undefined`.**

This function is called after the last frame is rendered and a file is created for download. It does not get called when a render is interrupted with `stop()`.

##### `stop(callback)`

Interrupt rendering and saves partial result. This is useful for handling user interruptions.
Expand All @@ -73,6 +73,22 @@ Parameters:

* `callback` (`() => void`, Optional) - Callback indicating the rendering is finished.

##### `seek({timeMs, getCameraKeyframes, getKeyframes})`

Move time to set a new position. Useful for peeking at different times in an animation without rendering.

Parameters:

* **`timeMs` (`number`)**

* **`getCameraKeyframes` (`() => CameraKeyframes`, Optional) - Default: `undefined`.**

This function is used to access the camera's keyframes, and is called just prior to rendering.

* **`getKeyframes` (`() => Object<string, Keyframes>`, Optional) - Default: `undefined`.**

This function is called after the last frame is rendered and a file is created for download. It does not get called when a render is interrupted with `stop()`.

## Source

[modules/main/src/adapters/deck-adapter.js](https://github.com/uber/hubble.gl/blob/master/modules/main/src/adapters/deck-adapter.js)
25 changes: 22 additions & 3 deletions modules/core/src/adapters/deck-adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export default class DeckAdapter {
this.getProps = this.getProps.bind(this);
this.render = this.render.bind(this);
this.stop = this.stop.bind(this);
this.seek = this.seek.bind(this);
this._deckOnLoad = this._deckOnLoad.bind(this);
this._getViewState = this._getViewState.bind(this);
this._getLayers = this._getLayers.bind(this);
Expand Down Expand Up @@ -108,17 +109,17 @@ export default class DeckAdapter {
/**
* @param {Object} params
* @param {() => import('../keyframes').CameraKeyframes} params.getCameraKeyframes
* @param {() => Object<string, import('../keyframes').Keyframes>} params.getKeyframes
* @param {typeof import('../encoders').FrameEncoder} params.Encoder
* @param {import('types').FrameEncoderSettings} params.encoderSettings
* @param {() => void} params.onStop
* @param {() => Object<string, import('../keyframes').Keyframes>} params.getKeyframes
*/
render({
getCameraKeyframes = undefined,
getKeyframes = undefined,
Encoder = PreviewEncoder,
encoderSettings = {},
onStop = undefined,
getKeyframes = undefined
onStop = undefined
}) {
if (getCameraKeyframes) {
this.scene.setCameraKeyframes(getCameraKeyframes());
Expand Down Expand Up @@ -148,6 +149,24 @@ export default class DeckAdapter {
this.videoCapture.stop(callback);
}

/**
* @param {Object} params
* @param {number} params.timeMs
* @param {() => import('../keyframes').CameraKeyframes} params.getCameraKeyframes
* @param {() => Object<string, import('../keyframes').Keyframes>} params.getKeyframes
*/
seek({timeMs, getCameraKeyframes = undefined, getKeyframes = undefined}) {
if (this.scene) {
if (getCameraKeyframes) {
this.scene.setCameraKeyframes(getCameraKeyframes());
}
if (getKeyframes) {
this.scene.setKeyframes(getKeyframes());
}
this.scene.animationLoop.timeline.setTime(timeMs);
}
}

async _deckOnLoad(deck) {
this.deck = deck;

Expand Down