mirror of
https://github.com/R0m1k3/xtremflow.git
synced 2026-10-20 05:28:05 +02:00
feat: Implement HLS streaming with file serving and FFmpeg transcoding for live and VOD content.
This commit is contained in:
1 parent
a7ca1a087f
commit
bffef49554
2 files changed
+56
-51
No files matched your search
+38
-23
@@ -147,39 +147,54 @@ Handler createStreamInitHandler() {
|
|||||||
'-hls_segment_filename', '${tempDir.path}/segment_%03d.ts',
|
'-hls_segment_filename', '${tempDir.path}/segment_%03d.ts',
|
||||||
];
|
];
|
||||||
|
|
||||||
// Video: ALWAYS Transcode to ensure standard HLS/MPEG-TS timestamps
|
// Detect if this is likely a Live TV stream (TS format) vs VOD (MKV/MP4)
|
||||||
// Copying causes DEMUXER_ERROR if source stamps are weird (common in MKV)
|
final isLiveStream = iptvUrl.endsWith('.ts') || iptvUrl.contains('/live/');
|
||||||
ffmpegArgs.addAll([
|
|
||||||
'-c:v', 'libx264',
|
|
||||||
'-preset', 'ultrafast', // Fast encoding for live/vod
|
|
||||||
'-tune', 'zerolatency',
|
|
||||||
'-profile:v', 'baseline',
|
|
||||||
'-level', '3.0',
|
|
||||||
'-pix_fmt', 'yuv420p',
|
|
||||||
'-g', '48', // 2s GOP
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (quality == 'low') {
|
// Video encoding strategy:
|
||||||
|
// - Live TV (.ts): Try to COPY video stream for maximum quality (no re-encoding loss)
|
||||||
|
// - VOD (MKV/MP4): Transcode to ensure timestamp compatibility
|
||||||
|
if (isLiveStream) {
|
||||||
|
// LIVE TV: Copy video stream for best quality
|
||||||
|
// Most IPTV live streams are already H.264 and browser-compatible
|
||||||
ffmpegArgs.addAll([
|
ffmpegArgs.addAll([
|
||||||
'-vf', 'scale=-2:480',
|
'-c:v', 'copy', // No re-encoding = best quality
|
||||||
'-b:v', '800k',
|
|
||||||
'-maxrate', '800k',
|
|
||||||
'-bufsize', '1600k',
|
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
// High/Medium (default)
|
// VOD: Transcode to fix timestamp issues in MKV containers
|
||||||
ffmpegArgs.addAll([
|
ffmpegArgs.addAll([
|
||||||
'-b:v', '2500k',
|
'-c:v', 'libx264',
|
||||||
'-maxrate', '2500k',
|
'-preset', 'fast', // Better quality than ultrafast
|
||||||
'-bufsize', '5000k',
|
'-tune', 'film', // Better for movies/series
|
||||||
|
'-profile:v', 'high', // High profile for better compression
|
||||||
|
'-level', '4.1', // Supports 1080p60
|
||||||
|
'-pix_fmt', 'yuv420p',
|
||||||
|
'-g', '48', // 2s GOP
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if (quality == 'low') {
|
||||||
|
ffmpegArgs.addAll([
|
||||||
|
'-vf', 'scale=-2:480',
|
||||||
|
'-b:v', '1500k',
|
||||||
|
'-maxrate', '1500k',
|
||||||
|
'-bufsize', '3000k',
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
// High quality (default) - 6Mbps for 1080p
|
||||||
|
ffmpegArgs.addAll([
|
||||||
|
'-b:v', '6000k',
|
||||||
|
'-maxrate', '8000k',
|
||||||
|
'-bufsize', '12000k',
|
||||||
|
'-crf', '18', // High quality CRF
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Audio: Always transcode to AAC
|
// Audio: Always transcode to AAC (browsers don't support AC3/DTS)
|
||||||
|
// Higher bitrate for better audio quality
|
||||||
ffmpegArgs.addAll([
|
ffmpegArgs.addAll([
|
||||||
'-c:a', 'aac',
|
'-c:a', 'aac',
|
||||||
'-b:a', '128k',
|
'-b:a', '192k', // Higher audio bitrate
|
||||||
'-ar', '44100',
|
'-ar', '48000', // Standard sample rate
|
||||||
'-ac', '2',
|
'-ac', '2',
|
||||||
|
|
||||||
playlistPath,
|
playlistPath,
|
||||||
|
|||||||
+18
-28
@@ -511,40 +511,30 @@
|
|||||||
|
|
||||||
log('Stream URL: ' + streamUrl.substring(0, 80) + '...');
|
log('Stream URL: ' + streamUrl.substring(0, 80) + '...');
|
||||||
|
|
||||||
// SPECIAL: /api/stream/ endpoint - differentiate Live vs VOD
|
// SPECIAL: /api/stream/ endpoint - ALWAYS returns JSON with HLS playlist
|
||||||
if (streamUrl.includes('/api/stream/')) {
|
if (streamUrl.includes('/api/stream/')) {
|
||||||
// Check if this is a live stream (URL param check since player.html doesn't have type param by default)
|
// Both Live TV and VOD use /api/stream/ which returns JSON with HLS playlist URL
|
||||||
const isLiveStream = streamUrl.includes('live') || urlParams.get('type') === 'live';
|
const isLiveStream = streamUrl.includes('live') || urlParams.get('type') === 'live';
|
||||||
|
log((isLiveStream ? 'Live TV' : 'VOD') + ' via /api/stream/ - fetching HLS...');
|
||||||
|
showLoading(isLiveStream ? 'Connecting to live stream...' : 'Initializing stream...');
|
||||||
|
|
||||||
if (isLiveStream) {
|
try {
|
||||||
// LIVE TV: Use MPEG-TS direct streaming
|
const response = await fetch(streamUrl);
|
||||||
log('Live TV via /api/stream/ - using MPEG-TS');
|
const data = await response.json();
|
||||||
showLoading('Connecting to live stream...');
|
|
||||||
await playWithMpegTS(streamUrl);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
// VOD: Fetch JSON to get HLS playlist (transcoding mode)
|
|
||||||
log('VOD via /api/stream/ - fetching HLS...');
|
|
||||||
showLoading('Initializing stream (transcoding audio)...');
|
|
||||||
|
|
||||||
try {
|
if (data.playlist) {
|
||||||
const response = await fetch(streamUrl);
|
const playlistUrl = window.location.origin + data.playlist;
|
||||||
const data = await response.json();
|
log('Got HLS playlist: ' + playlistUrl);
|
||||||
|
await playWithHLS(playlistUrl);
|
||||||
if (data.playlist) {
|
return;
|
||||||
const playlistUrl = window.location.origin + data.playlist;
|
} else {
|
||||||
log('Playing HLS: ' + playlistUrl);
|
showError('Invalid stream response');
|
||||||
await playWithHLS(playlistUrl);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
showError('Invalid stream response');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
log('Error: ' + err);
|
|
||||||
showError('Stream init failed');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
|
log('Error fetching playlist: ' + err);
|
||||||
|
showError('Stream init failed');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user