summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAntti-Ville Jansson <antti-ville.jansson@digia.com>2011-04-20 15:56:29 +0300
committerColin Guthrie <colin@mageia.org>2011-04-28 14:05:42 +0100
commit495c1ed2361f7bab5eaa6978d2fda624a2944bb9 (patch)
tree28dcd1574d7670b4bf72ce5b83b1fcc495b6500e
parentfea4942aa2b99ee71a1e854bbdab64c6766cd770 (diff)
core: Drop empty gaps in the memblockq when playing data from it.
It's possible that the memblockq of a sink input is rewound to a negative read index if the sink input is moved between sinks shortly after its creation. When this happens, pa_memblockq_peek() returns a memchunk whose 'memblock' field is NULL and whose 'length' field indicates the length of the gap caused by the negative read index. This will trigger an assert in play-memblockq.c. If the memblockq had a silence memchunk, pa_memblockq_peek() would return silence for the duration of the gap and the assert would be avoided. However, this approach would prevent the sink input from being drained and is thus not possible. Instead, we handle the aforementioned situation by dropping the gap indicated by the 'length' field of the memchunk and by peeking the actual data that comes after the gap. This scenario seems to be quite rare in everyday use, but it causes a severe bug in the handheld world. The assert can be triggered e.g. by loading two null sinks, playing a sample from the cache to one of them and then moving the created sink input between the two sinks. The rewinds done by the null sinks seem to be quite long (I don't know if this is normal behaviour or something fishy in module-null-sink). See also: 6bd34156b130c07b130de10111a12ef6dab18b52 virtual-sink: Fix a crash when moving the sink to a new master right after setup. https://tango.0pointer.de/pipermail/pulseaudio-discuss/2011-February/009105.html Reproduce: This problem can be reproduced with the following script: SAMPLE_PATH="/usr/share/sounds/alsa/" SAMPLE="Front_Left" pactl remove-sample $SAMPLE 2> /dev/null pactl upload-sample $SAMPLE_PATH$SAMPLE.wav mod1=`pactl load-module module-null-sink sink_name=null1` mod2=`pactl load-module module-null-sink sink_name=null2` pactl play-sample $SAMPLE null1 input=`pactl list | grep "Sink Input #" | tail -n 1 | cut -d# -f2` echo "Sample $SAMPLE playing as Sink Input #$input" pactl move-sink-input $input null2 pactl move-sink-input $input null1 pactl unload-module $mod1 pactl unload-module $mod2
-rw-r--r--src/pulsecore/play-memblockq.c13
1 files changed, 8 insertions, 5 deletions
diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c
index f075a5bf..66e47ea4 100644
--- a/src/pulsecore/play-memblockq.c
+++ b/src/pulsecore/play-memblockq.c
@@ -135,11 +135,14 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
return -1;
}
- /* FIXME: u->memblockq doesn't have a silence memchunk set, so
- * pa_memblockq_peek() will return 0 without returning any memblock if the
- * read index points to a hole. If the memblockq is rewound beyond index 0,
- * then there will be a hole. */
- pa_assert(chunk->memblock);
+ /* If there's no memblock, there's going to be data in the memblockq after
+ * a gap with length chunk->length. Drop the the gap and peek the actual
+ * data. There should always be some data coming - hence the assert. The
+ * gap will occur if the memblockq is rewound beyond index 0.*/
+ if (!chunk->memblock) {
+ pa_memblockq_drop(u->memblockq, chunk->length);
+ pa_assert_se(pa_memblockq_peek(u->memblockq, chunk) >= 0);
+ }
chunk->length = PA_MIN(chunk->length, nbytes);
pa_memblockq_drop(u->memblockq, chunk->length);