7fd1d78ffa
Features: - Optional sub-stream selection from already discovered streams - No additional scanning required - reuse existing results - UI: "Add Sub Stream" button to select secondary stream - UI: "Remove Sub Stream" button to clear selection - Smart stream routing in Frigate configs - Go2RTC: generates _main and _sub stream names - Frigate: detect on sub (CPU efficient), record on main (quality) - Frigate: auto-detection of stream resolution - Object detection: person, car, cat, dog - Motion-based recording by default - Live view streams configuration - Support for any resolution: HD, 4K, 8K+ - Comprehensive documentation with examples
104 lines
3.2 KiB
JavaScript
104 lines
3.2 KiB
JavaScript
/**
|
|
* Go2RTC Configuration Generator
|
|
* Generates proper go2rtc YAML configs based on stream type
|
|
* Following go2rtc documentation and best practices
|
|
*/
|
|
export class Go2RTCGenerator {
|
|
/**
|
|
* Generate go2rtc config for streams (main + optional sub)
|
|
* @param {Object} mainStream - Main stream object with type, protocol, and url
|
|
* @param {Object} subStream - Optional sub stream object
|
|
* @returns {string} YAML configuration string
|
|
*/
|
|
static generate(mainStream, subStream = null) {
|
|
const configs = [];
|
|
configs.push('streams:');
|
|
|
|
// Generate main stream config
|
|
const mainStreamName = this.generateStreamName(mainStream, 'main');
|
|
const mainSource = this.generateSource(mainStream);
|
|
configs.push(` '${mainStreamName}':`);
|
|
configs.push(` - ${mainSource}`);
|
|
|
|
// Generate sub stream config if provided
|
|
if (subStream) {
|
|
configs.push('');
|
|
const subStreamName = this.generateStreamName(subStream, 'sub');
|
|
const subSource = this.generateSource(subStream);
|
|
configs.push(` '${subStreamName}':`);
|
|
configs.push(` - ${subSource}`);
|
|
}
|
|
|
|
return configs.join('\n');
|
|
}
|
|
|
|
/**
|
|
* Generate stream name from IP address with suffix
|
|
* Format: "192_168_1_100_main" or "192_168_1_100_sub"
|
|
*/
|
|
static generateStreamName(stream, suffix) {
|
|
try {
|
|
const urlObj = new URL(stream.url);
|
|
const ip = urlObj.hostname.replace(/\./g, '_').replace(/:/g, '_');
|
|
return `${ip}_${suffix}`;
|
|
} catch (e) {
|
|
return `camera_stream_${suffix}`;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate source configuration based on stream type
|
|
*/
|
|
static generateSource(stream) {
|
|
// Handle JPEG snapshots with special exec:ffmpeg conversion
|
|
if (stream.type === 'JPEG') {
|
|
return this.generateJPEGSource(stream);
|
|
}
|
|
|
|
// Handle ONVIF
|
|
if (stream.type === 'ONVIF') {
|
|
return this.generateONVIFSource(stream);
|
|
}
|
|
|
|
// For all other types: use direct URL
|
|
return stream.url;
|
|
}
|
|
|
|
/**
|
|
* Generate JPEG snapshot conversion using exec:ffmpeg
|
|
* Converts static JPEG to RTSP stream with H264 encoding
|
|
*/
|
|
static generateJPEGSource(stream) {
|
|
return [
|
|
'exec:ffmpeg',
|
|
'-loglevel quiet',
|
|
'-f image2',
|
|
'-loop 1',
|
|
'-framerate 10',
|
|
`-i ${stream.url}`,
|
|
'-c:v libx264',
|
|
'-preset ultrafast',
|
|
'-tune zerolatency',
|
|
'-g 20',
|
|
'-f rtsp {output}'
|
|
].join(' ');
|
|
}
|
|
|
|
/**
|
|
* Generate ONVIF source
|
|
* Converts HTTP device service endpoint to onvif:// format
|
|
*/
|
|
static generateONVIFSource(stream) {
|
|
try {
|
|
const urlObj = new URL(stream.url);
|
|
const username = urlObj.username || 'admin';
|
|
const password = urlObj.password || '';
|
|
const host = urlObj.hostname;
|
|
const port = urlObj.port || '80';
|
|
return `onvif://${username}:${password}@${host}:${port}`;
|
|
} catch (e) {
|
|
return stream.url;
|
|
}
|
|
}
|
|
}
|