35293dec83
Implemented comprehensive BUBBLE protocol support for Chinese NVR/DVR cameras (ZOSI, SANNCE, ANNKE, FLOUREON, XMeye). This proprietary protocol requires HTTP with embedded credentials and special handling.
Changes:
- Added BUBBLE entries to brand databases with main/sub stream support
- Extended URL placeholder system to support {channel} syntax
- Implemented BUBBLE-specific stream generation with credential embedding
- Added BUBBLE stream detection via Content-Type: video/bubble
- Updated Frigate/Go2RTC generators to convert BUBBLE URLs to bubble:// format
- Added BUBBLE patterns to popular stream database
Technical details:
- BUBBLE uses HTTP protocol with credentials in URL (bubble://user:pass@host:port/path)
- Supports dual streams: stream=0 (main) and stream=1 (sub)
- Requires video=copy parameter for optimal performance in go2rtc
- Detection prioritized before generic HTTP checks to ensure correct identification
128 lines
4.0 KiB
JavaScript
128 lines
4.0 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);
|
|
}
|
|
|
|
// Handle BUBBLE protocol
|
|
if (stream.type === 'BUBBLE') {
|
|
return this.generateBubbleSource(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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate BUBBLE source
|
|
* Converts HTTP BUBBLE endpoint to bubble:// format for go2rtc
|
|
* Format: bubble://user:pass@host:port/bubble/live?ch=X&stream=Y#video=copy
|
|
*/
|
|
static generateBubbleSource(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';
|
|
const path = urlObj.pathname + urlObj.search;
|
|
return `bubble://${username}:${password}@${host}:${port}${path}#video=copy`;
|
|
} catch (e) {
|
|
return stream.url;
|
|
}
|
|
}
|
|
}
|