Repository navigation
Audio dies permanently after pause/resume of media (audio_renderer_start no-op on same-codec re-SETUP) #539
Description
Activity
@LukeProducts thanks! will get to this ASAP.
Do you think this is a old bug, or something new in iOS27beta? Since you are using iOS27beta, can you comment on #535?I think this is the same issue as #207 but for audio.
Might be worthwhile pointing an agent and gstreamer docs with that context and seeing if it is able to provide a proper fix for both cases
Thanks. I cannot confirm the exact failure on an older iOS device yet, so its regression status remains unverified.
The failure mechanism itself does not appear intrinsically specific to iOS 27. It occurs whenever the client re-SETUPs an already-running audio renderer with the same codec after the media timestamps paused while GStreamer's live pipeline clock kept advancing. If older clients use the same resume sequence, they should be affected as well, but I have not tested that directly.
I also reviewed #207. It is the same general clock/timestamp class of problem: after a client pause or sleep, incoming buffers are considered late. It is not exactly the same trigger or renderer path. Issue #207 handles a video restart after sleep/window updates. Here, the same-codec audio re-SETUP provides an explicit point at which to realign the audio renderer. Restarting the audio pipeline there is a conservative fix, and it has been reliable in my testing.
A flush followed by a new segment may be a cleaner and lighter GStreamer solution, but I have not validated it. Such a change would need to reset the appsrc segment/running-time consistently and preserve audio/video synchronization. I do not want to present that approach as a fix without testing it.
I added my iOS 27 comparison to #535: #535 (comment)
@LukeProducts I see some differences in how iOS 27 plays (Youtube) video in mirror mode. (goes to full screen on iPad while playing).
Pause resume is now working in iOS27, video restarts but there is a lag before audio restarts. @LukeProducts Can you confirm this bug in the now-released non-beta iOS17?
- ~~ We dont see new audio SETUP requests on pause/restart, but a lot of requests for audio packet resends~~
EDIT: I put a printf in audio_renderer_start to see if pause/resume triggers it. in current github master branch (2026-09-16) , it doesn't.
EDIT: both on iOS 26 and 18.7 the resume only send a RTSP AUDIOMODE request.
EDIT now the youtube is not going to fullscreen (not sure what the mode we were in before was) , and we do see the audio_renderer_start (following RTSP TEARDOWN and SETUP requests).
Thanks for checking. I assume “iOS17” in your question means iOS 27.
The reproduction documented here was on iOS 27 beta. I do not have a verified result from a non-beta UxPlay test to confirm it yet.
Your final edit describes the relevant TEARDOWN/SETUP path reaching
audio_renderer_start(). The distinction to check is whether audio stays silent for the rest of the session after a same-codec re-SETUP. A short delay followed by audio recovery would not reproduce the permanent-silence symptom reported here.
Environment
Symptom
When mirroring media (audio+video) and pausing/resuming playback on the client a few times, audio stops permanently for the rest of the session — video keeps working. There is no error in the log (even with
-d); audio is simply silent until UxPlay is restarted. It is reliably reproducible with start/pause/start/pause on a video.Root cause
On every resume, iOS sends a fresh audio stream SETUP, which calls
audio_renderer_start(). Inrenderers/audio_renderer.c:When the codec is unchanged (the normal case — AAC-ELD stays AAC-ELD), the function does nothing. But during the pause the live audio pipeline's clock kept advancing while the client's media timestamps did not, so
gst_audio_pipeline_base_timeis now far behind the pipeline's running-time. From the first post-resume buffer on, every buffer's PTS is in the past and the audiosink silently drops all of them as "too late" — audio is dead for the rest of the stream. (The video path already has a self-correcting retry loop invideo_process; the audio path has no equivalent.)Fix
Treat the same-codec re-SETUP like the codec-change branch: restart the pipeline and recapture the base time so the clock realigns.
@@ void audio_renderer_start(unsigned char *ct) { renderer = renderer_type[id]; gst_element_set_state (renderer->pipeline, GST_STATE_PLAYING); gst_audio_pipeline_base_time = gst_element_get_base_time(renderer->appsrc); + } else { + /* Same codec, renderer already running: this is a client re-SETUP, + * which iOS sends on every resume after a pause. During the pause + * the (live) pipeline clock ran ahead of the client's paused media + * timestamps, so from here the audiosink silently drops every + * buffer as "too late" and audio stays dead for the rest of the + * stream. Restart the pipeline to realign its clock/base-time. */ + gst_element_set_state (renderer->pipeline, GST_STATE_NULL); + gst_element_set_state (renderer->pipeline, GST_STATE_PLAYING); + gst_audio_pipeline_base_time = gst_element_get_base_time(renderer->appsrc); } } else if (id >= 0) {This costs a brief (sub-second) audio re-init on each resume, but audio survives pause/resume cycles. A lighter realign (flush + new segment) may be possible, but this mirrors the existing codec-change branch and has tested reliably.