bd73617e5c
PiperOrigin-RevId: 525854969
1262 lines
53 KiB
TypeScript
1262 lines
53 KiB
TypeScript
// Placeholder for internal dependency on assertTruthy
|
|
// Placeholder for internal dependency on jsloader
|
|
import {isWebKit} from '../../web/graph_runner/platform_utils';
|
|
// Placeholder for internal dependency on trusted resource url
|
|
|
|
// This file can serve as a common interface for most simple TypeScript
|
|
// libraries-- additionally, it can hook automatically into wasm_mediapipe_demo
|
|
// to autogenerate simple TS APIs from demos for instantaneous 1P integrations.
|
|
|
|
/**
|
|
* Simple interface for allowing users to set the directory where internal
|
|
* wasm-loading and asset-loading code looks (e.g. for .wasm and .data file
|
|
* locations).
|
|
*/
|
|
export declare interface FileLocator {
|
|
locateFile: (filename: string) => string;
|
|
mainScriptUrlOrBlob?: string;
|
|
}
|
|
|
|
/**
|
|
* A listener that receives the contents of a non-empty MediaPipe packet and
|
|
* its timestamp.
|
|
*/
|
|
export type SimpleListener<T> = (data: T, timestamp: number) => void;
|
|
|
|
/**
|
|
* A listener that receives the current MediaPipe packet timestamp. This is
|
|
* invoked even for empty packet.
|
|
*/
|
|
export type EmptyPacketListener = (timestamp: number) => void;
|
|
|
|
/**
|
|
* A listener that receives a single element of vector-returning output packet.
|
|
* Receives one element at a time (in order). Once all elements are processed,
|
|
* the listener is invoked with `data` set to `unknown` and `done` set to true.
|
|
* Intended for internal usage.
|
|
*/
|
|
export type VectorListener<T> = (data: T, done: boolean, timestamp: number) =>
|
|
void;
|
|
|
|
/**
|
|
* A listener that receives the CalculatorGraphConfig in binary encoding.
|
|
*/
|
|
export type CalculatorGraphConfigListener = (graphConfig: Uint8Array) => void;
|
|
|
|
/**
|
|
* The name of the internal listener that we use to obtain the calculator graph
|
|
* config. Intended for internal usage. Exported for testing only.
|
|
*/
|
|
export const CALCULATOR_GRAPH_CONFIG_LISTENER_NAME = '__graph_config__';
|
|
|
|
/**
|
|
* Declarations for Emscripten's WebAssembly Module behavior, so TS compiler
|
|
* doesn't break our JS/C++ bridge.
|
|
*/
|
|
export declare interface WasmModule {
|
|
canvas: HTMLCanvasElement|OffscreenCanvas|null;
|
|
HEAPU8: Uint8Array;
|
|
HEAPU32: Uint32Array;
|
|
HEAPF32: Float32Array;
|
|
HEAPF64: Float64Array;
|
|
errorListener?: ErrorListener;
|
|
_bindTextureToCanvas: () => boolean;
|
|
_changeBinaryGraph: (size: number, dataPtr: number) => void;
|
|
_changeTextGraph: (size: number, dataPtr: number) => void;
|
|
_free: (ptr: number) => void;
|
|
_malloc: (size: number) => number;
|
|
_processFrame: (width: number, height: number, timestamp: number) => void;
|
|
_setAutoRenderToScreen: (enabled: boolean) => void;
|
|
_waitUntilIdle: () => void;
|
|
|
|
// Exposed so that clients of this lib can access this field
|
|
dataFileDownloads?: {[url: string]: {loaded: number, total: number}};
|
|
|
|
// Wasm Module multistream entrypoints. Require
|
|
// gl_graph_runner_internal_multi_input as a build dependency.
|
|
stringToNewUTF8: (data: string) => number;
|
|
_bindTextureToStream: (streamNamePtr: number) => void;
|
|
_addBoundTextureToStream:
|
|
(streamNamePtr: number, width: number, height: number,
|
|
timestamp: number) => void;
|
|
_addBoolToInputStream:
|
|
(data: boolean, streamNamePtr: number, timestamp: number) => void;
|
|
_addDoubleToInputStream:
|
|
(data: number, streamNamePtr: number, timestamp: number) => void;
|
|
_addFloatToInputStream:
|
|
(data: number, streamNamePtr: number, timestamp: number) => void;
|
|
_addIntToInputStream:
|
|
(data: number, streamNamePtr: number, timestamp: number) => void;
|
|
_addStringToInputStream:
|
|
(dataPtr: number, streamNamePtr: number, timestamp: number) => void;
|
|
_addFlatHashMapToInputStream:
|
|
(keysPtr: number, valuesPtr: number, count: number, streamNamePtr: number,
|
|
timestamp: number) => void;
|
|
_addProtoToInputStream:
|
|
(dataPtr: number, dataSize: number, protoNamePtr: number,
|
|
streamNamePtr: number, timestamp: number) => void;
|
|
_addEmptyPacketToInputStream:
|
|
(streamNamePtr: number, timestamp: number) => void;
|
|
|
|
// Input side packets
|
|
_addBoolToInputSidePacket: (data: boolean, streamNamePtr: number) => void;
|
|
_addDoubleToInputSidePacket: (data: number, streamNamePtr: number) => void;
|
|
_addFloatToInputSidePacket: (data: number, streamNamePtr: number) => void;
|
|
_addIntToInputSidePacket: (data: number, streamNamePtr: number) => void;
|
|
_addStringToInputSidePacket: (dataPtr: number, streamNamePtr: number) => void;
|
|
_addProtoToInputSidePacket:
|
|
(dataPtr: number, dataSize: number, protoNamePtr: number,
|
|
streamNamePtr: number) => void;
|
|
|
|
// Map of output streams to packet listeners. Also built as part of
|
|
// gl_graph_runner_internal_multi_input.
|
|
simpleListeners?:
|
|
Record<string, SimpleListener<unknown>|VectorListener<unknown>>;
|
|
// Map of output streams to empty packet listeners.
|
|
emptyPacketListeners?: Record<string, EmptyPacketListener>;
|
|
_attachBoolListener: (streamNamePtr: number) => void;
|
|
_attachBoolVectorListener: (streamNamePtr: number) => void;
|
|
_attachDoubleListener: (streamNamePtr: number) => void;
|
|
_attachDoubleVectorListener: (streamNamePtr: number) => void;
|
|
_attachFloatListener: (streamNamePtr: number) => void;
|
|
_attachFloatVectorListener: (streamNamePtr: number) => void;
|
|
_attachIntListener: (streamNamePtr: number) => void;
|
|
_attachIntVectorListener: (streamNamePtr: number) => void;
|
|
_attachStringListener: (streamNamePtr: number) => void;
|
|
_attachStringVectorListener: (streamNamePtr: number) => void;
|
|
_attachProtoListener: (streamNamePtr: number, makeDeepCopy?: boolean) => void;
|
|
_attachProtoVectorListener:
|
|
(streamNamePtr: number, makeDeepCopy?: boolean) => void;
|
|
|
|
// Require dependency ":gl_graph_runner_audio_out"
|
|
_attachAudioListener: (streamNamePtr: number, makeDeepCopy?: boolean) => void;
|
|
|
|
// Require dependency ":gl_graph_runner_audio"
|
|
_addAudioToInputStream: (dataPtr: number, numChannels: number,
|
|
numSamples: number, streamNamePtr: number, timestamp: number) => void;
|
|
_configureAudio: (channels: number, samples: number, sampleRate: number,
|
|
streamNamePtr: number, headerNamePtr: number) => void;
|
|
|
|
// Get the graph configuration and invoke the listener configured under
|
|
// streamNamePtr
|
|
_getGraphConfig: (streamNamePtr: number, makeDeepCopy?: boolean) => void;
|
|
|
|
// TODO: Refactor to just use a few numbers (perhaps refactor away
|
|
// from gl_graph_runner_internal.cc entirely to use something a little more
|
|
// streamlined; new version is _processFrame above).
|
|
_processGl: (frameDataPtr: number) => number;
|
|
}
|
|
|
|
// Global declarations, for tapping into Window for Wasm blob running
|
|
declare global {
|
|
interface Window {
|
|
// Created by us using wasm-runner script
|
|
Module?: WasmModule|FileLocator;
|
|
// Created by wasm-runner script
|
|
ModuleFactory?: (fileLocator: FileLocator) => Promise<WasmModule>;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fetches each URL in urls, executes them one-by-one in the order they are
|
|
* passed, and then returns (or throws if something went amiss).
|
|
*/
|
|
declare function importScripts(...urls: Array<string|URL>): void;
|
|
|
|
/**
|
|
* Valid types of image sources which we can run our GraphRunner over.
|
|
*/
|
|
export type ImageSource =
|
|
HTMLCanvasElement|HTMLVideoElement|HTMLImageElement|ImageData|ImageBitmap;
|
|
|
|
|
|
/** A listener that will be invoked with an absl::StatusCode and message. */
|
|
export type ErrorListener = (code: number, message: string) => void;
|
|
|
|
/**
|
|
* Internal type of constructors used for initializing GraphRunner and
|
|
* subclasses.
|
|
*/
|
|
export type WasmMediaPipeConstructor<LibType> =
|
|
(new (
|
|
module: WasmModule, canvas?: HTMLCanvasElement|OffscreenCanvas|null) =>
|
|
LibType);
|
|
|
|
/**
|
|
* Simple class to run an arbitrary image-in/image-out MediaPipe graph (i.e.
|
|
* as created by wasm_mediapipe_demo BUILD macro), and either render results
|
|
* into canvas, or else return the output WebGLTexture. Takes a WebAssembly
|
|
* Module.
|
|
*/
|
|
export class GraphRunner {
|
|
// TODO: These should be protected/private, but are left exposed for
|
|
// now so that we can use proper TS mixins with this class as a base. This
|
|
// should be somewhat fixed when we create our .d.ts files.
|
|
readonly wasmModule: WasmModule;
|
|
readonly hasMultiStreamSupport: boolean;
|
|
autoResizeCanvas: boolean = true;
|
|
audioPtr: number|null;
|
|
audioSize: number;
|
|
|
|
/**
|
|
* Creates a new MediaPipe WASM module. Must be called *after* wasm Module has
|
|
* initialized. Note that we take control of the GL canvas from here on out,
|
|
* and will resize it to fit input.
|
|
*
|
|
* @param module The underlying Wasm Module to use.
|
|
* @param glCanvas The type of the GL canvas to use, or `null` if no GL
|
|
* canvas should be initialzed. Initializes an offscreen canvas if not
|
|
* provided.
|
|
*/
|
|
constructor(
|
|
module: WasmModule, glCanvas?: HTMLCanvasElement|OffscreenCanvas|null) {
|
|
this.wasmModule = module;
|
|
this.audioPtr = null;
|
|
this.audioSize = 0;
|
|
this.hasMultiStreamSupport =
|
|
(typeof this.wasmModule._addIntToInputStream === 'function');
|
|
|
|
if (glCanvas !== undefined) {
|
|
this.wasmModule.canvas = glCanvas;
|
|
} else if (typeof OffscreenCanvas !== 'undefined' && !isWebKit()) {
|
|
// If no canvas is provided, assume Chrome/Firefox and just make an
|
|
// OffscreenCanvas for GPU processing. Note that we exclude Safari
|
|
// since it does not (yet) support WebGL for OffscreenCanvas.
|
|
this.wasmModule.canvas = new OffscreenCanvas(1, 1);
|
|
} else {
|
|
console.warn(
|
|
'OffscreenCanvas not supported and GraphRunner constructor ' +
|
|
'glCanvas parameter is undefined. Creating backup canvas.');
|
|
this.wasmModule.canvas = document.createElement('canvas');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convenience helper to load a MediaPipe graph from a file and pass it to
|
|
* setGraph.
|
|
* @param graphFile The url of the MediaPipe graph file to load.
|
|
*/
|
|
async initializeGraph(graphFile: string): Promise<void> {
|
|
// Fetch and set graph
|
|
const response = await fetch(graphFile);
|
|
const graphData = await response.arrayBuffer();
|
|
const isBinary =
|
|
!(graphFile.endsWith('.pbtxt') || graphFile.endsWith('.textproto'));
|
|
this.setGraph(new Uint8Array(graphData), isBinary);
|
|
}
|
|
|
|
/**
|
|
* Convenience helper for calling setGraph with a string representing a text
|
|
* proto config.
|
|
* @param graphConfig The text proto graph config, expected to be a string in
|
|
* default JavaScript UTF-16 format.
|
|
*/
|
|
setGraphFromString(graphConfig: string): void {
|
|
this.setGraph((new TextEncoder()).encode(graphConfig), false);
|
|
}
|
|
|
|
/**
|
|
* Takes the raw data from a MediaPipe graph, and passes it to C++ to be run
|
|
* over the video stream. Will replace the previously running MediaPipe graph,
|
|
* if there is one.
|
|
* @param graphData The raw MediaPipe graph data, either in binary
|
|
* protobuffer format (.binarypb), or else in raw text format (.pbtxt or
|
|
* .textproto).
|
|
* @param isBinary This should be set to true if the graph is in
|
|
* binary format, and false if it is in human-readable text format.
|
|
*/
|
|
setGraph(graphData: Uint8Array, isBinary: boolean): void {
|
|
const size = graphData.length;
|
|
const dataPtr = this.wasmModule._malloc(size);
|
|
this.wasmModule.HEAPU8.set(graphData, dataPtr);
|
|
if (isBinary) {
|
|
this.wasmModule._changeBinaryGraph(size, dataPtr);
|
|
} else {
|
|
this.wasmModule._changeTextGraph(size, dataPtr);
|
|
}
|
|
this.wasmModule._free(dataPtr);
|
|
}
|
|
|
|
/**
|
|
* Configures the current graph to handle audio processing in a certain way
|
|
* for all its audio input streams. Additionally can configure audio headers
|
|
* (both input side packets as well as input stream headers), but these
|
|
* configurations only take effect if called before the graph is set/started.
|
|
* @param numChannels The number of channels of audio input. Only 1
|
|
* is supported for now.
|
|
* @param numSamples The number of samples that are taken in each
|
|
* audio capture.
|
|
* @param sampleRate The rate, in Hz, of the sampling.
|
|
* @param streamName The optional name of the input stream to additionally
|
|
* configure with audio information. This configuration only occurs before
|
|
* the graph is set/started. If unset, a default stream name will be used.
|
|
* @param headerName The optional name of the header input side packet to
|
|
* additionally configure with audio information. This configuration only
|
|
* occurs before the graph is set/started. If unset, a default header name
|
|
* will be used.
|
|
*/
|
|
configureAudio(numChannels: number, numSamples: number, sampleRate: number,
|
|
streamName?: string, headerName?: string) {
|
|
if (!this.wasmModule._configureAudio) {
|
|
console.warn(
|
|
'Attempting to use configureAudio without support for input audio. ' +
|
|
'Is build dep ":gl_graph_runner_audio" missing?');
|
|
}
|
|
streamName = streamName || 'input_audio';
|
|
this.wrapStringPtr(streamName, (streamNamePtr: number) => {
|
|
headerName = headerName || 'audio_header';
|
|
this.wrapStringPtr(headerName, (headerNamePtr: number) => {
|
|
this.wasmModule._configureAudio(streamNamePtr, headerNamePtr,
|
|
numChannels, numSamples, sampleRate);
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Allows disabling automatic canvas resizing, in case clients want to control
|
|
* control this.
|
|
* @param resize True will re-enable automatic canvas resizing, while false
|
|
* will disable the feature.
|
|
*/
|
|
setAutoResizeCanvas(resize: boolean): void {
|
|
this.autoResizeCanvas = resize;
|
|
}
|
|
|
|
/**
|
|
* Allows disabling the automatic render-to-screen code, in case clients don't
|
|
* need/want this. In particular, this removes the requirement for pipelines
|
|
* to have access to GPU resources, as well as the requirement for graphs to
|
|
* have "input_frames_gpu" and "output_frames_gpu" streams defined, so pure
|
|
* CPU pipelines and non-video pipelines can be created.
|
|
* NOTE: This only affects future graph initializations (via setGraph or
|
|
* initializeGraph), and does NOT affect the currently running graph, so
|
|
* calls to this should be made *before* setGraph/initializeGraph for the
|
|
* graph file being targeted.
|
|
* @param enabled True will re-enable automatic render-to-screen code and
|
|
* cause GPU resources to once again be requested, while false will
|
|
* disable the feature.
|
|
*/
|
|
setAutoRenderToScreen(enabled: boolean): void {
|
|
this.wasmModule._setAutoRenderToScreen(enabled);
|
|
}
|
|
|
|
/**
|
|
* Bind texture to our internal canvas, and upload image source to GPU.
|
|
* Returns tuple [width, height] of texture. Intended for internal usage.
|
|
*/
|
|
bindTextureToStream(imageSource: ImageSource, streamNamePtr?: number):
|
|
[number, number] {
|
|
if (!this.wasmModule.canvas) {
|
|
throw new Error('No OpenGL canvas configured.');
|
|
}
|
|
|
|
if (!streamNamePtr) {
|
|
// TODO: Remove this path once completely refactored away.
|
|
console.assert(this.wasmModule._bindTextureToCanvas());
|
|
} else {
|
|
this.wasmModule._bindTextureToStream(streamNamePtr);
|
|
}
|
|
const gl =
|
|
(this.wasmModule.canvas.getContext('webgl2') ||
|
|
this.wasmModule.canvas.getContext('webgl')) as WebGL2RenderingContext |
|
|
WebGLRenderingContext | null;
|
|
if (!gl) {
|
|
throw new Error(
|
|
'Failed to obtain WebGL context from the provided canvas. ' +
|
|
'`getContext()` should only be invoked with `webgl` or `webgl2`.');
|
|
}
|
|
gl.texImage2D(
|
|
gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, imageSource);
|
|
|
|
let width, height;
|
|
if ((imageSource as HTMLVideoElement).videoWidth) {
|
|
width = (imageSource as HTMLVideoElement).videoWidth;
|
|
height = (imageSource as HTMLVideoElement).videoHeight;
|
|
} else if ((imageSource as HTMLImageElement).naturalWidth) {
|
|
// TODO: Ensure this works with SVG images
|
|
width = (imageSource as HTMLImageElement).naturalWidth;
|
|
height = (imageSource as HTMLImageElement).naturalHeight;
|
|
} else {
|
|
width = imageSource.width;
|
|
height = imageSource.height;
|
|
}
|
|
|
|
if (this.autoResizeCanvas &&
|
|
(width !== this.wasmModule.canvas.width ||
|
|
height !== this.wasmModule.canvas.height)) {
|
|
this.wasmModule.canvas.width = width;
|
|
this.wasmModule.canvas.height = height;
|
|
}
|
|
|
|
return [width, height];
|
|
}
|
|
|
|
/**
|
|
* Takes the raw data from a JS image source, and sends it to C++ to be
|
|
* processed, waiting synchronously for the response. Note that we will resize
|
|
* our GL canvas to fit the input, so input size should only change
|
|
* infrequently.
|
|
* @param imageSource An image source to process.
|
|
* @param timestamp The timestamp of the current frame, in ms.
|
|
* @return texture? The WebGL texture reference, if one was produced.
|
|
*/
|
|
processGl(imageSource: ImageSource, timestamp: number): WebGLTexture
|
|
|undefined {
|
|
// Bind to default input stream
|
|
const [width, height] = this.bindTextureToStream(imageSource);
|
|
|
|
// 2 ints and a ll (timestamp)
|
|
const frameDataPtr = this.wasmModule._malloc(16);
|
|
this.wasmModule.HEAPU32[frameDataPtr / 4] = width;
|
|
this.wasmModule.HEAPU32[(frameDataPtr / 4) + 1] = height;
|
|
this.wasmModule.HEAPF64[(frameDataPtr / 8) + 1] = timestamp;
|
|
// outputPtr points in HEAPF32-space to running mspf calculations, which we
|
|
// don't use at the moment.
|
|
// tslint:disable-next-line:no-unused-variable
|
|
const outputPtr = this.wasmModule._processGl(frameDataPtr) / 4;
|
|
this.wasmModule._free(frameDataPtr);
|
|
|
|
// TODO: Hook up WebGLTexture output, when given.
|
|
// TODO: Allow user to toggle whether or not to render output into canvas.
|
|
return undefined;
|
|
}
|
|
|
|
/**
|
|
* Converts JavaScript string input parameters into C++ c-string pointers.
|
|
* See b/204830158 for more details. Intended for internal usage.
|
|
*/
|
|
wrapStringPtr(stringData: string, stringPtrFunc: (ptr: number) => void):
|
|
void {
|
|
if (!this.hasMultiStreamSupport) {
|
|
console.error(
|
|
'No wasm multistream support detected: ensure dependency ' +
|
|
'inclusion of :gl_graph_runner_internal_multi_input target');
|
|
}
|
|
const stringDataPtr = this.wasmModule.stringToNewUTF8(stringData);
|
|
stringPtrFunc(stringDataPtr);
|
|
this.wasmModule._free(stringDataPtr);
|
|
}
|
|
|
|
/**
|
|
* Converts JavaScript string input parameters into C++ c-string pointers.
|
|
* See b/204830158 for more details.
|
|
*/
|
|
wrapStringPtrPtr(stringData: string[], ptrFunc: (ptr: number) => void): void {
|
|
if (!this.hasMultiStreamSupport) {
|
|
console.error(
|
|
'No wasm multistream support detected: ensure dependency ' +
|
|
'inclusion of :gl_graph_runner_internal_multi_input target');
|
|
}
|
|
const uint32Array = new Uint32Array(stringData.length);
|
|
for (let i = 0; i < stringData.length; i++) {
|
|
uint32Array[i] = this.wasmModule.stringToNewUTF8(stringData[i]);
|
|
}
|
|
const heapSpace = this.wasmModule._malloc(uint32Array.length * 4);
|
|
this.wasmModule.HEAPU32.set(uint32Array, heapSpace >> 2);
|
|
|
|
ptrFunc(heapSpace);
|
|
for (const uint32ptr of uint32Array) {
|
|
this.wasmModule._free(uint32ptr);
|
|
}
|
|
this.wasmModule._free(heapSpace);
|
|
}
|
|
|
|
/**
|
|
* Invokes the callback with the current calculator configuration (in binary
|
|
* format).
|
|
*
|
|
* Consumers must deserialize the binary representation themselves as this
|
|
* avoids addding a direct dependency on the Protobuf JSPB target in the graph
|
|
* library.
|
|
*/
|
|
getCalculatorGraphConfig(
|
|
callback: CalculatorGraphConfigListener, makeDeepCopy?: boolean): void {
|
|
const listener = CALCULATOR_GRAPH_CONFIG_LISTENER_NAME;
|
|
|
|
// Create a short-lived listener to receive the binary encoded proto
|
|
this.setListener(listener, (data: Uint8Array) => {
|
|
callback(data);
|
|
});
|
|
this.wrapStringPtr(listener, (outputStreamNamePtr: number) => {
|
|
this.wasmModule._getGraphConfig(outputStreamNamePtr, makeDeepCopy);
|
|
});
|
|
|
|
delete this.wasmModule.simpleListeners![listener];
|
|
}
|
|
|
|
/**
|
|
* Ensures existence of the simple listeners table and registers the callback.
|
|
* Intended for internal usage.
|
|
*/
|
|
setListener<T>(outputStreamName: string, callbackFcn: SimpleListener<T>) {
|
|
this.wasmModule.simpleListeners = this.wasmModule.simpleListeners || {};
|
|
this.wasmModule.simpleListeners[outputStreamName] =
|
|
callbackFcn as SimpleListener<unknown>;
|
|
}
|
|
|
|
/**
|
|
* Ensures existence of the vector listeners table and registers the callback.
|
|
* Intended for internal usage.
|
|
*/
|
|
setVectorListener<T>(
|
|
outputStreamName: string, callbackFcn: SimpleListener<T[]>) {
|
|
let buffer: T[] = [];
|
|
this.wasmModule.simpleListeners = this.wasmModule.simpleListeners || {};
|
|
this.wasmModule.simpleListeners[outputStreamName] =
|
|
(data: unknown, done: boolean, timestamp: number) => {
|
|
if (done) {
|
|
callbackFcn(buffer, timestamp);
|
|
buffer = [];
|
|
} else {
|
|
buffer.push(data as T);
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Attaches a listener that will be invoked when the MediaPipe framework
|
|
* returns an error.
|
|
*/
|
|
attachErrorListener(callbackFcn: (code: number, message: string) => void) {
|
|
this.wasmModule.errorListener = callbackFcn;
|
|
}
|
|
|
|
/**
|
|
* Attaches a listener that will be invoked when the MediaPipe framework
|
|
* receives an empty packet on the provided output stream. This can be used
|
|
* to receive the latest output timestamp.
|
|
*
|
|
* Empty packet listeners are only active if there is a corresponding packet
|
|
* listener.
|
|
*
|
|
* @param outputStreamName The name of the graph output stream to receive
|
|
* empty packets from.
|
|
* @param callbackFcn The callback to receive the timestamp.
|
|
*/
|
|
attachEmptyPacketListener(
|
|
outputStreamName: string, callbackFcn: EmptyPacketListener) {
|
|
this.wasmModule.emptyPacketListeners =
|
|
this.wasmModule.emptyPacketListeners || {};
|
|
this.wasmModule.emptyPacketListeners[outputStreamName] = callbackFcn;
|
|
}
|
|
|
|
/**
|
|
* Takes the raw data from a JS audio capture array, and sends it to C++ to be
|
|
* processed.
|
|
* @param audioData An array of raw audio capture data, like
|
|
* from a call to getChannelData on an AudioBuffer.
|
|
* @param streamName The name of the MediaPipe graph stream to add the audio
|
|
* data to.
|
|
* @param timestamp The timestamp of the current frame, in ms.
|
|
*/
|
|
addAudioToStream(
|
|
audioData: Float32Array, streamName: string, timestamp: number) {
|
|
// numChannels and numSamples being 0 will cause defaults to be used,
|
|
// which will reflect values from last call to configureAudio.
|
|
this.addAudioToStreamWithShape(audioData, 0, 0, streamName, timestamp);
|
|
}
|
|
|
|
/**
|
|
* Takes the raw data from a JS audio capture array, and sends it to C++ to be
|
|
* processed, shaping the audioData array into an audio matrix according to
|
|
* the numChannels and numSamples parameters.
|
|
* @param audioData An array of raw audio capture data, like
|
|
* from a call to getChannelData on an AudioBuffer.
|
|
* @param numChannels The number of audio channels this data represents. If 0
|
|
* is passed, then the value will be taken from the last call to
|
|
* configureAudio.
|
|
* @param numSamples The number of audio samples captured in this data packet.
|
|
* If 0 is passed, then the value will be taken from the last call to
|
|
* configureAudio.
|
|
* @param streamName The name of the MediaPipe graph stream to add the audio
|
|
* data to.
|
|
* @param timestamp The timestamp of the current frame, in ms.
|
|
*/
|
|
addAudioToStreamWithShape(
|
|
audioData: Float32Array, numChannels: number, numSamples: number,
|
|
streamName: string, timestamp: number) {
|
|
// 4 bytes for each F32
|
|
const size = audioData.length * 4;
|
|
if (this.audioSize !== size) {
|
|
if (this.audioPtr) {
|
|
this.wasmModule._free(this.audioPtr);
|
|
}
|
|
this.audioPtr = this.wasmModule._malloc(size);
|
|
this.audioSize = size;
|
|
}
|
|
this.wasmModule.HEAPF32.set(audioData, this.audioPtr! / 4);
|
|
|
|
this.wrapStringPtr(streamName, (streamNamePtr: number) => {
|
|
this.wasmModule._addAudioToInputStream(
|
|
this.audioPtr!, numChannels, numSamples, streamNamePtr, timestamp);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Takes the relevant information from the HTML video or image element, and
|
|
* passes it into the WebGL-based graph for processing on the given stream at
|
|
* the given timestamp. Can be used for additional auxiliary GpuBuffer input
|
|
* streams. Processing will not occur until a blocking call (like
|
|
* processVideoGl or finishProcessing) is made. For use with
|
|
* 'gl_graph_runner_internal_multi_input'.
|
|
* @param imageSource Reference to the video frame we wish to add into our
|
|
* graph.
|
|
* @param streamName The name of the MediaPipe graph stream to add the frame
|
|
* to.
|
|
* @param timestamp The timestamp of the input frame, in ms.
|
|
*/
|
|
addGpuBufferToStream(
|
|
imageSource: ImageSource, streamName: string, timestamp: number): void {
|
|
this.wrapStringPtr(streamName, (streamNamePtr: number) => {
|
|
const [width, height] =
|
|
this.bindTextureToStream(imageSource, streamNamePtr);
|
|
this.wasmModule._addBoundTextureToStream(
|
|
streamNamePtr, width, height, timestamp);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Sends a boolean packet into the specified stream at the given timestamp.
|
|
* @param data The boolean data to send.
|
|
* @param streamName The name of the graph input stream to send data into.
|
|
* @param timestamp The timestamp of the input data, in ms.
|
|
*/
|
|
addBoolToStream(data: boolean, streamName: string, timestamp: number): void {
|
|
this.wrapStringPtr(streamName, (streamNamePtr: number) => {
|
|
this.wasmModule._addBoolToInputStream(data, streamNamePtr, timestamp);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Sends a double packet into the specified stream at the given timestamp.
|
|
* @param data The double data to send.
|
|
* @param streamName The name of the graph input stream to send data into.
|
|
* @param timestamp The timestamp of the input data, in ms.
|
|
*/
|
|
addDoubleToStream(data: number, streamName: string, timestamp: number): void {
|
|
this.wrapStringPtr(streamName, (streamNamePtr: number) => {
|
|
this.wasmModule._addDoubleToInputStream(data, streamNamePtr, timestamp);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Sends a float packet into the specified stream at the given timestamp.
|
|
* @param data The float data to send.
|
|
* @param streamName The name of the graph input stream to send data into.
|
|
* @param timestamp The timestamp of the input data, in ms.
|
|
*/
|
|
addFloatToStream(data: number, streamName: string, timestamp: number): void {
|
|
this.wrapStringPtr(streamName, (streamNamePtr: number) => {
|
|
// NOTE: _addFloatToStream and _addIntToStream are reserved for JS
|
|
// Calculators currently; we may want to revisit this naming scheme in the
|
|
// future.
|
|
this.wasmModule._addFloatToInputStream(data, streamNamePtr, timestamp);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Sends an integer packet into the specified stream at the given timestamp.
|
|
* @param data The integer data to send.
|
|
* @param streamName The name of the graph input stream to send data into.
|
|
* @param timestamp The timestamp of the input data, in ms.
|
|
*/
|
|
addIntToStream(data: number, streamName: string, timestamp: number): void {
|
|
this.wrapStringPtr(streamName, (streamNamePtr: number) => {
|
|
this.wasmModule._addIntToInputStream(data, streamNamePtr, timestamp);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Sends a string packet into the specified stream at the given timestamp.
|
|
* @param data The string data to send.
|
|
* @param streamName The name of the graph input stream to send data into.
|
|
* @param timestamp The timestamp of the input data, in ms.
|
|
*/
|
|
addStringToStream(data: string, streamName: string, timestamp: number): void {
|
|
this.wrapStringPtr(streamName, (streamNamePtr: number) => {
|
|
this.wrapStringPtr(data, (dataPtr: number) => {
|
|
this.wasmModule._addStringToInputStream(
|
|
dataPtr, streamNamePtr, timestamp);
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Sends a Record<string, string> packet into the specified stream at the
|
|
* given timestamp.
|
|
* @param data The records to send (will become a
|
|
* std::flat_hash_map<std::string, std::string).
|
|
* @param streamName The name of the graph input stream to send data into.
|
|
* @param timestamp The timestamp of the input data, in ms.
|
|
*/
|
|
addStringRecordToStream(
|
|
data: Record<string, string>, streamName: string,
|
|
timestamp: number): void {
|
|
this.wrapStringPtr(streamName, (streamNamePtr: number) => {
|
|
this.wrapStringPtrPtr(Object.keys(data), (keyList: number) => {
|
|
this.wrapStringPtrPtr(Object.values(data), (valueList: number) => {
|
|
this.wasmModule._addFlatHashMapToInputStream(
|
|
keyList, valueList, Object.keys(data).length, streamNamePtr,
|
|
timestamp);
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Sends a serialized protobuffer packet into the specified stream at the
|
|
* given timestamp, to be parsed into the specified protobuffer type.
|
|
* @param data The binary (serialized) raw protobuffer data.
|
|
* @param protoType The C++ namespaced type this protobuffer data corresponds
|
|
* to. It will be converted to this type when output as a packet into the
|
|
* graph.
|
|
* @param streamName The name of the graph input stream to send data into.
|
|
* @param timestamp The timestamp of the input data, in ms.
|
|
*/
|
|
addProtoToStream(
|
|
data: Uint8Array, protoType: string, streamName: string,
|
|
timestamp: number): void {
|
|
this.wrapStringPtr(streamName, (streamNamePtr: number) => {
|
|
this.wrapStringPtr(protoType, (protoTypePtr: number) => {
|
|
// Deep-copy proto data into Wasm heap
|
|
const dataPtr = this.wasmModule._malloc(data.length);
|
|
// TODO: Ensure this is the fastest way to copy this data.
|
|
this.wasmModule.HEAPU8.set(data, dataPtr);
|
|
this.wasmModule._addProtoToInputStream(
|
|
dataPtr, data.length, protoTypePtr, streamNamePtr, timestamp);
|
|
this.wasmModule._free(dataPtr);
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Sends an empty packet into the specified stream at the given timestamp,
|
|
* effectively advancing that input stream's timestamp bounds without
|
|
* sending additional data packets.
|
|
* @param streamName The name of the graph input stream to send the empty
|
|
* packet into.
|
|
* @param timestamp The timestamp of the empty packet, in ms.
|
|
*/
|
|
addEmptyPacketToStream(streamName: string, timestamp: number): void {
|
|
this.wrapStringPtr(streamName, (streamNamePtr: number) => {
|
|
this.wasmModule._addEmptyPacketToInputStream(streamNamePtr, timestamp);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Attaches a boolean packet to the specified input_side_packet.
|
|
* @param data The boolean data to send.
|
|
* @param sidePacketName The name of the graph input side packet to send data
|
|
* into.
|
|
*/
|
|
addBoolToInputSidePacket(data: boolean, sidePacketName: string): void {
|
|
this.wrapStringPtr(sidePacketName, (sidePacketNamePtr: number) => {
|
|
this.wasmModule._addBoolToInputSidePacket(data, sidePacketNamePtr);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Attaches a double packet to the specified input_side_packet.
|
|
* @param data The double data to send.
|
|
* @param sidePacketName The name of the graph input side packet to send data
|
|
* into.
|
|
*/
|
|
addDoubleToInputSidePacket(data: number, sidePacketName: string): void {
|
|
this.wrapStringPtr(sidePacketName, (sidePacketNamePtr: number) => {
|
|
this.wasmModule._addDoubleToInputSidePacket(data, sidePacketNamePtr);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Attaches a float packet to the specified input_side_packet.
|
|
* @param data The float data to send.
|
|
* @param sidePacketName The name of the graph input side packet to send data
|
|
* into.
|
|
*/
|
|
addFloatToInputSidePacket(data: number, sidePacketName: string): void {
|
|
this.wrapStringPtr(sidePacketName, (sidePacketNamePtr: number) => {
|
|
this.wasmModule._addFloatToInputSidePacket(data, sidePacketNamePtr);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Attaches a integer packet to the specified input_side_packet.
|
|
* @param data The integer data to send.
|
|
* @param sidePacketName The name of the graph input side packet to send data
|
|
* into.
|
|
*/
|
|
addIntToInputSidePacket(data: number, sidePacketName: string): void {
|
|
this.wrapStringPtr(sidePacketName, (sidePacketNamePtr: number) => {
|
|
this.wasmModule._addIntToInputSidePacket(data, sidePacketNamePtr);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Attaches a string packet to the specified input_side_packet.
|
|
* @param data The string data to send.
|
|
* @param sidePacketName The name of the graph input side packet to send data
|
|
* into.
|
|
*/
|
|
addStringToInputSidePacket(data: string, sidePacketName: string): void {
|
|
this.wrapStringPtr(sidePacketName, (sidePacketNamePtr: number) => {
|
|
this.wrapStringPtr(data, (dataPtr: number) => {
|
|
this.wasmModule._addStringToInputSidePacket(dataPtr, sidePacketNamePtr);
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Attaches a serialized proto packet to the specified input_side_packet.
|
|
* @param data The binary (serialized) raw protobuffer data.
|
|
* @param protoType The C++ namespaced type this protobuffer data corresponds
|
|
* to. It will be converted to this type for use in the graph.
|
|
* @param sidePacketName The name of the graph input side packet to send data
|
|
* into.
|
|
*/
|
|
addProtoToInputSidePacket(
|
|
data: Uint8Array, protoType: string, sidePacketName: string): void {
|
|
this.wrapStringPtr(sidePacketName, (sidePacketNamePtr: number) => {
|
|
this.wrapStringPtr(protoType, (protoTypePtr: number) => {
|
|
// Deep-copy proto data into Wasm heap
|
|
const dataPtr = this.wasmModule._malloc(data.length);
|
|
// TODO: Ensure this is the fastest way to copy this data.
|
|
this.wasmModule.HEAPU8.set(data, dataPtr);
|
|
this.wasmModule._addProtoToInputSidePacket(
|
|
dataPtr, data.length, protoTypePtr, sidePacketNamePtr);
|
|
this.wasmModule._free(dataPtr);
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Attaches a boolean packet listener to the specified output_stream.
|
|
* @param outputStreamName The name of the graph output stream to grab boolean
|
|
* data from.
|
|
* @param callbackFcn The function that will be called back with the data, as
|
|
* it is received. Note that the data is only guaranteed to exist for the
|
|
* duration of the callback, and the callback will be called inline, so it
|
|
* should not perform overly complicated (or any async) behavior.
|
|
*/
|
|
attachBoolListener(
|
|
outputStreamName: string, callbackFcn: SimpleListener<boolean>): void {
|
|
// Set up our TS listener to receive any packets for this stream.
|
|
this.setListener(outputStreamName, callbackFcn);
|
|
|
|
// Tell our graph to listen for bool packets on this stream.
|
|
this.wrapStringPtr(outputStreamName, (outputStreamNamePtr: number) => {
|
|
this.wasmModule._attachBoolListener(outputStreamNamePtr);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Attaches a bool[] packet listener to the specified output_stream.
|
|
* @param outputStreamName The name of the graph output stream to grab
|
|
* std::vector<bool> data from.
|
|
* @param callbackFcn The function that will be called back with the data, as
|
|
* it is received. Note that the data is only guaranteed to exist for the
|
|
* duration of the callback, and the callback will be called inline, so it
|
|
* should not perform overly complicated (or any async) behavior.
|
|
*/
|
|
attachBoolVectorListener(
|
|
outputStreamName: string, callbackFcn: SimpleListener<boolean[]>): void {
|
|
// Set up our TS listener to receive any packets for this stream.
|
|
this.setVectorListener(outputStreamName, callbackFcn);
|
|
|
|
// Tell our graph to listen for std::vector<bool> packets on this stream.
|
|
this.wrapStringPtr(outputStreamName, (outputStreamNamePtr: number) => {
|
|
this.wasmModule._attachBoolVectorListener(outputStreamNamePtr);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Attaches an int packet listener to the specified output_stream.
|
|
* @param outputStreamName The name of the graph output stream to grab int
|
|
* data from.
|
|
* @param callbackFcn The function that will be called back with the data, as
|
|
* it is received. Note that the data is only guaranteed to exist for the
|
|
* duration of the callback, and the callback will be called inline, so it
|
|
* should not perform overly complicated (or any async) behavior.
|
|
*/
|
|
attachIntListener(
|
|
outputStreamName: string, callbackFcn: SimpleListener<number>): void {
|
|
// Set up our TS listener to receive any packets for this stream.
|
|
this.setListener(outputStreamName, callbackFcn);
|
|
|
|
// Tell our graph to listen for int packets on this stream.
|
|
this.wrapStringPtr(outputStreamName, (outputStreamNamePtr: number) => {
|
|
this.wasmModule._attachIntListener(outputStreamNamePtr);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Attaches an int[] packet listener to the specified output_stream.
|
|
* @param outputStreamName The name of the graph output stream to grab
|
|
* std::vector<int> data from.
|
|
* @param callbackFcn The function that will be called back with the data, as
|
|
* it is received. Note that the data is only guaranteed to exist for the
|
|
* duration of the callback, and the callback will be called inline, so it
|
|
* should not perform overly complicated (or any async) behavior.
|
|
*/
|
|
attachIntVectorListener(
|
|
outputStreamName: string, callbackFcn: SimpleListener<number[]>): void {
|
|
// Set up our TS listener to receive any packets for this stream.
|
|
this.setVectorListener(outputStreamName, callbackFcn);
|
|
|
|
// Tell our graph to listen for std::vector<int> packets on this stream.
|
|
this.wrapStringPtr(outputStreamName, (outputStreamNamePtr: number) => {
|
|
this.wasmModule._attachIntVectorListener(outputStreamNamePtr);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Attaches a double packet listener to the specified output_stream.
|
|
* @param outputStreamName The name of the graph output stream to grab double
|
|
* data from.
|
|
* @param callbackFcn The function that will be called back with the data, as
|
|
* it is received. Note that the data is only guaranteed to exist for the
|
|
* duration of the callback, and the callback will be called inline, so it
|
|
* should not perform overly complicated (or any async) behavior.
|
|
*/
|
|
attachDoubleListener(
|
|
outputStreamName: string, callbackFcn: SimpleListener<number>): void {
|
|
// Set up our TS listener to receive any packets for this stream.
|
|
this.setListener(outputStreamName, callbackFcn);
|
|
|
|
// Tell our graph to listen for double packets on this stream.
|
|
this.wrapStringPtr(outputStreamName, (outputStreamNamePtr: number) => {
|
|
this.wasmModule._attachDoubleListener(outputStreamNamePtr);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Attaches a double[] packet listener to the specified output_stream.
|
|
* @param outputStreamName The name of the graph output stream to grab
|
|
* std::vector<double> data from.
|
|
* @param callbackFcn The function that will be called back with the data, as
|
|
* it is received. Note that the data is only guaranteed to exist for the
|
|
* duration of the callback, and the callback will be called inline, so it
|
|
* should not perform overly complicated (or any async) behavior.
|
|
*/
|
|
attachDoubleVectorListener(
|
|
outputStreamName: string, callbackFcn: SimpleListener<number[]>): void {
|
|
// Set up our TS listener to receive any packets for this stream.
|
|
this.setVectorListener(outputStreamName, callbackFcn);
|
|
|
|
// Tell our graph to listen for std::vector<double> packets on this stream.
|
|
this.wrapStringPtr(outputStreamName, (outputStreamNamePtr: number) => {
|
|
this.wasmModule._attachDoubleVectorListener(outputStreamNamePtr);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Attaches a float packet listener to the specified output_stream.
|
|
* @param outputStreamName The name of the graph output stream to grab float
|
|
* data from.
|
|
* @param callbackFcn The function that will be called back with the data, as
|
|
* it is received. Note that the data is only guaranteed to exist for the
|
|
* duration of the callback, and the callback will be called inline, so it
|
|
* should not perform overly complicated (or any async) behavior.
|
|
*/
|
|
attachFloatListener(
|
|
outputStreamName: string, callbackFcn: SimpleListener<number>): void {
|
|
// Set up our TS listener to receive any packets for this stream.
|
|
this.setListener(outputStreamName, callbackFcn);
|
|
|
|
// Tell our graph to listen for float packets on this stream.
|
|
this.wrapStringPtr(outputStreamName, (outputStreamNamePtr: number) => {
|
|
this.wasmModule._attachFloatListener(outputStreamNamePtr);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Attaches a float[] packet listener to the specified output_stream.
|
|
* @param outputStreamName The name of the graph output stream to grab
|
|
* std::vector<float> data from.
|
|
* @param callbackFcn The function that will be called back with the data, as
|
|
* it is received. Note that the data is only guaranteed to exist for the
|
|
* duration of the callback, and the callback will be called inline, so it
|
|
* should not perform overly complicated (or any async) behavior.
|
|
*/
|
|
attachFloatVectorListener(
|
|
outputStreamName: string, callbackFcn: SimpleListener<number[]>): void {
|
|
// Set up our TS listener to receive any packets for this stream.
|
|
this.setVectorListener(outputStreamName, callbackFcn);
|
|
|
|
// Tell our graph to listen for std::vector<float> packets on this stream.
|
|
this.wrapStringPtr(outputStreamName, (outputStreamNamePtr: number) => {
|
|
this.wasmModule._attachFloatVectorListener(outputStreamNamePtr);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Attaches a string packet listener to the specified output_stream.
|
|
* @param outputStreamName The name of the graph output stream to grab string
|
|
* data from.
|
|
* @param callbackFcn The function that will be called back with the data, as
|
|
* it is received. Note that the data is only guaranteed to exist for the
|
|
* duration of the callback, and the callback will be called inline, so it
|
|
* should not perform overly complicated (or any async) behavior.
|
|
*/
|
|
attachStringListener(
|
|
outputStreamName: string, callbackFcn: SimpleListener<string>): void {
|
|
// Set up our TS listener to receive any packets for this stream.
|
|
this.setListener(outputStreamName, callbackFcn);
|
|
|
|
// Tell our graph to listen for string packets on this stream.
|
|
this.wrapStringPtr(outputStreamName, (outputStreamNamePtr: number) => {
|
|
this.wasmModule._attachStringListener(outputStreamNamePtr);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Attaches a string[] packet listener to the specified output_stream.
|
|
* @param outputStreamName The name of the graph output stream to grab
|
|
* std::vector<std::string> data from.
|
|
* @param callbackFcn The function that will be called back with the data, as
|
|
* it is received. Note that the data is only guaranteed to exist for the
|
|
* duration of the callback, and the callback will be called inline, so it
|
|
* should not perform overly complicated (or any async) behavior.
|
|
*/
|
|
attachStringVectorListener(
|
|
outputStreamName: string, callbackFcn: SimpleListener<string[]>): void {
|
|
// Set up our TS listener to receive any packets for this stream.
|
|
this.setVectorListener(outputStreamName, callbackFcn);
|
|
|
|
// Tell our graph to listen for std::vector<string> packets on this stream.
|
|
this.wrapStringPtr(outputStreamName, (outputStreamNamePtr: number) => {
|
|
this.wasmModule._attachStringVectorListener(outputStreamNamePtr);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Attaches a serialized proto packet listener to the specified output_stream.
|
|
* @param outputStreamName The name of the graph output stream to grab binary
|
|
* serialized proto data from (in Uint8Array format).
|
|
* @param callbackFcn The function that will be called back with the data, as
|
|
* it is received. Note that by default the data is only guaranteed to
|
|
* exist for the duration of the callback, and the callback will be called
|
|
* inline, so it should not perform overly complicated (or any async)
|
|
* behavior. If the proto data needs to be able to outlive the call, you
|
|
* may set the optional makeDeepCopy parameter to true, or can manually
|
|
* deep-copy the data yourself.
|
|
* @param makeDeepCopy Optional convenience parameter which, if set to true,
|
|
* will override the default memory management behavior and make a deep
|
|
* copy of the underlying data, rather than just returning a view into the
|
|
* C++-managed memory. At the cost of a data copy, this allows the
|
|
* returned data to outlive the callback lifetime (and it will be cleaned
|
|
* up automatically by JS garbage collection whenever the user is finished
|
|
* with it).
|
|
*/
|
|
attachProtoListener(
|
|
outputStreamName: string, callbackFcn: SimpleListener<Uint8Array>,
|
|
makeDeepCopy?: boolean): void {
|
|
// Set up our TS listener to receive any packets for this stream.
|
|
this.setListener(outputStreamName, callbackFcn);
|
|
|
|
// Tell our graph to listen for binary serialized proto data packets on this
|
|
// stream.
|
|
this.wrapStringPtr(outputStreamName, (outputStreamNamePtr: number) => {
|
|
this.wasmModule._attachProtoListener(
|
|
outputStreamNamePtr, makeDeepCopy || false);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Attaches a listener for an array of serialized proto packets to the
|
|
* specified output_stream.
|
|
* @param outputStreamName The name of the graph output stream to grab a
|
|
* vector of binary serialized proto data from (in Uint8Array[] format).
|
|
* @param callbackFcn The function that will be called back with the data, as
|
|
* it is received. Note that by default the data is only guaranteed to
|
|
* exist for the duration of the callback, and the callback will be called
|
|
* inline, so it should not perform overly complicated (or any async)
|
|
* behavior. If the proto data needs to be able to outlive the call, you
|
|
* may set the optional makeDeepCopy parameter to true, or can manually
|
|
* deep-copy the data yourself.
|
|
* @param makeDeepCopy Optional convenience parameter which, if set to true,
|
|
* will override the default memory management behavior and make a deep
|
|
* copy of the underlying data, rather than just returning a view into the
|
|
* C++-managed memory. At the cost of a data copy, this allows the
|
|
* returned data to outlive the callback lifetime (and it will be cleaned
|
|
* up automatically by JS garbage collection whenever the user is finished
|
|
* with it).
|
|
*/
|
|
attachProtoVectorListener(
|
|
outputStreamName: string, callbackFcn: SimpleListener<Uint8Array[]>,
|
|
makeDeepCopy?: boolean): void {
|
|
// Set up our TS listener to receive any packets for this stream.
|
|
this.setVectorListener(outputStreamName, callbackFcn);
|
|
|
|
// Tell our graph to listen for a vector of binary serialized proto packets
|
|
// on this stream.
|
|
this.wrapStringPtr(outputStreamName, (outputStreamNamePtr: number) => {
|
|
this.wasmModule._attachProtoVectorListener(
|
|
outputStreamNamePtr, makeDeepCopy || false);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Attaches an audio packet listener to the specified output_stream, to be
|
|
* given a Float32Array as output.
|
|
* @param outputStreamName The name of the graph output stream to grab audio
|
|
* data from.
|
|
* @param callbackFcn The function that will be called back with the data, as
|
|
* it is received. Note that the data is only guaranteed to exist for the
|
|
* duration of the callback, and the callback will be called inline, so it
|
|
* should not perform overly complicated (or any async) behavior. If the
|
|
* audio data needs to be able to outlive the call, you may set the
|
|
* optional makeDeepCopy parameter to true, or can manually deep-copy the
|
|
* data yourself.
|
|
* @param makeDeepCopy Optional convenience parameter which, if set to true,
|
|
* will override the default memory management behavior and make a deep
|
|
* copy of the underlying data, rather than just returning a view into the
|
|
* C++-managed memory. At the cost of a data copy, this allows the
|
|
* returned data to outlive the callback lifetime (and it will be cleaned
|
|
* up automatically by JS garbage collection whenever the user is finished
|
|
* with it).
|
|
*/
|
|
attachAudioListener(
|
|
outputStreamName: string, callbackFcn: SimpleListener<Float32Array>,
|
|
makeDeepCopy?: boolean): void {
|
|
if (!this.wasmModule._attachAudioListener) {
|
|
console.warn(
|
|
'Attempting to use attachAudioListener without support for ' +
|
|
'output audio. Is build dep ":gl_graph_runner_audio_out" missing?');
|
|
}
|
|
|
|
// Set up our TS listener to receive any packets for this stream, and
|
|
// additionally reformat our Uint8Array into a Float32Array for the user.
|
|
this.setListener<Uint8Array>(outputStreamName, (data, timestamp) => {
|
|
// Should be very fast
|
|
const floatArray =
|
|
new Float32Array(data.buffer, data.byteOffset, data.length / 4);
|
|
callbackFcn(floatArray, timestamp);
|
|
});
|
|
|
|
// Tell our graph to listen for string packets on this stream.
|
|
this.wrapStringPtr(outputStreamName, (outputStreamNamePtr: number) => {
|
|
this.wasmModule._attachAudioListener(
|
|
outputStreamNamePtr, makeDeepCopy || false);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Forces all queued-up packets to be pushed through the MediaPipe graph as
|
|
* far as possible, performing all processing until no more processing can be
|
|
* done.
|
|
*/
|
|
finishProcessing(): void {
|
|
this.wasmModule._waitUntilIdle();
|
|
}
|
|
}
|
|
|
|
// Quick private helper to run the given script safely
|
|
async function runScript(scriptUrl: string) {
|
|
if (typeof importScripts === 'function') {
|
|
importScripts(scriptUrl.toString());
|
|
} else {
|
|
const script = document.createElement('script');
|
|
script.setAttribute('src', scriptUrl);
|
|
script.setAttribute('crossorigin', 'anonymous');
|
|
return new Promise<void>((resolve) => {
|
|
script.addEventListener('load', () => {
|
|
resolve();
|
|
}, false);
|
|
script.addEventListener('error', () => {
|
|
resolve();
|
|
}, false);
|
|
document.body.appendChild(script);
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helper type macro for use with createMediaPipeLib. Allows us to easily infer
|
|
* the type of a mixin-extended GraphRunner. Example usage:
|
|
* const GraphRunnerConstructor =
|
|
* SupportImage(SupportSerialization(GraphRunner));
|
|
* let mediaPipe: ReturnType<typeof GraphRunnerConstructor>;
|
|
* ...
|
|
* mediaPipe = await createMediaPipeLib(GraphRunnerConstructor, ...);
|
|
*/
|
|
// tslint:disable-next-line:no-any
|
|
export type ReturnType<T> = T extends (...args: unknown[]) => infer R ? R : any;
|
|
|
|
/**
|
|
* Global function to initialize Wasm blob and load runtime assets for a
|
|
* specialized MediaPipe library. This allows us to create a requested
|
|
* subclass inheriting from GraphRunner.
|
|
* @param constructorFcn The name of the class to instantiate via "new".
|
|
* @param wasmLoaderScript Url for the wasm-runner script; produced by the build
|
|
* process.
|
|
* @param assetLoaderScript Url for the asset-loading script; produced by the
|
|
* build process.
|
|
* @param fileLocator A function to override the file locations for assets
|
|
* loaded by the MediaPipe library.
|
|
* @return promise A promise which will resolve when initialization has
|
|
* completed successfully.
|
|
*/
|
|
export async function createMediaPipeLib<LibType>(
|
|
constructorFcn: WasmMediaPipeConstructor<LibType>,
|
|
wasmLoaderScript?: string|null,
|
|
assetLoaderScript?: string|null,
|
|
glCanvas?: HTMLCanvasElement|OffscreenCanvas|null,
|
|
fileLocator?: FileLocator): Promise<LibType> {
|
|
const scripts = [];
|
|
// Run wasm-loader script here
|
|
if (wasmLoaderScript) {
|
|
scripts.push(wasmLoaderScript);
|
|
}
|
|
// Run asset-loader script here
|
|
if (assetLoaderScript) {
|
|
scripts.push(assetLoaderScript);
|
|
}
|
|
// Load scripts in parallel, browser will execute them in sequence.
|
|
if (scripts.length) {
|
|
await Promise.all(scripts.map(runScript));
|
|
}
|
|
if (!self.ModuleFactory) {
|
|
throw new Error('ModuleFactory not set.');
|
|
}
|
|
|
|
// Until asset scripts work nicely with MODULARIZE, when we are given both
|
|
// self.Module and a fileLocator, we manually merge them into self.Module and
|
|
// use that. TODO: Remove this when asset scripts are fixed.
|
|
if (self.Module && fileLocator) {
|
|
const moduleFileLocator = self.Module as FileLocator;
|
|
moduleFileLocator.locateFile = fileLocator.locateFile;
|
|
if (fileLocator.mainScriptUrlOrBlob) {
|
|
moduleFileLocator.mainScriptUrlOrBlob = fileLocator.mainScriptUrlOrBlob;
|
|
}
|
|
}
|
|
// TODO: Ensure that fileLocator is passed in by all users
|
|
// and make it required
|
|
const module =
|
|
await self.ModuleFactory(self.Module as FileLocator || fileLocator);
|
|
// Don't reuse factory or module seed
|
|
self.ModuleFactory = self.Module = undefined;
|
|
return new constructorFcn(module, glCanvas);
|
|
}
|
|
|
|
/**
|
|
* Global function to initialize Wasm blob and load runtime assets for a generic
|
|
* MediaPipe library.
|
|
* @param wasmLoaderScript Url for the wasm-runner script; produced by the build
|
|
* process.
|
|
* @param assetLoaderScript Url for the asset-loading script; produced by the
|
|
* build process.
|
|
* @param fileLocator A function to override the file locations for assets
|
|
* loaded by the MediaPipe library.
|
|
* @return promise A promise which will resolve when initialization has
|
|
* completed successfully.
|
|
*/
|
|
export async function createGraphRunner(
|
|
wasmLoaderScript?: string,
|
|
assetLoaderScript?: string,
|
|
glCanvas?: HTMLCanvasElement|OffscreenCanvas|null,
|
|
fileLocator?: FileLocator): Promise<GraphRunner> {
|
|
return createMediaPipeLib(
|
|
GraphRunner, wasmLoaderScript, assetLoaderScript, glCanvas,
|
|
fileLocator);
|
|
}
|