mirror of
https://github.com/koush/scrypted.git
synced 2026-06-22 01:20:26 +01:00
hikvision: overlay support
This commit is contained in:
2
plugins/hikvision/.vscode/settings.json
vendored
2
plugins/hikvision/.vscode/settings.json
vendored
@@ -1,4 +1,4 @@
|
||||
|
||||
{
|
||||
"scrypted.debugHost": "127.0.0.1",
|
||||
"scrypted.debugHost": "scrypted-nvr",
|
||||
}
|
||||
4
plugins/hikvision/package-lock.json
generated
4
plugins/hikvision/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/hikvision",
|
||||
"version": "0.0.162",
|
||||
"version": "0.0.163",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/hikvision",
|
||||
"version": "0.0.162",
|
||||
"version": "0.0.163",
|
||||
"license": "Apache",
|
||||
"dependencies": {
|
||||
"@scrypted/common": "file:../../common",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/hikvision",
|
||||
"version": "0.0.162",
|
||||
"version": "0.0.163",
|
||||
"description": "Hikvision Plugin for Scrypted",
|
||||
"author": "Scrypted",
|
||||
"license": "Apache",
|
||||
|
||||
@@ -2,6 +2,7 @@ import { HttpFetchOptions } from '@scrypted/common/src/http-auth-fetch';
|
||||
import { MediaStreamConfiguration, MediaStreamOptions } from '@scrypted/sdk';
|
||||
import { Readable } from 'stream';
|
||||
import { Destroyable } from '../../rtsp/src/rtsp';
|
||||
import { TextOverlayRoot, VideoOverlayRoot } from './hikvision-overlay';
|
||||
|
||||
export interface HikvisionCameraStreamSetup {
|
||||
videoCodecType: string;
|
||||
@@ -22,4 +23,14 @@ export interface HikvisionAPI {
|
||||
putVcaResource(channel: string, resource: 'smart' | 'facesnap' | 'close'): Promise<boolean>;
|
||||
getCodecs(camNumber: string): Promise<MediaStreamOptions[]>;
|
||||
configureCodecs(camNumber: string, channelNumber: string, options: MediaStreamOptions): Promise<MediaStreamConfiguration>;
|
||||
|
||||
getOverlay(): Promise<{
|
||||
json: VideoOverlayRoot;
|
||||
xml: any;
|
||||
}>;
|
||||
getOverlayText(overlayId: string): Promise<{
|
||||
json: TextOverlayRoot;
|
||||
xml: any;
|
||||
}>;
|
||||
updateOverlayText(overlayId: string, entry: TextOverlayRoot): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import { CapabiltiesResponse } from './hikvision-api-capabilities';
|
||||
import { HikvisionAPI, HikvisionCameraStreamSetup } from "./hikvision-api-channels";
|
||||
import { ChannelResponse, ChannelsResponse } from './hikvision-xml-types';
|
||||
import { getDeviceInfo } from './probe';
|
||||
import { TextOverlayRoot, VideoOverlayRoot } from './hikvision-overlay';
|
||||
|
||||
export const detectionMap = {
|
||||
human: 'person',
|
||||
@@ -482,4 +483,47 @@ export class HikvisionCameraAPI implements HikvisionAPI {
|
||||
return [...defaultMap.values()];
|
||||
}
|
||||
}
|
||||
|
||||
async getOverlay() {
|
||||
const response = await this.request({
|
||||
method: 'GET',
|
||||
url: `http://${this.ip}/ISAPI/System/Video/inputs/channels/1/overlays`,
|
||||
responseType: 'text',
|
||||
headers: {
|
||||
'Content-Type': 'application/xml',
|
||||
},
|
||||
});
|
||||
const json = await xml2js.parseStringPromise(response.body) as VideoOverlayRoot;
|
||||
|
||||
return { json, xml: response.body };
|
||||
}
|
||||
|
||||
async getOverlayText(overlayId: string) {
|
||||
const response = await this.request({
|
||||
method: 'GET',
|
||||
url: `http://${this.ip}//ISAPI/System/Video/inputs/channels/1/overlays/text/${overlayId}`,
|
||||
responseType: 'text',
|
||||
headers: {
|
||||
'Content-Type': 'application/xml',
|
||||
},
|
||||
});
|
||||
const json = await xml2js.parseStringPromise(response.body) as TextOverlayRoot;
|
||||
|
||||
return { json, xml: response.body };
|
||||
}
|
||||
|
||||
async updateOverlayText(overlayId: string, entry: TextOverlayRoot) {
|
||||
const builder = new xml2js.Builder();
|
||||
const xml = builder.buildObject(entry);
|
||||
|
||||
await this.request({
|
||||
method: 'PUT',
|
||||
url: `http://${this.ip}//ISAPI/System/Video/inputs/channels/1/overlays/text/${overlayId}`,
|
||||
responseType: 'text',
|
||||
headers: {
|
||||
'Content-Type': 'application/xml',
|
||||
},
|
||||
body: xml
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
88
plugins/hikvision/src/hikvision-overlay.ts
Normal file
88
plugins/hikvision/src/hikvision-overlay.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
export interface VideoOverlayRoot {
|
||||
VideoOverlay: VideoOverlay;
|
||||
}
|
||||
|
||||
export interface VideoOverlay {
|
||||
$: VideoOverlayClass;
|
||||
normalizedScreenSize: NormalizedScreenSize[];
|
||||
attribute: Attribute[];
|
||||
fontSize: string[];
|
||||
TextOverlayList: TextOverlayListElement[];
|
||||
DateTimeOverlay: DateTimeOverlay[];
|
||||
channelNameOverlay: ChannelNameOverlay[];
|
||||
frontColorMode: string[];
|
||||
frontColor: string[];
|
||||
alignment: string[];
|
||||
boundary: string[];
|
||||
upDownboundary: string[];
|
||||
leftRightboundary: string[];
|
||||
}
|
||||
|
||||
export interface VideoOverlayClass {
|
||||
version: string;
|
||||
xmlns: string;
|
||||
}
|
||||
|
||||
export interface DateTimeOverlay {
|
||||
enabled: string[];
|
||||
positionX: string[];
|
||||
positionY: string[];
|
||||
dateStyle: string[];
|
||||
timeStyle: string[];
|
||||
displayWeek: string[];
|
||||
}
|
||||
|
||||
export interface TextOverlayListElement {
|
||||
$: TextOverlayList;
|
||||
TextOverlay: TextOverlay[];
|
||||
}
|
||||
|
||||
export interface TextOverlayList {
|
||||
size: string;
|
||||
}
|
||||
|
||||
export interface TextOverlay {
|
||||
id: string[];
|
||||
enabled: string[];
|
||||
positionX: string[];
|
||||
positionY: string[];
|
||||
displayText: string[];
|
||||
isPersistentText: string[];
|
||||
}
|
||||
|
||||
export interface Attribute {
|
||||
transparent: string[];
|
||||
flashing: string[];
|
||||
}
|
||||
|
||||
export interface ChannelNameOverlay {
|
||||
$: VideoOverlayClass;
|
||||
enabled: string[];
|
||||
positionX: string[];
|
||||
positionY: string[];
|
||||
}
|
||||
|
||||
export interface NormalizedScreenSize {
|
||||
normalizedScreenWidth: string[];
|
||||
normalizedScreenHeight: string[];
|
||||
}
|
||||
|
||||
|
||||
export interface TextOverlayRoot {
|
||||
TextOverlay: TextOverlay;
|
||||
}
|
||||
|
||||
export interface TextOverlay {
|
||||
$: Empty;
|
||||
id: string[];
|
||||
enabled: string[];
|
||||
positionX: string[];
|
||||
positionY: string[];
|
||||
displayText: string[];
|
||||
directAngle: string[];
|
||||
}
|
||||
|
||||
export interface Empty {
|
||||
version: string;
|
||||
xmlns: string;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { automaticallyConfigureSettings, checkPluginNeedsAutoConfigure } from "@scrypted/common/src/autoconfigure-codecs";
|
||||
import sdk, { Camera, DeviceCreatorSettings, DeviceInformation, FFmpegInput, Intercom, MediaObject, MediaStreamOptions, ObjectDetectionResult, ObjectDetectionTypes, ObjectDetector, ObjectsDetected, Reboot, RequestPictureOptions, ScryptedDeviceType, ScryptedInterface, ScryptedMimeTypes, ScryptedNativeId, Setting, VideoCameraConfiguration } from "@scrypted/sdk";
|
||||
import sdk, { Camera, DeviceCreatorSettings, DeviceInformation, FFmpegInput, Intercom, MediaObject, MediaStreamOptions, ObjectDetectionResult, ObjectDetectionTypes, ObjectDetector, ObjectsDetected, Reboot, RequestPictureOptions, ScryptedDeviceType, ScryptedInterface, ScryptedMimeTypes, ScryptedNativeId, Setting, VideoCameraConfiguration, VideoTextOverlay, VideoTextOverlays } from "@scrypted/sdk";
|
||||
import crypto from 'crypto';
|
||||
import { PassThrough } from "stream";
|
||||
import xml2js from 'xml2js';
|
||||
@@ -27,7 +27,7 @@ function channelToCameraNumber(channel: string) {
|
||||
return channel.substring(0, channel.length - 2);
|
||||
}
|
||||
|
||||
export class HikvisionCamera extends RtspSmartCamera implements Camera, Intercom, Reboot, ObjectDetector, VideoCameraConfiguration {
|
||||
export class HikvisionCamera extends RtspSmartCamera implements Camera, Intercom, Reboot, ObjectDetector, VideoCameraConfiguration, VideoTextOverlays {
|
||||
detectedChannels: Promise<Map<string, MediaStreamOptions>>;
|
||||
onvifIntercom = new OnvifIntercom(this);
|
||||
activeIntercom: Awaited<ReturnType<typeof startRtpForwarderProcess>>;
|
||||
@@ -43,6 +43,33 @@ export class HikvisionCamera extends RtspSmartCamera implements Camera, Intercom
|
||||
this.updateDeviceInfo();
|
||||
}
|
||||
|
||||
async getVideoTextOverlays(): Promise<Record<string, VideoTextOverlay>> {
|
||||
const client = this.getClient();
|
||||
const overlays = await client.getOverlay();
|
||||
const ret: Record<string, VideoTextOverlay> = {};
|
||||
|
||||
for (const overlay of overlays.json.VideoOverlay.TextOverlayList) {
|
||||
const to = overlay.TextOverlay[0];
|
||||
ret[to.id[0]] = {
|
||||
text: to.displayText[0],
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
async setVideoTextOverlay(id: string, value: VideoTextOverlay): Promise<void> {
|
||||
const client = this.getClient();
|
||||
const overlays = await client.getOverlay();
|
||||
// find the overlay by id
|
||||
const overlay = overlays.json.VideoOverlay.TextOverlayList.find(o => o.TextOverlay[0].id[0] === id);
|
||||
overlay.TextOverlay[0].enabled[0] = value.text ? 'true' : 'false';
|
||||
if (typeof value.text === 'string')
|
||||
overlay.TextOverlay[0].displayText = [value.text];
|
||||
client.updateOverlayText(id, {
|
||||
TextOverlay: overlay.TextOverlay[0],
|
||||
});
|
||||
}
|
||||
|
||||
async reboot() {
|
||||
const client = this.getClient();
|
||||
await client.reboot();
|
||||
@@ -626,6 +653,7 @@ class HikvisionProvider extends RtspProvider {
|
||||
ScryptedInterface.Reboot,
|
||||
ScryptedInterface.Camera,
|
||||
ScryptedInterface.MotionSensor,
|
||||
ScryptedInterface.VideoTextOverlays,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user