From 2a8ec7b3484d69180d05a25d257b325528bd7298 Mon Sep 17 00:00:00 2001 From: Jan Stabenow Date: Wed, 29 Jun 2022 23:43:36 +0200 Subject: [PATCH 1/7] Add HLS version selection (Electra Player compatibility) --- src/misc/controls/HLS.js | 13 +++++++ src/utils/restreamer.js | 74 ++++++++++++++++++++++++++++------------ 2 files changed, 66 insertions(+), 21 deletions(-) diff --git a/src/misc/controls/HLS.js b/src/misc/controls/HLS.js index 6658cb8..b1875ab 100644 --- a/src/misc/controls/HLS.js +++ b/src/misc/controls/HLS.js @@ -2,10 +2,12 @@ import React from 'react'; import { Trans } from '@lingui/macro'; import Grid from '@mui/material/Grid'; +import MenuItem from '@mui/material/MenuItem'; import TextField from '@mui/material/TextField'; import Typography from '@mui/material/Typography'; import Checkbox from '../Checkbox'; +import Select from '../Select'; function init(settings) { const initSettings = { @@ -13,6 +15,7 @@ function init(settings) { segmentDuration: 2, listSize: 6, cleanup: true, + version: 3, ...settings, }; @@ -55,6 +58,16 @@ export default function Control(props) { */} + + + + M3U8 manifest version. Version 3 has the longest browser/client compatibility. + + '' + o)], cleanup: [ { - pattern: `memfs:/${channel.channelid}_*.ts`, - max_files: parseInt(control.hls.listSize) + 2, - max_file_age_seconds: control.hls.cleanup ? parseInt(control.hls.segmentDuration) * (parseInt(control.hls.listSize) + 2) : 0, + pattern: control.hls.version >= 7 ? `memfs:/${channel.channelid}_*.mp4` : `memfs:/${channel.channelid}_*.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: `memfs:/${channel.channelid}.m3u8`, - max_file_age_seconds: control.hls.cleanup ? parseInt(control.hls.segmentDuration) * (parseInt(control.hls.listSize) + 2) : 0, + max_file_age_seconds: control.hls.cleanup ? parseInt(control.hls.segmentDuration) * (parseInt(control.hls.listSize) + 6) : 0, purge_on_delete: true, }, ], @@ -1481,25 +1481,57 @@ class Restreamer { output.options.push(...metadata_options); + // manifest versions + // https://developer.apple.com/documentation/http_live_streaming/about_the_ext-x-version_tag + // https://ffmpeg.org/ffmpeg-all.html#Options-53 + console.log(control.hls.version) if (control.hls.lhls === false) { // ordinary HLS - output.options.push( - '-f', - 'hls', - '-start_number', - '0', - '-hls_time', - '' + parseInt(control.hls.segmentDuration), - '-hls_list_size', - '' + parseInt(control.hls.listSize), - '-hls_flags', - 'append_list+delete_segments', - '-hls_segment_filename', - `{memfs}/${channel.channelid}_%04d.ts`, - '-y', - '-method', - 'PUT' - ); + if (control.hls.version >= 6 && control.hls.version < 7) { + output.options.push( + '-f', 'hls', + '-start_number', '0', + '-hls_time', '' + parseInt(control.hls.segmentDuration), + '-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', `{memfs}/${channel.channelid}_%04d.ts`, + '-segment_format_options', 'mpegts_flags=mpegts_copyts=1', + '-max_muxing_queue_size', '400', + '-y', + '-method', 'PUT' + ); + } else if (control.hls.version >= 7) { + output.options.push( + '-f', 'hls', + '-start_number', '0', + '-hls_time', '' + parseInt(control.hls.segmentDuration), + '-hls_list_size', '' + parseInt(control.hls.listSize), + '-hls_flags', 'append_list+delete_segments+program_date_time+independent_segments', + '-hls_delete_threshold', '4', + '-hls_segment_type', 'fmp4', + '-hls_fmp4_init_filename', `${channel.channelid}_init.mp4`, + '-hls_segment_filename', `{memfs}/${channel.channelid}_%04d.mp4`, + '-segment_format_options', 'mpegts_flags=mpegts_copyts=1', + '-max_muxing_queue_size', '400', + '-y', + '-method', 'PUT' + ); + } else { + output.options.push( + '-f', 'hls', + '-start_number', '0', + '-hls_time', '' + parseInt(control.hls.segmentDuration), + '-hls_list_size', '' + parseInt(control.hls.listSize), + '-hls_flags', 'append_list+delete_segments+program_date_time', + '-hls_delete_threshold', '4', + '-hls_segment_filename', `{memfs}/${channel.channelid}_%04d.ts`, + '-segment_format_options', 'mpegts_flags=mpegts_copyts=1', + '-max_muxing_queue_size', '400', + '-y', + '-method', 'PUT' + ); + }; } else { // Low Latency HLS output.address = `{memfs}/${channel.channelid}.mpd`; From 7239bf4d66640bdbca64ce26992f8063822acc8a Mon Sep 17 00:00:00 2001 From: Ingo Oppermann Date: Thu, 30 Jun 2022 09:48:44 +0200 Subject: [PATCH 2/7] Cosmetics --- src/misc/coders/Encoders/audio/Copy.js | 9 +-- src/misc/controls/HLS.js | 10 ++- src/utils/restreamer.js | 104 ++++++++++++++++--------- 3 files changed, 79 insertions(+), 44 deletions(-) diff --git a/src/misc/coders/Encoders/audio/Copy.js b/src/misc/coders/Encoders/audio/Copy.js index d6f90cf..9065c38 100644 --- a/src/misc/coders/Encoders/audio/Copy.js +++ b/src/misc/coders/Encoders/audio/Copy.js @@ -2,11 +2,10 @@ import React from 'react'; function createMapping(settings, stream) { const local = ['-codec:a', 'copy']; - /* - if(stream.codec === 'aac') { - local.push('-bsf:a', 'aac_adtstoasc'); - } -*/ + + //if (stream.codec === 'aac') { + // local.push('-bsf:a', 'aac_adtstoasc'); + //} const mapping = { global: [], diff --git a/src/misc/controls/HLS.js b/src/misc/controls/HLS.js index b1875ab..03eb0b4 100644 --- a/src/misc/controls/HLS.js +++ b/src/misc/controls/HLS.js @@ -61,11 +61,15 @@ export default function Control(props) { - M3U8 manifest version. Version 3 has the longest browser/client compatibility. + M3U8 manifest version. Version 3 has the best browser/client compatibility. diff --git a/src/utils/restreamer.js b/src/utils/restreamer.js index 1be85a3..71c993e 100644 --- a/src/utils/restreamer.js +++ b/src/utils/restreamer.js @@ -1484,54 +1484,86 @@ class Restreamer { // manifest versions // https://developer.apple.com/documentation/http_live_streaming/about_the_ext-x-version_tag // https://ffmpeg.org/ffmpeg-all.html#Options-53 - console.log(control.hls.version) + console.log(control.hls.version); if (control.hls.lhls === false) { // ordinary HLS - if (control.hls.version >= 6 && control.hls.version < 7) { + if (control.hls.version === 6) { output.options.push( - '-f', 'hls', - '-start_number', '0', - '-hls_time', '' + parseInt(control.hls.segmentDuration), - '-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', `{memfs}/${channel.channelid}_%04d.ts`, - '-segment_format_options', 'mpegts_flags=mpegts_copyts=1', - '-max_muxing_queue_size', '400', + '-f', + 'hls', + '-start_number', + '0', + '-hls_time', + '' + parseInt(control.hls.segmentDuration), + '-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', + `{memfs}/${channel.channelid}_%04d.ts`, + '-segment_format_options', + 'mpegts_flags=mpegts_copyts=1', + '-max_muxing_queue_size', + '400', '-y', - '-method', 'PUT' + '-method', + 'PUT' ); - } else if (control.hls.version >= 7) { + } else if (control.hls.version === 7) { output.options.push( - '-f', 'hls', - '-start_number', '0', - '-hls_time', '' + parseInt(control.hls.segmentDuration), - '-hls_list_size', '' + parseInt(control.hls.listSize), - '-hls_flags', 'append_list+delete_segments+program_date_time+independent_segments', - '-hls_delete_threshold', '4', - '-hls_segment_type', 'fmp4', - '-hls_fmp4_init_filename', `${channel.channelid}_init.mp4`, - '-hls_segment_filename', `{memfs}/${channel.channelid}_%04d.mp4`, - '-segment_format_options', 'mpegts_flags=mpegts_copyts=1', - '-max_muxing_queue_size', '400', + '-f', + 'hls', + '-start_number', + '0', + '-hls_time', + '' + parseInt(control.hls.segmentDuration), + '-hls_list_size', + '' + parseInt(control.hls.listSize), + '-hls_flags', + 'append_list+delete_segments+program_date_time+independent_segments', + '-hls_delete_threshold', + '4', + '-hls_segment_type', + 'fmp4', + '-hls_fmp4_init_filename', + `${channel.channelid}_init.mp4`, + '-hls_segment_filename', + `{memfs}/${channel.channelid}_%04d.mp4`, + '-segment_format_options', + 'mpegts_flags=mpegts_copyts=1', + '-max_muxing_queue_size', + '400', '-y', - '-method', 'PUT' + '-method', + 'PUT' ); } else { output.options.push( - '-f', 'hls', - '-start_number', '0', - '-hls_time', '' + parseInt(control.hls.segmentDuration), - '-hls_list_size', '' + parseInt(control.hls.listSize), - '-hls_flags', 'append_list+delete_segments+program_date_time', - '-hls_delete_threshold', '4', - '-hls_segment_filename', `{memfs}/${channel.channelid}_%04d.ts`, - '-segment_format_options', 'mpegts_flags=mpegts_copyts=1', - '-max_muxing_queue_size', '400', + '-f', + 'hls', + '-start_number', + '0', + '-hls_time', + '' + parseInt(control.hls.segmentDuration), + '-hls_list_size', + '' + parseInt(control.hls.listSize), + '-hls_flags', + 'append_list+delete_segments+program_date_time', + '-hls_delete_threshold', + '4', + '-hls_segment_filename', + `{memfs}/${channel.channelid}_%04d.ts`, + '-segment_format_options', + 'mpegts_flags=mpegts_copyts=1', + '-max_muxing_queue_size', + '400', '-y', - '-method', 'PUT' + '-method', + 'PUT' ); - }; + } } else { // Low Latency HLS output.address = `{memfs}/${channel.channelid}.mpd`; From 16e669f05d188f1e9649986a6ecf18bc2c71b830 Mon Sep 17 00:00:00 2001 From: Jan Stabenow Date: Thu, 30 Jun 2022 14:49:24 +0200 Subject: [PATCH 3/7] Mod adds a more fluent version compatible with tee_muxer --- src/utils/restreamer.js | 222 +++++++++++++++++++--------------------- 1 file changed, 106 insertions(+), 116 deletions(-) diff --git a/src/utils/restreamer.js b/src/utils/restreamer.js index 71c993e..b10d139 100644 --- a/src/utils/restreamer.js +++ b/src/utils/restreamer.js @@ -1481,127 +1481,117 @@ class Restreamer { output.options.push(...metadata_options); - // manifest versions + // Manifest versions // https://developer.apple.com/documentation/http_live_streaming/about_the_ext-x-version_tag // https://ffmpeg.org/ffmpeg-all.html#Options-53 - console.log(control.hls.version); - if (control.hls.lhls === false) { - // ordinary HLS - if (control.hls.version === 6) { - output.options.push( - '-f', - 'hls', - '-start_number', - '0', - '-hls_time', - '' + parseInt(control.hls.segmentDuration), - '-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', - `{memfs}/${channel.channelid}_%04d.ts`, - '-segment_format_options', - 'mpegts_flags=mpegts_copyts=1', - '-max_muxing_queue_size', - '400', - '-y', - '-method', - 'PUT' - ); - } else if (control.hls.version === 7) { - output.options.push( - '-f', - 'hls', - '-start_number', - '0', - '-hls_time', - '' + parseInt(control.hls.segmentDuration), - '-hls_list_size', - '' + parseInt(control.hls.listSize), - '-hls_flags', - 'append_list+delete_segments+program_date_time+independent_segments', - '-hls_delete_threshold', - '4', - '-hls_segment_type', - 'fmp4', - '-hls_fmp4_init_filename', - `${channel.channelid}_init.mp4`, - '-hls_segment_filename', - `{memfs}/${channel.channelid}_%04d.mp4`, - '-segment_format_options', - 'mpegts_flags=mpegts_copyts=1', - '-max_muxing_queue_size', - '400', - '-y', - '-method', - 'PUT' - ); + + // Returns the raw l/hls parameters for a EXT-X-VERSION + function hlsParams(lhls, version) { + if (lhls) { + // lhls + switch (version) { + default: + return [ + ['f', 'dash'], + ['strict', 'experimental'], + ['hls_playlist', '1'], + ['init_seg_name', `init-${channel.channelid}.$ext$`], + ['media_seg_name', `chunk-${channel.channelid}-$Number%05d$.$ext$`], + ['master_m3u8_publish_rate', '1'], + ['adaptation_sets', 'id=0,streams=v id=1,streams=a'], + ['lhls', '1'], + ['streaming', '1'], + ['seg_duration', '' + parseInt(control.hls.segmentDuration)], + ['frag_duration', '0.5'], + ['use_template', '1'], + ['remove_at_exit', '0'], + ['window_size', '' + parseInt(control.hls.listSize)], + ['http_persistent', '0'], + ['method', 'PUT'], + ]; + } } else { - output.options.push( - '-f', - 'hls', - '-start_number', - '0', - '-hls_time', - '' + parseInt(control.hls.segmentDuration), - '-hls_list_size', - '' + parseInt(control.hls.listSize), - '-hls_flags', - 'append_list+delete_segments+program_date_time', - '-hls_delete_threshold', - '4', - '-hls_segment_filename', - `{memfs}/${channel.channelid}_%04d.ts`, - '-segment_format_options', - 'mpegts_flags=mpegts_copyts=1', - '-max_muxing_queue_size', - '400', - '-y', - '-method', - 'PUT' - ); + // hls + switch (version) { + case 6: + return [ + ['f', 'hls'], + ['start_number', '0'], + ['hls_time', '' + parseInt(control.hls.segmentDuration)], + ['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', `{memfs}/${channel.channelid}_%04d.ts`], + ['segment_format_options', 'mpegts_flags=mpegts_copyts=1'], + ['max_muxing_queue_size', '400'], + ['method', 'PUT'], + ]; + + case 7: + return [ + ['f', 'hls'], + ['start_number', '0'], + ['hls_time', '' + parseInt(control.hls.segmentDuration)], + ['hls_list_size', '' + parseInt(control.hls.listSize)], + ['hls_flags', 'append_list+delete_segments+program_date_time+independent_segments'], + ['hls_delete_threshold', '4'], + ['hls_segment_type', 'fmp4'], + ['hls_fmp4_init_filename', `${channel.channelid}_init.mp4`], + ['hls_segment_filename', `{memfs}/${channel.channelid}_%04d.mp4`], + ['segment_format_options', 'mpegts_flags=mpegts_copyts=1'], + ['max_muxing_queue_size', '400'], + ['method', 'PUT'], + ]; + + default: + // case 3 + return [ + ['f', 'hls'], + ['start_number', '0'], + ['hls_time', '' + parseInt(control.hls.segmentDuration)], + ['hls_list_size', '' + parseInt(control.hls.listSize)], + ['hls_flags', 'append_list+delete_segments+program_date_time'], + ['hls_delete_threshold', '4'], + ['hls_segment_filename', `{memfs}/${channel.channelid}_%04d.ts`], + ['segment_format_options', 'mpegts_flags=mpegts_copyts=1'], + ['max_muxing_queue_size', '400'], + ['method', 'PUT'], + ]; + } } + } + const hls_params_raw = hlsParams(control.hls.lhls, control.hls.version); + + // 'tee_muxer' is required for the delivery of one output to multiple endpoints + // http://ffmpeg.org/ffmpeg-all.html#tee-1 + const tee_muxer = false; + + // Returns the l/hls parameters with or without tee_muxer + let hls_params = []; + if (tee_muxer) { + // ['f=hls', 'start_number=0', ...] + for (let i in hls_params_raw) { + if (hls_params_raw[i][0] !== 'segment_format_options' && hls_params_raw[i][0] !== 'max_muxing_queue_size') { + hls_params = [...hls_params, hls_params_raw[i][0] + '=' + hls_params_raw[i][1]]; + } + } + // ['f=hls:start_number=0...] + // Switch to hls_params += string? + hls_params = '[' + hls_params.toString().replace(/,/g, ':') + ']' + `{memfs}/${channel.channelid}.m3u8`; } else { - // Low Latency HLS - output.address = `{memfs}/${channel.channelid}.mpd`; - output.options.push( - '-f', - 'dash', - '-strict', - 'experimental', - '-hls_playlist', - '1', - '-init_seg_name', - `init-${channel.channelid}.$ext$`, - '-media_seg_name', - `chunk-${channel.channelid}-$Number%05d$.$ext$`, - '-master_m3u8_publish_rate', - '1', - '-adaptation_sets', - 'id=0,streams=v id=1,streams=a', - '-lhls', - '1', - '-streaming', - '1', - '-seg_duration', - '' + parseInt(control.hls.segmentDuration), - '-frag_duration', - '0.5', - '-use_template', - '1', - '-remove_at_exit', - '0', - '-window_size', - '' + parseInt(control.hls.listSize), - '-http_persistent', - '0', - '-y', - '-method', - 'PUT' - ); + // ['-f', 'hls', '-start_number', '0', ...] + for (let i in hls_params_raw) { + hls_params = [...hls_params, '-' + hls_params_raw[i][0], hls_params_raw[i][1]]; + } + } + + // Pushes the hls parameters into the output options + if (tee_muxer) { + output.options.push("-tag:v", "7", "-tag:a", "10", "-f", "tee"); + // WARN: It is a magic function. Returns 'Invalid process config' and the process.id is lost (Core v16.8.0) + // output.address = hls_params; + } else { + output.options.concat(hls_params); } proc.output.push(output); From faa2cc1af2739cd4a5d7cfe2f04dca057988e97c Mon Sep 17 00:00:00 2001 From: Jan Stabenow Date: Thu, 30 Jun 2022 16:03:41 +0200 Subject: [PATCH 4/7] Fix no-useless-concat + style --- src/utils/restreamer.js | 66 ++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/src/utils/restreamer.js b/src/utils/restreamer.js index b10d139..c2c8095 100644 --- a/src/utils/restreamer.js +++ b/src/utils/restreamer.js @@ -1485,31 +1485,28 @@ class Restreamer { // https://developer.apple.com/documentation/http_live_streaming/about_the_ext-x-version_tag // https://ffmpeg.org/ffmpeg-all.html#Options-53 - // Returns the raw l/hls parameters for a EXT-X-VERSION - function hlsParams(lhls, version) { + // Returns the raw l/hls parameters for an EXT-X-VERSION + function GetHlsParams(lhls, version) { if (lhls) { // lhls - switch (version) { - default: - return [ - ['f', 'dash'], - ['strict', 'experimental'], - ['hls_playlist', '1'], - ['init_seg_name', `init-${channel.channelid}.$ext$`], - ['media_seg_name', `chunk-${channel.channelid}-$Number%05d$.$ext$`], - ['master_m3u8_publish_rate', '1'], - ['adaptation_sets', 'id=0,streams=v id=1,streams=a'], - ['lhls', '1'], - ['streaming', '1'], - ['seg_duration', '' + parseInt(control.hls.segmentDuration)], - ['frag_duration', '0.5'], - ['use_template', '1'], - ['remove_at_exit', '0'], - ['window_size', '' + parseInt(control.hls.listSize)], - ['http_persistent', '0'], - ['method', 'PUT'], - ]; - } + return [ + ['f', 'dash'], + ['strict', 'experimental'], + ['hls_playlist', '1'], + ['init_seg_name', `init-${channel.channelid}.$ext$`], + ['media_seg_name', `chunk-${channel.channelid}-$Number%05d$.$ext$`], + ['master_m3u8_publish_rate', '1'], + ['adaptation_sets', 'id=0,streams=v id=1,streams=a'], + ['lhls', '1'], + ['streaming', '1'], + ['seg_duration', '' + parseInt(control.hls.segmentDuration)], + ['frag_duration', '0.5'], + ['use_template', '1'], + ['remove_at_exit', '0'], + ['window_size', '' + parseInt(control.hls.listSize)], + ['http_persistent', '0'], + ['method', 'PUT'], + ]; } else { // hls switch (version) { @@ -1526,7 +1523,6 @@ class Restreamer { ['max_muxing_queue_size', '400'], ['method', 'PUT'], ]; - case 7: return [ ['f', 'hls'], @@ -1542,9 +1538,8 @@ class Restreamer { ['max_muxing_queue_size', '400'], ['method', 'PUT'], ]; - + // case 3 default: - // case 3 return [ ['f', 'hls'], ['start_number', '0'], @@ -1560,25 +1555,28 @@ class Restreamer { } } } - const hls_params_raw = hlsParams(control.hls.lhls, control.hls.version); + const hls_params_raw = GetHlsParams(control.hls.lhls, control.hls.version); // 'tee_muxer' is required for the delivery of one output to multiple endpoints // http://ffmpeg.org/ffmpeg-all.html#tee-1 const tee_muxer = false; // Returns the l/hls parameters with or without tee_muxer - let hls_params = []; + let hls_params = ''; if (tee_muxer) { - // ['f=hls', 'start_number=0', ...] + // ['f=hls:start_number=0...] for (let i in hls_params_raw) { if (hls_params_raw[i][0] !== 'segment_format_options' && hls_params_raw[i][0] !== 'max_muxing_queue_size') { - hls_params = [...hls_params, hls_params_raw[i][0] + '=' + hls_params_raw[i][1]]; + hls_params += hls_params_raw[i][0] + '=' + hls_params_raw[i][1]; + if (i < hls_params_raw.length - 1) { + hls_params += ':'; + } } } - // ['f=hls:start_number=0...] - // Switch to hls_params += string? - hls_params = '[' + hls_params.toString().replace(/,/g, ':') + ']' + `{memfs}/${channel.channelid}.m3u8`; + // ['f=hls:start_number=0...]address.m3u8 + hls_params = `[` + hls_params + `]{memfs}/${channel.channelid}.m3u8`; } else { + hls_params = []; // ['-f', 'hls', '-start_number', '0', ...] for (let i in hls_params_raw) { hls_params = [...hls_params, '-' + hls_params_raw[i][0], hls_params_raw[i][1]]; @@ -1587,7 +1585,7 @@ class Restreamer { // Pushes the hls parameters into the output options if (tee_muxer) { - output.options.push("-tag:v", "7", "-tag:a", "10", "-f", "tee"); + output.options.push('-tag:v', '7', '-tag:a', '10', '-f', 'tee'); // WARN: It is a magic function. Returns 'Invalid process config' and the process.id is lost (Core v16.8.0) // output.address = hls_params; } else { From cb2d486888240438f4280bdc36ba894da6ec0f12 Mon Sep 17 00:00:00 2001 From: Jan Stabenow Date: Thu, 30 Jun 2022 19:05:26 +0200 Subject: [PATCH 5/7] Fix Malformed AAC bitstream detected for hls version 7 + array merge --- src/utils/restreamer.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/utils/restreamer.js b/src/utils/restreamer.js index c2c8095..2fd6294 100644 --- a/src/utils/restreamer.js +++ b/src/utils/restreamer.js @@ -1524,6 +1524,10 @@ class Restreamer { ['method', 'PUT'], ]; case 7: + // fix Malformed AAC bitstream detected for hls version 7 + if (control.hls.version === 7 && output.options.includes('-codec:a') && output.options.includes('copy')) { + output.options.push('-bsf:a', 'aac_adtstoasc'); + } return [ ['f', 'hls'], ['start_number', '0'], @@ -1589,7 +1593,7 @@ class Restreamer { // WARN: It is a magic function. Returns 'Invalid process config' and the process.id is lost (Core v16.8.0) // output.address = hls_params; } else { - output.options.concat(hls_params); + output.options.push(...hls_params); } proc.output.push(output); From 0be3a7030464294cebe15b70293170de7fdcef3a Mon Sep 17 00:00:00 2001 From: Jan Stabenow Date: Thu, 30 Jun 2022 19:09:45 +0200 Subject: [PATCH 6/7] Mod updates CHANGELOG --- CHANGELOG.md | 4 +++- package.json | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d0af8b..4533846 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,12 @@ # Restreamer-UI -#### v1.1.0 > v1.4.0 +#### v1.1.0 > v1.5.0 +- Add HLS version selection (Dwaynarang, Electra Player compatibility) - Add Owncast to publication services ([#369](https://github.com/datarhei/restreamer/issues/369)) - Add Telegram to publication services (thx Martin Held) - Add Polish translations (thx Robert RykaƂa) +- Mod Allow decoders and encoders to set global options - Fix process report naming - Fix publication service icon styles - Fix VAAPI encoder diff --git a/package.json b/package.json index a454a81..7f8b30d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "restreamer-ui", - "version": "1.4.0", + "version": "1.5.0", "bundle": "restreamer-v2.x.x", "private": false, "license": "Apache-2.0", From fd462775c171393041525b33b7d39ba72bb17e6a Mon Sep 17 00:00:00 2001 From: Ingo Oppermann Date: Thu, 30 Jun 2022 21:39:12 +0200 Subject: [PATCH 7/7] Slimming down HLS parameter assembly --- src/utils/restreamer.js | 47 ++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/src/utils/restreamer.js b/src/utils/restreamer.js index 2fd6294..2bad11b 100644 --- a/src/utils/restreamer.js +++ b/src/utils/restreamer.js @@ -1486,7 +1486,7 @@ class Restreamer { // https://ffmpeg.org/ffmpeg-all.html#Options-53 // Returns the raw l/hls parameters for an EXT-X-VERSION - function GetHlsParams(lhls, version) { + const getHLSParams = (lhls, version) => { if (lhls) { // lhls return [ @@ -1558,41 +1558,36 @@ class Restreamer { ]; } } - } - const hls_params_raw = GetHlsParams(control.hls.lhls, control.hls.version); + }; + const hls_params_raw = getHLSParams(control.hls.lhls, control.hls.version); - // 'tee_muxer' is required for the delivery of one output to multiple endpoints + // 'tee_muxer' is required for the delivery of one output to multiple endpoints without processing the input for each output // http://ffmpeg.org/ffmpeg-all.html#tee-1 const tee_muxer = false; // Returns the l/hls parameters with or without tee_muxer - let hls_params = ''; if (tee_muxer) { - // ['f=hls:start_number=0...] - for (let i in hls_params_raw) { - if (hls_params_raw[i][0] !== 'segment_format_options' && hls_params_raw[i][0] !== 'max_muxing_queue_size') { - hls_params += hls_params_raw[i][0] + '=' + hls_params_raw[i][1]; - if (i < hls_params_raw.length - 1) { - hls_params += ':'; + // f=hls:start_number=0... + const hls_params = hls_params_raw + .filter((o) => { + if (o[0] === 'segment_format_options' || o[0] === 'max_muxing_queue_size') { + return false; } - } - } - // ['f=hls:start_number=0...]address.m3u8 - hls_params = `[` + hls_params + `]{memfs}/${channel.channelid}.m3u8`; - } else { - hls_params = []; - // ['-f', 'hls', '-start_number', '0', ...] - for (let i in hls_params_raw) { - hls_params = [...hls_params, '-' + hls_params_raw[i][0], hls_params_raw[i][1]]; - } - } - // Pushes the hls parameters into the output options - if (tee_muxer) { + return true; + }) + .map((o) => o[0] + '=' + o[1]) + .join(':'); + output.options.push('-tag:v', '7', '-tag:a', '10', '-f', 'tee'); - // WARN: It is a magic function. Returns 'Invalid process config' and the process.id is lost (Core v16.8.0) - // output.address = hls_params; + // WARN: It is a magic function. Returns 'Invalid process config' and the process.id is lost (Core v16.8.0) <= this is not the case anymore with the latest dev branch + // ['f=hls:start_number=0...]address.m3u8 + output.address = `[` + hls_params + `]{memfs}/${channel.channelid}.m3u8`; } else { + // ['-f', 'hls', '-start_number', '0', ...] + // adding the '-' in front of the first option, then flatten everything + const hls_params = hls_params_raw.map((o) => ['-' + o[0], o[1]]).reduce((acc, val) => acc.concat(val), []); + output.options.push(...hls_params); }