Environment
- UxPlay 1.74 (built from current master)
- GStreamer 1.28.4, Windows 11 / MSYS2 UCRT64 (bug is in shared, platform-independent code)
- Client: iPhone 15 Pro, iOS 27 beta
Symptom
When the client stops mirroring, the video output window stays open showing a frozen last frame, instead of the renderer being reset/closed. On the next connection this also contributes to a stale display.
Root cause
raop_handler_teardown() in lib/raop_handlers.h handles three cases: teardown_96 (audio stream), teardown_110 (video stream, which calls video_reset(RESET_TYPE_RTP_SHUTDOWN)), and the "no type" case that destroys the sessions. On older iOS the client sent a type-110 stream teardown (triggering the video reset) before the final typeless TEARDOWN. iOS 26/27 sends the final TEARDOWN without a preceding type-110 (observed in the log: TEARDOWN request, 96=0, 110=0), so the video renderer's video_reset is never called and the window/last frame is left in place.
Fix
In the typeless teardown branch, when tearing down the mirror session, also call video_reset(RESET_TYPE_RTP_SHUTDOWN). It must be called after raop_rtp_mirror_destroy(): destroy joins the mirror receive thread, so no more buffers are pushed into the pipeline that the reset then tears down. (Calling the reset first races the still-running thread against pipeline teardown and can crash in libgstapp — observed as an access violation on reconnect.)
@@ raop_handler_teardown(raop_conn_t *conn, ...
if (conn->raop_rtp_mirror) {
+ /* iOS 26+ may send the final TEARDOWN without a type-110 stream
+ * teardown first; without this reset the video window (and a
+ * frozen last frame) stays open after the client disconnects.
+ * The reset must come AFTER destroy: destroy joins the mirror
+ * thread, so no more buffers are pushed into the pipeline that
+ * the reset is about to tear down (race -> crash in libgstapp). */
raop_rtp_mirror_destroy(conn->raop_rtp_mirror);
conn->raop_rtp_mirror = NULL;
+ raop->callbacks.video_reset(raop->callbacks.cls, RESET_TYPE_RTP_SHUTDOWN);
}
Environment
Symptom
When the client stops mirroring, the video output window stays open showing a frozen last frame, instead of the renderer being reset/closed. On the next connection this also contributes to a stale display.
Root cause
raop_handler_teardown()inlib/raop_handlers.hhandles three cases:teardown_96(audio stream),teardown_110(video stream, which callsvideo_reset(RESET_TYPE_RTP_SHUTDOWN)), and the "no type" case that destroys the sessions. On older iOS the client sent a type-110 stream teardown (triggering the video reset) before the final typeless TEARDOWN. iOS 26/27 sends the final TEARDOWN without a preceding type-110 (observed in the log:TEARDOWN request, 96=0, 110=0), so the video renderer'svideo_resetis never called and the window/last frame is left in place.Fix
In the typeless teardown branch, when tearing down the mirror session, also call
video_reset(RESET_TYPE_RTP_SHUTDOWN). It must be called afterraop_rtp_mirror_destroy(): destroy joins the mirror receive thread, so no more buffers are pushed into the pipeline that the reset then tears down. (Calling the reset first races the still-running thread against pipeline teardown and can crash in libgstapp — observed as an access violation on reconnect.)@@ raop_handler_teardown(raop_conn_t *conn, ... if (conn->raop_rtp_mirror) { + /* iOS 26+ may send the final TEARDOWN without a type-110 stream + * teardown first; without this reset the video window (and a + * frozen last frame) stays open after the client disconnects. + * The reset must come AFTER destroy: destroy joins the mirror + * thread, so no more buffers are pushed into the pipeline that + * the reset is about to tear down (race -> crash in libgstapp). */ raop_rtp_mirror_destroy(conn->raop_rtp_mirror); conn->raop_rtp_mirror = NULL; + raop->callbacks.video_reset(raop->callbacks.cls, RESET_TYPE_RTP_SHUTDOWN); }