Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

List of features proposed for deprecation or removal #2592

Open
2 of 3 tasks
toots opened this issue Aug 31, 2022 · 13 comments
Open
2 of 3 tasks

List of features proposed for deprecation or removal #2592

toots opened this issue Aug 31, 2022 · 13 comments
Assignees

Comments

@toots
Copy link
Member

toots commented Aug 31, 2022

This issue tracks all features proposes for deprecation of removal to anticipate them and discuss:

Deprecation

Deprecated support should be kept for at least 1 or two major release cycle.

  • GStreamer: GStreamer API is expected to be used with GUI applications. It's very high level and pretty opaque to debug. Most of its functionalities should be supported via ffmpeg. It's been effectively disabled in our CI for a while because it kept failing it randomly or was hard to build accross platforms so we don't even track if it works at this point.
  • Switch-based transitions: Those transitions do not pre-buffer data and have been historically hard to use and understand from users. Cross-based transitions are much more powerful and should be preferred. We might want to implement some helpers to facilitate migrating switch-based use cases to cross-based transitions

Removal

  • let json.stringify. This was me trying to be overzealous and code a rendering counter-point to let json.parse but, in fact, the type annotation does not work with generalized functions. We should return to json.stringify(...)
@mylselgan
Copy link

Can you please explain how removing an existing/working feature will positively affect the development?
I am using GStreamer in few of my scripts

@toots
Copy link
Member Author

toots commented Sep 20, 2022

Can you please explain how removing an existing/working feature will positively affect the development? I am using GStreamer in few of my scripts

From our perspective, this reduces the maintenance burden and potential for bugs (for instance memory leaks in the decoder).

Thank you for chiming in, though, this is exactly why those features are listed here! Our goal is to replace all possible use-case of gstreamer with a ffmpeg equivalent.

If you want to share more about how you're using gstreamer with liquidsoap, we could see how your use-case could be migrated to ffmpeg.

@toots
Copy link
Member Author

toots commented Oct 13, 2022

After more thinking, I am leaning toward implementing a single, minimal fallback operator and re-implementing switch and fallback with transitions using the cross operator on top of them. There's a proof of concept here: #2685. This is similar to the work that @smimram has done implementing a native API for those operators. Ultimately, source.dynamic could be used to implement the low-level fallback in the scripting language.

The effort is stalled at the moment because of some limitations in the clock implementation that we intent to fix in near future versions (most likely 2.3)

@mylselgan
Copy link

mylselgan commented Oct 16, 2022

I use gstreamer in this way

url = "https://mediaserviceslive.akamaized.net/hls/live/2038311/newsradio/index.m3u8"
s = input.gstreamer.audio(pipeline=url)

@mylselgan
Copy link

and in this issue #762

@smimram added a automatic restart for gstreamer pipeline if the input url unavailable. Particularly useful for Youtube Live URL which expires every 6 hours.
I Think this will be a very important feature.

@toots
Copy link
Member Author

toots commented Oct 16, 2022

input.ffmpeg can now be used to read HLS. We should test it again but I'm pretty sure that the stream automatically restarts when it gets disconnected.

@mylselgan
Copy link

I Have tested input.ffmpeg. It worked for the first 6 hours, When the m3u8 URL expires it feeds silence.

