From bbc0d125b89ff54970d8b03108ef07a6bd0587da Mon Sep 17 00:00:00 2001 From: Wojciech Kozyra Date: Thu, 11 Jul 2024 13:21:56 +0200 Subject: [PATCH] Fix prettier config --- demos/{.prettierc => .prettierrc} | 0 demos/1-videoconferencing/index.ts | 90 +++++++------- demos/2-tv_broadcast/index.ts | 189 ++++++++++++++--------------- demos/3-live_stream/index.ts | 150 +++++++++++------------ demos/utils/api.ts | 43 ++----- demos/utils/ffmpeg.ts | 91 ++++++-------- demos/utils/generate_types.ts | 13 +- demos/utils/gst.ts | 46 +++---- demos/utils/prepare_compositor.ts | 38 +++--- demos/utils/run.ts | 36 +++--- demos/utils/utils.ts | 45 +++---- 11 files changed, 327 insertions(+), 414 deletions(-) rename demos/{.prettierc => .prettierrc} (100%) diff --git a/demos/.prettierc b/demos/.prettierrc similarity index 100% rename from demos/.prettierc rename to demos/.prettierrc diff --git a/demos/1-videoconferencing/index.ts b/demos/1-videoconferencing/index.ts index fab9266f5..165f62ae7 100644 --- a/demos/1-videoconferencing/index.ts +++ b/demos/1-videoconferencing/index.ts @@ -1,19 +1,16 @@ -import { runCompositorExample } from "../utils/run"; -import { gstStreamWebcam } from "../utils/gst"; -import { downloadAsync, sleepAsync } from "../utils/utils"; -import { Component, Resolution } from "../types/api.d"; -import { - ffmpegSendVideoFromMp4, - ffplayStartPlayerAsync, -} from "../utils/ffmpeg"; -import path from "path"; +import { runCompositorExample } from '../utils/run'; +import { gstStreamWebcam } from '../utils/gst'; +import { downloadAsync, sleepAsync } from '../utils/utils'; +import { Component, Resolution } from '../types/api.d'; +import { ffmpegSendVideoFromMp4, ffplayStartPlayerAsync } from '../utils/ffmpeg'; +import path from 'path'; import { registerImageAsync, registerInputAsync, registerOutputAsync, startAsync, updateOutputAsync, -} from "../utils/api"; +} from '../utils/api'; const OUTPUT_RESOLUTION: Resolution = { width: 1920, @@ -22,44 +19,44 @@ const OUTPUT_RESOLUTION: Resolution = { const INPUT_PORT = 8000; const OUTPUT_PORT = 8002; -const IP = "127.0.0.1"; +const IP = '127.0.0.1'; const DISPLAY_LOGS = true; const BACKGROUND_URL = - "https://raw.githubusercontent.com/membraneframework-labs/video_compositor_snapshot_tests/main/demo_assets/triangles_background.png"; + 'https://raw.githubusercontent.com/membraneframework-labs/video_compositor_snapshot_tests/main/demo_assets/triangles_background.png'; const CALL_URL = - "https://raw.githubusercontent.com/membraneframework-labs/video_compositor_snapshot_tests/main/demo_assets/call.mp4"; + 'https://raw.githubusercontent.com/membraneframework-labs/video_compositor_snapshot_tests/main/demo_assets/call.mp4'; async function exampleAsync() { - const useWebCam = process.env.LIVE_COMPOSITOR_WEBCAM !== "false"; + const useWebCam = process.env.LIVE_COMPOSITOR_WEBCAM !== 'false'; await ffplayStartPlayerAsync(IP, DISPLAY_LOGS, OUTPUT_PORT); // sleep to make sure ffplay have a chance to start before compositor starts sending packets await sleepAsync(2000); - await registerImageAsync("background", { - asset_type: "png", + await registerImageAsync('background', { + asset_type: 'png', url: BACKGROUND_URL, }); - await registerInputAsync("input_1", { - type: "rtp_stream", - transport_protocol: useWebCam ? "tcp_server" : "udp", + await registerInputAsync('input_1', { + type: 'rtp_stream', + transport_protocol: useWebCam ? 'tcp_server' : 'udp', port: INPUT_PORT, video: { - decoder: "ffmpeg_h264", + decoder: 'ffmpeg_h264', }, }); - await registerOutputAsync("output_1", { - type: "rtp_stream", + await registerOutputAsync('output_1', { + type: 'rtp_stream', ip: IP, port: OUTPUT_PORT, video: { resolution: OUTPUT_RESOLUTION, encoder: { - type: "ffmpeg_h264", - preset: "medium", + type: 'ffmpeg_h264', + preset: 'medium', }, initial: { root: sceneWithInputs(1), @@ -70,19 +67,18 @@ async function exampleAsync() { if (useWebCam) { gstStreamWebcam(IP, INPUT_PORT, DISPLAY_LOGS); } else { - const callPath = path.join(__dirname, "../assets/call.mp4"); + const callPath = path.join(__dirname, '../assets/call.mp4'); await downloadAsync(CALL_URL, callPath); ffmpegSendVideoFromMp4(INPUT_PORT, callPath, DISPLAY_LOGS); } await startAsync(); const inputs = [ - 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, - 4, 3, 2, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, ]; for (let i = 0; i < inputs.length; i++) { - await updateOutputAsync("output_1", { + await updateOutputAsync('output_1', { video: { root: sceneWithInputs(inputs[i]), }, @@ -94,31 +90,31 @@ async function exampleAsync() { function sceneWithInputs(n: number): Component { const children: Array = Array.from({ length: n }, (_, i) => { const text: Component = { - type: "text", + type: 'text', text: `InputStream ${i} 🚀`, font_size: 25, - align: "center", - color_rgba: "#FFFFFFFF", - background_color_rgba: "#FF0000FF", - font_family: "Arial", + align: 'center', + color_rgba: '#FFFFFFFF', + background_color_rgba: '#FF0000FF', + font_family: 'Arial', }; const inputStreamTile: Component = { - type: "view", + type: 'view', children: [ { - type: "rescaler", + type: 'rescaler', child: { - type: "input_stream", - input_id: "input_1", + type: 'input_stream', + input_id: 'input_1', }, }, { - type: "view", + type: 'view', height: 50, bottom: 0, left: 0, - children: [{ type: "view" }, text, { type: "view" }], + children: [{ type: 'view' }, text, { type: 'view' }], }, ], }; @@ -127,31 +123,31 @@ function sceneWithInputs(n: number): Component { }); const tiles: Component = { - type: "tiles", - id: "tile", + type: 'tiles', + id: 'tile', padding: 5, children: children, transition: { duration_ms: 700, easing_function: { - function_name: "cubic_bezier", + function_name: 'cubic_bezier', points: [0.35, 0.22, 0.1, 0.8], }, }, }; const background: Component = { - type: "image", - image_id: "background", + type: 'image', + image_id: 'background', }; return { - type: "view", + type: 'view', width: OUTPUT_RESOLUTION.width, height: OUTPUT_RESOLUTION.height, children: [ { - type: "view", + type: 'view', width: OUTPUT_RESOLUTION.width, height: OUTPUT_RESOLUTION.height, top: 0, @@ -159,7 +155,7 @@ function sceneWithInputs(n: number): Component { children: [background], }, { - type: "view", + type: 'view', width: OUTPUT_RESOLUTION.width, height: OUTPUT_RESOLUTION.height, top: 0, diff --git a/demos/2-tv_broadcast/index.ts b/demos/2-tv_broadcast/index.ts index 800c46260..dcb7903ef 100644 --- a/demos/2-tv_broadcast/index.ts +++ b/demos/2-tv_broadcast/index.ts @@ -1,12 +1,9 @@ -import { - ffmpegSendVideoFromMp4, - ffplayStartPlayerAsync, -} from "../utils/ffmpeg"; -import { runCompositorExample } from "../utils/run"; -import { downloadAsync, sleepAsync } from "../utils/utils"; -import fs from "fs-extra"; -import path from "path"; -import { Component, Resolution } from "../types/api.d"; +import { ffmpegSendVideoFromMp4, ffplayStartPlayerAsync } from '../utils/ffmpeg'; +import { runCompositorExample } from '../utils/run'; +import { downloadAsync, sleepAsync } from '../utils/utils'; +import fs from 'fs-extra'; +import path from 'path'; +import { Component, Resolution } from '../types/api.d'; import { registerImageAsync, registerInputAsync, @@ -14,7 +11,7 @@ import { registerShaderAsync, startAsync, updateOutputAsync, -} from "../utils/api"; +} from '../utils/api'; const OUTPUT_RESOLUTION: Resolution = { width: 1920, @@ -24,74 +21,66 @@ const OUTPUT_RESOLUTION: Resolution = { const INPUT_PORT = 9002; const VIDEO_OUTPUT_PORT = 9004; const AUDIO_OUTPUT_PORT = 9006; -const IP = "127.0.0.1"; +const IP = '127.0.0.1'; const DISPLAY_LOGS = true; -const BUNNY_PATH = path.join(__dirname, "../assets/bunny.mp4"); -const TV_PATH = path.join(__dirname, "../assets/green_screen_example.mp4"); +const BUNNY_PATH = path.join(__dirname, '../assets/bunny.mp4'); +const TV_PATH = path.join(__dirname, '../assets/green_screen_example.mp4'); const REPORTER_URL = - "https://assets.mixkit.co/videos/preview/mixkit-female-reporter-reporting-with-microphone-in-hand-on-a-chroma-28293-large.mp4"; + 'https://assets.mixkit.co/videos/preview/mixkit-female-reporter-reporting-with-microphone-in-hand-on-a-chroma-28293-large.mp4'; const BUNNY_URL = - "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"; + 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4'; const BACKGROUND_URL = - "https://raw.githubusercontent.com/membraneframework-labs/video_compositor_snapshot_tests/main/demo_assets/news_room.jpeg"; + 'https://raw.githubusercontent.com/membraneframework-labs/video_compositor_snapshot_tests/main/demo_assets/news_room.jpeg'; const LOGO_URL = - "https://raw.githubusercontent.com/membraneframework-labs/video_compositor_snapshot_tests/main/demo_assets/logo.png"; + 'https://raw.githubusercontent.com/membraneframework-labs/video_compositor_snapshot_tests/main/demo_assets/logo.png'; async function exampleAsync() { - ffplayStartPlayerAsync( - IP, - DISPLAY_LOGS, - VIDEO_OUTPUT_PORT, - AUDIO_OUTPUT_PORT, - ); + ffplayStartPlayerAsync(IP, DISPLAY_LOGS, VIDEO_OUTPUT_PORT, AUDIO_OUTPUT_PORT); await sleepAsync(2000); - process.env.LIVE_COMPOSITOR_LOGGER_LEVEL = "debug"; + process.env.LIVE_COMPOSITOR_LOGGER_LEVEL = 'debug'; await downloadAsync(REPORTER_URL, TV_PATH); await downloadAsync(BUNNY_URL, BUNNY_PATH); - await registerInputAsync("tv_input", { - type: "rtp_stream", + await registerInputAsync('tv_input', { + type: 'rtp_stream', port: INPUT_PORT, video: { - decoder: "ffmpeg_h264", + decoder: 'ffmpeg_h264', }, }); - await registerInputAsync("bunny", { - type: "mp4", + await registerInputAsync('bunny', { + type: 'mp4', path: BUNNY_PATH, }); - await registerShaderAsync("remove_green_screen", { - source: await fs.readFile( - path.join(__dirname, "remove_green_screen.wgsl"), - "utf-8", - ), + await registerShaderAsync('remove_green_screen', { + source: await fs.readFile(path.join(__dirname, 'remove_green_screen.wgsl'), 'utf-8'), }); - await registerImageAsync("background", { - asset_type: "jpeg", + await registerImageAsync('background', { + asset_type: 'jpeg', url: BACKGROUND_URL, }); - await registerImageAsync("logo", { - asset_type: "png", + await registerImageAsync('logo', { + asset_type: 'png', url: LOGO_URL, }); - await registerOutputAsync("output_video", { - type: "rtp_stream", + await registerOutputAsync('output_video', { + type: 'rtp_stream', ip: IP, port: VIDEO_OUTPUT_PORT, video: { resolution: OUTPUT_RESOLUTION, encoder: { - type: "ffmpeg_h264", - preset: "ultrafast", + type: 'ffmpeg_h264', + preset: 'ultrafast', }, initial: { root: scene(undefined), @@ -99,14 +88,14 @@ async function exampleAsync() { }, }); - await registerOutputAsync("output_audio", { - type: "rtp_stream", + await registerOutputAsync('output_audio', { + type: 'rtp_stream', ip: IP, port: AUDIO_OUTPUT_PORT, audio: { encoder: { - channels: "stereo", - type: "opus", + channels: 'stereo', + type: 'opus', }, initial: { inputs: [], @@ -118,7 +107,7 @@ async function exampleAsync() { await startAsync(); // First update to set start position of the bunny for transition - await updateOutputAsync("output_video", { + await updateOutputAsync('output_video', { video: { root: scene(bunnyOutside), }, @@ -126,23 +115,23 @@ async function exampleAsync() { }); // Bunny transitions - await updateOutputAsync("output_video", { + await updateOutputAsync('output_video', { video: { root: scene(bunnyInside), }, schedule_time_ms: 7_000, }); - await updateOutputAsync("output_video", { + await updateOutputAsync('output_video', { video: { root: scene(finalBunnyPosition), }, schedule_time_ms: 13_000, }); - await updateOutputAsync("output_audio", { + await updateOutputAsync('output_audio', { audio: { - inputs: [{ input_id: "bunny" }], + inputs: [{ input_id: 'bunny' }], }, schedule_time_ms: 13_000, }); @@ -154,15 +143,15 @@ function scene(bunnyProducer?: () => Component): Component { : [news_report(), logo(), breakingNewsText()]; return { - type: "view", + type: 'view', children: components, }; } function bunnyOutside(): Component { return { - type: "view", - id: "bunny_view", + type: 'view', + id: 'bunny_view', width: OUTPUT_RESOLUTION.width, height: OUTPUT_RESOLUTION.height, top: 0, @@ -173,8 +162,8 @@ function bunnyOutside(): Component { function bunnyInside(): Component { return { - type: "view", - id: "bunny_view", + type: 'view', + id: 'bunny_view', width: OUTPUT_RESOLUTION.width, height: OUTPUT_RESOLUTION.height, top: 0, @@ -183,7 +172,7 @@ function bunnyInside(): Component { transition: { duration_ms: 1000, easing_function: { - function_name: "bounce", + function_name: 'bounce', }, }, }; @@ -191,8 +180,8 @@ function bunnyInside(): Component { function finalBunnyPosition(): Component { return { - type: "view", - id: "bunny_view", + type: 'view', + id: 'bunny_view', width: OUTPUT_RESOLUTION.width / 4, height: OUTPUT_RESOLUTION.height / 4, top: 20, @@ -202,7 +191,7 @@ function finalBunnyPosition(): Component { transition: { duration_ms: 1000, easing_function: { - function_name: "linear", + function_name: 'linear', }, }, }; @@ -210,28 +199,28 @@ function finalBunnyPosition(): Component { function news_report(): Component { const rescaledInputStream: Component = { - type: "rescaler", + type: 'rescaler', width: OUTPUT_RESOLUTION.width, height: OUTPUT_RESOLUTION.height, child: { - type: "input_stream", - input_id: "tv_input", + type: 'input_stream', + input_id: 'tv_input', }, }; const rescaledImage: Component = { - type: "rescaler", + type: 'rescaler', width: OUTPUT_RESOLUTION.width, height: OUTPUT_RESOLUTION.height, child: { - type: "image", - image_id: "background", + type: 'image', + image_id: 'background', }, }; return { - type: "shader", - shader_id: "remove_green_screen", + type: 'shader', + shader_id: 'remove_green_screen', children: [rescaledInputStream, rescaledImage], resolution: OUTPUT_RESOLUTION, }; @@ -239,15 +228,15 @@ function news_report(): Component { function bunny_input(): Component { return { - type: "rescaler", + type: 'rescaler', child: { - type: "view", + type: 'view', width: 1280, height: 720, children: [ { - type: "input_stream", - input_id: "bunny", + type: 'input_stream', + input_id: 'bunny', }, ], }, @@ -256,58 +245,58 @@ function bunny_input(): Component { function breakingNewsText(): Component { return { - type: "view", + type: 'view', width: OUTPUT_RESOLUTION.width, height: 180, bottom: 0, left: 0, - direction: "column", + direction: 'column', children: [ { - type: "text", - text: "BREAKING NEWS", + type: 'text', + text: 'BREAKING NEWS', width: 600, height: 50, font_size: 50, - weight: "bold", - align: "center", - color_rgba: "#FFFFFFFF", - background_color_rgba: "#FF0000FF", + weight: 'bold', + align: 'center', + color_rgba: '#FFFFFFFF', + background_color_rgba: '#FF0000FF', }, { - type: "text", - text: "LiveCompositor is rumored to allegedly compose video", + type: 'text', + text: 'LiveCompositor is rumored to allegedly compose video', font_size: 65, width: OUTPUT_RESOLUTION.width, height: 80, - align: "center", - color_rgba: "#FFFFFFFF", - background_color_rgba: "#808080FF", + align: 'center', + color_rgba: '#FFFFFFFF', + background_color_rgba: '#808080FF', }, { - type: "view", + type: 'view', width: OUTPUT_RESOLUTION.width, height: 50, children: [ { - type: "text", - text: "88:29", + type: 'text', + text: '88:29', font_size: 40, width: 200, height: 50, - align: "center", - color_rgba: "#FFFFFFFF", - background_color_rgba: "#000000FF", + align: 'center', + color_rgba: '#FFFFFFFF', + background_color_rgba: '#000000FF', }, { - type: "text", - text: "Leak docs can be found at https://compositor.live/docs/intro", + type: 'text', + text: 'Leak docs can be found at https://compositor.live/docs/intro', font_size: 40, width: OUTPUT_RESOLUTION.width - 200, height: 50, - align: "center", - color_rgba: "#000000FF", - background_color_rgba: "#FFFF00FF", + align: 'center', + color_rgba: '#000000FF', + background_color_rgba: '#FFFF00FF', }, ], }, @@ -317,14 +306,14 @@ function breakingNewsText(): Component { function logo(): Component { return { - type: "view", + type: 'view', top: 50, left: 50, - overflow: "fit", + overflow: 'fit', children: [ { - type: "image", - image_id: "logo", + type: 'image', + image_id: 'logo', }, ], }; diff --git a/demos/3-live_stream/index.ts b/demos/3-live_stream/index.ts index 0d3567982..cc38c58b2 100644 --- a/demos/3-live_stream/index.ts +++ b/demos/3-live_stream/index.ts @@ -1,21 +1,18 @@ -import path from "path"; -import * as readline from "readline"; - -import { - ffmpegSendVideoFromMp4, - ffplayStartPlayerAsync, -} from "../utils/ffmpeg"; -import { runCompositorExample } from "../utils/run"; -import { downloadAsync, sleepAsync } from "../utils/utils"; -import { Component, Resolution } from "../types/api.d"; -import { gstStreamWebcam } from "../utils/gst"; +import path from 'path'; +import * as readline from 'readline'; + +import { ffmpegSendVideoFromMp4, ffplayStartPlayerAsync } from '../utils/ffmpeg'; +import { runCompositorExample } from '../utils/run'; +import { downloadAsync, sleepAsync } from '../utils/utils'; +import { Component, Resolution } from '../types/api.d'; +import { gstStreamWebcam } from '../utils/gst'; import { registerImageAsync, registerInputAsync, registerOutputAsync, startAsync, updateOutputAsync, -} from "../utils/api"; +} from '../utils/api'; const OUTPUT_RESOLUTION: Resolution = { width: 1920, @@ -26,65 +23,60 @@ const WEBCAM_INPUT_PORT = 10000; const GAMEPLAY_PORT = 10002; const VIDEO_OUTPUT_PORT = 10004; const AUDIO_OUTPUT_PORT = 10006; -const IP = "127.0.0.1"; +const IP = '127.0.0.1'; const DISPLAY_LOGS = false; const GAMEPLAY_URL = - "https://raw.githubusercontent.com/membraneframework-labs/video_compositor_snapshot_tests/main/demo_assets/gameplay.mp4"; + 'https://raw.githubusercontent.com/membraneframework-labs/video_compositor_snapshot_tests/main/demo_assets/gameplay.mp4'; const DONATE_URL = - "https://raw.githubusercontent.com/membraneframework-labs/video_compositor_snapshot_tests/main/demo_assets/donate.gif"; + 'https://raw.githubusercontent.com/membraneframework-labs/video_compositor_snapshot_tests/main/demo_assets/donate.gif'; const CALL_URL = - "https://raw.githubusercontent.com/membraneframework-labs/video_compositor_snapshot_tests/main/demo_assets/call.mp4"; + 'https://raw.githubusercontent.com/membraneframework-labs/video_compositor_snapshot_tests/main/demo_assets/call.mp4'; async function exampleAsync() { - await ffplayStartPlayerAsync( - IP, - DISPLAY_LOGS, - VIDEO_OUTPUT_PORT, - AUDIO_OUTPUT_PORT, - ); + await ffplayStartPlayerAsync(IP, DISPLAY_LOGS, VIDEO_OUTPUT_PORT, AUDIO_OUTPUT_PORT); // sleep to make sure ffplay have a chance to start before compositor starts sending packets await sleepAsync(2000); - const gameplayPath = path.join(__dirname, "../assets/gameplay.mp4"); + const gameplayPath = path.join(__dirname, '../assets/gameplay.mp4'); await downloadAsync(GAMEPLAY_URL, gameplayPath); - await registerImageAsync("donate", { - asset_type: "gif", + await registerImageAsync('donate', { + asset_type: 'gif', url: DONATE_URL, }); - const useWebCam = process.env.LIVE_COMPOSITOR_WEBCAM !== "false"; - await registerInputAsync("webcam_input", { - type: "rtp_stream", + const useWebCam = process.env.LIVE_COMPOSITOR_WEBCAM !== 'false'; + await registerInputAsync('webcam_input', { + type: 'rtp_stream', port: WEBCAM_INPUT_PORT, - transport_protocol: useWebCam ? "tcp_server" : "udp", + transport_protocol: useWebCam ? 'tcp_server' : 'udp', video: { - decoder: "ffmpeg_h264", + decoder: 'ffmpeg_h264', }, }); - await registerInputAsync("gameplay", { - type: "rtp_stream", + await registerInputAsync('gameplay', { + type: 'rtp_stream', port: GAMEPLAY_PORT, video: { - decoder: "ffmpeg_h264", + decoder: 'ffmpeg_h264', }, audio: { - decoder: "opus", + decoder: 'opus', }, }); - await registerOutputAsync("video_output", { - type: "rtp_stream", + await registerOutputAsync('video_output', { + type: 'rtp_stream', ip: IP, port: VIDEO_OUTPUT_PORT, video: { resolution: OUTPUT_RESOLUTION, encoder: { - type: "ffmpeg_h264", - preset: "ultrafast", + type: 'ffmpeg_h264', + preset: 'ultrafast', }, initial: { root: baseScene(), @@ -92,17 +84,17 @@ async function exampleAsync() { }, }); - await registerOutputAsync("audio_output", { - type: "rtp_stream", + await registerOutputAsync('audio_output', { + type: 'rtp_stream', ip: IP, port: AUDIO_OUTPUT_PORT, audio: { encoder: { - channels: "stereo", - type: "opus", + channels: 'stereo', + type: 'opus', }, initial: { - inputs: [{ input_id: "gameplay" }], + inputs: [{ input_id: 'gameplay' }], }, }, }); @@ -110,7 +102,7 @@ async function exampleAsync() { if (useWebCam) { gstStreamWebcam(IP, WEBCAM_INPUT_PORT, DISPLAY_LOGS); } else { - const callPath = path.join(__dirname, "../assets/call.mp4"); + const callPath = path.join(__dirname, '../assets/call.mp4'); await downloadAsync(CALL_URL, callPath); ffmpegSendVideoFromMp4(WEBCAM_INPUT_PORT, callPath, DISPLAY_LOGS); } @@ -124,84 +116,84 @@ async function exampleAsync() { output: process.stdout, }); - rl.question("Enter donate content: ", async (donate_content) => { + rl.question('Enter donate content: ', async donate_content => { console.log(`Donate content: ${donate_content}`); await displayDonateAsync(donate_content); }); } async function displayDonateAsync(msg: string) { - await updateOutputAsync("video_output", { + await updateOutputAsync('video_output', { video: { root: { - type: "view", - children: [baseScene(), donateCard(msg, "start")], + type: 'view', + children: [baseScene(), donateCard(msg, 'start')], }, }, }); - await updateOutputAsync("video_output", { + await updateOutputAsync('video_output', { video: { root: { - type: "view", - children: [baseScene(), donateCard(msg, "middle")], + type: 'view', + children: [baseScene(), donateCard(msg, 'middle')], }, }, }); await sleepAsync(4500); - await updateOutputAsync("video_output", { + await updateOutputAsync('video_output', { video: { root: { - type: "view", - children: [baseScene(), donateCard(msg, "end")], + type: 'view', + children: [baseScene(), donateCard(msg, 'end')], }, }, }); } -function donateCard(msg: string, stage: "start" | "middle" | "end"): Component { +function donateCard(msg: string, stage: 'start' | 'middle' | 'end'): Component { const width = 480; let top; - if (stage === "start" || stage === "end") { + if (stage === 'start' || stage === 'end') { top = -270; - } else if (stage === "middle") { + } else if (stage === 'middle') { top = 30; } return { - type: "view", - id: "donate_view", + type: 'view', + id: 'donate_view', width, height: 270, top, left: OUTPUT_RESOLUTION.width / 2 - width / 2, - direction: "column", + direction: 'column', children: [ { - type: "view", + type: 'view', top: 0, left: 0, children: [ { - type: "image", - image_id: "donate", + type: 'image', + image_id: 'donate', }, ], }, { - type: "text", + type: 'text', width, text: msg, - weight: "extra_bold", + weight: 'extra_bold', font_size: 50, - align: "center", - color_rgba: "#FF0000FF", - font_family: "Comic Sans MS", + align: 'center', + color_rgba: '#FF0000FF', + font_family: 'Comic Sans MS', }, ], transition: { duration_ms: 1000, easing_function: { - function_name: "bounce", + function_name: 'bounce', }, }, }; @@ -209,29 +201,29 @@ function donateCard(msg: string, stage: "start" | "middle" | "end"): Component { function baseScene(): Component { return { - type: "view", + type: 'view', children: [ { - type: "rescaler", + type: 'rescaler', child: { - type: "input_stream", - input_id: "gameplay", + type: 'input_stream', + input_id: 'gameplay', }, }, { - type: "view", + type: 'view', width: 500, height: 300, top: 30, right: 30, children: [ { - type: "rescaler", - vertical_align: "top", - horizontal_align: "right", + type: 'rescaler', + vertical_align: 'top', + horizontal_align: 'right', child: { - type: "input_stream", - input_id: "webcam_input", + type: 'input_stream', + input_id: 'webcam_input', }, }, ], diff --git a/demos/utils/api.ts b/demos/utils/api.ts index e82c5d9d6..462bac42d 100644 --- a/demos/utils/api.ts +++ b/demos/utils/api.ts @@ -1,4 +1,4 @@ -import fetch from "node-fetch"; +import fetch from 'node-fetch'; import { ImageSpec, RegisterInput, @@ -6,9 +6,9 @@ import { ShaderSpec, UpdateOutputRequest, WebRendererSpec, -} from "../types/api.d"; +} from '../types/api.d'; -const COMPOSITOR_URL = "http://127.0.0.1:8081"; +const COMPOSITOR_URL = 'http://127.0.0.1:8081'; type CompositorRequestBody = | RegisterInput @@ -18,10 +18,7 @@ type CompositorRequestBody = | WebRendererSpec | ImageSpec; -export async function registerInputAsync( - inputId: string, - body: RegisterInput, -): Promise { +export async function registerInputAsync(inputId: string, body: RegisterInput): Promise { return sendAsync(`/api/input/${inputId}/register`, body); } @@ -29,10 +26,7 @@ export async function unregisterInputAsync(inputId: string): Promise { return sendAsync(`/api/input/${inputId}/unregister`, {}); } -export async function registerOutputAsync( - outputId: string, - body: RegisterOutput, -): Promise { +export async function registerOutputAsync(outputId: string, body: RegisterOutput): Promise { return sendAsync(`/api/output/${outputId}/register`, body); } @@ -42,15 +36,12 @@ export async function unregisterOutputAsync(outputId: string): Promise { export async function updateOutputAsync( outputId: string, - body: UpdateOutputRequest, + body: UpdateOutputRequest ): Promise { return sendAsync(`/api/output/${outputId}/update`, body); } -export async function registerShaderAsync( - shaderId: string, - body: ShaderSpec, -): Promise { +export async function registerShaderAsync(shaderId: string, body: ShaderSpec): Promise { return sendAsync(`/api/shader/${shaderId}/register`, body); } @@ -60,21 +51,16 @@ export async function unregisterShaderAsync(shaderId: string): Promise { export async function registerWebRendererAsync( rendererId: string, - body: WebRendererSpec, + body: WebRendererSpec ): Promise { return sendAsync(`/api/web_renderer/${rendererId}/register`, body); } -export async function unregisterWebRendererAsync( - rendererId: string, -): Promise { +export async function unregisterWebRendererAsync(rendererId: string): Promise { return sendAsync(`/api/web_renderer/${rendererId}/unregister`, {}); } -export async function registerImageAsync( - imageId: string, - body: ImageSpec, -): Promise { +export async function registerImageAsync(imageId: string, body: ImageSpec): Promise { return sendAsync(`/api/image/${imageId}/register`, body); } @@ -86,16 +72,13 @@ export async function startAsync(): Promise { return sendAsync(`/api/start`, {}); } -async function sendAsync( - endpoint: string, - body: CompositorRequestBody, -): Promise { +async function sendAsync(endpoint: string, body: CompositorRequestBody): Promise { let response; try { response = await fetch(COMPOSITOR_URL + endpoint, { - method: "POST", + method: 'POST', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, body: JSON.stringify(body), }); diff --git a/demos/utils/ffmpeg.ts b/demos/utils/ffmpeg.ts index 6eccb321f..42842881a 100644 --- a/demos/utils/ffmpeg.ts +++ b/demos/utils/ffmpeg.ts @@ -1,86 +1,77 @@ -import { COMPOSITOR_DIR } from "./prepare_compositor"; -import { SpawnPromise, spawn } from "./utils"; -import path from "path"; -import fs from "fs-extra"; +import { COMPOSITOR_DIR } from './prepare_compositor'; +import { SpawnPromise, spawn } from './utils'; +import path from 'path'; +import fs from 'fs-extra'; export async function ffplayStartPlayerAsync( ip: string, displayOutput: boolean, video_port: number, - audio_port: number | undefined = undefined, + audio_port: number | undefined = undefined ): Promise<{ spawn_promise: SpawnPromise }> { let sdpFilePath; if (audio_port === undefined) { sdpFilePath = path.join(COMPOSITOR_DIR, `video_input_${video_port}.sdp`); await writeVideoSdpFile(ip, video_port, sdpFilePath); } else { - sdpFilePath = path.join( - COMPOSITOR_DIR, - `video_audio_input_${video_port}_${audio_port}.sdp`, - ); + sdpFilePath = path.join(COMPOSITOR_DIR, `video_audio_input_${video_port}_${audio_port}.sdp`); await writeVideoAudioSdpFile(ip, video_port, audio_port, sdpFilePath); } - const promise = spawn( - "ffplay", - ["-protocol_whitelist", "file,rtp,udp", sdpFilePath], - { displayOutput }, - ); + const promise = spawn('ffplay', ['-protocol_whitelist', 'file,rtp,udp', sdpFilePath], { + displayOutput, + }); return { spawn_promise: promise }; } export function ffmpegSendVideoFromMp4( port: number, mp4Path: string, - displayOutput: boolean, + displayOutput: boolean ): SpawnPromise { return spawn( - "ffmpeg", + 'ffmpeg', [ - "-stream_loop", - "-1", - "-re", - "-i", + '-stream_loop', + '-1', + '-re', + '-i', mp4Path, - "-an", - "-c:v", - "libx264", - "-f", - "rtp", + '-an', + '-c:v', + 'libx264', + '-f', + 'rtp', `rtp://127.0.0.1:${port}?rtcpport=${port}`, ], - { displayOutput }, + { displayOutput } ); } -export function ffmpegStreamScreen( - ip: string, - port: number, - displayOutput: boolean, -): SpawnPromise { +export function ffmpegStreamScreen(ip: string, port: number, displayOutput: boolean): SpawnPromise { const platform = process.platform; let inputOptions: string[]; - if (platform === "darwin") { - inputOptions = ["-f", "avfoundation", "-i", "1"]; - } else if (platform === "linux") { - inputOptions = ["-f", "x11grab", "-i", ":0.0"]; + if (platform === 'darwin') { + inputOptions = ['-f', 'avfoundation', '-i', '1']; + } else if (platform === 'linux') { + inputOptions = ['-f', 'x11grab', '-i', ':0.0']; } else { - throw new Error("Unsupported platform"); + throw new Error('Unsupported platform'); } return spawn( - "ffmpeg", + 'ffmpeg', [ ...inputOptions, - "-vf", - "format=yuv420p", - "-c:v", - "libx264", - "-f", - "rtp", + '-vf', + 'format=yuv420p', + '-c:v', + 'libx264', + '-f', + 'rtp', `rtp://${ip}:${port}?rtcpport=${port}`, ], - { displayOutput }, + { displayOutput } ); } @@ -88,7 +79,7 @@ async function writeVideoAudioSdpFile( ip: string, video_port: number, audio_port: number, - destination: string, + destination: string ): Promise { fs.writeFile( destination, @@ -104,15 +95,11 @@ a=rtcp-mux m=audio ${audio_port} RTP/AVP 97 a=rtpmap:97 opus/48000/2 a=rtcp-mux -`, +` ); } -async function writeVideoSdpFile( - ip: string, - port: number, - destination: string, -): Promise { +async function writeVideoSdpFile(ip: string, port: number, destination: string): Promise { fs.writeFile( destination, ` @@ -124,6 +111,6 @@ m=video ${port} RTP/AVP 96 a=rtpmap:96 H264/90000 a=fmtp:96 packetization-mode=1 a=rtcp-mux -`, +` ); } diff --git a/demos/utils/generate_types.ts b/demos/utils/generate_types.ts index 55de3d15a..fc31ca72a 100644 --- a/demos/utils/generate_types.ts +++ b/demos/utils/generate_types.ts @@ -1,13 +1,10 @@ -import * as fs from "fs"; -import * as path from "path"; -import { compileFromFile } from "json-schema-to-typescript"; +import * as fs from 'fs'; +import * as path from 'path'; +import { compileFromFile } from 'json-schema-to-typescript'; async function generateTypes() { - const schemaPath = path.resolve( - __dirname, - "../../schemas/api_types.schema.json", - ); - const tsOutputPath = path.resolve(__dirname, "../types/api.d.ts"); + const schemaPath = path.resolve(__dirname, '../../schemas/api_types.schema.json'); + const tsOutputPath = path.resolve(__dirname, '../types/api.d.ts'); const typesTs = await compileFromFile(schemaPath, { additionalProperties: false, diff --git a/demos/utils/gst.ts b/demos/utils/gst.ts index b8a9671b6..ef1d54a20 100644 --- a/demos/utils/gst.ts +++ b/demos/utils/gst.ts @@ -1,11 +1,7 @@ -import { exec } from "child_process"; -import { SpawnPromise, spawn } from "./utils"; +import { exec } from 'child_process'; +import { SpawnPromise, spawn } from './utils'; -export function gstStartPlayer( - ip: string, - port: number, - displayOutput: boolean, -): SpawnPromise { +export function gstStartPlayer(ip: string, port: number, displayOutput: boolean): SpawnPromise { const gstCommand = `gst-launch-1.0 -v ` + `rtpptdemux name=demux ` + @@ -13,43 +9,29 @@ export function gstStartPlayer( `demux.src_96 ! "application/x-rtp,media=video,clock-rate=90000,encoding-name=H264" ! queue ! rtph264depay ! decodebin ! videoconvert ! autovideosink ` + `demux.src_97 ! "application/x-rtp,media=audio,clock-rate=48000,encoding-name=OPUS" ! queue ! rtpopusdepay ! decodebin ! audioconvert ! autoaudiosink `; - return spawn("bash", ["-c", gstCommand], { displayOutput }); + return spawn('bash', ['-c', gstCommand], { displayOutput }); } -export function gstStreamWebcam( - ip: string, - port: number, - displayOutput: boolean, -): SpawnPromise { - const isMacOS = process.platform === "darwin"; +export function gstStreamWebcam(ip: string, port: number, displayOutput: boolean): SpawnPromise { + const isMacOS = process.platform === 'darwin'; const [gstWebcamSource, gstEncoder, gstEncoderOptions] = isMacOS - ? ["avfvideosrc", "vtenc_h264", "bitrate=2000"] - : [ - "v4l2src", - "x264enc", - "tune=zerolatency bitrate=2000 speed-preset=superfast", - ]; + ? ['avfvideosrc', 'vtenc_h264', 'bitrate=2000'] + : ['v4l2src', 'x264enc', 'tune=zerolatency bitrate=2000 speed-preset=superfast']; - const plugins = [ - gstWebcamSource, - "videoconvert", - gstEncoder, - "rtph264pay", - "udpsink", - ]; + const plugins = [gstWebcamSource, 'videoconvert', gstEncoder, 'rtph264pay', 'udpsink']; checkGstPlugins(plugins); const gstCommand = `gst-launch-1.0 -v ` + `${gstWebcamSource} ! videoconvert ! ${gstEncoder} ${gstEncoderOptions} ! rtph264pay config-interval=1 pt=96 ! rtpstreampay ! queue ! tcpclientsink host=${ip} port=${port}`; - return spawn("bash", ["-c", gstCommand], { displayOutput }); + return spawn('bash', ['-c', gstCommand], { displayOutput }); } function checkGstPlugins(plugins: string[]) { - plugins.forEach((plugin) => { - isGstPluginAvailable(plugin).then((isAvailable) => { + plugins.forEach(plugin => { + isGstPluginAvailable(plugin).then(isAvailable => { if (!isAvailable) { throw Error(`Gstreamer plugin: ${plugin} is not available.`); } @@ -59,8 +41,8 @@ function checkGstPlugins(plugins: string[]) { function isGstPluginAvailable(pluginName: string): Promise { const command = `gst-inspect-1.0 ${pluginName}`; - return new Promise((resolve) => { - exec(command, (error) => { + return new Promise(resolve => { + exec(command, error => { if (error) { resolve(false); } else { diff --git a/demos/utils/prepare_compositor.ts b/demos/utils/prepare_compositor.ts index f4fbf72b0..ede16d86e 100644 --- a/demos/utils/prepare_compositor.ts +++ b/demos/utils/prepare_compositor.ts @@ -1,10 +1,10 @@ -import fs from "fs-extra"; -import path from "path"; -import { downloadAsync, spawn } from "./utils"; +import fs from 'fs-extra'; +import path from 'path'; +import { downloadAsync, spawn } from './utils'; -export const COMPOSITOR_DIR = path.join(__dirname, "../.video_compositor"); +export const COMPOSITOR_DIR = path.join(__dirname, '../.video_compositor'); -const VERSION = "v0.2.0-rc.5"; +const VERSION = 'v0.2.0-rc.5'; const COMPOSITOR_X86_64_LINUX_DOWNLOAD_URL = `https://github.com/membraneframework/live_compositor/releases/download/${VERSION}/video_compositor_linux_x86_64.tar.gz`; const COMPOSITOR_ARM_LINUX_DOWNLOAD_URL = `https://github.com/membraneframework/live_compositor/releases/download/${VERSION}/video_compositor_linux_aarch64.tar.gz`; @@ -12,10 +12,10 @@ const COMPOSITOR_X86_64_MAC_DOWNLOAD_URL = `https://github.com/membraneframework const COMPOSITOR_ARM_MAC_DOWNLOAD_URL = `https://github.com/membraneframework/live_compositor/releases/download/${VERSION}/video_compositor_darwin_aarch64.tar.gz`; export async function ensureCompositorReadyAsync(): Promise { - const versionFile = path.join(COMPOSITOR_DIR, ".version"); + const versionFile = path.join(COMPOSITOR_DIR, '.version'); if ( (await fs.pathExists(versionFile)) && - (await fs.readFile(versionFile, "utf8")).trim() === VERSION + (await fs.readFile(versionFile, 'utf8')).trim() === VERSION ) { return; } @@ -30,32 +30,32 @@ export async function ensureCompositorReadyAsync(): Promise { export async function prepareCompositorAsync() { await fs.remove(COMPOSITOR_DIR); await fs.mkdirp(COMPOSITOR_DIR); - console.log("Downloading video_compositor."); + console.log('Downloading video_compositor.'); await downloadAsync( getCompositorDownloadUrl(), - path.join(COMPOSITOR_DIR, "video_compositor.tar.gz"), + path.join(COMPOSITOR_DIR, 'video_compositor.tar.gz') ); - console.log("Unpacking video_compositor."); - await spawn("tar", ["-xvf", "video_compositor.tar.gz"], { + console.log('Unpacking video_compositor.'); + await spawn('tar', ['-xvf', 'video_compositor.tar.gz'], { displayOutput: true, cwd: COMPOSITOR_DIR, }); - await fs.writeFile(path.join(COMPOSITOR_DIR, ".version"), VERSION); + await fs.writeFile(path.join(COMPOSITOR_DIR, '.version'), VERSION); } function getCompositorDownloadUrl(): string { - if (process.platform === "linux") { - if (process.arch === "arm64") { + if (process.platform === 'linux') { + if (process.arch === 'arm64') { return COMPOSITOR_ARM_LINUX_DOWNLOAD_URL; - } else if (process.arch === "x64") { + } else if (process.arch === 'x64') { return COMPOSITOR_X86_64_LINUX_DOWNLOAD_URL; } - } else if (process.platform === "darwin") { - if (process.arch === "x64") { + } else if (process.platform === 'darwin') { + if (process.arch === 'x64') { return COMPOSITOR_X86_64_MAC_DOWNLOAD_URL; - } else if (process.arch === "arm64") { + } else if (process.arch === 'arm64') { return COMPOSITOR_ARM_MAC_DOWNLOAD_URL; } } - throw new Error("Unsupported platform."); + throw new Error('Unsupported platform.'); } diff --git a/demos/utils/run.ts b/demos/utils/run.ts index a95c5ac14..2966ddd39 100644 --- a/demos/utils/run.ts +++ b/demos/utils/run.ts @@ -1,12 +1,12 @@ -import { sleepAsync, spawn } from "./utils"; -import chalk from "chalk"; -import { Response } from "node-fetch"; -import path from "path"; -import { ensureCompositorReadyAsync } from "./prepare_compositor"; +import { sleepAsync, spawn } from './utils'; +import chalk from 'chalk'; +import { Response } from 'node-fetch'; +import path from 'path'; +import { ensureCompositorReadyAsync } from './prepare_compositor'; export async function runCompositorExample( fn: () => Promise, - displayOutput: boolean, + displayOutput: boolean ): Promise { await ensureCompositorReadyAsync(); const { command, args, cwd } = getCompositorRunCmd(); @@ -30,17 +30,13 @@ async function logError(err: any): Promise { const body = await err.response.json(); if (body.error_code && body.stack) { console.error(); - console.error( - chalk.red(`Request failed with error (${body.erorr_code}):`), - ); + console.error(chalk.red(`Request failed with error (${body.erorr_code}):`)); for (const errLine of body.stack) { console.error(chalk.red(` - ${errLine}`)); } } else { console.error(); - console.error( - chalk.red(`Request failed with status code ${err.response.status}`), - ); + console.error(chalk.red(`Request failed with status code ${err.response.status}`)); console.error(chalk.red(JSON.stringify(body, null, 2))); } } else { @@ -48,7 +44,7 @@ async function logError(err: any): Promise { } } -const COMPOSITOR_DIR = path.join(__dirname, "../.video_compositor"); +const COMPOSITOR_DIR = path.join(__dirname, '../.video_compositor'); function getCompositorRunCmd(): { command: string; @@ -57,21 +53,21 @@ function getCompositorRunCmd(): { } { if (process.env.LIVE_COMPOSITOR_SOURCE_DIR) { return { - command: "cargo", - args: ["run", "--release", "--bin", "video_compositor"], + command: 'cargo', + args: ['run', '--release', '--bin', 'video_compositor'], cwd: process.env.LIVE_COMPOSITOR_SOURCE_DIR, }; - } else if (process.platform === "linux") { + } else if (process.platform === 'linux') { return { - command: path.join(COMPOSITOR_DIR, "video_compositor/video_compositor"), + command: path.join(COMPOSITOR_DIR, 'video_compositor/video_compositor'), args: [], }; - } else if (process.platform === "darwin") { + } else if (process.platform === 'darwin') { return { - command: path.join(COMPOSITOR_DIR, "video_compositor/video_compositor"), + command: path.join(COMPOSITOR_DIR, 'video_compositor/video_compositor'), args: [], }; } - throw new Error("Unsupported platform."); + throw new Error('Unsupported platform.'); } diff --git a/demos/utils/utils.ts b/demos/utils/utils.ts index 354567621..82b10bd2b 100644 --- a/demos/utils/utils.ts +++ b/demos/utils/utils.ts @@ -1,15 +1,15 @@ -import fetch from "node-fetch"; -import fs from "fs-extra"; -import { promisify } from "util"; -import { Stream } from "stream"; -import { ChildProcess, spawn as nodeSpawn } from "child_process"; -import { cwd } from "process"; -import path from "path"; +import fetch from 'node-fetch'; +import fs from 'fs-extra'; +import { promisify } from 'util'; +import { Stream } from 'stream'; +import { ChildProcess, spawn as nodeSpawn } from 'child_process'; +import { cwd } from 'process'; +import path from 'path'; const pipeline = promisify(Stream.pipeline); const child_processes: ChildProcess[] = []; -process.on("exit", () => { +process.on('exit', () => { killAllChildren(); }); @@ -18,30 +18,24 @@ type SpawnOptions = { cwd?: string; }; -export function spawn( - command: string, - args: string[], - opts: SpawnOptions, -): SpawnPromise { - console.log(`Spawning: ${command} ${args.join(" ")}`); +export function spawn(command: string, args: string[], opts: SpawnOptions): SpawnPromise { + console.log(`Spawning: ${command} ${args.join(' ')}`); const child = nodeSpawn(command, args, { - stdio: opts.displayOutput ? "inherit" : "ignore", + stdio: opts.displayOutput ? 'inherit' : 'ignore', cwd: opts.cwd ?? cwd(), env: { ...process.env, - LIVE_COMPOSITOR_LOGGER_FORMAT: "compact", + LIVE_COMPOSITOR_LOGGER_FORMAT: 'compact', }, }); const promise = new Promise((resolve, reject) => { - child.on("exit", (code) => { + child.on('exit', code => { if (code === 0) { - console.log( - `Command "${command} ${args.join(" ")}" completed successfully.`, - ); + console.log(`Command "${command} ${args.join(' ')}" completed successfully.`); resolve(); } else { - const errorMessage = `Command "${command} ${args.join(" ")}" failed with exit code ${code}.`; + const errorMessage = `Command "${command} ${args.join(' ')}" failed with exit code ${code}.`; console.error(errorMessage); reject(new Error(errorMessage)); } @@ -69,17 +63,14 @@ export interface SpawnPromise extends Promise { child: ChildProcess; } -export async function downloadAsync( - url: string, - destination: string, -): Promise { +export async function downloadAsync(url: string, destination: string): Promise { if (fs.existsSync(destination)) { console.log(`File ${destination} already exists. Skipping download.`); return; } await fs.mkdirp(path.dirname(destination)); - const response = await fetch(url, { method: "GET", timeout: 0 }); + const response = await fetch(url, { method: 'GET', timeout: 0 }); if (response.status >= 400) { const err: any = new Error(`Request to ${url} failed. \n${response.body}`); err.response = response; @@ -93,7 +84,7 @@ export async function downloadAsync( } export async function sleepAsync(timeout_ms: number): Promise { - await new Promise((res) => { + await new Promise(res => { setTimeout(() => { res(); }, timeout_ms);