Skip to content

Commit 2cfc722

Browse files
committed
fix: enhance ffmpeg detection to support macOS and cross-platform installations
Fixes #13 - RTSP camera streaming failing on macOS due to ffmpeg not being detected. Problem: - Electron apps launched from Finder on macOS don't inherit user shell PATH - Homebrew installs ffmpeg to /opt/homebrew/bin (Apple Silicon) or /usr/local/bin (Intel Mac) - Current implementation only checks PATH, causing ffmpeg to not be found Solution: - Enhanced checkFfmpegAvailability() to check common installation paths across all platforms - Tries PATH first (backward compatible), then checks platform-specific locations: * macOS: Homebrew (Apple Silicon & Intel), MacPorts * Linux: apt/yum/pacman, Snap, Flatpak, manual installs * Windows: common Program Files locations - Provides helpful error messages with platform-specific installation instructions Impact: - macOS users with Homebrew-installed ffmpeg can now use RTSP cameras - Linux users with Snap/Flatpak installations are now supported - Better diagnostics when ffmpeg is not found - No breaking changes - fully backward compatible
1 parent b21ab7f commit 2cfc722

1 file changed

Lines changed: 71 additions & 19 deletions

File tree

src/services/RtspStreamService.ts

Lines changed: 71 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -136,28 +136,80 @@ export class RtspStreamService extends EventEmitter {
136136

137137
/**
138138
* Check if ffmpeg is available on the system
139+
* On macOS, Electron apps don't inherit user PATH, so check common install locations explicitly
140+
* Also checks common paths on Linux (Snap, Flatpak, etc.) and Windows
139141
*/
140142
private async checkFfmpegAvailability(): Promise<void> {
141-
try {
142-
const { stdout } = await execAsync('ffmpeg -version');
143-
const versionMatch = stdout.match(/ffmpeg version ([^\s]+)/);
144-
const version = versionMatch ? versionMatch[1] : 'unknown';
145-
146-
this.ffmpegStatus = {
147-
available: true,
148-
version
149-
};
150-
151-
console.log(`[RtspStreamService] ffmpeg found: version ${version}`);
152-
} catch (error) {
153-
const errorMessage = error instanceof Error ? error.message : String(error);
154-
this.ffmpegStatus = {
155-
available: false,
156-
error: errorMessage
157-
};
158-
159-
console.warn('[RtspStreamService] ffmpeg not found:', errorMessage);
143+
// Common ffmpeg installation paths across platforms
144+
// Order matters: try PATH first, then check platform-specific locations
145+
const ffmpegPaths = [
146+
'ffmpeg', // Try PATH first (works if launched from terminal or properly configured)
147+
148+
// ===== macOS =====
149+
// Homebrew (most common on macOS)
150+
'/opt/homebrew/bin/ffmpeg', // Homebrew on Apple Silicon (M1/M2/M3)
151+
'/usr/local/bin/ffmpeg', // Homebrew on Intel Mac
152+
'/opt/local/bin/ffmpeg', // MacPorts
153+
154+
// ===== Linux =====
155+
// Standard package manager locations (usually in PATH, but checking explicitly doesn't hurt)
156+
'/usr/bin/ffmpeg', // apt (Debian/Ubuntu), yum/dnf (Fedora/RHEL), pacman (Arch)
157+
158+
// Universal package managers (often not in Electron's PATH)
159+
'/snap/bin/ffmpeg', // Snap packages
160+
'/var/lib/flatpak/exports/bin/ffmpeg', // Flatpak system-wide
161+
'~/.local/share/flatpak/exports/bin/ffmpeg', // Flatpak user install
162+
163+
// Manual/compiled installations
164+
'/usr/local/bin/ffmpeg', // Common manual install location
165+
'~/bin/ffmpeg', // User home bin directory
166+
167+
// ===== Windows =====
168+
'C:\\ffmpeg\\bin\\ffmpeg.exe', // Common manual install
169+
'C:\\Program Files\\ffmpeg\\bin\\ffmpeg.exe', // Program Files install
170+
'C:\\Program Files (x86)\\ffmpeg\\bin\\ffmpeg.exe', // 32-bit on 64-bit system
171+
];
172+
173+
let lastError = '';
174+
175+
// Try each path in order
176+
for (const ffmpegPath of ffmpegPaths) {
177+
try {
178+
// Expand ~ to home directory if present
179+
const expandedPath = ffmpegPath.replace(/^~/, process.env.HOME || process.env.USERPROFILE || '');
180+
181+
// Quote the path to handle spaces
182+
const { stdout } = await execAsync(`"${expandedPath}" -version`);
183+
const versionMatch = stdout.match(/ffmpeg version ([^\s]+)/);
184+
const version = versionMatch ? versionMatch[1] : 'unknown';
185+
186+
this.ffmpegStatus = {
187+
available: true,
188+
version
189+
};
190+
191+
console.log(`[RtspStreamService] ffmpeg found at ${expandedPath}: version ${version}`);
192+
return; // Success! Exit the function
193+
} catch (error) {
194+
lastError = error instanceof Error ? error.message : String(error);
195+
// Continue to next path
196+
}
160197
}
198+
199+
// If we get here, ffmpeg wasn't found in any location
200+
this.ffmpegStatus = {
201+
available: false,
202+
error: `ffmpeg not found in any common location. Last error: ${lastError}`
203+
};
204+
205+
console.warn('[RtspStreamService] ffmpeg not found in any location');
206+
console.warn('[RtspStreamService] Searched paths:', ffmpegPaths);
207+
console.warn('[RtspStreamService] Install ffmpeg to enable RTSP camera viewing:');
208+
console.warn('[RtspStreamService] - macOS: brew install ffmpeg');
209+
console.warn('[RtspStreamService] - Ubuntu/Debian: sudo apt install ffmpeg');
210+
console.warn('[RtspStreamService] - Fedora/RHEL: sudo dnf install ffmpeg');
211+
console.warn('[RtspStreamService] - Arch: sudo pacman -S ffmpeg');
212+
console.warn('[RtspStreamService] - Windows: Download from ffmpeg.org');
161213
}
162214

163215
// ============================================================================

0 commit comments

Comments
 (0)