savonet / liquidsoap

Liquidsoap is a statically typed scripting general-purpose language with dedicated operators and backend for all thing media, streaming, file generation, automation, HTTP backend and more.
http://liquidsoap.info
GNU General Public License v2.0
1.42k stars 130 forks source link

Stable crashes of output every 47 hours #1268

Open StarveTheEgo opened 4 years ago

StarveTheEgo commented 4 years ago

Hello, dear liquidsoap developers! :-)

Describe the bug After updating to 1.4.2 (but at same time after modifying my radio script, so not really sure after what exact change), i am having exactly same error, that happens every 47 hours (+/ -30minutes)

2020/06/16 03:13:29 [output(dot)gstreamer:2] [rtmpsink0] Error: Could not write to resource.
2020/06/16 03:13:29 [output(dot)gstreamer:3] Error while processing output data: Could not write to resource.
2020/06/16 03:13:29 [output(dot)gstreamer:4] Stacktrace: Raised at file "io/gstreamer_io.ml", line 290, characters 34-60
Called from file "tools/gstreamer_utils.ml", line 159, characters 10-36
Called from file "io/gstreamer_io.ml", line 289, characters 10-113
2020/06/16 03:13:29 [output(dot)gstreamer:2] [audio_src] Error: Internal data stream error.
2020/06/16 03:13:29 [output(dot)gstreamer:3] Error while processing output data: Internal data stream error.
2020/06/16 03:13:29 [output(dot)gstreamer:4] Stacktrace: Raised at file "io/gstreamer_io.ml", line 290, characters 34-60
Called from file "tools/gstreamer_utils.ml", line 159, characters 10-36
Called from file "io/gstreamer_io.ml", line 289, characters 10-113
2020/06/16 03:13:34 [output(dot)gstreamer:3] Restarting pipeline
2020/06/16 03:13:34 [output(dot)gstreamer:3] Error while processing output data: Gstreamer.Failed
2020/06/16 03:13:34 [output(dot)gstreamer:4] Stacktrace: Raised by primitive operation at file "gstreamer.ml", line 394, characters 4-64
Called from file "io/gstreamer_io.ml", line 285, characters 14-153
2020/06/16 03:13:34 [output(dot)gstreamer:5] GStreamer pipeline: appsrc name="video_src" block=true caps="video/x-raw,format=RGBA,width=1920,height=1080,framerate=25/1,pixel-aspect-ratio=1/1" format=time blocksize=8294400 max-bytes=10240 ! videoconvert ! x264enc bitrate=12000 byte-stream=false key-int-max=60 bframes=0 aud=true tune=zerolatency speed-preset=ultrafast ! video/x-h264,profile=main ! queue ! mux. appsrc name="audio_src" block=true caps="audio/x-raw,format=S16LE,layout=interleaved,channels=2,rate=44100" format=time max-bytes=10240 ! audioconvert ! avenc_aac bitrate=320000 ! queue ! mux. flvmux streamable=true name=mux ! rtmpsink location="rtmp://frankfurt.restream.io/live/SOME_STREAM_KEY live=1"

// and after this i have casual spam every 30 seconds of my php command usage, which is normal and means, liquidsoap still works, while gstreamer output looks dead

To Reproduce Not sure, but i will just provide some base version of my radio script. Please, note, that i tried to anonymize it while posting here, so it will not work from scratch, sorry. I posted a script with a hope that there is maybe some issue in my script

audio_samplerate = 44100
video_width = 1920
video_height = 1080
video_samplerate = 25

set("log.level", 5)
set("log.stdout", true)
set("sandbox.rw", ['/writable/paths', '/are/here'])
set("frame.audio.samplerate", audio_samplerate)
set("frame.video.width", video_width)
set("frame.video.height", video_height)
set("frame.video.samplerate", video_samplerate)
set("gstreamer.add_borders", true)
set("request.grace_time", 80.0) # default: 600.0

set("video.converter.proportional_scale", false)
set("decoder.file_decoders",["META", "FFMPEG", "WAV", "MAD", "MIDI", "IMAGE","OGG","MAD","FLAC"])
set("gstreamer.max_buffers", 20)

# Radio Settings
settings_json = list.hd(default="", get_process_lines("command/to/get/json/settings"))
settings = of_json(default=[("list",[("key","value")])], settings_json)

