mirror of
https://github.com/koush/scrypted.git
synced 2026-02-03 06:03:27 +00:00
amcrest: publish continuous recording
This commit is contained in:
1
common/.gitignore
vendored
1
common/.gitignore
vendored
@@ -3,3 +3,4 @@ node_modules
|
||||
.gcloud/
|
||||
dist/
|
||||
scrypted.db
|
||||
src/test.ts
|
||||
|
||||
21
common/.vscode/launch.json
vendored
Normal file
21
common/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "pwa-node",
|
||||
"request": "launch",
|
||||
"name": "Launch Program",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"program": "${workspaceFolder}/dist/common/src/test.js",
|
||||
"preLaunchTask": "npm: build",
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/**/*.js"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
|
||||
@@ -120,7 +120,7 @@ export function createMonacoEvalDefaults(extraLibs: { [lib: string]: string }) {
|
||||
|
||||
const libs = Object.assign(getTypeDefs(), extraLibs);
|
||||
|
||||
function monacoEvalDefaultsFunction(monaco, safeLibs, libs) {
|
||||
function monacoEvalDefaultsFunction(monaco: any, safeLibs: any, libs: any) {
|
||||
monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions(
|
||||
Object.assign(
|
||||
{},
|
||||
@@ -223,7 +223,7 @@ export function createScriptDevice(baseInterfaces: string[]): ScriptDeviceImpl {
|
||||
const iface = methodInterfaces.get(method);
|
||||
if (iface) {
|
||||
allInterfaces.push(iface);
|
||||
device[method] = handler[method].bind(handler);
|
||||
(device as any)[method] = handler[method].bind(handler);
|
||||
}
|
||||
}
|
||||
return allInterfaces;
|
||||
|
||||
@@ -17,6 +17,18 @@ const configuration: RTCConfiguration = {
|
||||
],
|
||||
};
|
||||
|
||||
export function isPeerConnectionAlive(pc :RTCPeerConnection) {
|
||||
if (pc.iceConnectionState === 'disconnected'
|
||||
|| pc.iceConnectionState === 'failed'
|
||||
|| pc.iceConnectionState === 'closed')
|
||||
return false;
|
||||
if (pc.connectionState === 'closed'
|
||||
|| pc.connectionState === 'disconnected'
|
||||
|| pc.connectionState === 'failed')
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
let wrtc: any;
|
||||
function initalizeWebRtc() {
|
||||
if (wrtc)
|
||||
@@ -214,20 +226,12 @@ export async function startRTCPeerConnectionFFmpegInput(ffInput: FFMpegInput, op
|
||||
}
|
||||
|
||||
const checkConn = () => {
|
||||
if (pc.iceConnectionState === 'disconnected'
|
||||
|| pc.iceConnectionState === 'failed'
|
||||
|| pc.iceConnectionState === 'closed') {
|
||||
if (!isPeerConnectionAlive(pc))
|
||||
cleanup();
|
||||
}
|
||||
if (pc.connectionState === 'closed'
|
||||
|| pc.connectionState === 'disconnected'
|
||||
|| pc.connectionState === 'failed') {
|
||||
cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
pc.onconnectionstatechange = checkConn;
|
||||
pc.oniceconnectionstatechange = checkConn;
|
||||
pc.addEventListener('connectionstatechange', checkConn);
|
||||
pc.addEventListener('iceconnectionstatechange', checkConn);
|
||||
|
||||
setTimeout(() => {
|
||||
if (pc.connectionState !== 'connected') {
|
||||
@@ -273,6 +277,7 @@ export async function startRTCPeerConnection(console: Console, mediaObject: Medi
|
||||
});
|
||||
|
||||
await pc.setRemoteDescription(answer);
|
||||
return pc;
|
||||
}
|
||||
catch (e) {
|
||||
pc.close();
|
||||
@@ -314,7 +319,7 @@ export async function startBrowserRTCSignaling(camera: ScryptedDevice & RTCSigna
|
||||
camera.startRTCSignalingSession(session, options);
|
||||
}
|
||||
else {
|
||||
startRTCPeerConnectionForBrowser(console, await camera.getVideoStream(), session, options);
|
||||
return startRTCPeerConnectionForBrowser(console, await camera.getVideoStream(), session, options);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { findTrack } from './sdp-utils';
|
||||
import dgram from 'dgram';
|
||||
import net from 'net';
|
||||
import tls from 'tls';
|
||||
import { DIGEST } from 'http-auth-utils/src/index';
|
||||
import { DIGEST } from 'http-auth-utils/dist/index';
|
||||
import crypto from 'crypto';
|
||||
|
||||
export const RTSP_FRAME_MAGIC = 36;
|
||||
@@ -244,9 +244,11 @@ export class RtspClient extends RtspBase {
|
||||
});
|
||||
}
|
||||
|
||||
async setup(channel: number, path?: string) {
|
||||
async setup(channelOrPort: number, path?: string, udp?: boolean) {
|
||||
const protocol = udp ? 'UDP' : 'TCP';
|
||||
const client = udp ? 'client_port' : 'interleaved';
|
||||
const headers: any = {
|
||||
Transport: `RTP/AVP/TCP;unicast;interleaved=${channel}-${channel + 1}`,
|
||||
Transport: `RTP/AVP/${protocol};unicast;${client}=${channelOrPort}-${channelOrPort + 1}`,
|
||||
};
|
||||
const response = await this.request('SETUP', headers, path)
|
||||
if (response.headers.session) {
|
||||
@@ -266,14 +268,19 @@ export class RtspClient extends RtspBase {
|
||||
return response;
|
||||
}
|
||||
|
||||
async play() {
|
||||
async play(start: string = '0.000') {
|
||||
const headers: any = {
|
||||
Range: 'npt=0.000-',
|
||||
Range: `npt=${start}-`,
|
||||
};
|
||||
await this.request('PLAY', headers);
|
||||
return this.client;
|
||||
}
|
||||
|
||||
async pause() {
|
||||
await this.request('PAUSE');
|
||||
return this.client;
|
||||
}
|
||||
|
||||
async teardown() {
|
||||
try {
|
||||
return await this.request('TEARDOWN');
|
||||
|
||||
4
plugins/amcrest/package-lock.json
generated
4
plugins/amcrest/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/amcrest",
|
||||
"version": "0.0.90",
|
||||
"version": "0.0.91",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/amcrest",
|
||||
"version": "0.0.90",
|
||||
"version": "0.0.91",
|
||||
"license": "Apache",
|
||||
"dependencies": {
|
||||
"@koush/axios-digest-auth": "^0.8.5",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/amcrest",
|
||||
"version": "0.0.90",
|
||||
"version": "0.0.91",
|
||||
"description": "Amcrest Plugin for Scrypted",
|
||||
"author": "Scrypted",
|
||||
"license": "Apache",
|
||||
|
||||
@@ -50,18 +50,15 @@ export class RtspCamera extends CameraBase<UrlMediaStreamOptions> {
|
||||
return ret;
|
||||
}
|
||||
|
||||
async createVideoStream(vso: UrlMediaStreamOptions): Promise<MediaObject> {
|
||||
if (!vso)
|
||||
throw new Error('video streams not set up or no longer exists.');
|
||||
|
||||
addRtspCredentials(rtspUrl: string) {
|
||||
// ignore this deprecation warning. the WHATWG URL class will trim the password
|
||||
// off if it is empty, resulting in urls like rtsp://admin@foo.com/.
|
||||
// this causes ffmpeg to fail on sending a blank password.
|
||||
// we need to send it as follows: rtsp://admin:@foo.com/.
|
||||
// Note the trailing colon.
|
||||
// issue: https://github.com/koush/scrypted/issues/134
|
||||
const parsedUrl = url.parse(vso.url);
|
||||
this.console.log('rtsp stream url', vso.url);
|
||||
const parsedUrl = url.parse(rtspUrl);
|
||||
this.console.log('rtsp stream url', rtspUrl);
|
||||
const username = this.storage.getItem("username");
|
||||
const password = this.storage.getItem("password");
|
||||
if (username) {
|
||||
@@ -71,7 +68,10 @@ export class RtspCamera extends CameraBase<UrlMediaStreamOptions> {
|
||||
}
|
||||
|
||||
const stringUrl = url.format(parsedUrl);
|
||||
return stringUrl;
|
||||
}
|
||||
|
||||
createFfmpegMediaObject(stringUrl: string, vso: MediaStreamOptions) {
|
||||
const ret: FFMpegInput = {
|
||||
url: stringUrl,
|
||||
inputArguments: [
|
||||
@@ -84,6 +84,14 @@ export class RtspCamera extends CameraBase<UrlMediaStreamOptions> {
|
||||
return mediaManager.createFFmpegMediaObject(ret);
|
||||
}
|
||||
|
||||
async createVideoStream(vso: UrlMediaStreamOptions): Promise<MediaObject> {
|
||||
if (!vso)
|
||||
throw new Error('video streams not set up or no longer exists.');
|
||||
|
||||
const stringUrl = this.addRtspCredentials(vso.url);
|
||||
return this.createFfmpegMediaObject(stringUrl, vso);
|
||||
}
|
||||
|
||||
// hide the description from CameraBase that indicates it is only used for snapshots
|
||||
getUsernameDescription(): string {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user