diff --git a/src/misc/coders/Encoders/video/VP9.js b/src/misc/coders/Encoders/video/VP9.js index 0a2e4e9..cca572b 100644 --- a/src/misc/coders/Encoders/video/VP9.js +++ b/src/misc/coders/Encoders/video/VP9.js @@ -27,6 +27,8 @@ function createMapping(settings) { `${settings.bitrate}k`, '-r', `${settings.fps}`, + '-sc_threshold', + '0', '-pix_fmt', 'yuv420p', '-vsync', @@ -34,7 +36,12 @@ function createMapping(settings) { ]; if (settings.gop !== 'auto') { - local.push('-g', `${Math.round(parseInt(settings.fps) * parseInt(settings.gop)).toFixed(0)}`); + local.push( + '-g', + `${Math.round(parseInt(settings.fps) * parseInt(settings.gop)).toFixed(0)}`, + '-keyint_min', + `${Math.round(parseInt(settings.fps) * parseInt(settings.gop)).toFixed(0)}` + ); } const mapping = { diff --git a/src/misc/coders/Encoders/video/X264.js b/src/misc/coders/Encoders/video/X264.js index ad3bbd5..c02ad90 100644 --- a/src/misc/coders/Encoders/video/X264.js +++ b/src/misc/coders/Encoders/video/X264.js @@ -36,6 +36,8 @@ function createMapping(settings) { `${settings.bitrate}k`, '-r', `${settings.fps}`, + '-sc_threshold', + '0', '-pix_fmt', 'yuv420p', '-vsync', @@ -43,7 +45,12 @@ function createMapping(settings) { ]; if (settings.gop !== 'auto') { - local.push('-g', `${Math.round(parseInt(settings.fps) * parseInt(settings.gop)).toFixed(0)}`); + local.push( + '-g', + `${Math.round(parseInt(settings.fps) * parseInt(settings.gop)).toFixed(0)}`, + '-keyint_min', + `${Math.round(parseInt(settings.fps) * parseInt(settings.gop)).toFixed(0)}` + ); } if (settings.profile !== 'auto') { diff --git a/src/misc/coders/Encoders/video/X265.js b/src/misc/coders/Encoders/video/X265.js index 019cc6e..c41345e 100644 --- a/src/misc/coders/Encoders/video/X265.js +++ b/src/misc/coders/Encoders/video/X265.js @@ -36,6 +36,8 @@ function createMapping(settings) { `${settings.bitrate}k`, '-r', `${settings.fps}`, + '-sc_threshold', + '0', '-pix_fmt', 'yuv420p', '-vsync', @@ -43,7 +45,12 @@ function createMapping(settings) { ]; if (settings.gop !== 'auto') { - local.push('-g', `${Math.round(parseInt(settings.fps) * parseInt(settings.gop)).toFixed(0)}`); + local.push( + '-g', + `${Math.round(parseInt(settings.fps) * parseInt(settings.gop)).toFixed(0)}`, + '-keyint_min', + `${Math.round(parseInt(settings.fps) * parseInt(settings.gop)).toFixed(0)}` + ); } if (settings.profile !== 'auto') { diff --git a/src/utils/restreamer.js b/src/utils/restreamer.js index 9b245ba..886a6e5 100644 --- a/src/utils/restreamer.js +++ b/src/utils/restreamer.js @@ -1547,48 +1547,47 @@ class Restreamer { }); } - // set hls storage endpoint + // Set hls storage endpoint let hlsStore = 'memfs'; const output = { id: 'output_0', - address: `{` + hlsStore + `}/${channel.channelid}.m3u8`, + address: `{${hlsStore}}/${channel.channelid}.m3u8`, options: ['-dn', '-sn', ...outputs[0].options.map((o) => '' + o)], cleanup: [ { - pattern: control.hls.version >= 7 ? hlsStore + `:/${channel.channelid}_*.mp4` : hlsStore + `:/${channel.channelid}_*.ts`, + pattern: `${hlsStore}:/${channel.channelid}_*.` + (control.hls.version >= 7 ? 'mp4' : 'ts'), max_files: parseInt(control.hls.listSize) + 6, max_file_age_seconds: control.hls.cleanup ? parseInt(control.hls.segmentDuration) * (parseInt(control.hls.listSize) + 6) : 0, purge_on_delete: true, }, { - pattern: hlsStore + `:/${channel.channelid}.m3u8`, + pattern: `${hlsStore}:/${channel.channelid}.m3u8`, max_file_age_seconds: control.hls.cleanup ? parseInt(control.hls.segmentDuration) * (parseInt(control.hls.listSize) + 6) : 0, purge_on_delete: true, }, ], }; - const metadata = this.GetHTTPAddresses()[0] + '/' + channel.channelid + '/oembed.json'; - + // Injects a metadata link as title + const metadata = `${this.GetHTTPAddresses()[0]}/${channel.channelid}/oembed.json`; const metadata_options = ['-metadata', `title=${metadata}`, '-metadata', 'service_provider=datarhei-Restreamer']; - output.options.push(...metadata_options); - // fetch core config + // Fetch core config const core_config = this.ConfigActive(); - // fetch rtmp settings + // Fetch rtmp settings const rtmp_config = core_config.source.network.rtmp; let rtmp_enabled = false; if (control.rtmp && control.rtmp.enable && rtmp_config.enabled) { rtmp_enabled = true; } - // fetch srt settings + // Fetch srt settings const srt_config = core_config.source.network.srt; let srt_enabled = false; - if (control.srt && control.srt.enable && srt_config.enabled) { + if (control.srt.enable && srt_config.enabled) { srt_enabled = true; } @@ -1636,10 +1635,7 @@ class Restreamer { ['hls_list_size', '' + parseInt(control.hls.listSize)], ['hls_flags', 'append_list+delete_segments+program_date_time+independent_segments'], ['hls_delete_threshold', '4'], - [ - 'hls_segment_filename', - tee_muxer ? `{` + hlsStore + `^:}/${channel.channelid}_%04d.ts` : `{` + hlsStore + `}/${channel.channelid}_%04d.ts`, - ], + ['hls_segment_filename', `{${hlsStore}` + (tee_muxer ? '^:' : '') + `}/${channel.channelid}_%04d.ts`], ['method', 'PUT'], ]; case 7: @@ -1649,7 +1645,7 @@ class Restreamer { } // mp4 manifest cleanup output.cleanup.push({ - pattern: hlsStore + `:/${channel.channelid}.mp4`, + pattern: `${hlsStore}:/${channel.channelid}.mp4`, max_file_age_seconds: control.hls.cleanup ? parseInt(control.hls.segmentDuration) * (parseInt(control.hls.listSize) + 6) : 0, purge_on_delete: true, }); @@ -1662,10 +1658,7 @@ class Restreamer { ['hls_delete_threshold', '4'], ['hls_segment_type', 'fmp4'], ['hls_fmp4_init_filename', `${channel.channelid}.mp4`], - [ - 'hls_segment_filename', - tee_muxer ? `{` + hlsStore + `^:}/${channel.channelid}_%04d.mp4` : `{` + hlsStore + `}/${channel.channelid}_%04d.mp4`, - ], + ['hls_segment_filename', `{${hlsStore}` + (tee_muxer ? '^:' : '') + `}/${channel.channelid}_%04d.mp4`], ['method', 'PUT'], ]; // case 3 @@ -1677,10 +1670,7 @@ class Restreamer { ['hls_list_size', '' + parseInt(control.hls.listSize)], ['hls_flags', 'append_list+delete_segments+program_date_time'], ['hls_delete_threshold', '4'], - [ - 'hls_segment_filename', - tee_muxer ? `{` + hlsStore + `^:}/${channel.channelid}_%04d.ts` : `{` + hlsStore + `}/${channel.channelid}_%04d.ts`, - ], + ['hls_segment_filename', `{${hlsStore}` + (tee_muxer ? '^:' : '') + `}/${channel.channelid}_%04d.ts`], ['method', 'PUT'], ]; } @@ -1688,7 +1678,7 @@ class Restreamer { }; const hls_params_raw = getHLSParams(control.hls.lhls, control.hls.version); - // push -y + // Overwrite output files proc.options.push('-y'); // Returns the l/hls parameters with or without tee_muxer @@ -1706,11 +1696,7 @@ class Restreamer { // ['f=hls:start_number=0...]address.m3u8 // use tee_muxer formatting output.address = - `[` + - hls_params + - `]{` + - hlsStore + - `}/${channel.channelid}.m3u8` + + `[${hls_params}]{${hlsStore}}/${channel.channelid}.m3u8` + (rtmp_enabled ? `|[f=flv]{rtmp,name=${channel.channelid}.stream}` : '') + (srt_enabled ? `|[f=mpegts]{srt,name=${channel.channelid},mode=publish}` : ''); } else { @@ -1752,7 +1738,7 @@ class Restreamer { input: [ { id: 'input_0', - address: `{` + hlsStore + `}/${channel.channelid}.m3u8`, + address: `{${hlsStore}}/${channel.channelid}.m3u8`, options: [], }, ], diff --git a/src/views/Edit/Wizard/Sources/Network.js b/src/views/Edit/Wizard/Sources/Network.js index 9d3cfc6..f63e359 100644 --- a/src/views/Edit/Wizard/Sources/Network.js +++ b/src/views/Edit/Wizard/Sources/Network.js @@ -78,14 +78,13 @@ function Source(props) { )} - Username} value={settings.username} onChange={update('', 'username')} /> + Username} value={settings.username} onChange={update('', 'username')} disabled={protocol === 'srt'} /> Username for the device. - - Password} value={settings.password} onChange={update('', 'password')} /> + Password} value={settings.password} onChange={update('', 'password')} disabled={protocol === 'srt'} /> Password for the device.