Skip to content

Commit

Permalink
Update packages/webviz-core from internal repo (#658)
Browse files Browse the repository at this point in the history
Changelog:
-

Co-authored-by: Chris Hasson <[email protected]>
  • Loading branch information
hassoncs and hassoncs authored Jul 13, 2021
1 parent f0676c2 commit 0efdd2b
Show file tree
Hide file tree
Showing 82 changed files with 5,130 additions and 781 deletions.
605 changes: 420 additions & 185 deletions packages/webviz-core/package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion packages/webviz-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"@sentry/browser": "5.11.0",
"@welldone-software/why-did-you-render": "6.1.1",
"async-retry": "1.2.3",
"audiobuffer-to-wav": "1.0.0",
"buffer": "6.0.3",
"chart.js": "davidswinegar/Chart.js#0785c29a4c57b3e1b4243f8163df7950a00b2a80",
"chartjs-plugin-annotation": "0.5.7",
Expand Down Expand Up @@ -45,7 +46,6 @@
"nearley": "2.15.1",
"panzoom": "8.4.0",
"prettier": "1.16.0",
"puppeteer": "7.0.1",
"promise-queue": "2.2.5",
"prop-types": "15.6.2",
"rc-color-picker": "1.2.6",
Expand All @@ -69,6 +69,7 @@
"react-range": "1.8.6",
"react-reconciler": "^0.26.1",
"react-redux": "7.1.0",
"react-resizable": "3.0.1",
"react-resize-detector": "4.2.1",
"react-router": "5.0.1",
"react-router-dom": "5.0.1",
Expand Down Expand Up @@ -106,6 +107,7 @@
"fake-indexeddb": "2.0.4",
"fetch-mock": "7.2.5",
"flow-bin": "0.110.0",
"puppeteer": "7.0.1",
"react-test-renderer": "16.10.2",
"redux-devtools-extension": "^2.13.7"
},
Expand Down
9 changes: 5 additions & 4 deletions packages/webviz-core/script/record-local-bag-video.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const rmfr = require("rmfr");
const util = require("util");

require("@babel/register")();
const recordVideo = require("../shared/recordVideo").default;
const { recordVideoAsBuffer } = require("../shared/recordVideo");

const exec = util.promisify(child_process.exec);

Expand Down Expand Up @@ -91,14 +91,15 @@ async function main() {
})();

