[Bug]: fMP4 remux exits after automatic upstream failover with an audio codec change

Open
#1,888 3 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
43/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Active
Tech stack
python

Research direction

The report identifies the fMP4 HTTP output and fMP4Remux process, but names no source files or tests. Start by locating the remux manager and the automatic stream failover path, then trace how output initialization is handled when input codecs or sample rates change. Done means reproducing both reported transitions and ensuring the fMP4 output either recovers with valid initialization or ends with a recoverable signal.

Written by the indexing model from the issue text.

Description

Area: Backend Area: Streams Available In Dev M P2
Describe the bug

After an upstream fails and Dispatcharr automatically selects the next stream, the fMP4 remux process can exit while the channel input remains active. The fMP4 HTTP response then stops delivering new media, leaving clients frozen/reconnecting.

This was first observed with the private Android-TV client on an NVIDIA Shield, but the server-side failure was reproduced without the private Android-TV client or any IPTV provider: two independent HTTP readers feeding FFmpeg received the same synthetic Dispatcharr channel, one as fMP4 and one as MPEG-TS. No manual change_stream request was made.

In the independent reproduction, input 1 was H.264 640x360/25 + AC-3 48 kHz; input 2 was H.264 960x540/25 + AAC 32 kHz. Dispatcharr successfully switched to input 2, then its fMP4Remux logged Error muxing a packet, error -1094995529, stdout EOF, reader exit and broken pipe. The fMP4 reader received 1,575,756 bytes, then timed out after about 75 seconds total. The parallel TS response continued to 50,326,284 bytes over 130 seconds and decoded video from the second input.

TS comparison limitation: that FFmpeg TS decoder retained the original AC-3 audio decoder and reported errors after AAC arrived. Therefore this is not a claim of seamless audio on every TS client. A separate Android app TS test with an AAC sample-rate/resolution change passed, but that is a different fixture. HLS was not tested in this reproduction.

Expected: when an automatic input change invalidates the fMP4 remuxer's codec/init state, recover/recreate that output and provide a valid new initialization/discontinuity, or terminate the obsolete response with a recoverable signal. A dead remux output should not remain attached to an otherwise active channel indefinitely. The exact recovery design is up to the maintainers.

Sanitized server log from the independent reproduction (local server time):

