more support for dual tuners

This commit is contained in:
david 2025-04-01 18:07:45 -07:00
parent b98717201e
commit d1ad73fbf0
3 changed files with 53 additions and 37 deletions

View File

@ -2,13 +2,11 @@ import HttpServer from './http';
import TVWebSocket from './ws';
import Zap, { IZap } from './zap';
import * as readline from 'readline';
const HTTP_PORT = process.env.HTTP_PORT ? parseInt(process.env.HTTP_PORT, 10) : 8080;
const WS_PORT = process.env.WS_PORT ? parseInt(process.env.WS_PORT, 10) : 3001;
const STATIC_ROOT = process.cwd() + "/dist/static";
const TV_DEV_0 = process.env.TV_DEV_0 ?? '/dev/dvb/adapter0/dvr0'
const TV_DEV_1 = process.env.TV_DEV_1 ? '/dev/dvb/adapter0/dvr1' : null;
const TV_DEV_1 = process.env.TV_DEV_1 ?? '/dev/dvb/adapter0/dvr1';
const zap = new Zap();
@ -26,7 +24,8 @@ const getChannels = () =>
zap.getChannels();
const httpServer = new HttpServer(HTTP_PORT, STATIC_ROOT, tune, getChannels);
const tvWebSocket = new TVWebSocket(WS_PORT);
const tvWebSocket0 = new TVWebSocket(WS_PORT, TV_DEV_0);
// const tvWebSocket1 = new TVWebSocket(WS_PORT + 1, TV_DEV_1);
httpServer.start();

View File

@ -1,50 +1,67 @@
const host = window.location.hostname
const ws = new WebSocket(`ws://${host}:3001`);
const pc = new RTCPeerConnection({ iceServers: [] });
const ws0 = new WebSocket(`ws://${host}:3001`);
const ws1 = new WebSocket(`ws://${host}:3002`);
const pc0 = new RTCPeerConnection({ iceServers: [] });
const pc1 = new RTCPeerConnection({ iceServers: [] });
const video0 = document.getElementById('video0') as HTMLVideoElement;
const video1 = document.getElementById('video1') as HTMLVideoElement;
pc.onconnectionstatechange = (event) => {
console.log("onconnectionstatechange ", event)
}
pc.ondatachannel = (event) => {
console.log("ondatachannel ", event)
}
pc.ontrack = (event) => {
// 0
pc0.ontrack = (event) => {
console.log("Received track event", event.streams);
video0.srcObject = event.streams[0];
};
pc0.onicecandidate = ({ candidate }) => {
if (candidate) {
ws0.send(JSON.stringify({ type: 'ice-candidate', data: candidate }));
}
};
ws0.onopen = async () => {
pc0.addTransceiver('video', { direction: 'recvonly' });
pc0.addTransceiver('audio', { direction: 'recvonly' })
const offer = await pc0.createOffer();
await pc0.setLocalDescription(offer);
ws0.send(JSON.stringify({ type: 'offer', data: offer }));
}
ws0.onmessage = async (message) => {
const msg = JSON.parse(message.data);
if (msg.type === 'answer') {
await pc0.setRemoteDescription(msg.data);
}
else if (msg.type === 'ice-candidate') {
await pc0.addIceCandidate(msg.data);
}
};
// 1
pc1.ontrack = (event) => {
console.log("Received track event", event.streams);
video1.srcObject = event.streams[0];
};
pc.onicecandidate = ({ candidate }) => {
pc1.onicecandidate = ({ candidate }) => {
if (candidate) {
ws.send(JSON.stringify({ type: 'ice-candidate', data: candidate })); // Use 'candidate' instead of 'ice-candidate'
ws1.send(JSON.stringify({ type: 'ice-candidate', data: candidate }));
}
};
pc.onicegatheringstatechange = () => {
// console.log('ICE state:', pc.iceGatheringState);
};
ws.onopen = async () => {
pc.addTransceiver('video', { direction: 'recvonly' });
pc.addTransceiver('audio', { direction: 'recvonly' })
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
ws.send(JSON.stringify({ type: 'offer', data: offer }));
ws1.onopen = async () => {
pc1.addTransceiver('video', { direction: 'recvonly' });
pc1.addTransceiver('audio', { direction: 'recvonly' })
const offer = await pc1.createOffer();
await pc1.setLocalDescription(offer);
ws1.send(JSON.stringify({ type: 'offer', data: offer }));
}
ws.onmessage = async (message) => {
ws1.onmessage = async (message) => {
const msg = JSON.parse(message.data);
if (msg.type === 'answer') {
await pc.setRemoteDescription(msg.data);
await pc1.setRemoteDescription(msg.data);
}
else if (msg.type === 'ice-candidate') {
await pc.addIceCandidate(msg.data);
await pc1.addIceCandidate(msg.data);
}
};
;

View File

@ -3,14 +3,14 @@ import { ChildProcessWithoutNullStreams, spawn } from 'child_process';
import * as ws from 'ws';
// Constants
const VIDEO_DEVICE = '/dev/dvb/adapter0/dvr0'; // Video source device
const WIDTH = 640; // Video width
const HEIGHT = 480; // Video height
const FRAME_SIZE = WIDTH * HEIGHT * 1.5; // YUV420p frame size (460800 bytes)
export default class TVWebSocket {
public constructor(port: number) {
videoDevice: string;
public constructor(port: number, videoDevice) {
this.videoDevice = videoDevice
const ffmpegProcess = this.startFFmpeg();
const videoTrack = this.createVideoTrack(ffmpegProcess);
const audioTrack = this.createAudioTrack(ffmpegProcess);
@ -62,7 +62,7 @@ export default class TVWebSocket {
startFFmpeg = (): ChildProcessWithoutNullStreams => {
const p = spawn('ffmpeg', [
'-loglevel', 'debug',
'-i', VIDEO_DEVICE,
'-i', this.videoDevice,
// Video
'-map', '0:v:0',