feat: add Plex-style adaptive transcoding with bitrate limits and hardware fallback
This commit is contained in:
@ -87,6 +87,22 @@ function analyzeProbeResult(probeResult, url) {
|
||||
const videoCodec = videoStream?.codec_name?.toLowerCase() || 'unknown';
|
||||
const audioCodec = audioStream?.codec_name?.toLowerCase() || 'unknown';
|
||||
const container = format.format_name?.toLowerCase() || 'unknown';
|
||||
const toPositiveNumber = (value) => {
|
||||
const parsed = Number(value);
|
||||
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
||||
};
|
||||
const toKbps = (value) => {
|
||||
const bitrate = toPositiveNumber(value);
|
||||
return bitrate === null ? null : Math.round(bitrate / 1000);
|
||||
};
|
||||
const durationSeconds = toPositiveNumber(format.duration) ?? toPositiveNumber(videoStream?.duration) ?? toPositiveNumber(audioStream?.duration);
|
||||
const videoBitrateKbps = toKbps(videoStream?.bit_rate);
|
||||
const audioBitrateKbps = toKbps(audioStream?.bit_rate);
|
||||
const overallBitrateKbps = toKbps(format.bit_rate);
|
||||
const probeConfidence = durationSeconds !== null && videoBitrateKbps !== null &&
|
||||
(!audioStream || audioBitrateKbps !== null) && overallBitrateKbps !== null
|
||||
? 'complete'
|
||||
: 'partial';
|
||||
|
||||
// Check codec compatibility
|
||||
const videoOk = BROWSER_VIDEO_CODECS.some(c => videoCodec.includes(c));
|
||||
@ -131,6 +147,11 @@ function analyzeProbeResult(probeResult, url) {
|
||||
width: videoStream?.width || 0,
|
||||
height: videoStream?.height || 0,
|
||||
audioChannels: audioStream?.channels || 0, // For Smart Audio Copy
|
||||
durationSeconds,
|
||||
videoBitrateKbps,
|
||||
audioBitrateKbps,
|
||||
overallBitrateKbps,
|
||||
probeConfidence,
|
||||
container: container,
|
||||
compatible: compatible,
|
||||
needsRemux: needsRemux,
|
||||
@ -157,7 +178,12 @@ router.get('/', async (req, res) => {
|
||||
container: 'unknown',
|
||||
compatible: false,
|
||||
needsRemux: false,
|
||||
needsTranscode: true
|
||||
needsTranscode: true,
|
||||
durationSeconds: null,
|
||||
videoBitrateKbps: null,
|
||||
audioBitrateKbps: null,
|
||||
overallBitrateKbps: null,
|
||||
probeConfidence: 'partial'
|
||||
});
|
||||
}
|
||||
|
||||
@ -193,6 +219,11 @@ router.get('/', async (req, res) => {
|
||||
compatible: false,
|
||||
needsRemux: false,
|
||||
needsTranscode: true,
|
||||
durationSeconds: null,
|
||||
videoBitrateKbps: null,
|
||||
audioBitrateKbps: null,
|
||||
overallBitrateKbps: null,
|
||||
probeConfidence: 'partial',
|
||||
error: err.message
|
||||
});
|
||||
}
|
||||
|
||||
@ -29,7 +29,10 @@ transcodeSession.startCleanupInterval();
|
||||
* Body: { url: string, seekOffset?: number }
|
||||
*/
|
||||
router.post('/session', async (req, res) => {
|
||||
const { url, seekOffset, videoMode, videoCodec, audioCodec, audioChannels } = req.body;
|
||||
const {
|
||||
url, seekOffset, videoMode, videoCodec, audioCodec, audioChannels,
|
||||
targetVideoBitrateKbps, targetAudioBitrateKbps, allowHardwareFallback
|
||||
} = req.body;
|
||||
|
||||
if (!url) {
|
||||
return res.status(400).json({ error: 'URL is required' });
|
||||
@ -47,6 +50,9 @@ router.post('/session', async (req, res) => {
|
||||
hwEncoder: settings.hwEncoder || 'software',
|
||||
maxResolution: settings.maxResolution || '1080p',
|
||||
quality: settings.quality || 'medium',
|
||||
targetVideoBitrateKbps: targetVideoBitrateKbps ?? settings.targetTranscodeBitrateKbps ?? 6000,
|
||||
targetAudioBitrateKbps: targetAudioBitrateKbps ?? 192,
|
||||
allowHardwareFallback: allowHardwareFallback ?? settings.allowHardwareFallback ?? true,
|
||||
audioMixPreset: settings.audioMixPreset || 'auto', // Audio downmix preset
|
||||
// Upscaling options
|
||||
upscaleEnabled: settings.upscaleEnabled || false,
|
||||
@ -71,7 +77,10 @@ router.post('/session', async (req, res) => {
|
||||
res.json({
|
||||
sessionId: session.id,
|
||||
playlistUrl: `/api/transcode/${session.id}/stream.m3u8`,
|
||||
status: session.status
|
||||
status: session.status,
|
||||
requestedEncoder: session.requestedEncoder,
|
||||
effectiveEncoder: session.effectiveEncoder,
|
||||
hardwareFallbackOccurred: session.hardwareFallbackOccurred
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
@ -270,4 +279,3 @@ router.get('/', async (req, res) => {
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user