Skip to content

Commit

Permalink
Start rtsp viewer
Browse files Browse the repository at this point in the history
  • Loading branch information
GLDuval committed May 4, 2023
1 parent fe82542 commit 7cd97dc
Show file tree
Hide file tree
Showing 13 changed files with 161 additions and 3 deletions.
41 changes: 39 additions & 2 deletions package-lock.json

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

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"extends": "electron-snowpack/config/electron-builder.js"
},
"dependencies": {
"@cycjimmy/jsmpeg-player": "^6.0.5",
"@reduxjs/toolkit": "^1.8.0",
"@xstate/react": "^1.6.3",
"chalk": "^5.0.1",
Expand All @@ -53,8 +54,10 @@
"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",
"polished": "^4.1.4",
"qr-scanner": "^1.4.1",
"react": "^17.0.2",
Expand Down
12 changes: 12 additions & 0 deletions src/main/RtspServer/script.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import RTSPServer from 'node-rtsp-stream';

new RTSPServer.Stream({
name: 'name',
streamUrl: 'rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4',
wsPort: 9999,
ffmpegOptions: {
// options ffmpeg flags
'-stats': '', // an option with no neccessary value uses a blank string
'-r': 30, // options with required values specify the value after the key
},
});
1 change: 1 addition & 0 deletions src/main/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import '@/main/rtspServer';
import '@/main/audio';
import { log } from '@/main/logger';
import { APP_INFO_QUERY, APP_INFO_TYPE, AUDIO_STOP } from '@/main/preload';
Expand Down
3 changes: 3 additions & 0 deletions src/main/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ export type AUDIO_MSG_TYPE = {
stdout: string;
};

export const RTSP_START = 'rtsp_start';
export const RTSP_STOP = 'rtsp_stop';

if (contextBridge) {
// This is done this way because otherwise tests would try to import preload and it wouldn't work
// It's possible the issue is mostly in the import order.
Expand Down
6 changes: 6 additions & 0 deletions src/main/preload/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
AUDIO_STOP,
LOG_MSG,
LOG_MSG_TYPE,
RTSP_START,
RTSP_STOP,
} from '@/main/preload';
import { ipcRenderer } from 'electron';

Expand All @@ -29,4 +31,8 @@ export const preload = {
ipcRenderer.on(AUDIO_MSG, (_event, args) => cb(args as AUDIO_MSG_TYPE));
},
},
rtsp: {
start: (url: string) => ipcRenderer.invoke(RTSP_START, url),
stop: () => ipcRenderer.send(RTSP_STOP),
},
};
34 changes: 34 additions & 0 deletions src/main/rtspServer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { log } from '@/main/logger';
import { ipcMain } from 'electron';
import { execaNode, ExecaChildProcess } from 'execa';

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

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

ipcMain.handle('rtsp_start', (event, url: string) => {
const process = execaNode('script.ts');

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

return nextPort++;
});

ipcMain.on('rtsp_stop', (event, url: string) => {
const rtspProcess = rtspServers.get(url);

if (!rtspProcess) {
return;
}

rtspProcess.process.kill();
rtspServers.delete(url);
});
5 changes: 4 additions & 1 deletion src/renderer/components/Feed/Feeds/CameraFeed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +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';

interface Props {
feed: ICameraFeed;
Expand Down Expand Up @@ -145,6 +146,8 @@ const View: FC<Props> = ({ feed }) => {
/>
</QRFeed>
);
case CameraType.RTSP:
return <RTSPFeed />;
default:
return <TextFeed text="stream type not supported" />;
}
Expand All @@ -153,7 +156,7 @@ const View: FC<Props> = ({ feed }) => {
export const CameraFeed: FC<Props> = ({ feed }) => {
const [state] = useActor(rosService);
const connected =
state.matches('connected') || feed.camera.type === CameraType.WEBCAM;
!state.matches('connected') || feed.camera.type === CameraType.WEBCAM;
useEffect(() => {
log.debug('mounting camera', feed.camera.name);
return () => {
Expand Down
32 changes: 32 additions & 0 deletions src/renderer/components/Feed/Feeds/RTSPFeed/RTSPFeed.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React, { useEffect, useRef } from 'react';
import JSMpeg from '@cycjimmy/jsmpeg-player';
import { log } from '@/renderer/logger';

export const RTSPFeed = () => {
// Execute rtspServer node script with stream url as argument
const videoRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const startServer = async () => {
const port = (await window.preloadApi.rtsp.start('rtsp://')) as number;
log.info(`RTSP server started on port ${port}`);
};
startServer().catch((e) => log.error(e));
}, []);

// Create video element
useEffect(() => {
const videoWrapper = videoRef.current;
if (videoWrapper) {
new JSMpeg.VideoElement(videoWrapper, 'ws://localhost:9999', {
autoplay: true,
audio: false,
});
}
}, [videoRef]);

return (
<>
<div ref={videoRef} />
</>
);
};
Empty file.
1 change: 1 addition & 0 deletions src/renderer/store/modules/feed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export enum CameraType {
VP8 = 'vp8',
WEBCAM = 'webcam',
QR_CODE = 'qr_code',
RTSP = 'rtsp',
}

export type FeedType =
Expand Down
13 changes: 13 additions & 0 deletions src/renderer/types/jsmpeg.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
declare module '@cycjimmy/jsmpeg-player' {
class VideoElement {
constructor(
videoWrapper: HTMLDivElement,
url: string,
options: {
canvas?: HTMLCanvasElement;
autoplay?: boolean;
audio?: boolean;
}
);
}
}
13 changes: 13 additions & 0 deletions src/renderer/types/nodeRtspStream.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
declare module 'node-rtsp-stream' {
class Stream {
constructor(params: {
name: string;
streamUrl: string;
wsPort: number;
ffmpegOptions: {
'-stats': string;
'-r': number;
};
});
}
}

0 comments on commit 7cd97dc

Please sign in to comment.