Skip to content

Commit

Permalink
Finished streaming single screen using HLS and reduced latency to 10sec
Browse files Browse the repository at this point in the history
  • Loading branch information
nabil-nablotech committed Jan 13, 2024
1 parent 080c316 commit 4674a31
Show file tree
Hide file tree
Showing 16 changed files with 2,664 additions and 98 deletions.
6 changes: 2 additions & 4 deletions Backend/src/app/frontend/frontend.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,8 @@ export class FrontendController {
example: 'some.ts',
})
@Get('/hls/:filename')
async getHLSFiles(
@Param('filename') filename: string,
) {
const id = filename.substring(0,filename.indexOf("_"));
async getHLSFiles(@Param('filename') filename: string) {
const id = filename.substring(0, filename.indexOf('_'));
const filePath = `${__dirname}\\..\\..\\${process.env.HLS_OUTPUT_DIRECTORY}\\${id}\\${filename}`;
const file = createReadStream(filePath);
return new StreamableFile(file);
Expand Down
62 changes: 62 additions & 0 deletions Backend/src/cameraStream/UDPStream.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/* eslint-disable padded-blocks */
import { Server } from 'socket.io';
import type { Camera } from './cameraData';
import { cameraData } from './cameraData';
const { spawn } = require('child_process');
const fs = require('fs');

function encodeRTSPtoMpegts(camera: Camera, serverSocketInstance: Server) {
const ffmpegCommand = [
'-rtsp_transport',
'udp',
'-i',
camera.rtspUrl,
'-c:v',
'copy',
'-an',
'-f',
'mpegts',
'-',
];

// Spawn FFmpeg process
const ffmpegProcess = spawn('ffmpeg', ffmpegCommand);

// Event listeners for process output
ffmpegProcess.stdout.on('data', (data) => {
console.log(`streaming`);
serverSocketInstance.to('clients').emit('stream', { id: camera.id, data });
});

ffmpegProcess.stderr.on('data', (data) => {
console.error(`stderr: ${data}`);
});

ffmpegProcess.on('close', (code) => {
console.log(`FFmpeg process closed with code ${code}`);
encodeRTSPtoMpegts(camera, serverSocketInstance);
});

ffmpegProcess.on('error', (err) => {
console.error(`Error executing FFmpeg: ${err}`);
});
}

// Periodically check the RTSP streams and restart transcoding for each
function periodicCheck(serverSocketInstance: Server) {
console.log('Checking RTSP stream status...');
cameraData.forEach((camera) =>
encodeRTSPtoMpegts(camera, serverSocketInstance),
);
}

// Run once the application is run
export function initiateCameraStream(serverSocketInstance: Server) {
// Run the periodic check every 10 minutes (adjust as needed)
setInterval(() => periodicCheck(serverSocketInstance), 10 * 60 * 1000);

// Initial transcoding for each RTSP stream
cameraData.forEach((camera) =>
encodeRTSPtoMpegts(camera, serverSocketInstance),
);
}
17 changes: 14 additions & 3 deletions Backend/src/cameraStream/cameraStream.gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import {
MessageBody,
OnGatewayConnection,
WsException,
WebSocketServer,
} from '@nestjs/websockets';
import { Socket } from 'socket.io';
import { Socket, Server } from 'socket.io';
import { AuthGuard } from '../auth/auth.guard';
import {
Catch,
Expand All @@ -17,6 +18,7 @@ import {
UseGuards,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { initiateCameraStream } from './UDPStream';

@Catch(WsException, HttpException)
export class WsExceptionFilter implements WsExceptionFilter {
Expand All @@ -28,17 +30,25 @@ export class WsExceptionFilter implements WsExceptionFilter {
type Message = { id: number; data: Buffer };

@WebSocketGateway({
transports: ['websocket'],
transports: ['websocket', 'polling'],
cors: {
origin: '*',
methods: ['GET', 'POST'],
transports: ['websocket', 'polling'],
credentials: true,
},
namespace: '/',
allowEIO3: true,
})
@UseGuards(AuthGuard)
@UseFilters(WsExceptionFilter)
export class CameraStreamGateway implements OnGatewayConnection {
@WebSocketServer() io: Server;
constructor(private readonly jwtService: JwtService) {}

afterInit() {
// initiateCameraStream(this.io);
}

handleConnection(@ConnectedSocket() client: Socket) {
try {
new AuthGuard(this.jwtService).checkToken(client.handshake.headers);
Expand All @@ -47,6 +57,7 @@ export class CameraStreamGateway implements OnGatewayConnection {
return;
}
client.join('clients');
console.log(this.io.adapter);
}

@SubscribeMessage('message')
Expand Down
24 changes: 18 additions & 6 deletions Backend/src/cameraStream/cameraStream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,34 @@ function transcodeRTSPtoHLS(camera: Camera) {
);

const ffmpegCommand = [
'-rtsp_transport',
'udp',
'-i',
camera.rtspUrl,
'-c:v',
'libx264',
'-c:a',
'aac',
'-b:a',
'128000',
'-g',
'2',
'-keyint_min',
'2',
'-ac',
'2',
'-s',
'854x480',
'-b:v',
'800000',
'-hls_time',
'3',
'2',
'-hls_list_size',
'15',
'10',
'-start_number',
'1',
'-hls_flags',
'delete_segments',
'-strict',
'experimental',
'-threads',
'8',
'-preset',
'ultrafast',
`${__dirname}\\..\\${process.env.HLS_OUTPUT_DIRECTORY}\\${camera.id}\\${camera.id}_${process.env.HLS_FILE_NAME}`,
Expand Down
4 changes: 3 additions & 1 deletion Backend/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { ValidationPipe } from '@nestjs/common';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { IoAdapter } from '@nestjs/platform-socket.io';
import { initiateCameraStream } from './cameraStream/cameraStream';
const dgram = require('dgram');

async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
Expand Down Expand Up @@ -42,7 +44,7 @@ async function bootstrap() {
);

initiateCameraStream();

}

bootstrap();

Loading

0 comments on commit 4674a31

Please sign in to comment.