| "@angular/router": "^16.2.0", | "@angular/router": "^16.2.0", | ||||
| "@asymmetrik/ngx-leaflet": "^16.0.1", | "@asymmetrik/ngx-leaflet": "^16.0.1", | ||||
| "@asymmetrik/ngx-leaflet-markercluster": "^16.0.0", | "@asymmetrik/ngx-leaflet-markercluster": "^16.0.0", | ||||
| "express": "^4.19.2", | |||||
| "express-ws": "^5.0.2", | |||||
| "leaflet": "^1.9.4", | "leaflet": "^1.9.4", | ||||
| "rtsp-relay": "^1.8.0", | |||||
| "rtsp-relay": "^1.9.0", | |||||
| "rxjs": "~7.8.0", | "rxjs": "~7.8.0", | ||||
| "rxjs-websockets": "^9.0.0", | "rxjs-websockets": "^9.0.0", | ||||
| "tslib": "^2.3.0", | "tslib": "^2.3.0", |
| import {APP_INITIALIZER, NgModule} from '@angular/core'; | |||||
| import { NgModule} from '@angular/core'; | |||||
| import { BrowserModule } from '@angular/platform-browser'; | import { BrowserModule } from '@angular/platform-browser'; | ||||
| import { AppRoutingModule } from './app-routing.module'; | import { AppRoutingModule } from './app-routing.module'; | ||||
| import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; | ||||
| import {SharedModule} from "./shared/shared.module"; | import {SharedModule} from "./shared/shared.module"; | ||||
| import {HttpClientModule} from "@angular/common/http"; | import {HttpClientModule} from "@angular/common/http"; | ||||
| import {AppInitService} from "./shared/services/app-init.service"; | |||||
| export function appInitializerFactory(appInitializerService: AppInitService): () => Promise<any> { | |||||
| return () => appInitializerService.initializeApp(); | |||||
| } | |||||
| @NgModule({ | @NgModule({ | ||||
| SharedModule, | SharedModule, | ||||
| HttpClientModule | HttpClientModule | ||||
| ], | ], | ||||
| providers: [ | |||||
| { | |||||
| provide: APP_INITIALIZER, | |||||
| useFactory: appInitializerFactory, | |||||
| deps: [AppInitService], | |||||
| multi: true | |||||
| } | |||||
| ], | |||||
| providers: [], | |||||
| bootstrap: [AppComponent] | bootstrap: [AppComponent] | ||||
| }) | }) | ||||
| export class AppModule { } | export class AppModule { } |
| <h3>Camera Stream</h3> | <h3>Camera Stream</h3> | ||||
| <!--<canvas #videoPlayer></canvas>--> | |||||
| <canvas #videoPlayer></canvas> | |||||
| <video width="700" controls style="display: block; margin-left: auto; margin-right: auto"> | |||||
| <source src="assets/video/video.mp4" type="video/mp4"> | |||||
| </video> | |||||
| <!--<video width="700" controls style="display: block; margin-left: auto; margin-right: auto">--> | |||||
| <!-- <source src="assets/video/video.mp4" type="video/mp4">--> | |||||
| <!--</video>--> |
| export class CameraStreamComponent implements OnInit, AfterViewInit{ | export class CameraStreamComponent implements OnInit, AfterViewInit{ | ||||
| player?: Player; | player?: Player; | ||||
| // @ViewChild('videoPlayer') | |||||
| // videoPlayer?: ElementRef<HTMLCanvasElement>; | |||||
| @ViewChild('videoPlayer') | |||||
| videoPlayer?: ElementRef<HTMLCanvasElement>; | |||||
| ngOnInit() { | ngOnInit() { | ||||
| } | } | ||||
| constructor(private el: ElementRef, private renderer: Renderer2) {} | constructor(private el: ElementRef, private renderer: Renderer2) {} | ||||
| async ngAfterViewInit() { | async ngAfterViewInit() { | ||||
| // this.player = await loadPlayer({ | |||||
| // url: 'ws://localhost:8081', | |||||
| // canvas: this.videoPlayer!.nativeElement, | |||||
| // | |||||
| // onDisconnect: () => console.log('Connection lost!'), | |||||
| // }); | |||||
| // | |||||
| // console.log('Connected!', this.player); | |||||
| this.player = await loadPlayer({ | |||||
| url: 'ws://localhost:8080/stream', | |||||
| canvas: this.videoPlayer!.nativeElement, | |||||
| onDisconnect: () => console.log('Connection lost!'), | |||||
| }); | |||||
| console.log('Connected!', this.player); | |||||
| } | } | ||||
| import { Injectable } from '@angular/core'; | |||||
| import {config} from "../../../assets/config/config"; | |||||
| @Injectable({ | |||||
| providedIn: 'root' | |||||
| }) | |||||
| export class AppInitService { | |||||
| appConfig = config; | |||||
| constructor() {} | |||||
| initializeApp(): Promise<any> { | |||||
| return new Promise((resolve, reject) => { | |||||
| console.log('Loaded:', this.appConfig); | |||||
| resolve(true); | |||||
| }); | |||||
| } | |||||
| } |
| const WebSocket = require('ws'); | |||||
| const http = require('http'); | |||||
| const express = require('express'); | |||||
| const expressWs = require('express-ws'); | |||||
| const app = express(); | |||||
| expressWs(app); | |||||
| const server = http.createServer(); | |||||
| const { proxy, scriptUrl } = require('rtsp-relay')(app); | |||||
| const wss = new WebSocket.Server({ server }); | |||||
| const port = 8080; | |||||
| wss.on('connection', function connection(ws) { | |||||
| app.ws('/api/stream/:cameraIP', (ws, req) => | |||||
| proxy({ | |||||
| url: `rtsp://${req.params.cameraIP}:554/feed`, | |||||
| })(ws), | |||||
| ); | |||||
| ws.on('message', function incoming(message) { | |||||
| // Log tin nhắn nhận được | |||||
| console.log('Nhận được: %s', message); | |||||
| }); | |||||
| // Bắt sự kiện khi kết nối đóng | |||||
| ws.on('close', function close() { | |||||
| console.log('Client đã ngắt kết nối'); | |||||
| }); | |||||
| const handler = proxy({ | |||||
| url: 'rtsp://admin:hd543211@@xuanphuong32.dyndns.org:8001/cam/realmonitor?channel=7&subtype=0', | |||||
| verbose: false, | |||||
| }); | }); | ||||
| server.listen(8081, function listening() { | |||||
| console.log('WebSocket server đang lắng nghe trên cổng 8081'); | |||||
| app.ws('/stream', handler); | |||||
| const server = app.listen(port, () => { | |||||
| console.log(`RTSP Relay Server is listening on port ${port}`); | |||||
| }); | }); | ||||
| ffmpeg -rtsp_transport tcp -i rtsp://admin:hd543211@@xuanphuong32.dyndns.org:8001/cam/realmonitor?channel=7&subtype=0 ^ | |||||
| -f mpegts -codec:v mpeg1video -s 640x480 -b:v 800k -r 30 ^ | |||||
| -codec:a mp2 -b:a 128k ^ | |||||
| http://localhost:8081 |