feat: Implement hardware-accelerated HLS transcoding with granular controls, smart stereo downmixing, and robust support for MKV and 10-bit HEVC playback.

This commit is contained in:
Trevor Mears
2026-01-17 03:07:16 -08:00
parent efcf14a545
commit 2c34359b2c
11 changed files with 472 additions and 97 deletions
+8 -4
View File
@@ -112,11 +112,15 @@ function analyzeProbeResult(probeResult, url) {
}));
// Determine what processing is needed
// 1. Incompatible audio -> Transcode (highest priority)
const needsTranscode = !audioOk;
// 4. MKV files often cause OOM/decoding issues in browser fMP4 remux,
// so we force them to "needsTranscode" which uses HLS (more robust).
// The frontend will still use "copy" mode if codecs are compatible.
const isMkv = container.includes('matroska') || container.includes('webm') || url.endsWith('.mkv');
// 2. Compatible audio/video but incompatible container -> Remux
// This catches raw TS, MKV, AVI, etc. that have H.264/AAC but wrong container
// 1. Incompatible audio/video OR MKV -> Transcode (or HLS Copy)
const needsTranscode = !audioOk || !videoOk || isMkv;
// 2. Compatible audio/video but incompatible container (non-MKV) -> Remux (fMP4 pipe)
const needsRemux = !needsTranscode && (!containerOk || isRawTs);
const compatible = !needsTranscode && !needsRemux;
+6 -3
View File
@@ -34,9 +34,10 @@ router.get('/', async (req, res) => {
'-hide_banner',
'-loglevel', 'warning',
'-user_agent', userAgent,
// Low-latency startup: reduce probe/analyze time for faster first bytes
'-probesize', '32768',
'-analyzeduration', '500000', // 0.5 seconds - enough to detect audio
'-user_agent', userAgent,
// Standard probe size to handle complex containers (MKV) correctly
'-probesize', '5000000',
'-analyzeduration', '5000000',
// Error resilience: discard corrupt packets, generate timestamps, ignore DTS, no buffering
'-fflags', '+genpts+discardcorrupt+igndts+nobuffer',
// Ignore errors in stream and continue
@@ -58,6 +59,8 @@ router.get('/', async (req, res) => {
'-sn', '-dn',
// Copy streams without re-encoding
'-c', 'copy',
// Ensure extradata is correctly extracted/converted (fixes Annex B -> AVCC issues in Firefox)
'-bsf:v', 'dump_extra',
// NOTE: We intentionally do NOT use -bsf:a aac_adtstoasc here
// That filter only works for AAC audio and breaks AC3/EAC3/MP3.
// If AAC audio from MPEG-TS fails in MP4, use /api/transcode instead.
+4 -2
View File
@@ -29,7 +29,7 @@ transcodeSession.startCleanupInterval();
* Body: { url: string, seekOffset?: number }
*/
router.post('/session', async (req, res) => {
const { url, seekOffset } = req.body;
const { url, seekOffset, videoMode, videoCodec } = req.body;
if (!url) {
return res.status(400).json({ error: 'URL is required' });
@@ -46,7 +46,9 @@ router.post('/session', async (req, res) => {
seekOffset: seekOffset || 0,
hwEncoder: settings.hwEncoder || 'software',
maxResolution: settings.maxResolution || '1080p',
quality: settings.quality || 'medium'
quality: settings.quality || 'medium',
videoMode: videoMode, // 'copy' or 'encode'
videoCodec: videoCodec // 'h264', 'hevc', etc.
});
await session.start();