diff --git a/src/views/Edit/Sources/ALSA.js b/src/views/Edit/Sources/ALSA.js index 53c3fff..1d562db 100644 --- a/src/views/Edit/Sources/ALSA.js +++ b/src/views/Edit/Sources/ALSA.js @@ -155,7 +155,7 @@ function SourceIcon(props) { const id = 'alsa'; const name = ALSA; const capabilities = ['audio']; -const ffversion = '^4.1.0 || ^5.0.0'; +const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0'; const func = { initSettings, diff --git a/src/views/Edit/Sources/AVFoundation.js b/src/views/Edit/Sources/AVFoundation.js index 5b2e05b..072b7d9 100644 --- a/src/views/Edit/Sources/AVFoundation.js +++ b/src/views/Edit/Sources/AVFoundation.js @@ -220,7 +220,7 @@ function SourceIcon(props) { const id = 'avfoundation'; const name = AVFoundation; const capabilities = ['audio', 'video']; -const ffversion = '^4.1.0 || ^5.0.0'; +const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0'; const func = { initSettings, diff --git a/src/views/Edit/Sources/AudioLoop.js b/src/views/Edit/Sources/AudioLoop.js index cd195d6..2220f50 100644 --- a/src/views/Edit/Sources/AudioLoop.js +++ b/src/views/Edit/Sources/AudioLoop.js @@ -193,7 +193,7 @@ function SourceIcon(props) { const id = 'audioloop'; const name = Loop; const capabilities = ['audio']; -const ffversion = '^4.1.0 || ^5.0.0'; +const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0'; const func = { initSettings, diff --git a/src/views/Edit/Sources/Framebuffer.js b/src/views/Edit/Sources/Framebuffer.js index 9791939..2f80556 100644 --- a/src/views/Edit/Sources/Framebuffer.js +++ b/src/views/Edit/Sources/Framebuffer.js @@ -128,7 +128,7 @@ function SourceIcon(props) { const id = 'fbdev'; const name = Framebuffer; const capabilities = ['video']; -const ffversion = '^4.1.0 || ^5.0.0'; +const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0'; const func = { initSettings, diff --git a/src/views/Edit/Sources/Network.js b/src/views/Edit/Sources/Network.js index eab6091..6bcad4e 100644 --- a/src/views/Edit/Sources/Network.js +++ b/src/views/Edit/Sources/Network.js @@ -178,13 +178,15 @@ const initSkills = (initialSkills) => { }; const createInputs = (settings, config, skills) => { - settings = initSettings(settings); config = initConfig(config); + settings = initSettings(settings, config); skills = initSkills(skills); - let ffmpeg_version = 4; + let ffmpeg_version = 6; if (SemverSatisfies(skills.ffmpeg.version, '^5.0.0')) { ffmpeg_version = 5; + } else if (SemverSatisfies(skills.ffmpeg.version, '^4.1.0')) { + ffmpeg_version = 4; } const input = { @@ -1173,7 +1175,7 @@ function SourceIcon(props) { const id = 'network'; const name = Network source; const capabilities = ['audio', 'video']; -const ffversion = '^4.1.0 || ^5.0.0'; +const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0'; const func = { initSettings, diff --git a/src/views/Edit/Sources/Network.test.js b/src/views/Edit/Sources/Network.test.js index 9204145..e08c888 100644 --- a/src/views/Edit/Sources/Network.test.js +++ b/src/views/Edit/Sources/Network.test.js @@ -4,6 +4,18 @@ import '@testing-library/jest-dom'; import * as Network from './Network'; +const $skills_ffmpeg6 = { + ffmpeg: { + version: '6.1.1', + }, + formats: { + demuxers: ['rtsp'], + }, + protocols: { + input: ['http', 'https', 'rtmp', 'rtmps', 'srt'], + }, +}; + const $skills_ffmpeg5 = { ffmpeg: { version: '5.1.2', @@ -29,6 +41,7 @@ const $skills_ffmpeg4 = { }; const $config = { + channelid: 'external', rtmp: { enabled: true, app: '/live', @@ -64,9 +77,17 @@ test('source:network pull', async () => { expect(queryByText(`This protocol is unknown or not supported by the available FFmpeg binary.`)).toBeInTheDocument(); + rerender(); + + expect(queryByText(`This protocol is unknown or not supported by the available FFmpeg binary.`)).toBe(null); + rerender(); expect(queryByText(`This protocol is unknown or not supported by the available FFmpeg binary.`)).toBe(null); + + rerender(); + + expect(queryByText(`This protocol is unknown or not supported by the available FFmpeg binary.`)).toBe(null); }); const pullmatrix = { @@ -205,6 +226,56 @@ pullmatrix.tests = [ options: ['-fflags', '+genpts', '-thread_queue_size', 512], }, }, + { + name: 'RTSP', + settings: { ...pullmatrix.settings, address: 'rtsp://127.0.0.1/live/stream' }, + skills: $skills_ffmpeg6, + input: { + address: 'rtsp://admin:foobar@127.0.0.1/live/stream', + options: ['-fflags', '+genpts', '-thread_queue_size', 512, '-timeout', 5000000, '-rtsp_transport', 'tcp'], + }, + }, + { + name: 'RTMP', + settings: { ...pullmatrix.settings, address: 'rtmp://127.0.0.1/live/stream' }, + skills: $skills_ffmpeg6, + input: { + address: 'rtmp://admin:foobar@127.0.0.1/live/stream', + options: ['-fflags', '+genpts', '-thread_queue_size', 512, '-analyzeduration', 3000000], + }, + }, + { + name: 'HTTP', + settings: { ...pullmatrix.settings, address: 'http://127.0.0.1/live/stream.m3u8' }, + skills: $skills_ffmpeg6, + input: { + address: 'http://admin:foobar@127.0.0.1/live/stream.m3u8', + options: [ + '-fflags', + '+genpts', + '-thread_queue_size', + 512, + '-analyzeduration', + 20000000, + '-re', + '-r', + 25, + '-user_agent', + 'foobaz/1', + '-referer', + 'http://example.com', + ], + }, + }, + { + name: 'SRT', + settings: { ...pullmatrix.settings, address: 'srt://127.0.0.1?mode=caller&streamid=foobar' }, + skills: $skills_ffmpeg6, + input: { + address: 'srt://127.0.0.1?mode=caller&streamid=foobar', + options: ['-fflags', '+genpts', '-thread_queue_size', 512], + }, + }, ]; test.each(pullmatrix.tests)('source:network pull $name input with ffmpeg $skills.ffmpeg.version', async (data) => { @@ -231,6 +302,7 @@ test('source:network push', async () => { mode: 'push', push: { type: 'rtmp', + name: 'external', }, }; const handleChange = (settings) => { @@ -244,9 +316,17 @@ test('source:network push', async () => { expect(queryByText(`The available FFmpeg binary doesn't support any of the required protocols.`)).toBeInTheDocument(); + rerender(); + + expect(queryByText(`The available FFmpeg binary doesn't support any of the required protocols.`)).toBe(null); + rerender(); expect(queryByText(`The available FFmpeg binary doesn't support any of the required protocols.`)).toBe(null); + + rerender(); + + expect(queryByText(`The available FFmpeg binary doesn't support any of the required protocols.`)).toBe(null); }); test('source:network push RTMP', async () => { @@ -254,6 +334,7 @@ test('source:network push RTMP', async () => { mode: 'push', push: { type: 'rtmp', + name: 'external', }, }; const handleChange = (settings) => { @@ -278,6 +359,7 @@ test('source:network push SRT', async () => { mode: 'push', push: { type: 'srt', + name: 'external', }, }; const handleChange = (settings) => { @@ -302,6 +384,7 @@ const pushmatrix = { mode: 'push', push: { type: '', + name: 'external', }, }, tests: [], @@ -348,6 +431,26 @@ pushmatrix.tests = [ options: ['-fflags', '+genpts', '-thread_queue_size', 512], }, }, + { + name: 'RTMP', + settings: { ...pushmatrix.settings, push: { ...pushmatrix.push, type: 'rtmp' } }, + skills: $skills_ffmpeg6, + config: $config, + input: { + address: '{rtmp,name=external.stream}', + options: ['-fflags', '+genpts', '-thread_queue_size', 512, '-analyzeduration', 3000000], + }, + }, + { + name: 'SRT', + settings: { ...pushmatrix.settings, push: { ...pushmatrix.push, type: 'srt' } }, + skills: $skills_ffmpeg6, + config: $config, + input: { + address: '{srt,name=external.stream,mode=request}', + options: ['-fflags', '+genpts', '-thread_queue_size', 512], + }, + }, ]; test.each(pushmatrix.tests)('source:network push $name input with ffmpeg $skills.ffmpeg.version', async (data) => { diff --git a/src/views/Edit/Sources/NoAudio.js b/src/views/Edit/Sources/NoAudio.js index 3583118..b6f7668 100644 --- a/src/views/Edit/Sources/NoAudio.js +++ b/src/views/Edit/Sources/NoAudio.js @@ -44,7 +44,7 @@ function SourceIcon(props) { const id = 'noaudio'; const name = No audio; const capabilities = ['audio']; -const ffversion = '^4.1.0 || ^5.0.0'; +const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0'; const func = { initSettings, diff --git a/src/views/Edit/Sources/Raspicam.js b/src/views/Edit/Sources/Raspicam.js index 682942b..471704f 100644 --- a/src/views/Edit/Sources/Raspicam.js +++ b/src/views/Edit/Sources/Raspicam.js @@ -143,7 +143,7 @@ function SourceIcon(props) { const id = 'raspicam'; const name = Raspberry Pi camera; const capabilities = ['video']; -const ffversion = '^4.1.0 || ^5.0.0'; +const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0'; const func = { initSettings, diff --git a/src/views/Edit/Sources/V4L.js b/src/views/Edit/Sources/V4L.js index b01f29e..003c166 100644 --- a/src/views/Edit/Sources/V4L.js +++ b/src/views/Edit/Sources/V4L.js @@ -153,7 +153,7 @@ function SourceIcon(props) { const id = 'video4linux2'; const name = Hardware device; const capabilities = ['video']; -const ffversion = '^4.1.0 || ^5.0.0'; +const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0'; const func = { initSettings, diff --git a/src/views/Edit/Sources/VideoAudio.js b/src/views/Edit/Sources/VideoAudio.js index 8279e34..47b3ec0 100644 --- a/src/views/Edit/Sources/VideoAudio.js +++ b/src/views/Edit/Sources/VideoAudio.js @@ -44,7 +44,7 @@ function SourceIcon(props) { const id = 'videoaudio'; const name = Video source; const capabilities = ['audio']; -const ffversion = '^4.1.0 || ^5.0.0'; +const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0'; const func = { initSettings, diff --git a/src/views/Edit/Sources/VideoLoop.js b/src/views/Edit/Sources/VideoLoop.js index f532547..9350f33 100644 --- a/src/views/Edit/Sources/VideoLoop.js +++ b/src/views/Edit/Sources/VideoLoop.js @@ -201,7 +201,7 @@ function SourceIcon(props) { const id = 'videoloop'; const name = Loop; const capabilities = ['video']; -const ffversion = '^4.1.0 || ^5.0.0'; +const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0'; const func = { initSettings, diff --git a/src/views/Edit/Sources/VirtualAudio.js b/src/views/Edit/Sources/VirtualAudio.js index 3885a43..de0b0d5 100644 --- a/src/views/Edit/Sources/VirtualAudio.js +++ b/src/views/Edit/Sources/VirtualAudio.js @@ -186,7 +186,7 @@ function SourceIcon(props) { const id = 'virtualaudio'; const name = Virtual source; const capabilities = ['audio']; -const ffversion = '^4.1.0 || ^5.0.0'; +const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0'; const func = { initSettings, diff --git a/src/views/Edit/Sources/VirtualVideo.js b/src/views/Edit/Sources/VirtualVideo.js index f2b3c69..aeb8dfa 100644 --- a/src/views/Edit/Sources/VirtualVideo.js +++ b/src/views/Edit/Sources/VirtualVideo.js @@ -205,7 +205,7 @@ function SourceIcon(props) { const id = 'virtualvideo'; const name = Virtual source; const capabilities = ['video']; -const ffversion = '^4.1.0 || ^5.0.0'; +const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0'; const func = { initSettings, diff --git a/src/views/Edit/Wizard/Sources/InternalHLS.js b/src/views/Edit/Wizard/Sources/InternalHLS.js index ae4942e..929153f 100644 --- a/src/views/Edit/Wizard/Sources/InternalHLS.js +++ b/src/views/Edit/Wizard/Sources/InternalHLS.js @@ -9,9 +9,9 @@ import * as S from '../../Sources/Network'; import BoxTextarea from '../../../../misc/BoxTextarea'; import Textarea from '../../../../misc/Textarea'; -const initSettings = (initialSettings) => { +const initSettings = (initialSettings, config) => { const settings = { - ...S.func.initSettings(initialSettings), + ...S.func.initSettings(initialSettings, config), mode: 'push', }; @@ -21,8 +21,8 @@ const initSettings = (initialSettings) => { }; function Source(props) { - const settings = initSettings(props.settings); const config = S.func.initConfig(props.config); + const settings = initSettings(props.settings, config); const skills = S.func.initSkills(props.skills); const handleChange = (newSettings) => { diff --git a/src/views/Edit/Wizard/Sources/InternalRTMP.js b/src/views/Edit/Wizard/Sources/InternalRTMP.js index 0cb3e0b..fb6622c 100644 --- a/src/views/Edit/Wizard/Sources/InternalRTMP.js +++ b/src/views/Edit/Wizard/Sources/InternalRTMP.js @@ -11,9 +11,9 @@ import * as S from '../../Sources/Network'; import BoxTextarea from '../../../../misc/BoxTextarea'; import Textarea from '../../../../misc/Textarea'; -const initSettings = (initialSettings) => { +const initSettings = (initialSettings, config) => { const settings = { - ...S.func.initSettings(initialSettings), + ...S.func.initSettings(initialSettings, config), mode: 'push', }; @@ -24,8 +24,8 @@ const initSettings = (initialSettings) => { function Source(props) { const navigate = useNavigate(); - const settings = initSettings(props.settings); const config = S.func.initConfig(props.config); + const settings = initSettings(props.settings, config); const skills = S.func.initSkills(props.skills); const handleChange = (newSettings) => { diff --git a/src/views/Edit/Wizard/Sources/InternalSRT.js b/src/views/Edit/Wizard/Sources/InternalSRT.js index cf1b89d..c5e0bbd 100644 --- a/src/views/Edit/Wizard/Sources/InternalSRT.js +++ b/src/views/Edit/Wizard/Sources/InternalSRT.js @@ -11,9 +11,9 @@ import * as S from '../../Sources/Network'; import BoxTextarea from '../../../../misc/BoxTextarea'; import Textarea from '../../../../misc/Textarea'; -const initSettings = (initialSettings) => { +const initSettings = (initialSettings, config) => { const settings = { - ...S.func.initSettings(initialSettings), + ...S.func.initSettings(initialSettings, config), mode: 'push', }; @@ -24,8 +24,8 @@ const initSettings = (initialSettings) => { function Source(props) { const navigate = useNavigate(); - const settings = initSettings(props.settings); const config = S.func.initConfig(props.config); + const settings = initSettings(props.settings, config); const skills = S.func.initSkills(props.skills); const handleChange = (newSettings) => { diff --git a/src/views/Edit/Wizard/Sources/Network.js b/src/views/Edit/Wizard/Sources/Network.js index 4a352f7..88578c0 100644 --- a/src/views/Edit/Wizard/Sources/Network.js +++ b/src/views/Edit/Wizard/Sources/Network.js @@ -10,9 +10,9 @@ import * as S from '../../Sources/Network'; import Checkbox from '../../../../misc/Checkbox'; import Password from '../../../../misc/Password'; -const initSettings = (initialSettings) => { +const initSettings = (initialSettings, config) => { const settings = { - ...S.func.initSettings(initialSettings), + ...S.func.initSettings(initialSettings, config), mode: 'pull', }; @@ -20,8 +20,8 @@ const initSettings = (initialSettings) => { }; function Source(props) { - const settings = initSettings(props.settings); const config = S.func.initConfig(props.config); + const settings = initSettings(props.settings, config); const skills = S.func.initSkills(props.skills); const handleChange = (newSettings) => { diff --git a/src/views/Edit/Wizard/Sources/V4L.js b/src/views/Edit/Wizard/Sources/V4L.js index 055742b..7417a08 100644 --- a/src/views/Edit/Wizard/Sources/V4L.js +++ b/src/views/Edit/Wizard/Sources/V4L.js @@ -97,7 +97,7 @@ function Source(props) { options.push( {i18n._(t`No input device available`)} - + , ); }