# Livestream settings
livestream_settings = list.assoc(default=[], "livestream", settings)
restream_host = livestream_settings["endpoint"]
restream_key = livestream_settings["api_key"]

main_font_path = "/path/to/font.ttf"

author_info = ref ""
label_info = ref ""

song_author = ref 'Author'
song_title = ref 'Title'
song_album = ref ''
song_album_year = ref ''
author_website = ref ''
author_links = ref []
author_link_index = ref -1

song_label = ref ''
label_website = ref ''
label_links = ref []
label_link_index = ref -1

image_link = ref ''
image_profile_link = ref ''
image_author = ref 'Loading...'
image_desc = ref ''

def clear_app_cache() =
    ignore(get_process_lines("command/to/clear/app/cache"))
end

def output.restream.live(~id="",
  ~video_bitrate=2000,
  ~audio_encoder="fdkaacenc",
  ~audio_bitrate=128000,
  ~url="rtmp://moscow.restream.io/live",
  ~key,
  source) =
  video_pipeline = "videoconvert ! x264enc bitrate=#{video_bitrate} byte-stream=false key-int-max=60 bframes=0 aud=true tune=zerolatency speed-preset=ultrafast ! video/x-h264,profile=main ! queue ! mux."
  audio_pipeline = "audioconvert ! #{audio_encoder} bitrate=#{audio_bitrate} ! queue ! mux."
  pipeline = "flvmux streamable=true name=mux ! rtmpsink location=\"#{url}/#{key} live=1\""

  output.gstreamer.audio_video(
    id=id,
    video_pipeline=video_pipeline,
    audio_pipeline=audio_pipeline,
    pipeline=pipeline,
    fallible=true,
    on_error = fun(_) -> 5.0, # it supposed to restart after 5 seconds, but it just stops the output!
    on_start = clear_app_cache,
    source
  )
end

# @todo: escape '!'
def send_notification(value) =
    is_sent = test_process("command/to/send/youtube/chat/message $(notification)" % [("notification",
      string.utf8.escape(json_of(value))
    )])
    if (is_sent) then
        log('Successfully sent notification: ' ^ value)
    else
        log('Could not send notification: ' ^ value)
    end
end

def update_current_shop_links() =
    # AUTHOR LINK
    author_links_length = list.length(!author_links)
    if (author_links_length > 1) then
        if !author_link_index >= (author_links_length - 1) then
            author_link_index := 0
        else
            author_link_index := !author_link_index + 1
        end
    else
        author_link_index := 0
    end
    author_website := list.nth(default='', !author_links, !author_link_index)

    # LABEL LINK
    label_links_lengths = list.length(!label_links)
    if (label_links_lengths > 1) then
        if !label_link_index >= (label_links_lengths - 1) then
            label_link_index := 0
        else
            label_link_index := !label_link_index + 1
        end
    else
        label_link_index := 0
    end
    label_website := list.nth(default='', !label_links, !label_link_index)
end

def update_image() =
    ignore(get_process_lines("command/to/update/image"))
end

def get_song_notification() =
   if (list.length(!author_links) > 0) then
        author_info := " | You can find more info about $(artist) at: $(links)" % [
            ("artist", !song_author),
            ("links", string.concat(separator=" | ", !author_links))
        ]
   else
        author_info := ""
   end
   "Song: $(artist) - $(title)" % [("artist", !song_author), ("title", !song_title)]
   ^
   !author_info
end

def get_label_notification() =
   if (list.length(!label_links) > 0) then
        label_info := " ($(links))" % [
            ("links", string.concat(separator=" | ", !label_links))
        ]
   else
        label_info := ""
   end
   "Released on label \"$(label)\"" % [("label", !song_label)]
   ^
   !label_info
end

def apply_song(m) =
  track_id = m["id"]
  song_author := m["artist"]
  author_links := of_json(default=[], m["author_links"])
  label_links := of_json(default=[], m["label_links"])
  update_current_shop_links()

  song_title := m["title"]
  song_album := m["album"]
  song_album_year := m["album_year"]
  song_label := m["label"]

  log("Now playing $(title) by $(artist)" % [("artist", !song_author), ("title", !song_title)])

  ignore(get_process_lines("command/to/update/now/playing/song"))
  log("actually playing")
  update_image()
  send_notification(get_song_notification())

  if (string.length(!song_label) > 0) then
    send_notification(get_label_notification())
  end