console.log("Recording video...");
const { mediaFile: video } = await recordVideo({
const { mediaBuffer } = await recordVideoAsBuffer({
duration,
speed,
frameless,
framerate,
parallel,
bagPath: bag,
experimentalFeaturesSettings,
errorIsWhitelisted: () => true, // Ignore errors for local recordings
url,
crop,
dimensions: width && height ? { width, height } : undefined,
Expand All @@ -109,15 +110,15 @@ async function main() {
console.log("Saving video...");
if (program.mp3) {
const tmpVideoFile = `${__dirname}/tmp-video.mp4`;
fs.writeFileSync(tmpVideoFile, video);
fs.writeFileSync(tmpVideoFile, mediaBuffer);

await exec(`ffmpeg -y -i tmp-video.mp4 -i ${program.mp3} -vcodec copy -b:a 320k -shortest ${program.out}`, {
cwd: __dirname,
});

await rmfr(tmpVideoFile);
} else {
fs.writeFileSync(program.out, video);
fs.writeFileSync(program.out, mediaBuffer);
}

console.log("Done!");
Expand Down
30 changes: 30 additions & 0 deletions packages/webviz-core/shared/fileUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// @flow
//
// Copyright (c) 2020-present, Cruise LLC
//
// This source code is licensed under the Apache License, Version 2.0,
// found in the LICENSE file in the root directory of this source tree.
// You may not use this file except in compliance with the License.
import fs from "fs";
import rmfr from "rmfr";
import uuid from "uuid";

import globalEnvVars from "./globalEnvVars";

export function makeTempDirectory() {
const tmpDir = `${globalEnvVars.tempVideosDirectory}/__video-recording-tmp-${uuid.v4()}__`;
fs.mkdirSync(tmpDir);
return tmpDir;
}

// Executes the function passing in a new temp directory that will be automatically cleaned up afterwards.
export async function withTempDirectory<T>(fn: (*) => Promise<T>): Promise<T> {
const tmpDir = makeTempDirectory();
try {
return await fn(tmpDir);
} catch (error) {
throw error;
} finally {
rmfr(tmpDir);
}
}
94 changes: 58 additions & 36 deletions packages/webviz-core/shared/recordVideo.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ import rmfr from "rmfr";
import util from "util";
import uuid from "uuid";

import type { VideoMetadata } from "../src/players/automatedRun/AutomatedRunPlayer";
import type { VideoRecordingAction } from "../src/players/automatedRun/videoRecordingClient";
import convertVideoToGif from "./convertVideoToGif";
import delay from "./delay";
import { withTempDirectory } from "./fileUtils";
import globalEnvVars from "./globalEnvVars";
import promiseTimeout from "./promiseTimeout";
import runInBrowser from "./runInBrowser";
Expand All @@ -32,23 +34,7 @@ const waitForBrowserLoadTimeoutMs = 3 * 60000; // 3 minutes
const actionTimeDurationMs = 1000;
const pendingRequestPauseDurationMs = 1000; // Amount of time to wait for any pending XHR requests to settle

async function recordVideo({
speed = 1,
framerate = 30,
frameless = false,
dimensions = { width: 1920, height: 1080 },
crop,
parallel = 2,
duration,
bagPath,
url,
puppeteerLaunchConfig,
layout,
panelLayout,
mediaType,
errorIsWhitelisted,
experimentalFeaturesSettings,
}: {
type VideoConfig = {
bagPath?: ?string,
url: string,
puppeteerLaunchConfig?: any,
Expand All @@ -64,7 +50,50 @@ async function recordVideo({
mediaType?: "mp4" | "gif",
crop?: { width: number, height: number, top: number, left: number },
errorIsWhitelisted?: (string) => boolean,
}): Promise<{ mediaFile: Buffer, sampledImageFile: Buffer }> {
};

type RecordingOptions = {| outputDirectory: string |};

// Delegates to recordVideo, but returns the resulting video as a Buffer.
// *PERFORMANCE NOTE* This should only be used for short videos since it reads
// the entire video into memory. For long videos, use recordVideo directly
// without reading the video into a Buffer.
export async function recordVideoAsBuffer(
config: VideoConfig
): Promise<{ mediaBuffer: Buffer, sampledImageFile: Buffer, metadata: VideoMetadata }> {
return withTempDirectory(async (tmpDir) => {
const { mediaPath, sampledImageFile, metadata } = await recordVideo(config, { outputDirectory: tmpDir });

const mediaBuffer = await readFile(mediaPath);
await rmfr(mediaPath);

return { mediaBuffer, sampledImageFile, metadata };
});
}

// Records a video with the specified configuration.
// Writes the resulting media file (mp4/gif) into the specified directory.
export async function recordVideo(
config: VideoConfig,
options: RecordingOptions
): Promise<{ mediaPath: string, sampledImageFile: Buffer, metadata: VideoMetadata }> {
const {
speed = 1,
framerate = 30,
frameless = false,
dimensions = { width: 1920, height: 1080 },
crop,
parallel = 2,
duration,
bagPath,
url,
puppeteerLaunchConfig,
layout,
panelLayout,
mediaType,
errorIsWhitelisted,
experimentalFeaturesSettings,
} = config;
const screenshotsDir = `${globalEnvVars.tempVideosDirectory}/__video-recording-tmp-${uuid.v4()}__`;
await mkdir(screenshotsDir);

Expand All @@ -73,7 +102,7 @@ async function recordVideo({

let hasFailed = false;
try {
let msPerFrame;
let metadata: ?VideoMetadata;
const promises = new Array(parallel).fill().map(async (_, parallelIndex) => {
const urlObject = url ? new URL(url) : baseUrlObject;
// Update the base url to point to localhost if it's set to something else.
Expand Down Expand Up @@ -175,7 +204,7 @@ async function recordVideo({
} else if (actionObj.action === "finish") {
log.info("Finished!");
isRunning = false;
msPerFrame = actionObj.msPerFrame;
metadata = actionObj.metadata;
} else if (actionObj.action === "screenshot") {
await waitForXhrRequests(pendingRequestUrls);

Expand Down Expand Up @@ -207,8 +236,8 @@ async function recordVideo({

await Promise.all(promises);

if (msPerFrame == null) {
throw new Error("msPerFrame was not set");
if (metadata == null) {
throw new Error("metadata was not set. The recording must have failed.");
}
const screenshotFileNames = fs.readdirSync(screenshotsDir);
if (screenshotFileNames.length === 0) {
Expand All @@ -219,11 +248,12 @@ async function recordVideo({
}

const sampledImageFile = await readFile(`${screenshotsDir}/${last(screenshotFileNames)}`);
const outputFilename = "out.mp4";
const outputDirectory = options.outputDirectory;

// Once we're finished, we're going to stitch all the individual screenshots together
// into a video, with the framerate specified by the client (via `msPerFrame`).
log.info(`Creating video with framerate ${framerate}fps (${msPerFrame}ms per frame)`);
let mediaPath = `${outputDirectory}/out.mp4`;
log.info(`Creating video with framerate ${framerate}fps (${metadata.msPerFrame}ms per frame)`);
await exec(
[
`ffmpeg -y`,
Expand All @@ -234,26 +264,20 @@ async function recordVideo({
`-c:v libx264`,
`-preset faster`,
`-r ${framerate}`,
outputFilename,
mediaPath,
].join(" "),
{
cwd: screenshotsDir,
}
{ cwd: screenshotsDir }
);
let mediaPath = `${screenshotsDir}/${outputFilename}`;
log.info(`Video saved to ${mediaPath}`);

// Convert the output to other mediaTypes, if requested
if (mediaType === "gif") {
const gifPath = `${screenshotsDir}/out.gif`;
const gifPath = `${outputDirectory}/out.gif`;
await convertVideoToGif(mediaPath, gifPath);
mediaPath = gifPath;
}

const mediaFile = await readFile(mediaPath);
await rmfr(mediaPath);
return { mediaFile, sampledImageFile };
return { mediaPath, sampledImageFile, metadata };
} catch (error) {
hasFailed = true;
throw error;
Expand Down Expand Up @@ -297,5 +321,3 @@ export async function waitForXhrRequests(pendingRequestUrls: Set<string>) {
timeout = true;
}
}

export default recordVideo;
6 changes: 3 additions & 3 deletions packages/webviz-core/src/actions/panels.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,11 @@ export function applyPatchToLayout(patch: ?string, layout: PanelsState): PanelsS
sendNotification(
"Failed to apply patch on top of the layout.",
`Ignoring the patch "${patch}".\n\n${e}`,
"user",
"warn"
"app",
"error"
);
return layout;
}
return layout;
}

export const fetchLayout = (search: string): Dispatcher<SET_FETCHED_LAYOUT> => (dispatch) => {
Expand Down
37 changes: 37 additions & 0 deletions packages/webviz-core/src/actions/panels.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// @flow
//
// Copyright (c) 2021-present, Cruise LLC
//
// This source code is licensed under the Apache License, Version 2.0,
// found in the LICENSE file in the root directory of this source tree.
// You may not use this file except in compliance with the License.
import { applyPatchToLayout } from "webviz-core/src/actions/panels";
import { deflatePatch } from "webviz-core/src/util/layout";
import sendNotification from "webviz-core/src/util/sendNotification";

const DEFAULT_LAYOUT = {
layout: "",
savedProps: {},
globalVariables: {},
userNodes: {},
linkedGlobalVariables: [],
playbackConfig: { speed: 0.1, messageOrder: "receiveTime", timeDisplayMethod: "ROS" },
};

describe("panels", () => {
describe("applyPatchToLayout", () => {
it("handles patches", () => {
const patch = deflatePatch({ globalVariables: [{}, { foo: "bar" }] });
expect(applyPatchToLayout(patch, DEFAULT_LAYOUT)).toEqual({
...DEFAULT_LAYOUT,
globalVariables: { foo: "bar" },
});
});
it("handles invalid patches", () => {
applyPatchToLayout("abc", DEFAULT_LAYOUT);

expect(sendNotification).toHaveBeenLastCalledWith(expect.any(String), expect.any(String), "app", "error");
sendNotification.expectCalledDuringTest();
});
});
});
9 changes: 5 additions & 4 deletions packages/webviz-core/src/components/ChildToggle/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,15 @@ export default class ChildToggle extends React.Component<Props> {
}

addDocListener() {
// add a document listener to hide the dropdown body if
// it is expanded and the document is clicked on
document.addEventListener("click", this.onDocumentClick, true);
// add a document listener to hide the dropdown body if it is expanded and
// the document is clicked on. Using 'mousedown' here instead of 'click'
// since we don't want click-and-drag interactions to close the toggle.
document.addEventListener("mousedown", this.onDocumentClick, true);
}

removeDocListener() {
// cleanup the document listener
document.removeEventListener("click", this.onDocumentClick, true);
document.removeEventListener("mousedown", this.onDocumentClick, true);
}

componentDidUpdate(prevProps: Props) {
Expand Down
Loading

0 comments on commit 0efdd2b

Please sign in to comment.