Skip to content

Commit

Permalink
cameras: wip codec configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
koush committed Aug 9, 2024
1 parent 171b04f commit bc70803
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 27 deletions.
5 changes: 3 additions & 2 deletions plugins/onvif/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@ class OnvifCamera extends RtspSmartCamera implements ObjectDetector, Intercom, V
await client.reboot();
}

async setVideoStreamOptions(options: MediaStreamOptions): Promise<void> {
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() {
Expand Down
53 changes: 51 additions & 2 deletions plugins/onvif/src/onvif-api.ts
Original file line number Diff line number Diff line change
@@ -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');
Expand Down Expand Up @@ -168,6 +168,55 @@ export class OnvifCameraAPI {
return ret;
}

async getVideoEncoderConfigurationOptions(profileToken: string, configurationToken: string): Promise<VideoStreamConfiguration> {
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));
Expand Down
50 changes: 27 additions & 23 deletions plugins/onvif/src/onvif-configure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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) {
Expand All @@ -85,14 +90,14 @@ export async function configureCodecs(client: OnvifCameraAPI, options: MediaStre
configuration.rateControl.encodingInterval = 1;
}

return new Promise<void>((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) {
Expand All @@ -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),
Expand Down

0 comments on commit bc70803

Please sign in to comment.