# streamsynchronizer

Enables gapless playback of heterogenous streams groups. This element is used inside playsink.

streamsynchronizer ensures only one stream group is active downstream at any given time. Whenever a pad receives a `STREAM_START` with a group-id different than the current one the pad is blocked until all other pads also receive a `STREAM_START` with the same group-id.

Once all pads have received a `STREAM_START` with the same group-id, the previous group is completed and all the pads unblocked.

When a group is completed, a patched GstSegment is emitted downstream so that the running time 0 from upstream of the new group becomes the running time of the end of the previous group.

### Warning: deadlocks with multiple pads with different group-ids

All pads connected to a streamsynchronizer are expected to share a group-id. Therefore, sending `STREAM_START` in two or more sinkpads of streamsynchronizer with different group-ids will not allow for playback.

As streamsynchronizer is part of playsink, this can easily happen accidentally when mixing streams from different elements. The following is a minimal naive pipeline exhibiting this problem:

     # Will get stuck! The streams from audiotestsrc and videotestsrc don't
     # share a group-id.
     gst-launch-1.0 \
       playsink name=myplaysink \
       audiotestsrc ! myplaysink.audio_sink \
       videotestsrc ! myplaysink.video_sink

## What are stream groups and group-ids

A stream group is a group of streams that are meant to be played together. Two streams belong to the same group if they set the same group-id in their `STREAM_START` event (see gst_event_set_group_id).

The most common example is video and audio from the same .mp4 file. Demuxers will ensure that the streams they output from their srcpads share the same group-id.

Another example is an out-of-band .srt subtitles file. In this case, despite being a separate file, the subtitle stream should use the same group-id as the video its meant to be used with, as they both need to be played together. uridecodebin3 takes care of this automatically.

## Why do we need stream groups for gapless playback

In theory, a player could implement gapless playback by performing some kind of auto-plugging, keeping track of the playlists themselves and adding probes to patch the GstSegment. This is a lot of work, especially to get it done correctly.

The goal of streamsynchronizer and group-ids is to standardize a solution for gapless playback so that applications don't have to implement it from the ground up, and for that solution to be generalizable to the worst possible cases.

Every new stream group received upstream of streamsynchronizer is expected to start at running-time of zero. Hence, once the previous file is drained, you can even remove a demuxer element and replace it by a demuxer for a different format, and streamsynchronizer will take care of adjusting the segment so that the data of the second file plays after the second. If there is more than one stream within the same group (e.g. audio and video) the shift will be done by the duration of the longest stream.

## Gapless playback caveats

Given the substantial number of edge cases that need to be handled across a large code surface, bugs in gapless playback are not uncommon.

In general (not only in GStreamer) gapless playback requires several things to work:

- Buffering of the new file done well ahead of the previous file. For this purpose urisourcebin emits (and decodebin3, uridecodebin3, playbin and playbin3 propagate) the about-to-finish signal to notify what is the optimal time at which to provide the next uri to play.

  - Note that this requirement applies to the entire pipeline, including decoders and audio sinks.

- Extremely well bounded audio files. This is particularly hard for compressed audio formats. See: codec delay, audio frame sizes. Achieving arbitrary length of audio in many compressed formats will require either:

  - Container features like MP4 edit lists to clip the priming and padding PCM samples at the beginning and the end of the file, plus elements correctly clipping the data accordingly.
  - Features of the specific audio format used to perform the same clipping at the decoder level.

- All autoplugging during the switch must be done without pausing the pipeline.

- Playback must be possible without resetting the audio device (e.g. to select a different sampling rate or audio format), or such a switch to be done gapless.

## Example command line

gst-play supports the `--gapless` flag which can be used to test gapless playback. It will instantiate a playbin/playbin3 with a playsink, which itself will contain a streamsynchronizer.

     # BEWARE: high-pitch audio sweep, lower your volume.
     wget https://www2.iis.fraunhofer.de/AAC/gapless-sweep_part1_iis.m4a
     wget https://www2.iis.fraunhofer.de/AAC/gapless-sweep_part2_iis.m4a
     gst-play-1.0 --gapless \
       gapless-sweep_part1_iis.m4a \
       gapless-sweep_part2_iis.m4a \

## Hierarchy

    GObject
        ╰──GInitiallyUnowned
            ╰──GstObject
                ╰──GstElement
                    ╰──streamsynchronizer

## Factory details

Authors: – Sebastian Dröge

Classification: – `Generic`

Rank – none

Plugin – playback

Package – GStreamer Base Plug-ins

## Pad Templates

### `sink_%u`

    ANY

Presence – request

Direction – sink

Object type – GstPad

### `src_%u`

    ANY

Presence – sometimes

Direction – src

Object type – GstPad

The results of the search are