11:07:15 fMP4Remux started
11:07:20 Codec 'ac3' is not supported by bitstream filter 'aac_adtstoasc'
11:07:20 Non-AAC audio detected, retrying without BSF
11:07:20 Restarted without BSF; Init segment stored (1213 bytes)
11:08:14 URL [synthetic input 1] failed after 3 attempts, trying next stream
11:08:14 Stream switch completed
11:08:14 Successfully switched to stream ID [synthetic input 2]
11:08:15 [aost#0:1/copy] Error submitting a packet to the muxer: Invalid data found when processing input
11:08:15 [out#0/mp4] Error muxing a packet
11:08:15 Task finished with error code: -1094995529 (Invalid data found when processing input)
11:08:15 fMP4Remux: FFmpeg stdout EOF
11:08:15 fMP4Remux: Reader loop exited
11:08:15 fMP4Remux: FFmpeg stdin error: [Errno 32] Broken pipe
11:08:55 No clients remain, stopping fMP4 manager

The original real-channel incident had the same post-switch muxer/EOF/broken-pipe sequence. Reconnecting fMP4 clients subsequently received no fresh video and hit the no-data timeout. Its first input's exact audio format was not captured, so the synthetic codec transition above is a controlled reproducer, not an assertion about that original input.

Related search found #1704 about TS discontinuities. This report concerns the fMP4 server process terminating after automatic failover on 0.33.0; it does not assume the same root cause. No Dispatcharr source changes were made for these tests.

Additional independent reproduction: AAC sample-rate change without remux exit

A second controlled run used AAC on both inputs, changing from 48 kHz (440 Hz test tone) to 32 kHz (880 Hz test tone), with the same automatic H.264 resolution change. Again, there were no app clients, no provider streams and no manual switch request.

In this variant the fMP4 remux process stayed alive and delivered 2,576,389 bytes over 130.8 seconds. However, the saved output contained only one ftyp and one moov, followed by 128 moof/mdat pairs; the initialization still declared AAC at 48 kHz. FFmpeg reported after the switch:

Number of scalefactor bands in group (51) exceeds limit (49).
Error submitting packet to decoder: Invalid data found when processing input

Offline decoding of the entire fMP4 capture yielded only 59.008 seconds of audio, containing the first input's approximately 440 Hz tone, despite a declared track duration of approximately 127.95 seconds and 3,198 video frames in the live decoder run. No usable second-input audio was recovered. This is consistent with new AAC packets being delivered under the old initialization; it is not merely an app buffering label.

The simultaneous MPEG-TS capture continued for 130.5 seconds (50,487,776 bytes). Offline decoding recovered the second input's 880 Hz tone correctly. TS timestamps reset during the change, so I am not claiming a completely seamless timeline in every client.

The server logged exactly one initial fMP4 init segment, then a successful automatic input switch about 59 seconds later, without a new init segment or remux restart. These are two observed variants of output-state handling after failover:

  • AC-3 to AAC: server fMP4 muxer exits.
  • AAC 48 kHz to AAC 32 kHz: output continues, but the retained init causes audio decoding failure.

This second variant is also one independent controlled reproduction. The exact server-side fix and whether these should be split into separate issues remain for maintainer triage.

How can we recreate this bug?
  1. Use an isolated test channel with two custom streams in order and a custom processing profile. Generate real-time MPEG-TS locally with FFmpeg (lavfi); no provider credentials or network media are required.
  2. Input 1: red 640x360/25, H.264, stereo AC-3 at 48 kHz. Input 2: blue 960x540/25, H.264, stereo AAC at 32 kHz. Use a 1-second GOP. Both are delivered as MPEG-TS to Dispatcharr's input.
  3. Make input 1 stop after roughly 60 seconds and reject subsequent opens, so Dispatcharr's ordinary retries exhaust and select input 2. Input 2 should run for at least another 120 seconds. Our profile used a fixed absolute cutoff, so reopening input 1 did not start another successful 60-second producer.
  4. Open /proxy/ts/stream/<test-channel-uuid>?output_format=fmp4 with an authorized test user. Wait until initial frames decode. Then open a second reader for the same UUID with output_format=ts. Starting fMP4 first avoids a separate cold concurrent-output initialization error observed in a preliminary attempt.
  5. Feed each response into an independent FFmpeg process, e.g. ffmpeg -hide_banner -loglevel info -i pipe:0 -map 0:v:0 -map 0:a:0 -progress pipe:1 -f null -, and record received-byte counts plus server logs. Our HTTP reader used a 15-second read timeout, a 130-second overall capture limit and a 64 MiB cap per output; its timeout happens AFTER the server remux process exits.
  6. Let Dispatcharr switch automatically. Do not call change_stream, retune the client, or restart the channel. Observe the fMP4 muxer failure/reader EOF while the second input and TS output remain active.

For reference, these are the producer commands (run under a wrapper which rejects reopening input 1 after the absolute cutoff). The first -t must be the remaining seconds until that cutoff, not a new 60 seconds on each retry:

ffmpeg -nostdin -hide_banner -loglevel warning -re \
  -f lavfi -i color=c=red:size=640x360:rate=25 \
  -f lavfi -i sine=frequency=440:sample_rate=48000 \
  -map 0:v -map 1:a -c:v libx264 -preset ultrafast -tune zerolatency \
  -g 25 -pix_fmt yuv420p -c:a ac3 -ac 2 -muxrate 3000000 \
  -t REMAINING_SECONDS -f mpegts pipe:1

ffmpeg -nostdin -hide_banner -loglevel warning -re \
  -f lavfi -i color=c=blue:size=960x540:rate=25 \
  -f lavfi -i sine=frequency=880:sample_rate=32000 \
  -map 0:v -map 1:a -c:v libx264 -preset ultrafast -tune zerolatency \
  -g 25 -pix_fmt yuv420p -c:a aac -ac 2 -muxrate 3000000 \
  -t 180 -f mpegts pipe:1

Both producers were finite and software encoded. All test users/profiles/streams/channels were isolated and removed after readers disconnected. The successful independent run is one controlled reproduction; it does not establish that every input switch or every codec pair fails.

Dispatcharr Version

0.33.0 (build 20261024004414; image ghcr.io/dispatcharr/dispatcharr:latest; revision e71bd6cd50931e1381f85521b7f914c47f9528b7)

Docker Version

29.8.2, build 7fc2dff; Unraid 7.3.4

What's in your Docker Compose file?

Not using Docker Compose. Installed through Unraid DockerMan, official ghcr.io/dispatcharr/dispatcharr image. Synthetic reproduction uses software libx264 and AC-3/AAC producers, so no GPU encoder is required for it. No Dispatcharr code modifications.

Client Information

Independent reproduction: two Python requests streaming HTTP readers on Windows, each feeding a separate FFmpeg decoder via stdin. One fMP4 reader and one MPEG-TS reader, same isolated channel. the private Android-TV client is not running against that test channel. Initial real-world symptom: a private Android-TV client on NVIDIA Shield, native Dispatcharr source with local buffering. The public report is based on the independent reproduction and Dispatcharr server logs, not solely on the app freeze.

Dominant language
Python
Stars
4.2k
Forks
324
Avg merge
1d 20h
Merged PRs (30d)
29

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from Dispatcharr/Dispatcharr

All issues in Dispatcharr/Dispatcharr

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.