[alsa-devel] pcm_dshare calculates size incorrectly on buffer rollover

Takashi Iwai tiwai at suse.de
Thu Oct 18 15:13:19 CEST 2018


On Fri, 12 Oct 2018 20:21:07 +0200,
Brendan Shanks wrote:
> 
> I'm working on an embedded system based on an Ambarella H1 SoC (32-bit ARM
> Cortex A9). Audio playback through ALSA (and GStreamer) works fine when
> playing to the raw hw device. When playing through dshare (my normal
> configuration), playback stops after exactly 7 hours 16 minutes, for about
> 3 minutes. During the 3 minutes, the playback thread consumes an entire CPU
> core. After the 3 minutes, GStreamer reports an xrun, recovers from it, and
> playback goes back to normal.
> 
> After some debugging, the problem seems to be in
> snd_pcm_dshare_sync_area(). When dshare->appl_ptr rolls over to 0, 'size'
> becomes huge, 3036676576. 'slave_size' is also much bigger than it should
> be, 1258291680. This is what 'size' is set to when the for loop starts, and
> I believe the for loop then spends ~3 minutes copying a huge amount of
> samples.
> This also explains the 7h16m time, it's linked to the PCM boundary which is
> 1258291200. At 48 kHz, 1258291200 samples takes 7h16m54s.
> 
> I'm not sure what the fix should be though. Is this really a bug in dshare,
> or are bad values being set somewhere else? GStreamer? Or maybe the
> period/buffer size (480/9600) is causing problems?

Maybe some 32bit boundary overflow?  I vaguely remember of it.


Takashi


