Skip to content

Commit

Permalink
diagnostics: gpu decode test
Browse files Browse the repository at this point in the history
  • Loading branch information
koush committed Sep 11, 2024
1 parent 9633554 commit 341cfa1
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 16 deletions.
4 changes: 2 additions & 2 deletions plugins/diagnostics/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions plugins/diagnostics/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@scrypted/diagnostics",
"version": "0.0.6",
"version": "0.0.8",
"scripts": {
"scrypted-setup-project": "scrypted-setup-project",
"prescrypted-setup-project": "scrypted-package-json",
Expand All @@ -20,7 +20,7 @@
"diagnostics"
],
"scrypted": {
"name": "Scrypted Diagnostics Plugin",
"name": "Scrypted Diagnostics",
"type": "API",
"interfaces": [
"Settings"
Expand Down
62 changes: 54 additions & 8 deletions plugins/diagnostics/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import child_process from 'child_process';
import sharp from 'sharp';
import net from 'net';
import fs from 'fs';
import os from 'os';
import sdk, { Camera, MediaObject, MediaStreamDestination, MotionSensor, Notifier, OnOff, ScryptedDevice, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, Setting, Settings, VideoCamera } from '@scrypted/sdk';
import { StorageSettings } from '@scrypted/sdk/storage-settings';
import { httpFetch, httpFetchParseIncomingMessage } from '../../../server/src/fetch/http-fetch';

import { safeKillFFmpeg } from '@scrypted/common/src/media-helpers';
import { Deferred } from '@scrypted/common/src/deferred';
class DiagnosticsPlugin extends ScryptedDeviceBase implements Settings {
storageSettings = new StorageSettings(this, {
testDevice: {
group: 'Device',
title: 'Test Device',
title: 'Validation Device',
description: "Select a device to validate.",
type: 'device',
deviceFilter: `type === '${ScryptedDeviceType.Camera}' || type === '${ScryptedDeviceType.Doorbell}' || type === '${ScryptedDeviceType.Notifier}'`,
immediate: true,
Expand Down Expand Up @@ -140,17 +143,28 @@ class DiagnosticsPlugin extends ScryptedDeviceBase implements Settings {
throw new Error('Doorbell button not found.');
});

await this.validate('Recent Motion', async () => {
await this.validate('Motion Detection', async () => {
if (!device.interfaces.includes(ScryptedInterface.MotionSensor))
throw new Error('Motion Sensor not found. Enabling a software motion sensor extension is recommended.');

const lastMotion = this.loggedMotion.get(device.id);
if (!lastMotion)
throw new Error('No recent motion detected. Go wave your hand in front of the camera.');
if (Date.now() - lastMotion > 8 * 60 * 60 * 1000)
throw new Error('Last motion was over 8 hours ago.');
if (device.providedInterfaces.includes(ScryptedInterface.MotionSensor)) {
if (device.interfaces.find(i => i.startsWith('ObjectDetection:true')))
this.warnStep('Camera hardware provides motion events, but a software motion detector is enabked. Consider disabling the software motion detector.');
}
});

if (device.interfaces.includes(ScryptedInterface.MotionSensor)) {
await this.validate('Recent Motion', async () => {

const lastMotion = this.loggedMotion.get(device.id);
if (!lastMotion)
throw new Error('No recent motion detected. Go wave your hand in front of the camera.');
if (Date.now() - lastMotion > 8 * 60 * 60 * 1000)
throw new Error('Last motion was over 8 hours ago.');
});
}


if (device.type === ScryptedDeviceType.Doorbell) {
await this.validate('Recent Button Press', async () => {
const lastButton = this.loggedButton.get(device.id);
Expand Down Expand Up @@ -362,6 +376,38 @@ class DiagnosticsPlugin extends ScryptedDeviceBase implements Settings {
});
}

if (nvrPlugin) {
await this.validate("GPU Decode", async () => {
const ffmpegPath = await sdk.mediaManager.getFFmpegPath();
const args = [
'-y',
'-hwaccel', 'auto',
'-i', 'https://github.com/koush/scrypted-sample-cameraprovider/raw/main/fs/dog.mp4',
'-f', 'rawvideo',
'pipe:3',
];
const cp = child_process.spawn(ffmpegPath, args, {
stdio: ['pipe', 'pipe', 'pipe', 'pipe'],
});

const deferred = new Deferred<void>();
deferred.promise.catch(() => { }).finally(() => safeKillFFmpeg(cp));
cp.stdio[3]?.on('data', () => { });

cp.stderr!.on('data', data => {
const str = data.toString();
if (str.includes('nv12'))
deferred.resolve();
});

setTimeout(() => {
deferred.reject(new Error('GPU Decode failed.'));
}, 5000);

await deferred.promise;
});
}

this.console.log(''.padEnd(80, '='));
this.console.log('System Validation Complete');
this.console.log(''.padEnd(80, '='));
Expand Down
8 changes: 4 additions & 4 deletions server/src/media-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ export async function safeKillFFmpeg(cp: ChildProcess) {
cp.on('exit', resolve);
// this will allow ffmpeg to send rtsp TEARDOWN etc
try {
cp.stdin.on('error', () => { });
cp.stdin.write('q\n');
cp.stdin!.on('error', () => { });
cp.stdin!.write('q\n');
}
catch (e) {
}
Expand Down Expand Up @@ -55,8 +55,8 @@ export function ffmpegLogInitialOutput(console: Console, cp: ChildProcess, forev
if (!SCRYPTED_FFMPEG_NOISY && !forever && (str.indexOf('frame=') !== -1 || str.indexOf('size=') !== -1)) {
log(str);
log('video/audio detected, discarding further input');
cp.stdout.removeListener('data', ret);
cp.stderr.removeListener('data', ret);
cp.stdout!.removeListener('data', ret);
cp.stderr!.removeListener('data', ret);
return;
}

Expand Down

0 comments on commit 341cfa1

Please sign in to comment.