end

# PERIODIC LINKS
exec_at(freq=10., pred = {true}, update_current_shop_links)

# AUTHOR'S SHOP/PERSONAL LINK
def get_shop_link_text()
    if !author_website == "" then
       "<link should appear soon>"
    else
        !author_website
    end
end

# TRACK ALBUM
def get_track_album_text() =
    if !song_album == "" then
        ""
    else
       "Album: $(album) ($(year))" % [
            ("album", !song_album),
            ("year", !song_album_year)
        ]
    end
end

# SONG LABEL
def get_track_label_text() =
    if !song_label == "" then
        ""
    else
       "Label: $(label)" % [
            ("label", !song_label),
        ]
    end
end

# SONG LABEL LINK
def get_track_label_link() =
    !label_website
end

# IMAGE ANNOTATION
def get_image_author()
    "Photo by " ^ !image_author
end

# IMAGE DESCRIPTION
def get_image_desc()
    !image_desc
end

# IMAGE LINK
def get_image_link()
    !image_link
end

# TEXT FOR IMAGE PROFILE LINK
def get_image_profile_link()
    !image_profile_link
end

def get_photo_notification() =
    "Photo: $(photo_link) | Photographer: $(author) ($(profile_link))" % [
        ("photo_link", !image_link),
        ("author", !image_author),
        ("profile_link", !image_profile_link)
    ]
end

def apply_image(m) =
      image_link := m["image_link"]
      image_profile_link := m["profile_link"]
      image_author := m["author"]
      image_desc := m["description"]
      log("Got new image: $(annotation)" % [("annotation", !image_author)])
end

exec_at(freq=30., pred = {true}, update_image)

#
def get_image() =
  uri = list.hd(default="", get_process_lines("command/to/update/image"))
  [request.create(uri)]
end

def get_next_track(param="") =
  uri = list.hd(default="",get_process_lines("command/that/returns/single/annotated/local/file/path"))
  [request.create(uri)]
end

# MUSIC
audio = request.dynamic.list(id="audio_stream", timeout=15.0, length=10.0, get_next_track)
audio = skip_blank(audio)
audio = amplify(1.,override="replay_gain",audio)
audio = on_track(apply_song,audio)
audio = crossfade(audio, smart = true, fade_in = 0.0)
audio = mksafe(audio)

# A string with Title - Author data
def get_track_name_text()
   "$(artist) - $(title)" % [
        ("artist", !song_author),
        ("title", !song_title)
    ]
end

# VIDEO BASE

video = request.dynamic.list(id="image", timeout=15.0, get_image)
video = on_track(apply_image, video)

# BOTTOM SCREEN PART

# TOP BACKGROUND
top_background = blank()
top_background = video.fill(top_background)
top_background = video.scale(xscale=1.0, yscale=0.04,x=0,y=0,top_background)
top_background = video.opacity(0.4, top_background)
video = add([video, mksafe(top_background)])

# TOP INFO TEXT
video = video.add_text.sdl(
    font = main_font_path,
    size = 22,
    speed = 40,
    x = 10,
    y = 1,
    "Some radio information string",
    video
)

# BOTTOM BACKGROUND
bottom_background = blank()
bottom_background = video.fill(bottom_background)
bottom_background = video.scale(xscale=1.0, yscale=0.5,x=0,y=825,bottom_background)
bottom_background = video.opacity(0.5, bottom_background)
video = add([video, mksafe(bottom_background)])

#
# SONG INFO
#

# SONG NAME (AUTHOR - TITLE)
video = video.add_text.sdl(
    font = main_font_path,
    size = 28,
    speed = 0,
    x = 30,
    y = 833,
    get_track_name_text,
    video
)

# AUTHOR'S SHOP LINK
video = video.add_text.sdl(
    font = main_font_path,
    size = 28,
    speed = 0,
    x = 30,
    y = 893,
    get_track_album_text,
    video
)

# SONG ALBUM
video = video.add_text.sdl(
    font = main_font_path,
    size = 28,
    speed = 0,
    x = 30,
    y = 945,
    get_shop_link_text,
    video
)

