diff --git a/NEWS b/NEWS index 847caf8be..883efab8c 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,39 @@ +PulseAudio 16.2 + +A bug fix release. + + * A couple of time-smoother-2 fixes, mainly manifesting in issues for GStreamer clients + * Fix a crash in some restricted environments + * Minor RTP spec compliance fix + * Minur completion and tests fixups + +Contributors + +Arun Raghavan +Georg Chini +Igor V. Kovalenko +flyingOwl + + +PulseAudio 16.1 + +A bug fix release. + + * Fix parsing of percentage volumes with decimal points in pactl + * Fix crash with the "pacmd play-file" command when reads from the disk aren't frame-aligned + * Fix module-rtp-recv sometimes thinking it's receiving an Opus stream when it's not + * Fix frequent crashing in module-combine-sink, regression in 16.0 + * Fix crashing on 32-bit architectures when using the GStreamer codecs for LDAC and AptX + +Contributors + +Georg Chini +Igor V. Kovalenko +Jaechul Lee +Jan Palus +Sean Greenslade + + PulseAudio 16.0 Changes at a glance: diff --git a/shell-completion/bash/pactl b/shell-completion/bash/pactl index b269815d2..7e24be670 100644 --- a/shell-completion/bash/pactl +++ b/shell-completion/bash/pactl @@ -500,7 +500,7 @@ _pulseaudio() --start -k --kill --check --system= -D --daemonize= --fail= --high-priority= --realtime= --disallow-module-loading= --disallow-exit= --exit-idle-time= --scache-idle-time= --log-level= -v --log-target= --log-meta= --log-time= - --log-backtrace= -p --dl-search-path= --resample-method= --use-pit-file= + --log-backtrace= -p --dl-search-path= --resample-method= --use-pid-file= --no-cpu-limit= --disable-shm= --enable-memfd= -L --load= -F --file= -C -n' _init_completion -n = || return diff --git a/src/modules/bluetooth/a2dp-codec-gst.c b/src/modules/bluetooth/a2dp-codec-gst.c index 8ef74be9c..11839c580 100644 --- a/src/modules/bluetooth/a2dp-codec-gst.c +++ b/src/modules/bluetooth/a2dp-codec-gst.c @@ -22,6 +22,7 @@ #endif #include +#include #include #include @@ -82,7 +83,7 @@ fail: static GstCaps *gst_create_caps_from_sample_spec(const pa_sample_spec *ss) { gchar *sample_format; GstCaps *caps; - int channel_mask; + uint64_t channel_mask; switch (ss->format) { case PA_SAMPLE_S16LE: diff --git a/src/modules/rtp/sap.c b/src/modules/rtp/sap.c index 7fb1a3895..8614022f8 100644 --- a/src/modules/rtp/sap.c +++ b/src/modules/rtp/sap.c @@ -213,7 +213,9 @@ int pa_sap_recv(pa_sap_context *c, bool *goodbye) { if ((unsigned) size >= sizeof(MIME_TYPE) && pa_streq(e, MIME_TYPE)) { e += sizeof(MIME_TYPE); size -= (int) sizeof(MIME_TYPE); - } else if ((unsigned) size < sizeof(PA_SDP_HEADER)-1 || strncmp(e, PA_SDP_HEADER, sizeof(PA_SDP_HEADER)-1)) { + } else if ((unsigned) size < sizeof(PA_SDP_HEADER)-1 || strncmp(e, PA_SDP_HEADER, sizeof(PA_SDP_HEADER)-1) + || strcspn(e, "\r\n") != sizeof(PA_SDP_HEADER)-1) { + /* SDP header does not start with v=0[\r]\n */ pa_log_warn("Invalid SDP header."); goto fail; } diff --git a/src/modules/rtp/sdp.c b/src/modules/rtp/sdp.c index e130509df..bf131a27e 100644 --- a/src/modules/rtp/sdp.c +++ b/src/modules/rtp/sdp.c @@ -73,7 +73,7 @@ char *pa_sdp_build(int af, const void *src, const void *dst, const char *name, u pa_assert_se(inet_ntop(af, dst, buf_dst, sizeof(buf_dst))); return pa_sprintf_malloc( - PA_SDP_HEADER + PA_SDP_HEADER "\n" "o=%s %lu 0 IN %s %s\n" "s=%s\n" "c=IN %s %s\n" @@ -129,18 +129,31 @@ pa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *i, int is_goodbye) { i->origin = i->session_name = NULL; i->salen = 0; i->payload = 255; + i->enable_opus = false; - if (!pa_startswith(t, PA_SDP_HEADER)) { + if (pa_startswith(t, PA_SDP_HEADER)) { + t += sizeof(PA_SDP_HEADER) - 1; + + /* CR delimiter is optional */ + if (*t == '\r') + t++; + + /* LF delimiter is mandatory */ + if (*t == '\n') + t++; + else { + pa_log("Failed to parse SDP data: missing header record terminator LF."); + goto fail; + } + } else { pa_log("Failed to parse SDP data: invalid header."); goto fail; } - t += sizeof(PA_SDP_HEADER)-1; - while (*t) { size_t l; - l = strcspn(t, "\n"); + l = strcspn(t, "\r\n"); if (l <= 2) { pa_log("Failed to parse SDP data: line too short: >%s<.", t); @@ -240,8 +253,17 @@ pa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *i, int is_goodbye) { t += l; + /* CR delimiter is optional */ + if (*t == '\r') + t++; + + /* LF delimiter is mandatory */ if (*t == '\n') t++; + else { + pa_log("Failed to parse SDP data: missing record terminator LF."); + goto fail; + } } if (!i->origin || (!is_goodbye && (!i->salen || i->payload > 127 || !ss_valid || port == 0))) { diff --git a/src/modules/rtp/sdp.h b/src/modules/rtp/sdp.h index 28c755a27..80ab3636e 100644 --- a/src/modules/rtp/sdp.h +++ b/src/modules/rtp/sdp.h @@ -26,7 +26,7 @@ #include -#define PA_SDP_HEADER "v=0\n" +#define PA_SDP_HEADER "v=0" typedef struct pa_sdp_info { char *origin; diff --git a/src/pulse/util.c b/src/pulse/util.c index 5933b77e8..c7b828cc2 100644 --- a/src/pulse/util.c +++ b/src/pulse/util.c @@ -167,7 +167,7 @@ char *pa_get_host_name(char *s, size_t l) { char *pa_get_home_dir(char *s, size_t l) { char *e; - char *dir; + char *dir = NULL; #ifdef HAVE_PWD_H struct passwd *r; #endif diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 3108ae765..0f0dc56fc 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -1016,20 +1016,29 @@ size_t pa_sink_process_input_underruns(pa_sink *s, size_t left_to_play) { if (i->origin_sink) { size_t filter_result, left_to_play_origin; - /* The recursive call works in the origin sink domain ... */ - left_to_play_origin = pa_convert_size(left_to_play, &i->sink->sample_spec, &i->origin_sink->sample_spec); + /* The combine sink sets i->origin sink but has a different threading model + * than the filter sinks. Therefore the recursion below may not be executed + * because pa_sink_process_input_underruns() was not called in the thread + * context of the origin sink. + * FIXME: It is unclear if some other kind of recursion would be necessary + * for the combine sink. */ + if (!i->module || !pa_safe_streq(i->module->name, "module-combine-sink")) { - /* .. and returns the time to sleep before waking up. We need the - * underrun duration for comparisons, so we undo the subtraction on - * the return value... */ - filter_result = left_to_play_origin - pa_sink_process_input_underruns(i->origin_sink, left_to_play_origin); + /* The recursive call works in the origin sink domain ... */ + left_to_play_origin = pa_convert_size(left_to_play, &i->sink->sample_spec, &i->origin_sink->sample_spec); - /* ... and convert it back to the master sink domain */ - filter_result = pa_convert_size(filter_result, &i->origin_sink->sample_spec, &i->sink->sample_spec); + /* .. and returns the time to sleep before waking up. We need the + * underrun duration for comparisons, so we undo the subtraction on + * the return value... */ + filter_result = left_to_play_origin - pa_sink_process_input_underruns(i->origin_sink, left_to_play_origin); - /* Remember the longest underrun so far */ - if (filter_result > result) - result = filter_result; + /* ... and convert it back to the master sink domain */ + filter_result = pa_convert_size(filter_result, &i->origin_sink->sample_spec, &i->sink->sample_spec); + + /* Remember the longest underrun so far */ + if (filter_result > result) + result = filter_result; + } } if (uf == 0) { diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c index 147aa2288..255f4b61a 100644 --- a/src/pulsecore/sound-file-stream.c +++ b/src/pulsecore/sound-file-stream.c @@ -185,7 +185,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk tchunk.length = (size_t) n * fs; - pa_memblockq_push(u->memblockq, &tchunk); + pa_memblockq_push_align(u->memblockq, &tchunk); pa_memblock_unref(tchunk.memblock); } diff --git a/src/pulsecore/time-smoother_2.c b/src/pulsecore/time-smoother_2.c index e14b52f72..46cc5e9cc 100644 --- a/src/pulsecore/time-smoother_2.c +++ b/src/pulsecore/time-smoother_2.c @@ -295,7 +295,7 @@ pa_usec_t pa_smoother_2_get(pa_smoother_2 *s, pa_usec_t time_stamp) { /* If the smoother has not started, just return system time since resume */ if (!s->start_time) { - if (time_stamp >= s->resume_time) + if (time_stamp >= s->resume_time && !s->paused) current_time = time_stamp - s->resume_time; else current_time = 0; @@ -307,7 +307,8 @@ pa_usec_t pa_smoother_2_get(pa_smoother_2 *s, pa_usec_t time_stamp) { /* If we are initializing, add the time since resume to the card time at pause_time */ else if (s->init) { current_time += (s->pause_time - s->start_time - s->time_offset - s->fixup_time) * s->time_factor; - current_time += (time_stamp - s->resume_time) * s->time_factor; + if (time_stamp > s->resume_time) + current_time += (time_stamp - s->resume_time) * s->time_factor; /* Smoother is running, calculate current sound card time */ } else diff --git a/src/tests/cpu-volume-test.c b/src/tests/cpu-volume-test.c index 5de8c8304..a779fcce3 100644 --- a/src/tests/cpu-volume-test.c +++ b/src/tests/cpu-volume-test.c @@ -43,6 +43,7 @@ static void run_volume_test( int channels, bool correct, bool perf) { + fail_unless(align % channels == 0); PA_DECLARE_ALIGNED(8, int16_t, s[SAMPLES]) = { 0 }; PA_DECLARE_ALIGNED(8, int16_t, s_ref[SAMPLES]) = { 0 }; @@ -56,8 +57,6 @@ static void run_volume_test( samples_ref = s_ref + (8 - align); samples_orig = s_orig + (8 - align); nsamples = SAMPLES - (8 - align); - if (nsamples % channels) - nsamples -= nsamples % channels; size = nsamples * sizeof(int16_t); pa_random(samples, size); @@ -119,12 +118,12 @@ START_TEST (svolume_mmx_test) { pa_log_debug("Checking MMX svolume"); for (i = 1; i <= 3; i++) { - for (j = 0; j < 7; j++) - run_volume_test(mmx_func, orig_func, j, i, true, false); + for (j = 0; j <= 7; j += i) + run_volume_test(mmx_func, orig_func, j, i, true, j == 0); } run_volume_test(mmx_func, orig_func, 7, 1, true, true); - run_volume_test(mmx_func, orig_func, 7, 2, true, true); - run_volume_test(mmx_func, orig_func, 7, 3, true, true); + run_volume_test(mmx_func, orig_func, 6, 2, true, true); + run_volume_test(mmx_func, orig_func, 6, 3, true, true); } END_TEST @@ -146,12 +145,12 @@ START_TEST (svolume_sse_test) { pa_log_debug("Checking SSE2 svolume"); for (i = 1; i <= 3; i++) { - for (j = 0; j < 7; j++) - run_volume_test(sse_func, orig_func, j, i, true, false); + for (j = 0; j < 7; j += i) + run_volume_test(sse_func, orig_func, j, i, true, j == 0); } run_volume_test(sse_func, orig_func, 7, 1, true, true); - run_volume_test(sse_func, orig_func, 7, 2, true, true); - run_volume_test(sse_func, orig_func, 7, 3, true, true); + run_volume_test(sse_func, orig_func, 6, 2, true, true); + run_volume_test(sse_func, orig_func, 6, 3, true, true); } END_TEST #endif /* defined (__i386__) || defined (__amd64__) */ @@ -175,12 +174,12 @@ START_TEST (svolume_arm_test) { pa_log_debug("Checking ARM svolume"); for (i = 1; i <= 3; i++) { - for (j = 0; j < 7; j++) - run_volume_test(arm_func, orig_func, j, i, true, false); + for (j = 0; j < 7; j += i) + run_volume_test(arm_func, orig_func, j, i, true, j == 0); } run_volume_test(arm_func, orig_func, 7, 1, true, true); - run_volume_test(arm_func, orig_func, 7, 2, true, true); - run_volume_test(arm_func, orig_func, 7, 3, true, true); + run_volume_test(arm_func, orig_func, 6, 2, true, true); + run_volume_test(arm_func, orig_func, 6, 3, true, true); } END_TEST #endif /* defined (__arm__) && defined (__linux__) */ @@ -207,11 +206,11 @@ START_TEST (svolume_orc_test) { pa_log_debug("Checking Orc svolume"); for (i = 1; i <= 2; i++) { - for (j = 0; j < 7; j++) - run_volume_test(orc_func, orig_func, j, i, true, false); + for (j = 0; j < 7; j += i) + run_volume_test(orc_func, orig_func, j, i, true, j == 0); } run_volume_test(orc_func, orig_func, 7, 1, true, true); - run_volume_test(orc_func, orig_func, 7, 2, true, true); + run_volume_test(orc_func, orig_func, 6, 2, true, true); } END_TEST diff --git a/src/utils/pactl.c b/src/utils/pactl.c index 35163f277..2761ebaaf 100644 --- a/src/utils/pactl.c +++ b/src/utils/pactl.c @@ -2527,16 +2527,16 @@ static int parse_volume(const char *vol_spec, pa_volume_t *vol, enum volume_flag vs = pa_xstrdup(vol_spec); *vol_flags = (pa_startswith(vs, "+") || pa_startswith(vs, "-")) ? VOL_RELATIVE : VOL_ABSOLUTE; - if (strchr(vs, '.')) - *vol_flags |= VOL_LINEAR; if (pa_endswith(vs, "%")) { *vol_flags |= VOL_PERCENT; vs[strlen(vs)-1] = 0; } - if (pa_endswith(vs, "db") || pa_endswith(vs, "dB")) { + else if (pa_endswith(vs, "db") || pa_endswith(vs, "dB")) { *vol_flags |= VOL_DECIBEL; vs[strlen(vs)-2] = 0; } + else if (strchr(vs, '.')) + *vol_flags |= VOL_LINEAR; atod_input = vs; @@ -2597,7 +2597,7 @@ static int parse_volumes(char *args[], unsigned n) { volume.channels = n; for (i = 0; i < volume.channels; i++) { - enum volume_flags flags; + enum volume_flags flags = 0; if (parse_volume(args[i], &volume.values[i], &flags) < 0) return -1;