bruor 43 Posted 1 hour ago Posted 1 hour ago (edited) @LukeTagging you here since creation of this header is something you could add to your custom builds of ffmpeg to add some resilience to stream startup. Bonus points if you would consider sending a bit more of a buffer to a client when they try to join a live stream. I've been annoyed by buffering at the start of LiveTV feeds. I use the force transcoding option in my playback settings for MPEGTS so that I can pause/skip backwards while watching live tv. When Emby uses ffmpeg to compose the segments and HLS manifest it doesn't contain an #EXT-X-START:TIME-OFFSET=0 header to tell the player to begin playback from the first segment in the file. Default behavior on the player side when this header is missing is to jump to the last segment in the list (verified in logs), which skips over all data in the tuner buffer and results in periodic pauses until the player falls back in time enough segments to be arriving ahead of the current playback position. For example, ffmpeg generates roughly 8 segments of startup content in my current setup, but without this header being present, the player will start playing from segment 8 instead of segment 1. Once the header is added, I can verify in the logs that the player always requests the first segment available in the m3u8 manifest. I'm using the emby-server docker container and I've hacked a workaround in place for this by overriding the emby startup script to call an ffmpeg wrapper. The wrapper redirects manifest output to a .m3u8.raw file, uses a loop to check it for updates and when a new version is found it creates the intended original target m3u8 file with the necessary header. CAVEAT: This works well for the first person that tunes into a live channel. However, due to internal logic within Emby, additional sessions that join an in-progress live stream aren't given enough buffer data for this to have a meaningful impact. These add-on streams only receive enough data from the internal tuner cache to create 1-2 segments at startup which causes the emby client/player to pause a bit at startup while it waits for additional segments to be created and published. Docker compose bind mounts: - ./ffmpeg-wrapper-override.sh:/bin/ffmpeg-wrapper-override.sh:ro - ./emby-server-run-override.sh:/etc/services.d/emby-server/run:ro ffmpeg-wrapper-override.sh: #!/bin/sh # Version 2.8.0 - HLS Segment Pinner (Universal) REAL_FFMPEG="/bin/ffmpeg" # Smart Patcher: Only updates when the manifest actually changes maintain_proxy_smart() { OFFICIAL_M3U=$1 SHADOW_M3U=$2 FFMPEG_PID=$3 LAST_MTIME=0 until [ -f "$SHADOW_M3U" ]; do kill -0 "$FFMPEG_PID" 2>/dev/null || exit sleep 0.1 done while kill -0 "$FFMPEG_PID" 2>/dev/null; do if [ -f "$SHADOW_M3U" ]; then CURRENT_MTIME=$(stat -c %Y "$SHADOW_M3U" 2>/dev/null) if [ "$CURRENT_MTIME" != "$LAST_MTIME" ]; then # Inject the 'Start at 0' tag and commit atomically sed '2i#EXT-X-START:TIME-OFFSET=0' "$SHADOW_M3U" > "${OFFICIAL_M3U}.tmp" mv "${OFFICIAL_M3U}.tmp" "$OFFICIAL_M3U" LAST_MTIME=$CURRENT_MTIME fi fi sleep 0.1 done } # TRIGGER: If Emby is generating an HLS manifest, we intercept. TRIGGER=0 for arg in "$@"; do if [ "$arg" = "-segment_list" ]; then TRIGGER=1 break fi done if [ "$TRIGGER" -eq 1 ]; then WRAPPER_PID=$$ # Rebuild arguments to redirect the official manifest to a shadow file for arg do shift if [ "$PREV_ARG" = "-segment_list" ]; then REAL_PATH="$arg" SHADOW_PATH="${arg}.raw" set -- "$@" "$SHADOW_PATH" else set -- "$@" "$arg" fi PREV_ARG="$arg" done if [ -n "$REAL_PATH" ]; then maintain_proxy_smart "$REAL_PATH" "$SHADOW_PATH" "$WRAPPER_PID" & fi exec "$REAL_FFMPEG" "$@" else # Fallback for library scans, probes, and non-HLS tasks exec "$REAL_FFMPEG" "$@" fi emby-server-run-override.sh: #!/usr/bin/with-contenv sh # Maintain original config ownership logic if [ "$(ls -nd /config | tr -s '[:space:]' | cut -d' ' -f3)" -ne "$UID" ] || [ "$(ls -nd /config | tr -s '[:space:]' | cut -d' ' -f4)" -ne "$GID" ]; then chown "$UID":"$GID" -R /config fi # Maintain GPU device discovery (Critical for Hardware Acceleration) for d in $(find /dev/dri -type c 2>/dev/null); do gid=$(stat -c %g "${d}") [ -z "${GIDLIST}" ] && GIDLIST=${gid} || GIDLIST="${GIDLIST},${gid}" done # Launch EmbyServer # We change ONLY the -ffmpeg flag to point to your new wrapper path if [ -n "$(uname -a | grep -q synology)" ] || [ "$IGNORE_VAAPI_ENABLED_FLAG" = "true" ]; then s6-applyuidgid -U /system/EmbyServer \ -programdata /config \ -ffdetect /bin/ffdetect \ -ffmpeg /bin/ffmpeg-wrapper-override.sh \ -ffprobe /bin/ffprobe \ -ignore_vaapi_enabled_flag \ -restartexitcode 3 else s6-applyuidgid -U /system/EmbyServer \ -programdata /config \ -ffdetect /bin/ffdetect \ -ffmpeg /bin/ffmpeg-wrapper-override.sh \ -ffprobe /bin/ffprobe \ -restartexitcode 3 fi UPDATES: Make sure you disable the bind mounts when doing updates so you can inspect the /etc/services.d/emby-server/run file for changes that you need to bring into your override. Edited 1 hour ago by bruor
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now