# SONG LABEL
video = video.add_text.sdl(
    font = main_font_path,
    size = 28,
    speed = 0,
    x = 30,
    y = 990,
    get_track_label_text,
    video
)

video = video.add_text.sdl(
    font = main_font_path,
    size = 28,
    speed = 0,
    x = 30,
    y = 1028,
    get_track_label_link,
    video
)

# IMAGE ANNOTATION
video = video.add_text.sdl(
    font = main_font_path,
    size = 28,
    speed = 0,
    x = 1095,
    y = 833,
    get_deer_image_author,
    video
)

# IMAGE DESCRIPTION
video = video.add_text.sdl(
    font = main_font_path,
    size = 24,
    speed = 0,
    x = 1095,
    y = 878,
    get_deer_image_desc,
    video
)

# IMAGE LINK
video = video.add_text.sdl(
    font = main_font_path,
    size = 28,
    speed = 0,
    x = 1095,
    y = 923,
    get_image_link,
    video
)

# IMAGE PROFILE LINK
video = video.add_text.sdl(
    font = main_font_path,
    size = 28,
    speed = 0,
    x = 1095,
    y = 968,
    get_image_profile_link,
    video
)

#
# INFORMATION TEXT
#
video = video.add_text.sdl(
    color = 16777215,
    font = main_font_path,
    size = 24,
    speed = 0,
    x = 1095,
    y = 1028,
    "Additional information string",
    video
)

# Final combined stream
combined_stream = mksafe(mux_video(video=video, audio))

#output.dummy(combined_stream)
output.restream.live(
    audio_encoder = "avenc_aac",
    #audio_encoder = "voaacenc",
    video_bitrate = 12000,
    audio_bitrate= 320000,
    url = restream_host,
    key = restream_key,
    combined_stream
)

I am using liquidsoap-daemon to run it as service

Expected behavior Expected behavior is not to stop streaming every 47 hours (so i will not have to restart radio service every 2 days)

Version details

Install method Installed via opam, current setup is:

base-bigarray           base
base-bytes              base        Bytes library distributed with the OCaml com
base-threads            base
base-unix               base
camomile                1.0.2       A Unicode library
conf-libpcre            1           Virtual package relying on a libpcre system
conf-m4                 1           Virtual package relying on m4
conf-pkg-config         1.2         Virtual package relying on pkg-config instal
conf-sdl-image          1           Virtual package relying on a sdl-image syste
conf-sdl-mixer          1           Virtual package relying on a sdl-mixer syste
conf-sdl-ttf            1           Virtual package relying on a sdl-ttf system
cry                     0.6.5       OCaml client for the various icecast & shout
dtools                  0.4.2       Library providing various helper functions t
dune                    2.5.1       Fast, portable, and opinionated build system
dune-configurator       2.5.1       Helper library for gathering system configur
dune-private-libs       2.5.1       Private libraries of Dune
duppy                   0.8.0       Library providing monadic threads
fdkaac                  0.3.2       Fraunhofer FDK AAC Codec Library
ffmpeg                  0.4.1       Bindings for the ffmpeg library which provid
flac                    0.1.7       Interface for the Free Lossless Audio Codec
gavl                    0.1.6       Bindings for the gavl library which provides
gen                     0.5.3       Iterators for OCaml, both restartable and co
gstreamer               0.3.0       Bindings for the GStreamer library which pro
lame                    0.3.3       Bindings for the lame library which provides
liquidsoap              1.4.2       Swiss-army knife for multimedia streaming
mad                     0.4.5       Bindings for the mad library which provides
menhir                  20200211    An LR(1) parser generator
menhirLib               20200211    Runtime support library for parsers generate
menhirSdk               20200211    Compile-time library for auxiliary tools rel
mm                      0.5.0       The mm library contains high-level to create
ocaml                   4.08.0      The OCaml compiler (virtual package)
ocaml-base-compiler     4.08.0      Official release 4.08.0
ocaml-config            1           OCaml Switch Configuration
ocaml-migrate-parsetree 1.7.3       Convert OCaml parsetrees between different v
ocamlbuild              0.14.0      OCamlbuild is a build system with builtin ru
ocamlfind               1.8.1       A library manager for OCaml
ocamlsdl                0.9.1       Interface between OCaml and SDL
ogg                     0.5.2       Interface for Ogg Bitstream Library, otherwi
pcre                    7.4.3       Bindings to the Perl Compatibility Regular E
ppx_derivers            1.2.1       Shared [@@deriving] plugin registry
ppx_tools_versioned     5.4.0       A variant of ppx_tools based on ocaml-migrat
result                  1.5         Compatibility Result module
samplerate              0.1.4       Bindings for the samplerate library which pr
sdl-liquidsoap          1           Virtual package installing liquidsoap's sdl
sedlex                  2.1         An OCaml lexer generator for Unicode
sexplib0                v0.13.0     Library containing the definition of S-expre
taglib                  0.3.6       Bindings for the taglib library which provid
uchar                   0.0.2       Compatibility library for OCaml's Uchar modu
vorbis                  0.7.1       Bindings to libvorbis
toots commented 4 years ago