url = process.read("/usr/local/bin/youtube-dl ytsearch20:..............")
s = input.ffmpeg(url)
2022/10/18 09:45:20 >>> LOG START
2022/10/18 09:45:15 [main:3] Liquidsoap 2.1.2
2022/10/18 09:45:15 [main:3] Using: bytes=[distributed with OCaml 4.02 or above] pcre=7.5.0 sedlex=3.0 menhirLib=20220210 curl=0.9.2 uri=4.2.0 dtools=0.4.4 duppy=0.9.2 cry=0.6.7 mm=0.8.1 ogg=0.7.3 ogg.decoder=0.7.3 vorbis=0.8.0 vorbis.d>
2022/10/18 09:45:15 [clock:3] Using builtin (low-precision) implementation for latency control
2022/10/18 09:45:16 [lang:2] WARNING: "set" is deprecated and will be removed in future version. Please use `settings.path.to.key.set(value)`
2022/10/18 09:45:16 [lang:2] WARNING: "set" is deprecated and will be removed in future version. Please use `settings.path.to.key.set(value)`
2022/10/18 09:45:16 [lang:2] WARNING: "set" is deprecated and will be removed in future version. Please use `settings.path.to.key.set(value)`
2022/10/18 09:45:16 [lang:2] WARNING: "set" is deprecated and will be removed in future version. Please use `settings.path.to.key.set(value)`
2022/10/18 09:45:16 [lang:2] WARNING: "set" is deprecated and will be removed in future version. Please use `settings.path.to.key.set(value)`
2022/10/18 09:45:17 [lang:2] WARNING: "set" is deprecated and will be removed in future version. Please use `settings.path.to.key.set(value)`
2022/10/18 09:45:17 [lang:2] WARNING: "set" is deprecated and will be removed in future version. Please use `settings.path.to.key.set(value)`
2022/10/18 09:45:17 [lang:2] WARNING: "set" is deprecated and will be removed in future version. Please use `settings.path.to.key.set(value)`
2022/10/18 09:45:17 [lang:2] WARNING: "set" is deprecated and will be removed in future version. Please use `settings.path.to.key.set(value)`
2022/10/18 09:45:20 [frame:3] Using 44100Hz audio, 25Hz video, 44100Hz main.
2022/10/18 09:45:20 [frame:3] Video frame size set to: 1280x720
2022/10/18 09:45:20 [frame:3] Frame size must be a multiple of 1764 ticks = 1764 audio samples = 1 video samples.
2022/10/18 09:45:20 [frame:3] Targeting 'frame.duration': 0.04s = 1764 audio samples = 1764 ticks.
2022/10/18 09:45:20 [frame:3] Frames last 0.04s = 1764 audio samples = 1 video samples = 1764 ticks.
2022/10/18 09:45:20 [sandbox:3] Sandboxing disabled
2022/10/18 09:45:20 [video.converter:3] Using preferred video converter: ffmpeg.
2022/10/18 09:45:20 [audio.converter:3] Using samplerate converter: libsamplerate.
2022/10/18 09:45:20 [video.text:3] Using native implementation
2022/10/18 09:45:20 [clock.main:3] Streaming loop starts in auto-sync mode
2022/10/18 09:45:20 [clock.main:3] Delegating synchronisation to CPU clock
2022/10/18 09:45:20 [switch_1:3] Switch to drop_video_0.
2022/10/18 09:45:20 [mksafe:3] Switch to safe_blank.
2022/10/18 09:45:20 [/tamilnews247:3] Connecting mount /tamilnews247 for source@localhost...
2022/10/18 09:45:20 [/tamilnews247:3] Connection setup was successful.
2022/10/18 09:45:21 [mksafe:3] Switch to input.ffmpeg_0 with transition.
2022/10/18 10:59:54 [clock.main:2] We must catchup 2.97 seconds!
2022/10/18 10:59:59 [clock.main:2] We must catchup 3.05 seconds!
2022/10/18 11:00:06 [clock.main:2] We must catchup 5.64 seconds!
2022/10/18 11:00:14 [clock.main:2] We must catchup 8.83 seconds (we've been late for 100 rounds)!
2022/10/18 11:00:20 [clock.main:2] We must catchup 8.97 seconds (we've been late for 100 rounds)!
2022/10/18 11:00:25 [clock.main:2] We must catchup 9.35 seconds (we've been late for 100 rounds)!
2022/10/18 11:00:30 [clock.main:2] We must catchup 4.25 seconds!
2022/10/18 11:02:14 [clock.main:2] We must catchup 3.27 seconds!
2022/10/18 11:02:19 [clock.main:2] We must catchup 3.32 seconds!
2022/10/18 11:02:24 [clock.main:2] We must catchup 3.35 seconds!
2022/10/18 11:02:29 [clock.main:2] We must catchup 3.72 seconds!
2022/10/18 11:02:37 [clock.main:2] We must catchup 6.08 seconds!
2022/10/18 11:02:42 [clock.main:2] We must catchup 6.17 seconds (we've been late for 100 rounds)!
2022/10/18 11:02:47 [clock.main:2] We must catchup 6.69 seconds (we've been late for 100 rounds)!
2022/10/18 11:06:44 [clock.main:2] We must catchup 3.71 seconds!
2022/10/18 11:06:52 [clock.main:2] We must catchup 6.59 seconds!
2022/10/18 11:06:57 [clock.main:2] We must catchup 6.68 seconds (we've been late for 100 rounds)!
2022/10/18 11:07:03 [clock.main:2] We must catchup 7.09 seconds (we've been late for 100 rounds)!
2022/10/18 11:07:08 [clock.main:2] We must catchup 2.06 seconds!
.
.
. same lines
.
2022/10/18 11:49:12 [clock.main:2] We must catchup 31.59 seconds (we've been late for 100 rounds)!
2022/10/18 11:49:17 [stderr:3] [tls @ 0x7f9d7c039500] Error in the pull function.
2022/10/18 11:49:17 [stderr:3] [hls @ 0x7f9d7c003040] keepalive request failed for 'https://rr5---sn-5hneknek.googlevideo.com/videoplayback/id/V9u4YGe60fg.2/itag/91/source/yt_live_broadcast/expire/1666088119/ei/VyhOY4mGL9Gh-gaUso_ABQ/ip>
2022/10/18 11:49:18 [clock.main:2] We must catchup 32.00 seconds (we've been late for 100 rounds)!
2022/10/18 11:49:23 [clock.main:2] We must catchup 26.99 seconds (we've been late for 100 rounds)!
2022/10/18 11:49:28 [clock.main:2] We must catchup 21.91 seconds (we've been late for 100 rounds)!
2022/10/18 11:49:33 [clock.main:2] We must catchup 12.01 seconds (we've been late for 100 rounds)!
2022/10/18 11:49:38 [clock.main:2] We must catchup 7.11 seconds (we've been late for 100 rounds)!
2022/10/18 13:01:57 [clock.main:2] We must catchup 1.03 seconds!
2022/10/18 13:02:17 [clock.main:2] We must catchup 1.01 seconds!
2022/10/18 13:07:48 [clock.main:2] We must catchup 7.28 seconds!
2022/10/18 13:09:17 [clock.main:2] We must catchup 1.06 seconds!
2022/10/18 13:12:27 [clock.main:2] We must catchup 1.53 seconds!
.
.
. same lines
.
.
2022/10/18 15:01:57 [clock.main:2] We must catchup 1.06 seconds!
2022/10/18 15:26:27 [clock.main:2] We must catchup 1.06 seconds!
2022/10/18 15:45:21 [stderr:3] [https @ 0x7f9d7454b880] HTTP error 403 Forbidden
2022/10/18 15:45:21 [stderr:3] [hls @ 0x7f9d7c003040] keepalive request failed for 'https://manifest.googlevideo.com/api/manifest/hls_playlist/expire/1666088119/ei/VyhOY4mGL9Gh-gaUso_ABQ/ip/2a01:4f8:c0c:3332:0:0:0:1/id/V9u4YGe60fg.2/ita>
2022/10/18 15:45:21 [stderr:3] ' with error: 'Server returned 403 Forbidden (access denied)' when parsing playlist
2022/10/18 15:45:21 [stderr:3] [https @ 0x7f9d7454b880] HTTP error 403 Forbidden
2022/10/18 15:45:21 [stderr:3] [hls @ 0x7f9d7c003040] Failed to reload playlist 0
2022/10/18 15:45:21 [stderr:3] [https @ 0x7f9d7454b880] HTTP error 403 Forbidden
2022/10/18 15:45:21 [stderr:3] [hls @ 0x7f9d7c003040] Failed to reload playlist 0
2022/10/18 15:45:21 [input.ffmpeg_0:2] Feeding failed: Ffmpeg_decoder.End_of_file
2022/10/18 15:45:21 [mksafe:3] Switch to safe_blank with forgetful transition.
2022/10/18 15:45:21 [stderr:3] [https @ 0x7f9d80005d40] HTTP error 403 Forbidden
2022/10/18 15:45:23 [stderr:3] [https @ 0x7f9d8030f6c0] HTTP error 403 Forbidden
2022/10/18 15:45:25 [stderr:3] [https @ 0x7f9d800d6ac0] HTTP error 403 Forbidden
2022/10/18 15:45:27 [stderr:3] [https @ 0x7f9d801a0780] HTTP error 403 Forbidden
2022/10/18 15:45:29 [stderr:3] [https @ 0x7f9d801a0780] HTTP error 403 Forbidden
2022/10/18 15:45:31 [stderr:3] [https @ 0x7f9d8030f6c0] HTTP error 403 Forbidden
2022/10/18 15:45:34 [stderr:3] [https @ 0x7f9d800a3200] HTTP error 403 Forbidden

@toots
Copy link
Member Author

toots commented Oct 20, 2022

You should try to reload the url regularly. Since this takes time I'd suggest to reload it for instance once every hour:

youtube_url = ref("")

def reload_youtube_url() =
  youtube_url := process.read("/usr/local/bin/youtube-dl ytsearch20:..............")
end

# Initial load
reload_youtube_url()

# Reload every hour starting in one hour
thread.run(delay=60*60, every=60*60, reload_youtube_url)

s = input.ffmpeg({!youtube_url})

@JonathanThorpe
Copy link

JonathanThorpe commented Nov 27, 2022

I haven't looked into replacing GStreamer with ffmpeg, but I find its pipeline functionality very powerful and complementary to Liquidsoap.

Here are a couple of examples I use:

Receives an RTP stream from a Barix Instreamer:

source = mksafe(input.gstreamer.audio(pipeline='udpsrc port=5004 caps="application/x-rtp, media=(string)audio, clock-rate=(int)48000, format=(string)S16BE, channels=(int)2, payload=(int)96" ! queue ! rtpL16depay', max=1.0))

Similarly, I can create an output RTP stream as well:
output.gstreamer.audio(pipeline='audioconvert ! audioresample ! rtpL16pay pt=10 ! application/x-rtp, clock-rate=48000, channels=2, format=S16LE ! udpsink host=172.24.1.2 port=6002 async=true', source)

I feel the input could probably be handled by ffmpeg, but what about the outputs?

@mylselgan
Copy link

Hi, currently we have output.youtube.live as a wrapper around output.gstreamer.audio_video to stream live to Youtube. How can I migrate to ffmpeg.

@toots
Copy link
Member Author

toots commented Apr 9, 2023

I haven't looked into replacing GStreamer with ffmpeg, but I find its pipeline functionality very powerful and complementary to Liquidsoap.

Here are a couple of examples I use:

Receives an RTP stream from a Barix Instreamer:

source = mksafe(input.gstreamer.audio(pipeline='udpsrc port=5004 caps="application/x-rtp, media=(string)audio, clock-rate=(int)48000, format=(string)S16BE, channels=(int)2, payload=(int)96" ! queue ! rtpL16depay', max=1.0))

Similarly, I can create an output RTP stream as well: output.gstreamer.audio(pipeline='audioconvert ! audioresample ! rtpL16pay pt=10 ! application/x-rtp, clock-rate=48000, channels=2, format=S16LE ! udpsink host=172.24.1.2 port=6002 async=true', source)

I feel the input could probably be handled by ffmpeg, but what about the outputs?

Both input and output can be handled by ffmpeg! On the receiving side, input.ffmpeg is a Swiss knife that is intended to support pretty much anything that can be decoded with the ffmpeg CLI. Options can be passed in a similar way that you do with the CLI. For instance, this is the code to create a RTMP listening server:

# Read an RTMP stream.
# @category Source / Input
# @param ~max_buffer Maximum data buffer in seconds
# @param ~listen Act as a RTMP server and wait for incoming connection
# @param url URL to read RTMP from, in the form `rtmp://IP:PORT/ENDPOINT`
def input.rtmp(~id=null(), ~max_buffer=5., ~listen=true, url)
  input.ffmpeg(id=id, max_buffer=max_buffer, format="live_flv", self_sync=true,
               int_args=[("listen", listen ? 1 : 0)], url)
end

On the sending side, output.url is the dedicated operator to use with the %ffmpeg encoder. It knows how to interact properly between the destination url and the encoding settings. Here's an example to send to a youtube RTMP url:

output.url(
  url="rtmp://a.rtmp.youtube.com/live2/#{key}",
   %ffmpeg(
      format="flv",
      %audio(codec="aac", b="128k"),
      %video(codec="libx264", b="4000k")
   ),
   s
)

@toots
Copy link
Member Author

toots commented Apr 9, 2023

Hi, currently we have output.youtube.live as a wrapper around output.gstreamer.audio_video to stream live to Youtube. How can I migrate to ffmpeg.

The latest 2.1.4 and future 2.2.0 should both have a output.youtube.live.rtmp operator that uses ffmpeg under the hood (when compiled with it).

The syntax is pretty much the previous example:

output.youtube.live.rtmp(
  key="...",
   %ffmpeg(
      format="flv",
      %audio(codec="aac", b="128k"),
      %video(codec="libx264", b="4000k")
   ),
   s
)

@toots
Copy link
Member Author

toots commented Apr 9, 2023

Thanks for all the feedback y'all. Obviously gstreamer should stay on a little more. It's just been difficult to debug historically for us and we're limited in developer time so we most likely will prioritize ffmpeg debugging and also support, as exemplified here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants