From 172a7e69cc96e07fc6b9c2daff47dcebd254fcca Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Sat, 19 Feb 2022 01:54:52 -0800 Subject: [PATCH] homekit: use ffmpeg pipe for mp4 parsing --- common/src/ffmpeg-mp4-parser-session.ts | 51 ++++++++----------- .../src/types/camera/camera-recording.ts | 11 ++-- 2 files changed, 26 insertions(+), 36 deletions(-) diff --git a/common/src/ffmpeg-mp4-parser-session.ts b/common/src/ffmpeg-mp4-parser-session.ts index b52560d49..28ab8dd95 100644 --- a/common/src/ffmpeg-mp4-parser-session.ts +++ b/common/src/ffmpeg-mp4-parser-session.ts @@ -1,49 +1,38 @@ -import { createServer, Socket } from 'net'; import child_process from 'child_process'; import { ChildProcess } from 'child_process'; -import { FFMpegInput } from '@scrypted/sdk/types'; -import { listenZero } from './listen-cluster'; import sdk from "@scrypted/sdk"; import { ffmpegLogInitialOutput } from './media-helpers'; import { MP4Atom, parseFragmentedMP4 } from './stream-parser'; +import { Readable } from 'stream'; const { mediaManager } = sdk; export interface FFMpegFragmentedMP4Session { - socket: Socket; cp: ChildProcess; generator: AsyncGenerator; } export async function startFFMPegFragmetedMP4Session(inputArguments: string[], audioOutputArgs: string[], videoOutputArgs: string[], console: Console): Promise { - return new Promise(async (resolve) => { - const server = createServer(socket => { - server.close(); + const args = inputArguments.slice(); + args.push( + ...videoOutputArgs, + ...audioOutputArgs, + '-movflags', 'frag_keyframe+empty_moov+default_base_moof', + '-f', 'mp4', + 'pipe:3', + ); - resolve({ - socket, - cp, - generator: parseFragmentedMP4(socket), - }); - }); - const serverPort = await listenZero(server); + args.unshift('-hide_banner'); + console.log(args.join(' ')); - const args = inputArguments.slice(); - args.push( - '-f', 'mp4', - ...videoOutputArgs, - ...audioOutputArgs, - '-movflags', 'frag_keyframe+empty_moov+default_base_moof', - `tcp://127.0.0.1:${serverPort}` - ); - - args.unshift('-hide_banner'); - console.log(args.join(' ')); - - const cp = child_process.spawn(await mediaManager.getFFmpegPath(), args, { - // stdio: 'ignore', - }); - - ffmpegLogInitialOutput(console, cp); + const cp = child_process.spawn(await mediaManager.getFFmpegPath(), args, { + stdio: ['pipe', 'pipe', 'pipe', 'pipe',] }); + + ffmpegLogInitialOutput(console, cp); + + return { + cp, + generator: parseFragmentedMP4(cp.stdio[3] as Readable), + }; } diff --git a/plugins/homekit/src/types/camera/camera-recording.ts b/plugins/homekit/src/types/camera/camera-recording.ts index 09b69eb74..89d6496fc 100644 --- a/plugins/homekit/src/types/camera/camera-recording.ts +++ b/plugins/homekit/src/types/camera/camera-recording.ts @@ -51,7 +51,6 @@ export async function* handleFragmentsRequests(device: ScryptedDevice & VideoCam const socketUrl = new URL(ffmpegInput.url); const socket = net.connect(parseInt(socketUrl.port), socketUrl.hostname); session = { - socket, cp: undefined, generator: parseFragmentedMP4(socket), } @@ -101,8 +100,8 @@ export async function* handleFragmentsRequests(device: ScryptedDevice & VideoCam } else { audioArgs = [ + '-acodec', 'copy', '-bsf:a', 'aac_adtstoasc', - '-acodec', 'copy' ]; } @@ -136,11 +135,13 @@ export async function* handleFragmentsRequests(device: ScryptedDevice & VideoCam } console.log(`motion recording started`); - const { socket, cp, generator } = session; + const { cp, generator } = session; let pending: Buffer[] = []; try { + let i = 0; for await (const box of generator) { const { header, type, data } = box; + // console.log('motion fragment box', type); // every moov/moof frame designates an iframe? pending.push(header, data); @@ -148,17 +149,17 @@ export async function* handleFragmentsRequests(device: ScryptedDevice & VideoCam if (type === 'moov' || type === 'mdat') { const fragment = Buffer.concat(pending); pending = []; + console.log(`motion fragment #${++i} sent. size:`, fragment.length); yield fragment; } - // console.log('mp4 box type', type, length); } console.log(`motion recording finished`); } catch (e) { console.log(`motion recording complete ${e}`); + generator.throw(e); } finally { - socket.destroy(); cp?.kill('SIGKILL'); } }