Skip to content

Commit

Permalink
Working rtsp stream viewer draft
Browse files Browse the repository at this point in the history
  • Loading branch information
GLDuval committed May 5, 2023
1 parent 7cd97dc commit 9e6461d
Show file tree
Hide file tree
Showing 12 changed files with 131 additions and 63 deletions.
48 changes: 47 additions & 1 deletion package-lock.json

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

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@
"date-fns": "^2.28.0",
"electron-log": "^4.4.6",
"execa": "^6.1.0",
"jsmpeg": "^1.0.0",
"lodash": "^4.17.21",
"nanoid": "^3.3.1",
"node-rtsp-stream": "^0.0.9",
Expand Down
13 changes: 8 additions & 5 deletions src/main/RtspServer/script.ts → script/rtspServer.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import RTSPServer from 'node-rtsp-stream';
const Stream = require('node-rtsp-stream');

new RTSPServer.Stream({
name: 'name',
streamUrl: 'rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4',
wsPort: 9999,
// Get node args
const args = process.argv.slice(2);

new Stream({
name: 'RTSP-Stream',
streamUrl: args[0],
wsPort: parseInt(args[1]),
ffmpegOptions: {
// options ffmpeg flags
'-stats': '', // an option with no neccessary value uses a blank string
Expand Down
3 changes: 2 additions & 1 deletion snowpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module.exports = {
extends: 'electron-snowpack/config/snowpack.js',
mount: {
'src/shared': '/shared',
script: '/script',
},
alias: {
'@/': './src/',
Expand All @@ -12,4 +13,4 @@ module.exports = {
packageOptions: {
knownEntrypoints: ['date-fns/fp/format', 'react-is'],
},
}
};
2 changes: 1 addition & 1 deletion src/main/preload/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@ export const preload = {
},
rtsp: {
start: (url: string) => ipcRenderer.invoke(RTSP_START, url),
stop: () => ipcRenderer.send(RTSP_STOP),
stop: (url: string) => ipcRenderer.send(RTSP_STOP, url),
},
};
25 changes: 18 additions & 7 deletions src/main/rtspServer.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,35 @@
import { log } from '@/main/logger';
import { ipcMain } from 'electron';
import { execaNode, ExecaChildProcess } from 'execa';
import { app, ipcMain } from 'electron';
import { ExecaChildProcess, execa } from 'execa';
import path from 'path';
import { isDev } from '@/main/isDev';

interface RtspProcess {
process: ExecaChildProcess;
wsPort: number;
}

const rtspServers: Map<string, RtspProcess> = new Map();
let nextPort = 9000;

// Stack array of ports from (9000 to 9060) to use for rtsp servers
const ports = Array.from({ length: 61 }, (_, i) => i + 9000);

ipcMain.handle('rtsp_start', (event, url: string) => {
const process = execaNode('script.ts');
const nextPort = ports.shift() ?? 9000;
const process = execa('node', [
isDev
? './script/rtspServer.js'
: path.join(app.getAppPath(), '../renderer/script/rtspServer.js'),
url,
nextPort.toString(),
]);

log.info('starting rtsp process');
rtspServers.set(url, {
process,
wsPort: nextPort,
});

return nextPort++;
return nextPort;
});

ipcMain.on('rtsp_stop', (event, url: string) => {
Expand All @@ -28,7 +38,8 @@ ipcMain.on('rtsp_stop', (event, url: string) => {
if (!rtspProcess) {
return;
}

log.info('stopping rtsp process');
ports.push(rtspProcess.wsPort);
rtspProcess.process.kill();
rtspServers.delete(url);
});
4 changes: 2 additions & 2 deletions src/renderer/components/Feed/Feeds/CameraFeed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import * as React from 'react';
import { FC, useEffect, useRef, useState } from 'react';
import { log } from '@/renderer/logger';
import { QRFeed } from './QRFeed/QRFeed';
import { RTSPFeed } from './RTSPFeed/RTSPFeed';
import { RTSPFeed } from './RTSPFeed';

interface Props {
feed: ICameraFeed;
Expand Down Expand Up @@ -147,7 +147,7 @@ const View: FC<Props> = ({ feed }) => {
</QRFeed>
);
case CameraType.RTSP:
return <RTSPFeed />;
return <RTSPFeed url={feed.camera.topic} />;
default:
return <TextFeed text="stream type not supported" />;
}
Expand Down
51 changes: 51 additions & 0 deletions src/renderer/components/Feed/Feeds/RTSPFeed.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React, { useEffect, useRef } from 'react';
import JSMpeg from '@cycjimmy/jsmpeg-player';
import { log } from '@/renderer/logger';

interface Props {
url: string;
}

export const RTSPFeed = ({ url }: Props) => {
const videoRef = useRef<HTMLDivElement>(null);
const canvasRef = useRef<HTMLCanvasElement>(null);

useEffect(() => {
log.info(`Starting RTSP server for ${url}`);
const videoWrapper = videoRef.current;
const canvas = canvasRef.current;
let videoElement: JSMpeg.VideoElement | null = null;
const startServer = async () => {
const port = (await window.preloadApi.rtsp.start(url)) as number;
log.info(`RTSP server started on port ${port}`);
if (videoWrapper && canvas) {
videoElement = new JSMpeg.VideoElement(
videoWrapper,
`ws://localhost:${port}`,
{
canvas,
autoplay: true,
audio: false,
}
);
}
};
startServer().catch((e) => log.error(e));

return () => {
log.info('Stopping RTSP server');
window.preloadApi.rtsp.stop(url);
if (videoElement) {
videoElement.destroy();
}
};
}, [url]);

return (
<>
<div ref={videoRef}>
<canvas ref={canvasRef} />
</div>
</>
);
};
32 changes: 0 additions & 32 deletions src/renderer/components/Feed/Feeds/RTSPFeed/RTSPFeed.tsx

This file was deleted.

Empty file.
2 changes: 2 additions & 0 deletions src/renderer/types/jsmpeg.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,7 @@ declare module '@cycjimmy/jsmpeg-player' {
audio?: boolean;
}
);

destroy(): void;
}
}
13 changes: 0 additions & 13 deletions src/renderer/types/nodeRtspStream.d.ts

This file was deleted.

0 comments on commit 9e6461d

Please sign in to comment.