Browse Source

mp4 export extension wip

David Sehnal 4 years ago
parent
commit
a4c6d1e0e6

+ 3 - 4
src/apps/viewer/index.ts

@@ -34,7 +34,7 @@ import { StateObjectSelector } from '../../mol-state';
 import { PluginStateObject } from '../../mol-plugin-state/objects';
 import { StateTransforms } from '../../mol-plugin-state/transforms';
 import { createVolumeRepresentationParams } from '../../mol-plugin-state/helpers/volume-representation-params';
-import { Mp4EncoderTestUI } from '../../extensions/mp4-export/ui';
+import { Mp4Export } from '../../extensions/mp4-export';
 
 require('mol-plugin-ui/skin/light.scss');
 
@@ -52,7 +52,8 @@ const Extensions = {
     'rcsb-assembly-symmetry': PluginSpec.Behavior(RCSBAssemblySymmetry),
     'rcsb-validation-report': PluginSpec.Behavior(RCSBValidationReport),
     'anvil-membrane-orientation': PluginSpec.Behavior(ANVILMembraneOrientation),
-    'g3d': PluginSpec.Behavior(G3DFormat)
+    'g3d': PluginSpec.Behavior(G3DFormat),
+    'mp4-export': PluginSpec.Behavior(Mp4Export)
 };
 
 const DefaultViewerOptions = {
@@ -135,8 +136,6 @@ export class Viewer {
             : elementOrId;
         if (!element) throw new Error(`Could not get element with id '${elementOrId}'`);
         this.plugin = createPlugin(element, spec);
-
-        this.plugin.customStructureControls.set('mp4-encoder', Mp4EncoderTestUI as any);
     }
 
     setRemoteSnapshot(id: string) {

+ 12 - 2
src/extensions/mp4-export/encoder.ts

@@ -1,3 +1,9 @@
+/**
+ * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
 import * as HME from 'h264-mp4-encoder';
 import { Viewport } from '../../mol-canvas3d/camera/util';
 import { ImagePass } from '../../mol-canvas3d/passes/image';
@@ -13,7 +19,7 @@ export interface Mp4EncoderParams<A extends PluginStateAnimation = PluginStateAn
     animation: PluginStateAnimation.Instance<A>,
     width: number,
     height: number,
-    viewport?: Viewport,
+    viewport: Viewport,
     /** default is 30 */
     fps?: number,
     /** Number from 10 (best quality, slowest) to 51 (worst, fastest) */
@@ -32,7 +38,11 @@ export async function encodeMp4Animation<A extends PluginStateAnimation>(plugin:
     const encoder = await HME.createH264MP4Encoder();
 
     const { width, height } = params;
-    const vw = params.viewport?.width ?? width, vh = params.viewport?.height ?? height;
+    let vw = params.viewport.width, vh = params.viewport.height;
+
+    // dimensions must be a multiple of 2
+    if (vw % 2 !== 0) vw -= 1;
+    if (vh % 2 !== 0) vh -= 1;
 
     encoder.width = vw;
     encoder.height = vh;

+ 30 - 0
src/extensions/mp4-export/index.ts

@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { PluginBehavior } from '../../mol-plugin/behavior/behavior';
+import { Mp4EncoderUI } from './ui';
+
+export const Mp4Export = PluginBehavior.create<{ }>({
+    name: 'extension-mp4-export',
+    category: 'misc',
+    display: {
+        name: 'MP4 Animation Export'
+    },
+    ctor: class extends PluginBehavior.Handler<{ }> {
+        register(): void {
+            this.ctx.customStructureControls.set('mp4-export', Mp4EncoderUI as any);
+        }
+
+        update() {
+            return false;
+        }
+
+        unregister() {
+            this.ctx.customStructureControls.delete('mp4-export');
+        }
+    },
+    params: () => ({ })
+});

+ 13 - 7
src/extensions/mp4-export/ui.tsx

@@ -1,7 +1,14 @@
+/**
+ * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
 import React from 'react';
 import { AnimateCameraSpin } from '../../mol-plugin-state/animation/built-in/camera-spin';
 import { CollapsableControls, CollapsableState } from '../../mol-plugin-ui/base';
 import { Button } from '../../mol-plugin-ui/controls/common';
+import { SubscriptionsOutlinedSvg } from '../../mol-plugin-ui/controls/icons';
 import { Task } from '../../mol-task';
 import { download } from '../../mol-util/download';
 import { encodeMp4Animation, Mp4Encoder } from './encoder';
@@ -10,12 +17,12 @@ interface State {
     data?: { movie: Uint8Array };
 }
 
-export class Mp4EncoderTestUI extends CollapsableControls<{}, State> {
+export class Mp4EncoderUI extends CollapsableControls<{}, State> {
     protected defaultState(): State & CollapsableState  {
         return {
-            header: 'Export MP4',
-            isCollapsed: false,
-            brand: { accent: 'cyan' }
+            header: 'Export Animation',
+            isCollapsed: true,
+            brand: { accent: 'cyan', svg: SubscriptionsOutlinedSvg }
         };
     }
     protected renderControls(): JSX.Element | null {
@@ -27,18 +34,17 @@ export class Mp4EncoderTestUI extends CollapsableControls<{}, State> {
 
     save() {
         download(new Blob([this.state.data!.movie]), 'test.mp4');
-        // download(this.state.data!.image, 'test.png');
     }
 
     gen() {
         const task = Task.create('Export Animation', async ctx => {
+            const resolution = this.plugin.helpers.viewportScreenshot?.getSizeAndViewport()!;
             const movie = await encodeMp4Animation(this.plugin, ctx, {
                 animation: {
                     definition: AnimateCameraSpin,
                     params: { durationInMs: 2000, speed: 1, direction: 'cw' }
                 },
-                width: 1280,
-                height: 720,
+                ...resolution,
                 quantizationParameter: 18,
                 pass: this.plugin.helpers.viewportScreenshot?.imagePass!,
             });

+ 1 - 2
src/mol-plugin-ui/controls/screenshot.tsx

@@ -1,7 +1,6 @@
 /**
- * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
- * @author Alexander Rose <alexander.rose@weirdbyte.de>
  * @author David Sehnal <david.sehnal@gmail.com>
  */