Thanks for this detailed report! At first glance, it looks like the error is coming from the gstreamer internals:

          GU.flush ~log:self#log
            ~on_error:(fun err -> raise (Flushing_error err))
            el.bin )

I think it's safe to assume that whatever bug you are encountering will need to be chased there. Couple of thoughts about this:

Related to this: I would consider giving the internal ffmpeg encoder in the master branch for this. We're actively working on it so I wouldn't say it's necessarily ready for production right now but we'd love some early testing and feedback, which would help gets us ready quicker and, when we'll be, this should be a much better solution that gstreamer.

StarveTheEgo commented 4 years ago

Thank you for your time :-)

I'm not sure if gstreamer is designed to stream for that amount of time

Before last update of everything (liquidsoap, gstreamer and dependencies) it was working for months without interruptions (with output crash in the end)

But also, probably, issue appears more often after i changed quality from 720p to 1080p, which could just speed up that crash

I will check what is gstreamer designed to do, thank you for this direction

You can set gstreamer's logs verbosity via the LIQ_GST_DEBUG_LEVEL environment variable

I will set gstreamer verbose logging tonight and report on next crash (after 47 hours :-D)

Then i will give ffmpeg encoder a try and participate in testing this way :-)

StarveTheEgo commented 4 years ago

Or your suggestion is to better try ffmpeg in first place? Not a problem for me, especially if you say it will be better solution in the future

toots commented 4 years ago

Thank you for your time :-)

I'm not sure if gstreamer is designed to stream for that amount of time

Before last update of everything (liquidsoap, gstreamer and dependencies) it was working for months without interruptions (with output crash in the end)

But also, probably, issue appears more often after i changed quality from 720p to 1080p, which could just speed up that crash

It's possible. I don't think that we've touched much on gstreamer for a while.

I will check what is gstreamer designed to do, thank you for this direction

You can set gstreamer's logs verbosity via the LIQ_GST_DEBUG_LEVEL environment variable

I will set gstreamer verbose logging tonight and report on next crash (after 47 hours :-D)

Great

toots commented 4 years ago

Or your suggestion is to better try ffmpeg in first place? Not a problem for me, especially if you say it will be better solution in the future

My vision long-term is to deprecate gstreamer and encourage to use ffmpeg as much as possible. Gstreamer is designed for GUI applications, it's been complicated to retrofit it for our use-case and I'm anticipating a lot of similar glitches in the future.

StarveTheEgo commented 4 years ago

