From bc70803cc0d282f9be47050764e9d68f19ad0227 Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Fri, 9 Aug 2024 10:35:09 -0700 Subject: [PATCH] cameras: wip codec configuration --- plugins/onvif/src/main.ts | 5 +-- plugins/onvif/src/onvif-api.ts | 53 ++++++++++++++++++++++++++-- plugins/onvif/src/onvif-configure.ts | 50 ++++++++++++++------------ 3 files changed, 81 insertions(+), 27 deletions(-) diff --git a/plugins/onvif/src/main.ts b/plugins/onvif/src/main.ts index 1d19d857fe..fce6c04bd2 100644 --- a/plugins/onvif/src/main.ts +++ b/plugins/onvif/src/main.ts @@ -30,10 +30,11 @@ class OnvifCamera extends RtspSmartCamera implements ObjectDetector, Intercom, V await client.reboot(); } - async setVideoStreamOptions(options: MediaStreamOptions): Promise { + async setVideoStreamOptions(options: MediaStreamOptions) { const client = await this.getClient(); - await configureCodecs(client, options); + const ret = await configureCodecs(this.console, client, options); this.rtspMediaStreamOptions = undefined; + return ret; } async updateDeviceInfo() { diff --git a/plugins/onvif/src/onvif-api.ts b/plugins/onvif/src/onvif-api.ts index 847bf33b0d..7c382ae443 100644 --- a/plugins/onvif/src/onvif-api.ts +++ b/plugins/onvif/src/onvif-api.ts @@ -1,6 +1,6 @@ -import { HttpFetchOptions, HttpFetchResponseType, AuthFetchCredentialState, authHttpFetch } from '@scrypted/common/src/http-auth-fetch'; +import { AuthFetchCredentialState, authHttpFetch, HttpFetchOptions } from '@scrypted/common/src/http-auth-fetch'; +import { VideoStreamConfiguration } from '@scrypted/sdk'; import { EventEmitter } from 'events'; -import https from 'https'; import { Readable } from 'stream'; const onvif = require('onvif'); @@ -168,6 +168,55 @@ export class OnvifCameraAPI { return ret; } + async getVideoEncoderConfigurationOptions(profileToken: string, configurationToken: string): Promise { + const options: any = await promisify(cb => this.cam.getVideoEncoderConfigurationOptions({ profileToken }, cb)); + const codecs: string[] = []; + if (options.H264) + codecs.push('h264'); + if (options.H265) + codecs.push('h265'); + + let qualityRange: [number, number]; + const resolutions: [number, number][] = []; + let fpsRange: [number, number]; + let keyframeIntervalRange: [number, number]; + const profiles: string[] = []; + const bitrateControls: string[] = []; + let bitrateRange: [number, number]; + + const H264 = options?.extension?.H264 || options?.H264; + if (H264) { + if (H264?.H264ProfilesSupported) + profiles.push(...H264.H264ProfilesSupported.map(p => p.toLowerCase())); + if (H264?.resolutionsAvailable) + resolutions.push(...H264.resolutionsAvailable.map(r => [r.width, r.height])); + if (H264?.frameRateRange?.min || H264?.frameRateRange?.max) + fpsRange = [H264.frameRateRange.min, H264.frameRateRange.max]; + if (H264?.govLengthRange?.min || H264?.govLengthRange?.max) + keyframeIntervalRange = [H264.govLengthRange.min, H264.govLengthRange.max]; + if (H264?.bitrateRange?.min || H264?.bitrateRange?.max) + bitrateRange = [H264.bitrateRange.min, H264.bitrateRange.max]; + } + if (options.qualityRange?.min || options?.qualityRange?.max) + qualityRange = [options.qualityRange.min, options.qualityRange.max]; + + // if (config?.) + + return { + codecs, + qualityRange, + fpsRange, + keyframeIntervalRange, + resolutions, + profiles, + bitrateRange, + } + } + + async setVideoEncoderConfiguration(configuration: any) { + return promisify(cb => this.cam.setVideoEncoderConfiguration(configuration, cb)); + } + async getProfiles() { if (!this.profiles) { this.profiles = promisify(cb => this.cam.getProfiles(cb)); diff --git a/plugins/onvif/src/onvif-configure.ts b/plugins/onvif/src/onvif-configure.ts index 81e370a6ce..cdbd23a764 100644 --- a/plugins/onvif/src/onvif-configure.ts +++ b/plugins/onvif/src/onvif-configure.ts @@ -22,24 +22,29 @@ export function computeBitrate(bitrate: number) { return bitrate * 1000; } -export async function configureCodecs(client: OnvifCameraAPI, options: MediaStreamOptions) { +export async function configureCodecs(console: Console, client: OnvifCameraAPI, options: MediaStreamOptions) { const profiles: any[] = await client.getProfiles(); const profile = profiles.find(profile => profile.$.token === options.id); const configuration = profile.videoEncoderConfiguration; const videoOptions = options.video; - switch (videoOptions?.codec) { - case 'h264': - configuration.encoding = 'H264'; + if (videoOptions?.codec) { + let key: string; + switch (videoOptions.codec) { + case 'h264': + key = 'H264'; + break; + case 'h265': + key = 'H265'; + break; + } + if (key) { + configuration.encoding = key; - if (videoOptions?.idrIntervalMillis && videoOptions?.fps) { - configuration.H264 ||= {}; - configuration.H264.govLength = Math.floor(videoOptions?.fps * videoOptions?.idrIntervalMillis / 1000); - } if (videoOptions?.keyframeInterval) { - configuration.H264 ||= {}; - configuration.H264.govLength = videoOptions?.keyframeInterval; + configuration[key] ||= {}; + configuration[key].govLength = videoOptions?.keyframeInterval; } if (videoOptions?.profile) { let profile: string; @@ -55,11 +60,11 @@ export async function configureCodecs(client: OnvifCameraAPI, options: MediaStre break; } if (profile) { - configuration.H264 ||= {}; - configuration.H264.profile = profile; + configuration[key] ||= {}; + configuration[key].profile = profile; } } - break; + } } if (videoOptions?.width && videoOptions?.height) { @@ -85,14 +90,14 @@ export async function configureCodecs(client: OnvifCameraAPI, options: MediaStre configuration.rateControl.encodingInterval = 1; } - return new Promise((r, f) => { - client.cam.setVideoEncoderConfiguration(configuration, (e: Error, result: any) => { - if (e) - return f(e); - - r(); - }) - }); + await client.setVideoEncoderConfiguration(configuration); + const configuredCodec = await client.getVideoEncoderConfigurationOptions(profile.$.token, configuration.$.token); + const codecs = await getCodecs(console, client); + const foundCodec = codecs.find(codec => codec.id === options.id); + return { + ...foundCodec, + ...configuredCodec, + } } export async function getCodecs(console: Console, client: OnvifCameraAPI) { @@ -115,8 +120,7 @@ export async function getCodecs(console: Console, client: OnvifCameraAPI) { width: videoEncoderConfiguration?.resolution?.width, height: videoEncoderConfiguration?.resolution?.height, codec: videoEncoderConfiguration?.encoding?.toLowerCase(), - idrIntervalMillis: computeInterval(videoEncoderConfiguration?.rateControl?.frameRateLimit, - videoEncoderConfiguration?.$.GovLength), + keyframeInterval: videoEncoderConfiguration?.$?.GovLength, }, audio: { bitrate: computeBitrate(audioEncoderConfiguration?.bitrate),