Fix appsrc state regression when adding source bins during pipeline startup#1293
Conversation
…tartup When a source bin (appsrc) is added dynamically, `linkPeersLocked` forced it to PLAYING with `SetState(gst.StatePlaying)`. This races with the pipeline's own NULL → PLAYING transition: dynamic linking is enabled at `StateStarted`, which is set before `pipeline.SetState(PLAYING)` is called, so a track can be added while the pipeline is still NULL or mid-transition. GStreamer's state propagation (`gst_bin_element_set_state`) always recurses into child bins - the "already at target state" skip does not apply to them. A source bin manually set to PLAYING therefore gets forced back down to READY by the pipeline's NULL → READY step. That downward transition deactivates the appsrc's pads and stops its streaming task, and depending on interleaving (e.g. a stale cached ASYNC_START causing subsequent upward steps to skip the child) the bin can be stranded below PLAYING while the rest of the pipeline reaches PLAYING. nothing is pushed downstream anymore. Using `gst_element_sync_state_with_parent()` instead of forcing PLAYING. It targets the parent's pending state (the final target of an in-flight transition), falling back to its current state: - Pipeline not yet transitioning (NULL): no-op; the pipeline's upcoming transition brings the bin up along with all other children - Pipeline mid NULL → PLAYING: commands PLAYING, but never above the pipeline's target, so it converges with the transition instead of being dragged down from a state ahead of it. - Pipeline already PLAYING: commands PLAYING directly, same as before. This should fix one of the biggest remaining pipeline frozen errors and eliminate the need to forcefully re-add flushing source.
|
AV-sync stats summary: view in run #29199604527 |
|
What happens on The first paragraph talks about this transition and how the bin can be forced back to |
Quick gstreamer context: states are ordered NULL < READY < PAUSED < PLAYING and that's valid for new as well as existing logic, so READY is actually below PLAYING - that's why getting forced from PLAYING back to READY (the bug in the first paragraph) is a harmful downgrade that shuts off the source's data flow. The listing doesn't mention NULL -> READY because we never target READY: the pipeline only ever gets one command, SetState(PLAYING), and NULL -> READY is just an internal stepping stone gstreamer walks through on the way there. sync_state_with_parent() keys off the parent's final target state, not whatever rung it's currently on, so mid-transition it returns PLAYING, and if the transition hasn't started yet it returns NULL (a no-op, letting the bin ride up with everyone else). In other words, there's no moment where the sync call would ever set the bin to READY. Or I didn't get the question? |
Thank you @milos-lk for the explanation. That was the answer to my question. I missed the internal/external bit and parent state is tracking the intended state. |
When a source bin (appsrc) is added dynamically,
linkPeersLockedforced it to PLAYING withSetState(gst.StatePlaying). This races with the pipeline's own NULL → PLAYING transition: dynamic linking is enabled atStateStarted, which is set beforepipeline.SetState(PLAYING)is called, so a track can be added while the pipeline is still NULL or mid-transition. GStreamer's state propagation (gst_bin_element_set_state) always recurses into child bins - the "already at target state" skip does not apply to them. A source bin manually set to PLAYING therefore gets forced back down to READY by the pipeline's NULL → READY step. That downward transition deactivates the appsrc's pads and stops its streaming task, and depending on interleaving (e.g. a stale cached ASYNC_START causing subsequent upward steps to skip the child) the bin can be stranded below PLAYING while the rest of the pipeline reaches PLAYING. nothing is pushed downstream anymore.Using
gst_element_sync_state_with_parent()instead of forcing PLAYING. It targets the parent's pending state (the final target of an in-flight transition), falling back to its current state:This should fix one of the biggest remaining pipeline frozen errors and eliminate the need to forcefully re-add flushing source.