From 3e757bd0147d135d403c09fd89964f3f9b079e9e Mon Sep 17 00:00:00 2001 From: Everett Summer Date: Sat, 28 Oct 2023 15:07:01 +0800 Subject: [PATCH] Optimize keyframe (#63) * change tile size to 10 fixed value and format from png to jpg to avoid large image * fix some issues --- package.json | 2 +- src/JobManager/JobMetadataHelperImpl.ts | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index a6ef37c..5c7f8b8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mira-video-manager", - "version": "1.5.1", + "version": "1.5.2", "description": "Video Process for mira project", "main": "index.js", "scripts": { diff --git a/src/JobManager/JobMetadataHelperImpl.ts b/src/JobManager/JobMetadataHelperImpl.ts index 2570a16..f1384af 100644 --- a/src/JobManager/JobMetadataHelperImpl.ts +++ b/src/JobManager/JobMetadataHelperImpl.ts @@ -29,9 +29,11 @@ import { getAverageColor } from 'fast-average-color-node'; import { VideoOutputMetadata } from '../domains/VideoOutputMetadata'; import { JobMetadataHelper } from './JobMetadataHelper'; import { readdir } from 'fs/promises'; +import { StringDecoder } from 'string_decoder'; const COMMAND_TIMEOUT = 5000; +const TILE_SIZE = 10; // fixed tile size to avoid large image const SCALE_HEIGHT = 120; const FRAMES_INTERVAL = 5000; // milliseconds const MAX_PIC_WIDTH = 16256; // this is based on formula ((Width * 8) + 1024)*(Height + 128) < INT_MAX. @@ -92,15 +94,16 @@ export class JobMetadataHelperImpl implements JobMetadataHelper { * @param jobLogger */ public async generatePreviewImage(videoPath: string, metaData: VideoOutputMetadata, jobLogger: pino.Logger): Promise { - metaData.tileSize = Math.ceil(Math.sqrt(metaData.duration / FRAMES_INTERVAL)); + metaData.tileSize = TILE_SIZE; metaData.frameHeight = SCALE_HEIGHT; metaData.frameWidth = Math.round(SCALE_HEIGHT * (metaData.width/metaData.height)); - if (metaData.tileSize * metaData.frameWidth > MAX_PIC_WIDTH) { - metaData.tileSize = Math.floor(MAX_PIC_WIDTH / metaData.frameWidth); - } + // unlikely we need to calculate this as tileSize will be a small value + // if (metaData.tileSize * metaData.frameWidth > MAX_PIC_WIDTH) { + // metaData.tileSize = Math.floor(MAX_PIC_WIDTH / metaData.frameWidth); + // } const imageDirPath = dirname(videoPath); const imageFilenameBase = `keyframes-${basename(videoPath, extname(videoPath))}`; - const keyframeImagePath = join(imageDirPath, `${imageFilenameBase}-%3d.png`); + const keyframeImagePath = join(imageDirPath, `${imageFilenameBase}-%3d.jpg`); // generate tiles for key frames every 1 second await this.runCommand('ffmpeg', ['-y', '-i', videoPath, '-vf', @@ -109,11 +112,12 @@ export class JobMetadataHelperImpl implements JobMetadataHelper { ], jobLogger); const filenameList = await readdir(imageDirPath); - metaData.keyframeImagePathList = filenameList.filter(f => f.endsWith('.png') && f.startsWith(imageFilenameBase)).map(f => join(imageDirPath, f)); + metaData.keyframeImagePathList = filenameList.filter(f => f.endsWith('.jpg') && f.startsWith(imageFilenameBase)).map(f => join(imageDirPath, f)); } private async runCommand(cmdExc: string, cmdArgs: string[], logger: pino.Logger): Promise { console.log(cmdExc + ' ' + cmdArgs.join(' ')); + const decoder = new StringDecoder('utf8'); return new Promise((resolve, reject) => { try { const child = spawn(cmdExc, cmdArgs, { @@ -121,10 +125,10 @@ export class JobMetadataHelperImpl implements JobMetadataHelper { stdio: 'pipe' }); child.stdout.on('data', (data) => { - logger.info(data); + logger.info(decoder.end(data)); }); child.stderr.on('data', (data) => { - logger.error(data); + logger.error(decoder.end(data)); }); child.on('close', (code) => { if (code !== 0) {