snapshot: publish beta with sharp + fallback

This commit is contained in:
Koushik Dutta
2023-11-22 11:16:05 -08:00
parent 0f948ea672
commit e62b4ad68b
4 changed files with 299 additions and 349 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/snapshot",
"version": "0.2.1",
"version": "0.2.10",
"description": "Snapshot Plugin for Scrypted",
"scripts": {
"scrypted-setup-project": "scrypted-setup-project",
@@ -32,13 +32,11 @@
"DeviceProvider"
]
},
"optionalDependencies": {
"@koush/sharp": "^0.32.7"
},
"dependencies": {
"@koush/axios-digest-auth": "^0.8.5",
"@types/node": "^18.16.18",
"axios": "^1.4.0",
"sharp": "^0.32.6",
"whatwg-mimetype": "^3.0.0"
},
"devDependencies": {

View File

@@ -1,5 +1,14 @@
import sdk, { BufferConverter, Image, ImageOptions, MediaObject, MediaObjectOptions, ScryptedDeviceBase, ScryptedMimeTypes } from "@scrypted/sdk";
import sharp from '@koush/sharp';
import type sharp from 'sharp';
export let sharpInstance: typeof sharp;
try {
sharpInstance = require('sharp');
console.log('sharp loaded');
}
catch (e) {
console.warn('sharp failed to load', e);
}
async function createVipsMediaObject(image: VipsImage): Promise<Image & MediaObject> {
const ret: Image & MediaObject = await sdk.mediaManager.createMediaObject(image, ScryptedMimeTypes.Image, {
@@ -65,7 +74,7 @@ export class VipsImage implements Image {
resolveWithObject: true,
});
const newImage = sharp(data, {
const newImage = sharpInstance(data, {
raw: info,
});
@@ -87,8 +96,8 @@ export class VipsImage implements Image {
}
}
export async function loadVipsImage(data: Buffer, sourceId: string) {
const image = sharp(data, {
export async function loadVipsImage(data: Buffer|string, sourceId: string) {
const image = sharpInstance(data, {
failOnError: false,
});
const metadata = await image.metadata();

View File

@@ -10,7 +10,7 @@ import path from 'path';
import MimeType from 'whatwg-mimetype';
import { ffmpegFilterImage, ffmpegFilterImageBuffer } from './ffmpeg-image-filter';
import { ImageWriter, ImageWriterNativeId } from './image-writer';
import { loadVipsImage, VipsImage } from './image-reader';
import { loadVipsImage, sharpInstance, VipsImage } from './image-reader';
const { mediaManager, systemManager } = sdk;
@@ -303,6 +303,20 @@ class SnapshotMixin extends SettingsMixinDeviceBase<Camera> implements Camera {
}, async () => {
this.debugConsole?.log("Resizing picture from camera", options?.picture);
if (sharpInstance) {
const vips = await loadVipsImage(picture, this.id);
try {
const ret = await vips.toBuffer({
resize: options?.picture,
format: 'jpg',
});
return ret;
}
finally {
vips.close();
}
}
// try {
// const mo = await mediaManager.createMediaObject(picture, 'image/jpeg', {
// sourceId: this.id,
@@ -326,24 +340,13 @@ class SnapshotMixin extends SettingsMixinDeviceBase<Camera> implements Camera {
// throw e;
// }
// return ffmpegFilterImageBuffer(picture, {
// console: this.debugConsole,
// ffmpegPath: await mediaManager.getFFmpegPath(),
// resize: options?.picture,
// timeout: 10000,
// });
return ffmpegFilterImageBuffer(picture, {
console: this.debugConsole,
ffmpegPath: await mediaManager.getFFmpegPath(),
resize: options?.picture,
timeout: 10000,
});
const vips = await loadVipsImage(picture, this.id);
try {
const ret = await vips.toBuffer({
resize: options?.picture,
format: 'jpg',
});
return ret;
}
finally {
vips.close();
}
});
}
catch (e) {
@@ -364,6 +367,25 @@ class SnapshotMixin extends SettingsMixinDeviceBase<Camera> implements Camera {
const xmax = Math.max(...this.storageSettings.values.snapshotCropScale.map(([x, y]) => x)) / 100;
const ymax = Math.max(...this.storageSettings.values.snapshotCropScale.map(([x, y]) => y)) / 100;
if (sharpInstance) {
const vips = await loadVipsImage(picture, this.id);
try {
const ret = await vips.toBuffer({
crop: {
left: xmin * vips.width,
top: ymin * vips.height,
width: (xmax - xmin) * vips.width,
height: (ymax - ymin) * vips.height,
},
format: 'jpg',
});
return ret;
}
finally {
vips.close();
}
}
// try {
// const mo = await mediaManager.createMediaObject(picture, 'image/jpeg');
// const image = await mediaManager.convertMediaObject<Image>(mo, ScryptedMimeTypes.Image);
@@ -387,35 +409,18 @@ class SnapshotMixin extends SettingsMixinDeviceBase<Camera> implements Camera {
// throw e;
// }
// return ffmpegFilterImageBuffer(picture, {
// console: this.debugConsole,
// ffmpegPath: await mediaManager.getFFmpegPath(),
// crop: {
// fractional: true,
// left: xmin,
// top: ymin,
// width: xmax - xmin,
// height: ymax - ymin,
// },
// timeout: 10000,
// });
const vips = await loadVipsImage(picture, this.id);
try {
const ret = await vips.toBuffer({
crop: {
left: xmin * vips.width,
top: ymin * vips.height,
width: (xmax - xmin) * vips.width,
height: (ymax - ymin) * vips.height,
},
format: 'jpg',
});
return ret;
}
finally {
vips.close();
}
return ffmpegFilterImageBuffer(picture, {
console: this.debugConsole,
ffmpegPath: await mediaManager.getFFmpegPath(),
crop: {
fractional: true,
left: xmin,
top: ymin,
width: xmax - xmin,
height: ymax - ymin,
},
timeout: 10000,
});
}
clearErrorImages() {
@@ -546,7 +551,7 @@ export function parseDims<T extends string>(dict: DimDict<T>) {
ret[t] = parseFloat(val?.substring(0, val?.length - 1)) / 100;
}
else {
ret[t] = parseFloat(val);
ret[t] = val ? parseFloat(val) : undefined;
}
}
return ret;
@@ -609,11 +614,6 @@ class SnapshotPlugin extends AutoenableMixinProvider implements MixinProvider, B
const ffmpegInput = JSON.parse(data.toString()) as FFmpegInput;
const args = [
...ffmpegInput.inputArguments,
...(ffmpegInput.h264EncoderArguments || []),
];
const {
width,
height,
@@ -636,17 +636,65 @@ class SnapshotPlugin extends AutoenableMixinProvider implements MixinProvider, B
bottom: mime.parameters.get('bottom'),
});
const filename = ffmpegInput.url?.startsWith('file:') && new URL(ffmpegInput.url).pathname;
if (filename && sharpInstance) {
const vips = await loadVipsImage(filename, options?.sourceId);
const resize = width && {
width,
height,
};
if (fractional) {
if (resize.width)
resize.width *= vips.width;
if (resize.height)
resize.height *= vips.height;
}
const crop = left && {
left,
top,
width: right - left,
height: bottom - top,
};
if (cropFractional) {
crop.left *= vips.width;
crop.top *= vips.height;
crop.width *= vips.width;
crop.height *= vips.height;
}
try {
const ret = await vips.toBuffer({
resize,
crop,
format: 'jpg',
});
return ret;
}
finally {
vips.close();
}
}
const args = [
...ffmpegInput.inputArguments,
...(ffmpegInput.h264EncoderArguments || []),
];
return ffmpegFilterImage(args, {
console: this.debugConsole,
ffmpegPath: await mediaManager.getFFmpegPath(),
resize: (isNaN(width) && isNaN(height))
resize: width === undefined && height === undefined
? undefined
: {
width,
height,
fractional,
},
crop: (isNaN(left) && isNaN(top) && isNaN(right) && isNaN(bottom))
crop: left === undefined || right === undefined || top === undefined || bottom === undefined
? undefined
: {
left,