Okay, not very related, noob question: I tried to build and install liquidsoap-full, using this guide (https://github.com/savonet/liquidsoap-full/blob/master/README.md), but did not have success

I face error messages while trying to run make install (otherwise i do not know what i built it for)

At start, it said that it could not find 'ocamlfind', so i installed this via apt Then it said it could not find 'prce', so i installed this package via apt too, apt install libpcre-ocaml-dev ^ Worth to mention, both packaged were installed by opam, but still i had those errors, so decided to lucky try with apt installation

And now it says:

ocamlfind: Package `sedlex' not found

But sedlex package is installed in opam too

opam install sedlex
[NOTE] Package sedlex is already installed (current version is 2.2).

And i did not have a luck finding apt package for this

eval $(opam config env) is always executed

What could help me to fix this issue? :-)

I am also sleepy, maybe there is some way to magically pin everything and install it via opam? I really feel like i went some wrong way at installing this

smimram commented 4 years ago

It seems that for some reason your opam packages are not detected. For sedlex you can install libsedlex-ocaml-dev via apt.

StarveTheEgo commented 4 years ago

Okay, so, managed to do clean install of everything liquidsoap-related without involving apt

One problem i faced, i was unable to install tsdl due to compile error I guess, i'll just wait author or tsdl to fix it or something (or maybe my setup requires some fixes, i will check tonight) Changed video.add_text.sdl to video.add_text.gd in my script

But this is not relevant

Here are notable lines from my script now, i am trying to use ffmpeg to stream to YouTube:

audio_samplerate = 44100
video_width = 1920
video_height = 1080
video_framerate = 25

set("frame.audio.samplerate", audio_samplerate)
set("frame.video.width", video_width)
set("frame.video.height", video_height)
set("frame.video.framerate", video_framerate)
set("ffmpeg.scaling_algorithm","fast_bilinear")

output.youtube.live.ffmpeg(
  on_start = clear_app_cache,
  on_stop = log_output_stop,
  bitrate = 12000,
  url = restream_host,
  key = restream_key,
  quality = "veryfast",
  combined_stream
)

My current ffmpeg is:

ffmpeg version 3.4.6-0ubuntu0.18.04.1 Copyright (c) 2000-2019 the FFmpeg developers
  built with gcc 7 (Ubuntu 7.3.0-16ubuntu3)
  configuration: --prefix=/usr --extra-version=0ubuntu0.18.04.1 --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu --enable-gpl --disable-stripping --enable-avresample --enable-avisynth --enable-gnutls --enable-ladspa --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librubberband --enable-librsvg --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzmq --enable-libzvbi --enable-omx --enable-openal --enable-opengl --enable-sdl2 --enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-chromaprint --enable-frei0r --enable-libopencv --enable-libx264 --enable-shared

Here are some results:

  1. Stream audio sounds intermittent
  2. I get constant spam of:
    2020/06/29 05:26:29 [clock.main:2] We must catchup 6.16 seconds!ate= 790.7kbits/s speed=0.318x

    (catchup duration increases every time)

  3. Some notable lines from ffmpeg output:
    
    Guessed Channel Layout for Input Stream #0.1 : stereo
    [libx264 @ 0x5598058d37a0] -qscale is ignored, -crf is recommended.
    [libx264 @ 0x5598058d37a0] VBV maxrate unspecified, assuming CBR
    [libx264 @ 0x5598058d37a0] 264 - core 152 r2854 e9a5903 - H.264/MPEG-4 AVC codec - Copyleft 2003-2017 - http://www.videolan.org/x264.html - options: cabac=1 ref=1 deblock=1:0:0 analyse=0x3:0x113 me=hex subme=2 psy=1 psy_rd=1.00:0.00 mixed_ref=0 me_range=16 chroma_me=1 trellis=0 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=0 threads=6 lookahead_threads=2 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 weightb=1 open_gop=0 weightp=1 keyint=50 keyint_min=5 scenecut=40 intra_refresh=0 rc_lookahead=10 rc=cbr mbtree=1 bitrate=12000 ratetol=1.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 vbv_maxrate=12000 vbv_bufsize=512 nal_hrd=none filler=0 ip_ratio=1.40 aq=1:1.00
    Output #0, flv, to 'rtmp://stream_link_here/key':
    Metadata:
    encoder         : Lavf57.83.100
    Stream #0:0: Video: h264 (libx264) ([7][0][0][0] / 0x0007), yuv420p, 1920x1080, q=-1--1, 12000 kb/s, 25 fps, 1k tbn, 25 tbc
    Metadata:
      encoder         : Lavc57.107.100 libx264
    Side data:
      cpb: bitrate max/min/avg: 0/0/12000000 buffer size: 512000 vbv_delay: -1
    Stream #0:1: Audio: mp3 (libmp3lame) ([2][0][0][0] / 0x0002), 44100 Hz, stereo, s16p, 712 kb/s
    Metadata:
      encoder         : Lavc57.107.100 libmp3lame


What i would love to be able to control in my script:
1. Set audio encoder other than libmp3lame
2. Change audio bitrate
3. Pass some special options for h264 (for example, tune=zerolatency and key-int-max)

And about this issue thread, - do we close it (as gstreamer is planned to be deprecated) and create issue to track ffmpeg things?

Update 30.06.2020: will try external ffmpeg output with full customization and report later
StarveTheEgo commented 4 years ago

Okay, so i tried to test with 720p first, like this:

set("frame.video.width", 1280)
set("frame.video.height", 720)
set("frame.video.framerate", 25)
set("frame.audio.samplerate", 44100)
set("frame.audio.channels", 2)

def output.restream.ffmpeg(
    ~id="",
    ~url="rtmp://restream.io/live",
    ~video_codec="libx264",
    ~video_bitrate="3M",
    ~audio_codec="aac",
    ~audio_bitrate="128K",
    ~output_quality = "ultrafast",
    ~framerate = 25,
    key,
    source
) =

    output.external.ffmpeg(
        id = id,
        fallible = true,
         "-coder 1 -r #{framerate} -g 50 -pixel_format yuv420p -b:v #{video_bitrate} -minrate #{video_bitrate} -maxrate #{video_bitrate} -bufsize 3128K -vcodec #{video_codec} -c:a #{audio_codec} -b:a #{audio_bitrate} -profile:a aac_low -preset #{output_quality} -tune zerolatency -threads 0 -rtmp_live live -f flv \"#{url}/#{key}\"",
        source
    )
end

I am quite new to ffmpeg usage, so i could let some mistakes to be there, though i used official documentation After playing with parameters this was best result i achieved so far, stream is less laggy, but:

I believe this issue is solely around ffmpeg arguments i provide there, so can you please suggest anything related to those?

Htop shows only like ~13% load of cores, so i do not think i have no resources for this

StarveTheEgo commented 4 years ago

With verbose logging it spams with:

 [NULL @ 0x7fe448550380] sample/frame number mismatch in adjacent frames

Worth to mention, that i tried to go back to gstreamer (but still with dev build) - audio sounds choppy there too

StarveTheEgo commented 4 years ago

I am trying to play with settings for few days in a row already Since it now sounds same way choppy with gstreamer, i am trying to run gstreamer version first (as it was working before)

Some notable lines from my script:

audio_samplerate = 48000 # trying 48000 this time instead of 44100
video_width = 1280 # trying 1280x720 for first; while it was working for 1920x1080
video_height = 720
video_framerate = 25

def output.restream.live(~id="",
 ~video_bitrate=2000,
 ~audio_encoder="fdkaacenc",
 ~audio_bitrate=128000,
 ~url="rtmp://restream.io/live",
 ~key,
 source) =
 video_pipeline = "videoconvert ! x264enc bitrate=#{video_bitrate} byte-stream=false key-int-max=50 bframes=0 aud=true tune=zerolatency speed-preset=ultrafast ! video/x-h264,profile=main ! queue ! mux."
 audio_pipeline = "audioconvert ! #{audio_encoder} bitrate=#{audio_bitrate} ! queue ! mux."
 pipeline = "flvmux streamable=true name=mux ! rtmpsink location=\"#{url}/#{key} live=1\""

 output.gstreamer.audio_video(
   id=id,
   video_pipeline=video_pipeline,
   audio_pipeline=audio_pipeline,
   pipeline=pipeline,
   fallible=true,
   on_error = fun(_) -> 15.0,
   source
 )
end

output.restream.live(
   audio_encoder = "avenc_aac",
   #audio_encoder = "voaacenc",
   video_bitrate = 4000,
   audio_bitrate= 128000, # trying 128000 instead of 320000
   url = restream_host,
   key = restream_key,
   combined_stream
)

I am currently out of ideas (but still trying daily), so decided to post an example how it sounds here: https://www.youtube.com/watch?v=MX33Cm1cXa0 Perhaps, you could bring me an idea from just hearing this :-)

StarveTheEgo commented 4 years ago

Okay, since no one answers here, i will get back to latest release version and will turn gstreamer verbose logging on, so we can at least monitor this issue :-) Will report in 2 days !

toots commented 4 years ago

Hello! I'm catching up on all current bugs, I hope to get back to this soon.

toots commented 4 years ago

Hi! I'm gonna move this from 1.4.3 to 2.0.0. Version 2 is definitely the right one to debug video. Let's revisit this shortly when the code is almost feature-complete.