After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 750694 - alsasink: Brief gap after unpause if audio clock is not the pipeline clock
alsasink: Brief gap after unpause if audio clock is not the pipeline clock
Status: RESOLVED OBSOLETE
Product: GStreamer
Classification: Platform
Component: gst-plugins-base
git master
Other Linux
: Normal normal
: git master
Assigned To: GStreamer Maintainers
GStreamer Maintainers
Depends on:
Blocks:
 
 
Reported: 2015-06-10 11:07 UTC by Carlos Rafael Giani
Modified: 2018-11-03 11:38 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
gst-play patch , do not commit! Just for testing purposes (505 bytes, patch)
2015-06-10 11:07 UTC, Carlos Rafael Giani
none Details | Review

Description Carlos Rafael Giani 2015-06-10 11:07:21 UTC
Created attachment 304947 [details] [review]
gst-play patch , do not commit! Just for testing purposes

If playback is paused, later unpaused, and the audio clock is not the pipeline clock, then after unpause, a short audio sequence can be heard, then a brief gap, then audio continues. This is 100% reproducible, even with gst-play, if the attached gst-play-alsasink-test.patch is used. Patch gstreamer core with this, rebuild, and then play an audio file and pause&unpause a few times.

The problem can be fixed/cirumvented by using snd_pcm_drain() instead of snd_pcm_drop() in the gst_alsasink_reset() definition. (snd_pcm_nonblock() has to be used before and after calling snd_pcm_drain(), since draining in nonblock mode won't work properly.)

There are two open questions. First, it is unclear if draining instead of dropping is a viable option. Draining will introduce a small delay (since it blocks until draining is complete).

Second, it is not fully known *why* this error occurs, and why draining helps. I can only speculate:

Draining essentially instructs ALSA to block until the playback pointer catches up with the write pointer. Drop just stops playback immediately. Therefore, if drop is called, all pending writes are discarded, but the *current* one is finished (in case a write is happening concurrently in the background). The write pointer inside ALSA is moved forward, but the playback pointer isn't, since drop stopped playback. Then, after unpause, the sink resynchronizes itself, which leads to nullsamples being inserted prior to the actual audio data. These nullsamples are what cause the gap. The problem then boils down to the fact that ALSA's internal write pointer is moved forward, but the playback pointer isn't: the playback pointer is still in front of the old samples that were written *before* the pause. In other words, after unpause, ALSA plays a few "old" samples that were written earlier.

I see no way to tell ALSA to drop and *not* move forward the write pointer. So, draining really seems to be the only simple choice. Another, nontrivial option would be mmap, since it might be possible then to do a manual memset() after dropping, which would clear out any of these "old" samples.

Ideas? Thoughts?
Comment 1 Nicolas Dufresne (ndufresne) 2018-06-06 20:26:25 UTC
Wow, I've been chasing this for a while and didn't know there was such an easy way to reproduce. By way, no need to patch gst-play-1.0:

gst-plugins-base]$ gst-play-1.0 --audiosink="alsasink provide-clock=0" audio-file

And this seems from a distance similar to https://bugzilla.gnome.org/show_bug.cgi?id=774945 where some sink would drop data when doing pause/unpause.
Comment 2 Nicolas Dufresne (ndufresne) 2018-06-06 20:46:49 UTC
If I understand well, we call reset to unblock snd_pcm_write(). But this will also discard the data being written (or buffered). From a ringbuffer/clock perspective, it is like if this data was played, so the clock jumps forward, resulting in a gap which get filled with silence.

I think the implementation of reset() is correct, but it's usage in pause is wrong. It should call GstBaseSink::unlock/unlock_stop() instead, and alsasink should implement this. But for that, we'd need to move away from snd_pcm_wait() and retrieve a poll descriptor. A bit like what was done for ALSA midi src recently.
Comment 3 GStreamer system administrator 2018-11-03 11:38:01 UTC
-- GitLab Migration Automatic Message --

This bug has been migrated to freedesktop.org's GitLab instance and has been closed from further activity.

You can subscribe and participate further through the new bug through this link to our GitLab instance: https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/issues/193.