From d1f26ede2f20f818af8530aa176cc3c900b66c8e Mon Sep 17 00:00:00 2001 From: david Date: Mon, 21 Apr 2025 15:55:53 -0700 Subject: [PATCH] data in websocket --- src/http.ts | 7 +----- src/server.ts | 15 ++++++------ src/static/index.html | 19 +++++++-------- src/static/js/video.ts | 52 +++++++++++++++++++++++++++++++++++++-- src/ws.ts | 28 +++++++++++++++++++-- src/zap.ts | 55 ++++++++++++++++++------------------------ 6 files changed, 117 insertions(+), 59 deletions(-) diff --git a/src/http.ts b/src/http.ts index 4f41483..2428586 100644 --- a/src/http.ts +++ b/src/http.ts @@ -8,7 +8,7 @@ export default class HttpServer { private httpServer: http.Server; private port: number; private root: string; - public constructor(port: number, root: string, tune: (ch: string, adp?: number) => void, getChannels: ()=>string[], getSignal: (adapter:number)=>object) { + public constructor(port: number, root: string, tune: (ch: string, adp?: number) => void, getChannels: ()=>string[]) { this.port = port; this.root = root; this.httpServer = http.createServer((req, res) => { @@ -27,11 +27,6 @@ export default class HttpServer { body = JSON.stringify(getChannels()); status = 200; break; - case "signal": - const adapter = parseInt(url.searchParams.get('adapter')); - body = JSON.stringify(getSignal(adapter)); - status = 200; - break; } break; diff --git a/src/server.ts b/src/server.ts index 17cecfc..9ab196a 100644 --- a/src/server.ts +++ b/src/server.ts @@ -20,16 +20,17 @@ const tune = (reqChannel: string, reqAdapter?: number) => { }); } -const getChannels = () => - zap.getChannels(); +const getChannels = zap.getChannels.bind(zap) +const getMetaData0 = zap.getMetaData.bind(zap,0) +const getMetaData1 = zap.getMetaData.bind(zap,1) -const getSignal = (adapter: number) => - zap.getSignal(adapter) -const httpServer = new HttpServer(HTTP_PORT, STATIC_ROOT, tune, getChannels, getSignal); -const tvWebSocket0 = new TVWebSocket(WS_PORT, TV_DEV_0); -const tvWebSocket1 = new TVWebSocket(WS_PORT + 1, TV_DEV_1); +const httpServer = new HttpServer(HTTP_PORT, STATIC_ROOT, tune, getChannels); +const tvWebSocket0 = new TVWebSocket(WS_PORT, TV_DEV_0, getMetaData0); +const tvWebSocket1 = new TVWebSocket(WS_PORT + 1, TV_DEV_1, getMetaData1); httpServer.start(); +tune('ION',0); +tune("KGW",1); process.stdin.setEncoding("utf8"); diff --git a/src/static/index.html b/src/static/index.html index 6675c38..26cd3cb 100644 --- a/src/static/index.html +++ b/src/static/index.html @@ -2,7 +2,7 @@ - WebRTC Stream + PDX Airwave TV @@ -11,24 +11,23 @@
-

Video streams

-

WebRTC

+

Portland Airwave Television

+
- +

N/A

+
-

N/A

- +

- +

N/A

+
-

N/A

- - +

diff --git a/src/static/js/video.ts b/src/static/js/video.ts index c6fc4ba..6abd54f 100644 --- a/src/static/js/video.ts +++ b/src/static/js/video.ts @@ -13,10 +13,40 @@ const config = { } ] }; + + +interface IData { + channel: string, + signal: number, + cn: number +} + const pc0 = new RTCPeerConnection(config); const pc1 = new RTCPeerConnection(config); -const video0 = document.getElementById('video0') as HTMLVideoElement; -const video1 = document.getElementById('video1') as HTMLVideoElement; +const video0 = document.getElementById('video-0') as HTMLVideoElement; +const video1 = document.getElementById('video-1') as HTMLVideoElement; +const station0 = document.getElementById('station-0') as HTMLHeadElement; +const station1 = document.getElementById('station-1') as HTMLHeadElement; +const signal0 = document.getElementById('signal-0') as HTMLHeadElement; +const signal1 = document.getElementById('signal-1') as HTMLHeadElement; + +// 0 +const dataChannel0 = pc0.createDataChannel('meta'); +console.log("📡 Data channel created by client"); + +dataChannel0.onopen = () => { + console.log('📬 Client: Data channel opened'); +}; + +dataChannel0.onmessage = (event) => { + const json = JSON.parse(event.data) as IData; + station0.textContent = json.channel; + signal0.textContent = `Signal: ${json.signal}\tC/N: ${json.cn}` +}; + +dataChannel0.onclose = () => { + console.log("📴 Client: Data channel closed"); +}; // 0 pc0.ontrack = (event) => { @@ -49,6 +79,24 @@ ws0.onmessage = async (message) => { }; // 1 +const dataChannel1 = pc1.createDataChannel('meta'); +console.log("📡 Data channel created by client"); + +dataChannel1.onopen = () => { + console.log('📬 Client: Data channel opened'); +}; + +dataChannel1.onmessage = (event) => { + const json = JSON.parse(event.data) as IData; + station1.textContent = json.channel; + signal1.textContent = `Signal: ${json.signal}\tC/N: ${json.cn}` +}; + +dataChannel1.onclose = () => { + console.log("📴 Client: Data channel closed"); +}; + + pc1.ontrack = (event) => { console.log("Received track event", event.streams); video1.srcObject = event.streams[0]; diff --git a/src/ws.ts b/src/ws.ts index 95afcab..db3d4b7 100644 --- a/src/ws.ts +++ b/src/ws.ts @@ -9,7 +9,7 @@ const FRAME_SIZE = WIDTH * HEIGHT * 1.5; // YUV420p frame size (460800 bytes) export default class TVWebSocket { videoDevice: string; - public constructor(port: number, videoDevice) { + public constructor(port: number, videoDevice, getMetaData: ()=>any) { this.videoDevice = videoDevice const ffmpegProcess = this.startFFmpeg(); const videoTrack = this.createVideoTrack(ffmpegProcess); @@ -25,7 +25,29 @@ export default class TVWebSocket { wss.on('connection', async (ws: ws.WebSocket) => { const peerConnection: RTCPeerConnection = this.createPeerConnection(videoTrack, audioTrack); + peerConnection.ondatachannel = (event) => { + const dataChannel = event.channel; // This is the data channel created by the client + dataChannel.onopen = () => { + console.log('📬 Server: Data channel opened'); + // Now you can send data through the channel + setInterval(() => { + if (dataChannel.readyState === 'open') { + const metaData = getMetaData(); // Example function to fetch data + dataChannel.send(JSON.stringify(metaData)); // Send data to the client + } + + }, 1000); + }; + + dataChannel.onmessage = (event) => { + console.log("📦 Server received message:", event.data); + }; + + dataChannel.onclose = () => { + console.log("📴 Server: Data channel closed"); + }; + } ws.on('message', async (message: Buffer) => { const { type, data } = JSON.parse(message.toString()); @@ -71,10 +93,12 @@ export default class TVWebSocket { // Video '-map', '0:v:0', - '-vf', `scale=${WIDTH}:${HEIGHT}`, + '-framerate', '24', + '-vf', `scale=${WIDTH}:${HEIGHT}:flags=fast_bilinear`, '-vcodec', 'rawvideo', '-pix_fmt', 'yuv420p', '-f', 'rawvideo', + '-threads', '1', //quality '-fflags', '+discardcorrupt', diff --git a/src/zap.ts b/src/zap.ts index 11397b0..dc9f251 100644 --- a/src/zap.ts +++ b/src/zap.ts @@ -5,10 +5,10 @@ export interface IZap { process: ChildProcessWithoutNullStreams | null, channel: string, adapter: 0 | 1 - strength: { - signal: string; - cn: string; - }, + + signal: string; + cn: string; + } export default class Zap { @@ -17,27 +17,27 @@ export default class Zap { private channelNameList: string[]; private fileName: string; - private regex = /Signal=\s*(-?\d+(\.\d+)?dBm)\s+C\/N=\s*(\d+(\.\d+)?dB)/; + private regex = /Signal=\s*(-?\d+(\.\d+)?dBm)\s+C\/N=\s*(\d+(\.\d+)?dB)/; public constructor(fileName = "dvb_channel.conf", channel = "ION") { const zap0: IZap = { process: null, - channel, + channel: "None", adapter: 0, - strength: { - signal: "None", - cn: "None" - } + + signal: "None", + cn: "None" + } const zap1: IZap = { process: null, - channel, + channel: "None", adapter: 1, - strength: { - signal: "None", - cn: "None" - } + + signal: "None", + cn: "None" + } this.zap0 = zap0; this.zap1 = zap1; @@ -53,19 +53,12 @@ export default class Zap { return this.channelNameList; } - public getSignal(adapter: number) { - if(adapter == 0){ - return this.zap0.strength; - } - else if (adapter == 1){ - return this.zap1.strength; - } - else { - return {signal: 'N/A', cn: 'N/A'} - } + public getMetaData(adapter: 0 | 1) { + const { channel, signal, cn } = adapter === 0 ? this.zap0 : this.zap1 + return { channel, signal, cn } } - private nextChannel(channel: string) :string { + private nextChannel(channel: string): string { const size = this.channelNameList.length; const currentIndex = this.channelNameList.indexOf(channel); if (currentIndex >= 0) { @@ -76,11 +69,11 @@ export default class Zap { } } - private mod(n: number, m: number) :number{ + private mod(n: number, m: number): number { return ((n % m) + m) % m; } - private previousChannel(channel: string):string { + private previousChannel(channel: string): string { const size = this.channelNameList.length; const currentIndex = this.channelNameList.indexOf(channel); if (currentIndex >= 0) { @@ -138,10 +131,8 @@ export default class Zap { clearTimeout(lockTimer); const match = output.match(this.regex); zap.channel = verifiedChannel; - zap.strength = { - signal: match[1], - cn: match[3] - } + zap.signal = match[1], + zap.cn = match[3] resolve(zap); }