> Any advice is appreciated, my modified snd_pcm_dshare_sync_area() and the
> output are below.
> 
> Brendan Shanks
> 
> 
> $ more /proc/asound/card0/pcm1p/sub0/hw_params
> access: MMAP_INTERLEAVED
> format: S16_LE
> subformat: STD
> channels: 4
> rate: 48000 (48000/1)
> period_size: 480
> buffer_size: 9600
> $ more /proc/asound/card0/pcm1p/sub0/sw_params
> tstamp_mode: ENABLE
> period_step: 1
> avail_min: 480
> start_threshold: 1
> stop_threshold: 1258291200
> silence_threshold: 0
> silence_size: 1258291200
> boundary: 1258291200
> 
> --- output from running 'gst-launch-1.0 -e audiotestsrc ! alsasink' with
> prints included as below
> snd_pcm_dshare_sync_area size 3036676576, dshare->appl_ptr 0
> dshare->last_appl_ptr 1258290720
> slave_hw_ptr1 1258282080 slave_period_size 480
> slave_hw_ptr2 1258282080 slave_buffer_size 9600
> slave_hw_ptr3 1258291680 slave_boundary 1258291200
> slave_hw_ptr4 1258291680 slave_appl_ptr 0
> slave_size1 1258291680
> size 1258291680
> appl_ptr 9120 size 1258291680 boundary 1258291200
> last_appl_ptr 0
> slave_appl_ptr 0
> dshare->slave_appl_ptr 480
> exiting
> 
> snd_pcm_dshare_sync_area size 3036676576, dshare->appl_ptr 0
> dshare->last_appl_ptr 1258290720
> slave_hw_ptr1 8965920 slave_period_size 480
> slave_hw_ptr2 8965920 slave_buffer_size 9600
> slave_hw_ptr3 8975520 slave_boundary 1258291200
> slave_hw_ptr4 8975520 slave_appl_ptr 8975040
> slave_size1 480
> size 480
> appl_ptr 9120 size 480 boundary 1258291200
> last_appl_ptr 0
> slave_appl_ptr 8640
> dshare->slave_appl_ptr 8975520
> exiting
> 
> --- snd_pcm_dshare_sync_area() from alsa-lib-1.1.6 with prints added:
> static void snd_pcm_dshare_sync_area(snd_pcm_t *pcm)
> {
>         snd_pcm_direct_t *dshare = pcm->private_data;
>         snd_pcm_uframes_t slave_hw_ptr, slave_appl_ptr, slave_size;
>         snd_pcm_uframes_t appl_ptr, size;
>         const snd_pcm_channel_area_t *src_areas, *dst_areas;
>         int print = 0;
> 
>         /* calculate the size to transfer */
>         size = dshare->appl_ptr - dshare->last_appl_ptr;
>         if (! size)
>                 return;
>         if (size > 9600)
>         {
>                 printf("snd_pcm_dshare_sync_area size %lu, dshare->appl_ptr
> %lu dshare->last_appl_ptr %lu\n",
>                         size, dshare->appl_ptr, dshare->last_appl_ptr);
>                 print = 1;
>         }
>         slave_hw_ptr = dshare->slave_hw_ptr;
> if (print) printf("slave_hw_ptr1 %lu slave_period_size %lu", slave_hw_ptr,
> dshare->slave_period_size);
>         /* don't write on the last active period - this area may be cleared
>          * by the driver during write operation...
>          */
>         slave_hw_ptr -= slave_hw_ptr % dshare->slave_period_size;
> if (print) printf("slave_hw_ptr2 %lu slave_buffer_size %lu", slave_hw_ptr,
> dshare->slave_buffer_size);
>         slave_hw_ptr += dshare->slave_buffer_size;
> if (print) printf("slave_hw_ptr3 %lu slave_boundary %lu", slave_hw_ptr,
> dshare->slave_boundary);
>         if (dshare->slave_hw_ptr > dshare->slave_boundary)
>                 slave_hw_ptr -= dshare->slave_boundary;
> if (print) printf("slave_hw_ptr4 %lu slave_appl_ptr %lu", slave_hw_ptr,
> dshare->slave_appl_ptr);
>         if (slave_hw_ptr < dshare->slave_appl_ptr)
>                 slave_size = slave_hw_ptr + (dshare->slave_boundary -
> dshare->slave_appl_ptr);
>         else
>                 slave_size = slave_hw_ptr - dshare->slave_appl_ptr;
> if (print) printf("slave_size1 %lu", slave_size);
>         if (slave_size < size)
>                 size = slave_size;
>         if (! size)
>                 return;
> if (print) printf("size %lu", size);
> 
>         /* add sample areas here */
>         src_areas = snd_pcm_mmap_areas(pcm);
>         dst_areas = snd_pcm_mmap_areas(dshare->spcm);
>         appl_ptr = dshare->last_appl_ptr % pcm->buffer_size;
> if (print) printf("appl_ptr %lu size %lu boundary %lu", appl_ptr, size,
> pcm->boundary);
>         dshare->last_appl_ptr += size;
>         dshare->last_appl_ptr %= pcm->boundary;
> if (print) printf("last_appl_ptr %lu", dshare->last_appl_ptr);
>         slave_appl_ptr = dshare->slave_appl_ptr % dshare->slave_buffer_size;
> if (print) printf("slave_appl_ptr %lu", slave_appl_ptr);
>         dshare->slave_appl_ptr += size;
>         dshare->slave_appl_ptr %= dshare->slave_boundary;
> if (print) printf("dshare->slave_appl_ptr %lu", dshare->slave_appl_ptr);
>         for (;;) {
>                 snd_pcm_uframes_t transfer = size;
>                 if (appl_ptr + transfer > pcm->buffer_size)
>                         transfer = pcm->buffer_size - appl_ptr;
>                 if (slave_appl_ptr + transfer > dshare->slave_buffer_size)
>                         transfer = dshare->slave_buffer_size -
> slave_appl_ptr;
>                 share_areas(dshare, src_areas, dst_areas, appl_ptr,
> slave_appl_ptr, transfer);
>                 size -= transfer;
>                 if (! size)
>                         break;
>                 slave_appl_ptr += transfer;
>                 slave_appl_ptr %= dshare->slave_buffer_size;
>                 appl_ptr += transfer;
>                 appl_ptr %= pcm->buffer_size;
>         }
> if (print) printf("exiting");
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel at alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
> 


More information about the Alsa-devel mailing list