2004-08-13 13:22:44 +00:00
|
|
|
/***
|
2006-06-19 21:53:48 +00:00
|
|
|
This file is part of PulseAudio.
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-02-13 15:35:19 +00:00
|
|
|
Copyright 2004-2006 Lennart Poettering
|
|
|
|
|
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
|
|
|
|
|
|
2006-06-19 21:53:48 +00:00
|
|
|
PulseAudio is free software; you can redistribute it and/or modify
|
2004-11-14 14:58:54 +00:00
|
|
|
it under the terms of the GNU Lesser General Public License as published
|
2009-03-03 20:23:02 +00:00
|
|
|
by the Free Software Foundation; either version 2.1 of the License,
|
2004-08-13 13:22:44 +00:00
|
|
|
or (at your option) any later version.
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-06-19 21:53:48 +00:00
|
|
|
PulseAudio is distributed in the hope that it will be useful, but
|
2004-08-13 13:22:44 +00:00
|
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
General Public License for more details.
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-11-14 14:58:54 +00:00
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
2014-11-26 14:14:51 +01:00
|
|
|
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
|
2004-08-13 13:22:44 +00:00
|
|
|
***/
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
|
#include <config.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include <string.h>
|
2004-08-14 20:25:32 +00:00
|
|
|
#include <stdio.h>
|
2004-10-27 00:10:12 +00:00
|
|
|
#include <string.h>
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2006-06-19 21:53:48 +00:00
|
|
|
#include <pulse/def.h>
|
|
|
|
|
#include <pulse/timeval.h>
|
2009-04-04 23:19:53 +03:00
|
|
|
#include <pulse/rtclock.h>
|
2006-06-19 21:53:48 +00:00
|
|
|
#include <pulse/xmalloc.h>
|
2011-03-09 10:00:20 +01:00
|
|
|
#include <pulse/fork-detect.h>
|
2006-05-17 16:34:18 +00:00
|
|
|
|
2006-06-19 21:53:48 +00:00
|
|
|
#include <pulsecore/pstream-util.h>
|
2016-11-22 22:18:56 +02:00
|
|
|
#include <pulsecore/sample-util.h>
|
2006-06-19 21:53:48 +00:00
|
|
|
#include <pulsecore/log.h>
|
|
|
|
|
#include <pulsecore/hashmap.h>
|
2007-10-28 19:13:50 +00:00
|
|
|
#include <pulsecore/macro.h>
|
2009-04-04 23:19:53 +03:00
|
|
|
#include <pulsecore/core-rtclock.h>
|
2010-01-15 01:25:42 +01:00
|
|
|
#include <pulsecore/core-util.h>
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2006-02-17 12:10:58 +00:00
|
|
|
#include "internal.h"
|
2011-03-09 10:00:20 +01:00
|
|
|
#include "stream.h"
|
2006-02-17 12:10:58 +00:00
|
|
|
|
2012-11-22 09:58:08 +05:30
|
|
|
/* #define STREAM_DEBUG */
|
|
|
|
|
|
2009-04-01 00:36:18 +02:00
|
|
|
#define AUTO_TIMING_INTERVAL_START_USEC (10*PA_USEC_PER_MSEC)
|
|
|
|
|
#define AUTO_TIMING_INTERVAL_END_USEC (1500*PA_USEC_PER_MSEC)
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
#define SMOOTHER_HISTORY_TIME (5000*PA_USEC_PER_MSEC)
|
2020-01-15 20:37:12 +01:00
|
|
|
#ifndef USE_SMOOTHER_2
|
|
|
|
|
#define SMOOTHER_ADJUST_TIME (1000*PA_USEC_PER_MSEC)
|
2008-05-15 23:34:41 +00:00
|
|
|
#define SMOOTHER_MIN_HISTORY (4)
|
2020-01-15 20:37:12 +01:00
|
|
|
#endif
|
2004-10-27 00:10:12 +00:00
|
|
|
|
2006-01-27 16:25:31 +00:00
|
|
|
pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map) {
|
2008-05-15 23:34:41 +00:00
|
|
|
return pa_stream_new_with_proplist(c, name, ss, map, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void reset_callbacks(pa_stream *s) {
|
|
|
|
|
s->read_callback = NULL;
|
|
|
|
|
s->read_userdata = NULL;
|
|
|
|
|
s->write_callback = NULL;
|
|
|
|
|
s->write_userdata = NULL;
|
|
|
|
|
s->state_callback = NULL;
|
|
|
|
|
s->state_userdata = NULL;
|
|
|
|
|
s->overflow_callback = NULL;
|
|
|
|
|
s->overflow_userdata = NULL;
|
|
|
|
|
s->underflow_callback = NULL;
|
|
|
|
|
s->underflow_userdata = NULL;
|
|
|
|
|
s->latency_update_callback = NULL;
|
|
|
|
|
s->latency_update_userdata = NULL;
|
|
|
|
|
s->moved_callback = NULL;
|
|
|
|
|
s->moved_userdata = NULL;
|
|
|
|
|
s->suspended_callback = NULL;
|
|
|
|
|
s->suspended_userdata = NULL;
|
|
|
|
|
s->started_callback = NULL;
|
|
|
|
|
s->started_userdata = NULL;
|
2009-02-12 03:18:05 +01:00
|
|
|
s->event_callback = NULL;
|
|
|
|
|
s->event_userdata = NULL;
|
2009-03-30 18:46:12 +02:00
|
|
|
s->buffer_attr_callback = NULL;
|
|
|
|
|
s->buffer_attr_userdata = NULL;
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
|
|
|
|
|
2011-02-28 13:23:23 +05:30
|
|
|
static pa_stream *pa_stream_new_with_proplist_internal(
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_context *c,
|
|
|
|
|
const char *name,
|
|
|
|
|
const pa_sample_spec *ss,
|
|
|
|
|
const pa_channel_map *map,
|
2011-02-28 13:23:23 +05:30
|
|
|
pa_format_info * const *formats,
|
2011-06-18 15:36:18 -07:00
|
|
|
unsigned int n_formats,
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_proplist *p) {
|
|
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_stream *s;
|
2011-06-18 15:36:18 -07:00
|
|
|
unsigned int i;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
2011-06-18 15:36:18 -07:00
|
|
|
pa_assert((ss == NULL && map == NULL) || (formats == NULL && n_formats == 0));
|
|
|
|
|
pa_assert(n_formats < PA_MAX_FORMATS);
|
2006-01-27 16:25:31 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
|
2008-05-15 23:34:41 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(c, name || (p && pa_proplist_contains(p, PA_PROP_MEDIA_NAME)), PA_ERR_INVALID);
|
2006-01-27 16:25:31 +00:00
|
|
|
|
|
|
|
|
s = pa_xnew(pa_stream, 1);
|
2007-10-28 19:13:50 +00:00
|
|
|
PA_REFCNT_INIT(s);
|
2004-08-14 20:25:32 +00:00
|
|
|
s->context = c;
|
2004-10-27 00:10:12 +00:00
|
|
|
s->mainloop = c->mainloop;
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2004-09-26 17:02:26 +00:00
|
|
|
s->direction = PA_STREAM_NODIRECTION;
|
2008-05-15 23:34:41 +00:00
|
|
|
s->state = PA_STREAM_UNCONNECTED;
|
|
|
|
|
s->flags = 0;
|
|
|
|
|
|
2011-02-28 13:23:23 +05:30
|
|
|
if (ss)
|
|
|
|
|
s->sample_spec = *ss;
|
|
|
|
|
else
|
2012-03-28 01:01:32 +05:30
|
|
|
pa_sample_spec_init(&s->sample_spec);
|
2011-02-28 13:23:23 +05:30
|
|
|
|
|
|
|
|
if (map)
|
|
|
|
|
s->channel_map = *map;
|
|
|
|
|
else
|
|
|
|
|
pa_channel_map_init(&s->channel_map);
|
|
|
|
|
|
|
|
|
|
s->n_formats = 0;
|
|
|
|
|
if (formats) {
|
2011-06-18 15:36:18 -07:00
|
|
|
s->n_formats = n_formats;
|
|
|
|
|
for (i = 0; i < n_formats; i++)
|
2011-02-28 13:23:23 +05:30
|
|
|
s->req_formats[i] = pa_format_info_copy(formats[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We'll get the final negotiated format after connecting */
|
|
|
|
|
s->format = NULL;
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2008-06-13 21:56:19 +00:00
|
|
|
s->direct_on_input = PA_INVALID_INDEX;
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
s->proplist = p ? pa_proplist_copy(p) : pa_proplist_new();
|
|
|
|
|
if (name)
|
|
|
|
|
pa_proplist_sets(s->proplist, PA_PROP_MEDIA_NAME, name);
|
2006-01-27 16:25:31 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
s->channel = 0;
|
2013-06-27 19:28:09 +02:00
|
|
|
s->channel_valid = false;
|
2006-02-20 04:05:16 +00:00
|
|
|
s->syncid = c->csyncid++;
|
2007-11-21 01:30:40 +00:00
|
|
|
s->stream_index = PA_INVALID_INDEX;
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
s->requested_bytes = 0;
|
2004-08-14 20:25:32 +00:00
|
|
|
memset(&s->buffer_attr, 0, sizeof(s->buffer_attr));
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2011-12-11 16:07:38 +01:00
|
|
|
/* We initialize the target length here, so that if the user
|
2008-06-25 02:28:33 +02:00
|
|
|
* passes no explicit buffering metrics the default is similar to
|
|
|
|
|
* what older PA versions provided. */
|
2008-06-27 00:34:17 +02:00
|
|
|
|
|
|
|
|
s->buffer_attr.maxlength = (uint32_t) -1;
|
2011-02-28 13:23:23 +05:30
|
|
|
if (ss)
|
|
|
|
|
s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, ss); /* 250ms of buffering */
|
2011-03-02 12:54:02 +05:30
|
|
|
else {
|
|
|
|
|
/* FIXME: We assume a worst-case compressed format corresponding to
|
|
|
|
|
* 48000 Hz, 2 ch, S16 PCM, but this can very well be incorrect */
|
|
|
|
|
pa_sample_spec tmp_ss = {
|
|
|
|
|
.format = PA_SAMPLE_S16NE,
|
|
|
|
|
.rate = 48000,
|
|
|
|
|
.channels = 2,
|
|
|
|
|
};
|
|
|
|
|
s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, &tmp_ss); /* 250ms of buffering */
|
|
|
|
|
}
|
2008-06-27 00:34:17 +02:00
|
|
|
s->buffer_attr.minreq = (uint32_t) -1;
|
|
|
|
|
s->buffer_attr.prebuf = (uint32_t) -1;
|
|
|
|
|
s->buffer_attr.fragsize = (uint32_t) -1;
|
2008-06-25 02:28:33 +02:00
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
s->device_index = PA_INVALID_INDEX;
|
|
|
|
|
s->device_name = NULL;
|
2013-06-27 19:28:09 +02:00
|
|
|
s->suspended = false;
|
|
|
|
|
s->corked = false;
|
2007-11-21 01:30:40 +00:00
|
|
|
|
2009-07-20 15:49:33 +01:00
|
|
|
s->write_memblock = NULL;
|
|
|
|
|
s->write_data = NULL;
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_memchunk_reset(&s->peek_memchunk);
|
2007-10-28 19:13:50 +00:00
|
|
|
s->peek_data = NULL;
|
2006-02-17 17:00:33 +00:00
|
|
|
s->record_memblockq = NULL;
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
memset(&s->timing_info, 0, sizeof(s->timing_info));
|
2013-06-27 19:28:09 +02:00
|
|
|
s->timing_info_valid = false;
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2004-09-26 17:02:26 +00:00
|
|
|
s->previous_time = 0;
|
2011-08-02 14:34:20 +02:00
|
|
|
s->latest_underrun_at_index = -1;
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2006-04-12 17:17:23 +00:00
|
|
|
s->read_index_not_before = 0;
|
|
|
|
|
s->write_index_not_before = 0;
|
|
|
|
|
for (i = 0; i < PA_MAX_WRITE_INDEX_CORRECTIONS; i++)
|
|
|
|
|
s->write_index_corrections[i].valid = 0;
|
|
|
|
|
s->current_write_index_correction = 0;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
s->auto_timing_update_event = NULL;
|
2013-06-27 19:28:09 +02:00
|
|
|
s->auto_timing_update_requested = false;
|
2009-04-01 00:36:18 +02:00
|
|
|
s->auto_timing_interval_usec = AUTO_TIMING_INTERVAL_START_USEC;
|
2004-10-27 00:10:12 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
reset_callbacks(s);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
s->smoother = NULL;
|
2006-04-12 17:17:23 +00:00
|
|
|
|
2006-04-24 19:29:15 +00:00
|
|
|
/* Refcounting is strictly one-way: from the "bigger" to the "smaller" object. */
|
2006-01-11 01:17:39 +00:00
|
|
|
PA_LLIST_PREPEND(pa_stream, c->streams, s);
|
2006-04-24 19:29:15 +00:00
|
|
|
pa_stream_ref(s);
|
2006-03-07 18:29:27 +00:00
|
|
|
|
2006-03-07 16:04:32 +00:00
|
|
|
return s;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2011-02-28 13:23:23 +05:30
|
|
|
pa_stream *pa_stream_new_with_proplist(
|
|
|
|
|
pa_context *c,
|
|
|
|
|
const char *name,
|
|
|
|
|
const pa_sample_spec *ss,
|
|
|
|
|
const pa_channel_map *map,
|
|
|
|
|
pa_proplist *p) {
|
|
|
|
|
|
|
|
|
|
pa_channel_map tmap;
|
|
|
|
|
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(c, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 12 || (ss->format != PA_SAMPLE_S32LE && ss->format != PA_SAMPLE_S32BE), PA_ERR_NOTSUPPORTED);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15 || (ss->format != PA_SAMPLE_S24LE && ss->format != PA_SAMPLE_S24BE), PA_ERR_NOTSUPPORTED);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15 || (ss->format != PA_SAMPLE_S24_32LE && ss->format != PA_SAMPLE_S24_32BE), PA_ERR_NOTSUPPORTED);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(c, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID);
|
|
|
|
|
|
|
|
|
|
if (!map)
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(c, map = pa_channel_map_init_auto(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT), PA_ERR_INVALID);
|
|
|
|
|
|
2011-06-18 15:36:18 -07:00
|
|
|
return pa_stream_new_with_proplist_internal(c, name, ss, map, NULL, 0, p);
|
2011-02-28 13:23:23 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_stream *pa_stream_new_extended(
|
|
|
|
|
pa_context *c,
|
|
|
|
|
const char *name,
|
|
|
|
|
pa_format_info * const *formats,
|
2011-06-18 15:36:18 -07:00
|
|
|
unsigned int n_formats,
|
2011-02-28 13:23:23 +05:30
|
|
|
pa_proplist *p) {
|
|
|
|
|
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 21, PA_ERR_NOTSUPPORTED);
|
|
|
|
|
|
2011-06-18 15:36:18 -07:00
|
|
|
return pa_stream_new_with_proplist_internal(c, name, NULL, NULL, formats, n_formats, p);
|
2011-02-28 13:23:23 +05:30
|
|
|
}
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
static void stream_unlink(pa_stream *s) {
|
|
|
|
|
pa_operation *o, *n;
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
if (!s->context)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* Detach from context */
|
|
|
|
|
|
2011-12-11 16:07:38 +01:00
|
|
|
/* Unref all operation objects that point to us */
|
2008-05-15 23:34:41 +00:00
|
|
|
for (o = s->context->operations; o; o = n) {
|
|
|
|
|
n = o->next;
|
|
|
|
|
|
|
|
|
|
if (o->stream == s)
|
|
|
|
|
pa_operation_cancel(o);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Drop all outstanding replies for this stream */
|
|
|
|
|
if (s->context->pdispatch)
|
|
|
|
|
pa_pdispatch_unregister_reply(s->context->pdispatch, s);
|
|
|
|
|
|
|
|
|
|
if (s->channel_valid) {
|
2011-11-11 16:22:24 +02:00
|
|
|
pa_hashmap_remove((s->direction == PA_STREAM_RECORD) ? s->context->record_streams : s->context->playback_streams, PA_UINT32_TO_PTR(s->channel));
|
2008-05-15 23:34:41 +00:00
|
|
|
s->channel = 0;
|
2013-06-27 19:28:09 +02:00
|
|
|
s->channel_valid = false;
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PA_LLIST_REMOVE(pa_stream, s->context->streams, s);
|
|
|
|
|
pa_stream_unref(s);
|
|
|
|
|
|
|
|
|
|
s->context = NULL;
|
2004-10-27 00:10:12 +00:00
|
|
|
|
2006-04-12 17:17:23 +00:00
|
|
|
if (s->auto_timing_update_event) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s->mainloop);
|
2006-04-12 17:17:23 +00:00
|
|
|
s->mainloop->time_free(s->auto_timing_update_event);
|
2004-10-27 00:10:12 +00:00
|
|
|
}
|
2004-11-17 00:05:25 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
reset_callbacks(s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void stream_free(pa_stream *s) {
|
2011-02-28 13:23:23 +05:30
|
|
|
unsigned int i;
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
|
|
|
|
|
stream_unlink(s);
|
|
|
|
|
|
2009-07-20 15:49:33 +01:00
|
|
|
if (s->write_memblock) {
|
2012-02-15 02:09:02 +01:00
|
|
|
if (s->write_data)
|
|
|
|
|
pa_memblock_release(s->write_memblock);
|
|
|
|
|
pa_memblock_unref(s->write_memblock);
|
2009-07-20 15:49:33 +01:00
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
if (s->peek_memchunk.memblock) {
|
|
|
|
|
if (s->peek_data)
|
|
|
|
|
pa_memblock_release(s->peek_memchunk.memblock);
|
2006-02-17 15:42:47 +00:00
|
|
|
pa_memblock_unref(s->peek_memchunk.memblock);
|
2007-10-28 19:13:50 +00:00
|
|
|
}
|
2006-02-17 15:42:47 +00:00
|
|
|
|
|
|
|
|
if (s->record_memblockq)
|
|
|
|
|
pa_memblockq_free(s->record_memblockq);
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (s->proplist)
|
|
|
|
|
pa_proplist_free(s->proplist);
|
|
|
|
|
|
|
|
|
|
if (s->smoother)
|
2020-01-15 20:37:12 +01:00
|
|
|
#ifdef USE_SMOOTHER_2
|
|
|
|
|
pa_smoother_2_free(s->smoother);
|
|
|
|
|
#else
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_smoother_free(s->smoother);
|
2020-01-15 20:37:12 +01:00
|
|
|
#endif
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2011-02-28 13:23:23 +05:30
|
|
|
for (i = 0; i < s->n_formats; i++)
|
2011-06-18 11:35:41 -07:00
|
|
|
pa_format_info_free(s->req_formats[i]);
|
|
|
|
|
|
|
|
|
|
if (s->format)
|
|
|
|
|
pa_format_info_free(s->format);
|
2011-02-28 13:23:23 +05:30
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_xfree(s->device_name);
|
2004-08-14 20:25:32 +00:00
|
|
|
pa_xfree(s);
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
void pa_stream_unref(pa_stream *s) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
if (PA_REFCNT_DEC(s) <= 0)
|
2004-08-14 20:25:32 +00:00
|
|
|
stream_free(s);
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_stream* pa_stream_ref(pa_stream *s) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
PA_REFCNT_INC(s);
|
2004-08-14 20:25:32 +00:00
|
|
|
return s;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2018-05-27 05:43:01 +01:00
|
|
|
pa_stream_state_t pa_stream_get_state(const pa_stream *s) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
return s->state;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2018-05-27 05:45:36 +01:00
|
|
|
pa_context* pa_stream_get_context(const pa_stream *s) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
return s->context;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2018-05-27 05:49:23 +01:00
|
|
|
uint32_t pa_stream_get_index(const pa_stream *s) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2006-02-20 04:05:16 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_ANY(s->context, !pa_detect_fork(), PA_ERR_FORKED, PA_INVALID_INDEX);
|
2006-02-20 04:05:16 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
return s->stream_index;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-01-27 16:25:31 +00:00
|
|
|
void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
if (s->state == st)
|
|
|
|
|
return;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
pa_stream_ref(s);
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
s->state = st;
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2006-04-24 19:29:15 +00:00
|
|
|
if (s->state_callback)
|
|
|
|
|
s->state_callback(s, s->state_userdata);
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if ((st == PA_STREAM_FAILED || st == PA_STREAM_TERMINATED))
|
|
|
|
|
stream_unlink(s);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_stream_unref(s);
|
|
|
|
|
}
|
2006-04-24 19:29:15 +00:00
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
static void request_auto_timing_update(pa_stream *s, bool force) {
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (!(s->flags & PA_STREAM_AUTO_TIMING_UPDATE))
|
|
|
|
|
return;
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (s->state == PA_STREAM_READY &&
|
|
|
|
|
(force || !s->auto_timing_update_requested)) {
|
|
|
|
|
pa_operation *o;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2012-11-22 09:58:08 +05:30
|
|
|
#ifdef STREAM_DEBUG
|
|
|
|
|
pa_log_debug("Automatically requesting new timing data");
|
|
|
|
|
#endif
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if ((o = pa_stream_update_timing_info(s, NULL, NULL))) {
|
|
|
|
|
pa_operation_unref(o);
|
2013-06-27 19:28:09 +02:00
|
|
|
s->auto_timing_update_requested = true;
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (s->auto_timing_update_event) {
|
2011-04-08 17:18:12 +03:00
|
|
|
if (s->suspended && !force) {
|
|
|
|
|
pa_assert(s->mainloop);
|
|
|
|
|
s->mainloop->time_free(s->auto_timing_update_event);
|
|
|
|
|
s->auto_timing_update_event = NULL;
|
|
|
|
|
} else {
|
|
|
|
|
if (force)
|
|
|
|
|
s->auto_timing_interval_usec = AUTO_TIMING_INTERVAL_START_USEC;
|
2009-04-01 00:36:18 +02:00
|
|
|
|
2011-04-08 17:18:12 +03:00
|
|
|
pa_context_rttime_restart(s->context, s->auto_timing_update_event, pa_rtclock_now() + s->auto_timing_interval_usec);
|
2009-04-01 00:36:18 +02:00
|
|
|
|
2011-04-08 17:18:12 +03:00
|
|
|
s->auto_timing_interval_usec = PA_MIN(AUTO_TIMING_INTERVAL_END_USEC, s->auto_timing_interval_usec*2);
|
|
|
|
|
}
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_context *c = userdata;
|
|
|
|
|
pa_stream *s;
|
2004-08-13 13:22:44 +00:00
|
|
|
uint32_t channel;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_KILLED || command == PA_COMMAND_RECORD_STREAM_KILLED);
|
|
|
|
|
pa_assert(t);
|
|
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
pa_context_ref(c);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-08-13 13:22:44 +00:00
|
|
|
if (pa_tagstruct_getu32(t, &channel) < 0 ||
|
|
|
|
|
!pa_tagstruct_eof(t)) {
|
2006-02-20 04:05:16 +00:00
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
2004-08-14 20:25:32 +00:00
|
|
|
goto finish;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-12-03 13:22:05 +02:00
|
|
|
if (!(s = pa_hashmap_get(command == PA_COMMAND_PLAYBACK_STREAM_KILLED ? c->playback_streams : c->record_streams, PA_UINT32_TO_PTR(channel))))
|
2004-08-14 20:25:32 +00:00
|
|
|
goto finish;
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (s->state != PA_STREAM_READY)
|
|
|
|
|
goto finish;
|
|
|
|
|
|
2006-02-20 22:41:02 +00:00
|
|
|
pa_context_set_error(c, PA_ERR_KILLED);
|
2004-08-14 20:25:32 +00:00
|
|
|
pa_stream_set_state(s, PA_STREAM_FAILED);
|
|
|
|
|
|
|
|
|
|
finish:
|
|
|
|
|
pa_context_unref(c);
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
static void check_smoother_status(pa_stream *s, bool aposteriori, bool force_start, bool force_stop) {
|
2008-06-26 19:00:42 +02:00
|
|
|
pa_usec_t x;
|
|
|
|
|
|
|
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(!force_start || !force_stop);
|
|
|
|
|
|
|
|
|
|
if (!s->smoother)
|
|
|
|
|
return;
|
|
|
|
|
|
2009-04-04 22:56:38 +03:00
|
|
|
x = pa_rtclock_now();
|
2008-06-26 19:00:42 +02:00
|
|
|
|
|
|
|
|
if (s->timing_info_valid) {
|
|
|
|
|
if (aposteriori)
|
|
|
|
|
x -= s->timing_info.transport_usec;
|
|
|
|
|
else
|
|
|
|
|
x += s->timing_info.transport_usec;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (s->suspended || s->corked || force_stop)
|
2020-01-15 20:37:12 +01:00
|
|
|
#ifdef USE_SMOOTHER_2
|
|
|
|
|
pa_smoother_2_pause(s->smoother, x);
|
|
|
|
|
#else
|
2008-06-26 19:00:42 +02:00
|
|
|
pa_smoother_pause(s->smoother, x);
|
2020-01-15 20:37:12 +01:00
|
|
|
#endif
|
2009-11-20 19:48:08 +01:00
|
|
|
else if (force_start || s->buffer_attr.prebuf == 0) {
|
|
|
|
|
|
|
|
|
|
if (!s->timing_info_valid &&
|
|
|
|
|
!aposteriori &&
|
|
|
|
|
!force_start &&
|
|
|
|
|
!force_stop &&
|
|
|
|
|
s->context->version >= 13) {
|
|
|
|
|
|
|
|
|
|
/* If the server supports STARTED events we take them as
|
|
|
|
|
* indications when audio really starts/stops playing, if
|
|
|
|
|
* we don't have any timing info yet -- instead of trying
|
|
|
|
|
* to be smart and guessing the server time. Otherwise the
|
2011-12-11 16:07:38 +01:00
|
|
|
* unknown transport delay adds too much noise to our time
|
2009-11-20 19:48:08 +01:00
|
|
|
* calculations. */
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-15 20:37:12 +01:00
|
|
|
#ifdef USE_SMOOTHER_2
|
|
|
|
|
pa_smoother_2_resume(s->smoother, x);
|
|
|
|
|
#else
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_smoother_resume(s->smoother, x, true);
|
2020-01-15 20:37:12 +01:00
|
|
|
#endif
|
2009-11-20 19:48:08 +01:00
|
|
|
}
|
2009-04-05 02:26:02 +02:00
|
|
|
|
2008-06-26 19:00:42 +02:00
|
|
|
/* Please note that we have no idea if playback actually started
|
|
|
|
|
* if prebuf is non-zero! */
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-08 17:18:12 +03:00
|
|
|
static void auto_timing_update_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata);
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_context *c = userdata;
|
|
|
|
|
pa_stream *s;
|
|
|
|
|
uint32_t channel;
|
|
|
|
|
const char *dn;
|
2013-06-27 19:28:09 +02:00
|
|
|
bool suspended;
|
2007-11-21 01:30:40 +00:00
|
|
|
uint32_t di;
|
2009-03-30 18:46:12 +02:00
|
|
|
pa_usec_t usec = 0;
|
2008-05-15 23:34:41 +00:00
|
|
|
uint32_t maxlength = 0, fragsize = 0, minreq = 0, tlength = 0, prebuf = 0;
|
2007-11-21 01:30:40 +00:00
|
|
|
|
|
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_MOVED || command == PA_COMMAND_RECORD_STREAM_MOVED);
|
|
|
|
|
pa_assert(t);
|
|
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
|
|
|
|
|
|
|
|
|
pa_context_ref(c);
|
|
|
|
|
|
|
|
|
|
if (c->version < 12) {
|
|
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_getu32(t, &channel) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &di) < 0 ||
|
|
|
|
|
pa_tagstruct_gets(t, &dn) < 0 ||
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_tagstruct_get_boolean(t, &suspended) < 0) {
|
|
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (c->version >= 13) {
|
|
|
|
|
|
|
|
|
|
if (command == PA_COMMAND_RECORD_STREAM_MOVED) {
|
|
|
|
|
if (pa_tagstruct_getu32(t, &maxlength) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &fragsize) < 0 ||
|
|
|
|
|
pa_tagstruct_get_usec(t, &usec) < 0) {
|
|
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (pa_tagstruct_getu32(t, &maxlength) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &tlength) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &prebuf) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &minreq) < 0 ||
|
|
|
|
|
pa_tagstruct_get_usec(t, &usec) < 0) {
|
|
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!pa_tagstruct_eof(t)) {
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!dn || di == PA_INVALID_INDEX) {
|
|
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-03 13:22:05 +02:00
|
|
|
if (!(s = pa_hashmap_get(command == PA_COMMAND_PLAYBACK_STREAM_MOVED ? c->playback_streams : c->record_streams, PA_UINT32_TO_PTR(channel))))
|
2007-11-21 01:30:40 +00:00
|
|
|
goto finish;
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (s->state != PA_STREAM_READY)
|
|
|
|
|
goto finish;
|
|
|
|
|
|
|
|
|
|
if (c->version >= 13) {
|
|
|
|
|
if (s->direction == PA_STREAM_RECORD)
|
|
|
|
|
s->timing_info.configured_source_usec = usec;
|
|
|
|
|
else
|
|
|
|
|
s->timing_info.configured_sink_usec = usec;
|
|
|
|
|
|
|
|
|
|
s->buffer_attr.maxlength = maxlength;
|
|
|
|
|
s->buffer_attr.fragsize = fragsize;
|
|
|
|
|
s->buffer_attr.tlength = tlength;
|
|
|
|
|
s->buffer_attr.prebuf = prebuf;
|
|
|
|
|
s->buffer_attr.minreq = minreq;
|
|
|
|
|
}
|
|
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_xfree(s->device_name);
|
|
|
|
|
s->device_name = pa_xstrdup(dn);
|
|
|
|
|
s->device_index = di;
|
|
|
|
|
|
|
|
|
|
s->suspended = suspended;
|
|
|
|
|
|
2011-04-08 17:18:12 +03:00
|
|
|
if ((s->flags & PA_STREAM_AUTO_TIMING_UPDATE) && !suspended && !s->auto_timing_update_event) {
|
|
|
|
|
s->auto_timing_interval_usec = AUTO_TIMING_INTERVAL_START_USEC;
|
|
|
|
|
s->auto_timing_update_event = pa_context_rttime_new(s->context, pa_rtclock_now() + s->auto_timing_interval_usec, &auto_timing_update_callback, s);
|
2013-06-27 19:28:09 +02:00
|
|
|
request_auto_timing_update(s, true);
|
2011-04-08 17:18:12 +03:00
|
|
|
}
|
|
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
check_smoother_status(s, true, false, false);
|
|
|
|
|
request_auto_timing_update(s, true);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
if (s->moved_callback)
|
|
|
|
|
s->moved_callback(s, s->moved_userdata);
|
|
|
|
|
|
|
|
|
|
finish:
|
|
|
|
|
pa_context_unref(c);
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
void pa_command_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
|
|
|
|
pa_context *c = userdata;
|
|
|
|
|
pa_stream *s;
|
|
|
|
|
uint32_t channel;
|
|
|
|
|
pa_usec_t usec = 0;
|
|
|
|
|
uint32_t maxlength = 0, fragsize = 0, minreq = 0, tlength = 0, prebuf = 0;
|
|
|
|
|
|
|
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(command == PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED || command == PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED);
|
|
|
|
|
pa_assert(t);
|
|
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
|
|
|
|
|
|
|
|
|
pa_context_ref(c);
|
|
|
|
|
|
|
|
|
|
if (c->version < 15) {
|
|
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_getu32(t, &channel) < 0) {
|
|
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (command == PA_COMMAND_RECORD_STREAM_MOVED) {
|
|
|
|
|
if (pa_tagstruct_getu32(t, &maxlength) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &fragsize) < 0 ||
|
|
|
|
|
pa_tagstruct_get_usec(t, &usec) < 0) {
|
|
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (pa_tagstruct_getu32(t, &maxlength) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &tlength) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &prebuf) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &minreq) < 0 ||
|
|
|
|
|
pa_tagstruct_get_usec(t, &usec) < 0) {
|
|
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!pa_tagstruct_eof(t)) {
|
|
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-03 13:22:05 +02:00
|
|
|
if (!(s = pa_hashmap_get(command == PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED ? c->playback_streams : c->record_streams, PA_UINT32_TO_PTR(channel))))
|
2009-03-30 18:46:12 +02:00
|
|
|
goto finish;
|
|
|
|
|
|
|
|
|
|
if (s->state != PA_STREAM_READY)
|
|
|
|
|
goto finish;
|
|
|
|
|
|
|
|
|
|
if (s->direction == PA_STREAM_RECORD)
|
|
|
|
|
s->timing_info.configured_source_usec = usec;
|
|
|
|
|
else
|
|
|
|
|
s->timing_info.configured_sink_usec = usec;
|
|
|
|
|
|
|
|
|
|
s->buffer_attr.maxlength = maxlength;
|
|
|
|
|
s->buffer_attr.fragsize = fragsize;
|
|
|
|
|
s->buffer_attr.tlength = tlength;
|
|
|
|
|
s->buffer_attr.prebuf = prebuf;
|
|
|
|
|
s->buffer_attr.minreq = minreq;
|
|
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
request_auto_timing_update(s, true);
|
2009-03-30 18:46:12 +02:00
|
|
|
|
|
|
|
|
if (s->buffer_attr_callback)
|
|
|
|
|
s->buffer_attr_callback(s, s->buffer_attr_userdata);
|
|
|
|
|
|
|
|
|
|
finish:
|
|
|
|
|
pa_context_unref(c);
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_context *c = userdata;
|
|
|
|
|
pa_stream *s;
|
|
|
|
|
uint32_t channel;
|
2013-06-27 19:28:09 +02:00
|
|
|
bool suspended;
|
2007-11-21 01:30:40 +00:00
|
|
|
|
|
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_SUSPENDED || command == PA_COMMAND_RECORD_STREAM_SUSPENDED);
|
|
|
|
|
pa_assert(t);
|
|
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
|
|
|
|
|
|
|
|
|
pa_context_ref(c);
|
|
|
|
|
|
|
|
|
|
if (c->version < 12) {
|
|
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_getu32(t, &channel) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &suspended) < 0 ||
|
|
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-03 13:22:05 +02:00
|
|
|
if (!(s = pa_hashmap_get(command == PA_COMMAND_PLAYBACK_STREAM_SUSPENDED ? c->playback_streams : c->record_streams, PA_UINT32_TO_PTR(channel))))
|
2007-11-21 01:30:40 +00:00
|
|
|
goto finish;
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (s->state != PA_STREAM_READY)
|
|
|
|
|
goto finish;
|
|
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
s->suspended = suspended;
|
|
|
|
|
|
2011-04-08 17:18:12 +03:00
|
|
|
if ((s->flags & PA_STREAM_AUTO_TIMING_UPDATE) && !suspended && !s->auto_timing_update_event) {
|
|
|
|
|
s->auto_timing_interval_usec = AUTO_TIMING_INTERVAL_START_USEC;
|
|
|
|
|
s->auto_timing_update_event = pa_context_rttime_new(s->context, pa_rtclock_now() + s->auto_timing_interval_usec, &auto_timing_update_callback, s);
|
2013-06-27 19:28:09 +02:00
|
|
|
request_auto_timing_update(s, true);
|
2011-04-08 17:18:12 +03:00
|
|
|
}
|
|
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
check_smoother_status(s, true, false, false);
|
|
|
|
|
request_auto_timing_update(s, true);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
if (s->suspended_callback)
|
|
|
|
|
s->suspended_callback(s, s->suspended_userdata);
|
|
|
|
|
|
|
|
|
|
finish:
|
|
|
|
|
pa_context_unref(c);
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
void pa_command_stream_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
|
|
|
|
pa_context *c = userdata;
|
|
|
|
|
pa_stream *s;
|
|
|
|
|
uint32_t channel;
|
|
|
|
|
|
|
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(command == PA_COMMAND_STARTED);
|
|
|
|
|
pa_assert(t);
|
|
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
|
|
|
|
|
|
|
|
|
pa_context_ref(c);
|
|
|
|
|
|
|
|
|
|
if (c->version < 13) {
|
|
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_getu32(t, &channel) < 0 ||
|
|
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-03 13:22:05 +02:00
|
|
|
if (!(s = pa_hashmap_get(c->playback_streams, PA_UINT32_TO_PTR(channel))))
|
2008-05-15 23:34:41 +00:00
|
|
|
goto finish;
|
|
|
|
|
|
|
|
|
|
if (s->state != PA_STREAM_READY)
|
|
|
|
|
goto finish;
|
|
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
check_smoother_status(s, true, true, false);
|
|
|
|
|
request_auto_timing_update(s, true);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
if (s->started_callback)
|
2008-09-09 20:28:20 -07:00
|
|
|
s->started_callback(s, s->started_userdata);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
finish:
|
|
|
|
|
pa_context_unref(c);
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-12 03:18:05 +01:00
|
|
|
void pa_command_stream_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
|
|
|
|
pa_context *c = userdata;
|
|
|
|
|
pa_stream *s;
|
|
|
|
|
uint32_t channel;
|
|
|
|
|
pa_proplist *pl = NULL;
|
|
|
|
|
const char *event;
|
|
|
|
|
|
|
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_EVENT || command == PA_COMMAND_RECORD_STREAM_EVENT);
|
|
|
|
|
pa_assert(t);
|
|
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
|
|
|
|
|
|
|
|
|
pa_context_ref(c);
|
|
|
|
|
|
|
|
|
|
if (c->version < 15) {
|
|
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pl = pa_proplist_new();
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_getu32(t, &channel) < 0 ||
|
|
|
|
|
pa_tagstruct_gets(t, &event) < 0 ||
|
|
|
|
|
pa_tagstruct_get_proplist(t, pl) < 0 ||
|
|
|
|
|
!pa_tagstruct_eof(t) || !event) {
|
|
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-03 13:22:05 +02:00
|
|
|
if (!(s = pa_hashmap_get(command == PA_COMMAND_PLAYBACK_STREAM_EVENT ? c->playback_streams : c->record_streams, PA_UINT32_TO_PTR(channel))))
|
2009-02-12 03:18:05 +01:00
|
|
|
goto finish;
|
|
|
|
|
|
|
|
|
|
if (s->state != PA_STREAM_READY)
|
|
|
|
|
goto finish;
|
|
|
|
|
|
2011-04-10 16:24:33 +05:30
|
|
|
if (pa_streq(event, PA_STREAM_EVENT_FORMAT_LOST)) {
|
2011-06-23 22:21:03 +02:00
|
|
|
/* Let client know what the running time was when the stream had to be killed */
|
|
|
|
|
pa_usec_t stream_time;
|
|
|
|
|
if (pa_stream_get_time(s, &stream_time) == 0)
|
|
|
|
|
pa_proplist_setf(pl, "stream-time", "%llu", (unsigned long long) stream_time);
|
2011-04-10 16:24:33 +05:30
|
|
|
}
|
|
|
|
|
|
2009-02-12 03:18:05 +01:00
|
|
|
if (s->event_callback)
|
|
|
|
|
s->event_callback(s, event, pl, s->event_userdata);
|
|
|
|
|
|
|
|
|
|
finish:
|
|
|
|
|
pa_context_unref(c);
|
|
|
|
|
|
|
|
|
|
if (pl)
|
|
|
|
|
pa_proplist_free(pl);
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
void pa_command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_stream *s;
|
|
|
|
|
pa_context *c = userdata;
|
2004-08-13 13:22:44 +00:00
|
|
|
uint32_t bytes, channel;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(command == PA_COMMAND_REQUEST);
|
|
|
|
|
pa_assert(t);
|
|
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
pa_context_ref(c);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-08-13 13:22:44 +00:00
|
|
|
if (pa_tagstruct_getu32(t, &channel) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &bytes) < 0 ||
|
|
|
|
|
!pa_tagstruct_eof(t)) {
|
2006-02-20 04:05:16 +00:00
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
2004-08-14 20:25:32 +00:00
|
|
|
goto finish;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-12-03 13:22:05 +02:00
|
|
|
if (!(s = pa_hashmap_get(c->playback_streams, PA_UINT32_TO_PTR(channel))))
|
2004-08-14 20:25:32 +00:00
|
|
|
goto finish;
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (s->state != PA_STREAM_READY)
|
|
|
|
|
goto finish;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
s->requested_bytes += bytes;
|
|
|
|
|
|
2012-11-22 09:58:08 +05:30
|
|
|
#ifdef STREAM_DEBUG
|
|
|
|
|
pa_log_debug("got request for %lli, now at %lli", (long long) bytes, (long long) s->requested_bytes);
|
|
|
|
|
#endif
|
2010-01-08 20:07:34 +01:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (s->requested_bytes > 0 && s->write_callback)
|
2009-04-01 23:05:09 +02:00
|
|
|
s->write_callback(s, (size_t) s->requested_bytes, s->write_userdata);
|
2004-08-14 20:25:32 +00:00
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
finish:
|
|
|
|
|
pa_context_unref(c);
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-27 06:00:02 +01:00
|
|
|
int64_t pa_stream_get_underflow_index(const pa_stream *p) {
|
2011-08-02 14:34:20 +02:00
|
|
|
pa_assert(p);
|
|
|
|
|
return p->latest_underrun_at_index;
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2006-02-20 04:05:16 +00:00
|
|
|
pa_stream *s;
|
|
|
|
|
pa_context *c = userdata;
|
|
|
|
|
uint32_t channel;
|
2011-08-02 14:34:20 +02:00
|
|
|
int64_t offset = -1;
|
2006-02-20 04:05:16 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(command == PA_COMMAND_OVERFLOW || command == PA_COMMAND_UNDERFLOW);
|
|
|
|
|
pa_assert(t);
|
|
|
|
|
pa_assert(c);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
2006-02-20 04:05:16 +00:00
|
|
|
|
|
|
|
|
pa_context_ref(c);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2011-08-02 14:34:20 +02:00
|
|
|
if (pa_tagstruct_getu32(t, &channel) < 0) {
|
|
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (c->version >= 23 && command == PA_COMMAND_UNDERFLOW) {
|
|
|
|
|
if (pa_tagstruct_gets64(t, &offset) < 0) {
|
|
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!pa_tagstruct_eof(t)) {
|
2006-02-20 04:05:16 +00:00
|
|
|
pa_context_fail(c, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-12-03 13:22:05 +02:00
|
|
|
if (!(s = pa_hashmap_get(c->playback_streams, PA_UINT32_TO_PTR(channel))))
|
2006-02-20 04:05:16 +00:00
|
|
|
goto finish;
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (s->state != PA_STREAM_READY)
|
|
|
|
|
goto finish;
|
2011-08-02 14:34:20 +02:00
|
|
|
|
|
|
|
|
if (offset != -1)
|
|
|
|
|
s->latest_underrun_at_index = offset;
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2008-06-26 19:00:42 +02:00
|
|
|
if (s->buffer_attr.prebuf > 0)
|
2013-06-27 19:28:09 +02:00
|
|
|
check_smoother_status(s, true, false, true);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
request_auto_timing_update(s, true);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2008-06-26 00:43:30 +02:00
|
|
|
if (command == PA_COMMAND_OVERFLOW) {
|
|
|
|
|
if (s->overflow_callback)
|
|
|
|
|
s->overflow_callback(s, s->overflow_userdata);
|
|
|
|
|
} else if (command == PA_COMMAND_UNDERFLOW) {
|
|
|
|
|
if (s->underflow_callback)
|
|
|
|
|
s->underflow_callback(s, s->underflow_userdata);
|
2006-02-20 04:05:16 +00:00
|
|
|
}
|
2004-08-14 20:25:32 +00:00
|
|
|
|
2011-03-02 12:41:26 +01:00
|
|
|
finish:
|
2004-08-14 20:25:32 +00:00
|
|
|
pa_context_unref(c);
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
static void invalidate_indexes(pa_stream *s, bool r, bool w) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2006-04-12 17:17:23 +00:00
|
|
|
|
2012-11-22 09:58:08 +05:30
|
|
|
#ifdef STREAM_DEBUG
|
|
|
|
|
pa_log_debug("invalidate r:%u w:%u tag:%u", r, w, s->context->ctag);
|
|
|
|
|
#endif
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-04-12 17:17:23 +00:00
|
|
|
if (s->state != PA_STREAM_READY)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (w) {
|
|
|
|
|
s->write_index_not_before = s->context->ctag;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-04-12 17:17:23 +00:00
|
|
|
if (s->timing_info_valid)
|
2013-06-27 19:28:09 +02:00
|
|
|
s->timing_info.write_index_corrupt = true;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2012-11-22 09:58:08 +05:30
|
|
|
#ifdef STREAM_DEBUG
|
|
|
|
|
pa_log_debug("write_index invalidated");
|
|
|
|
|
#endif
|
2006-04-12 17:17:23 +00:00
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-04-12 17:17:23 +00:00
|
|
|
if (r) {
|
|
|
|
|
s->read_index_not_before = s->context->ctag;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-04-12 17:17:23 +00:00
|
|
|
if (s->timing_info_valid)
|
2013-06-27 19:28:09 +02:00
|
|
|
s->timing_info.read_index_corrupt = true;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2012-11-22 09:58:08 +05:30
|
|
|
#ifdef STREAM_DEBUG
|
|
|
|
|
pa_log_debug("read_index invalidated");
|
|
|
|
|
#endif
|
2006-04-12 17:17:23 +00:00
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
request_auto_timing_update(s, true);
|
2004-10-27 00:10:12 +00:00
|
|
|
}
|
|
|
|
|
|
2009-04-05 02:13:43 +03:00
|
|
|
static void auto_timing_update_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) {
|
2006-04-12 17:17:23 +00:00
|
|
|
pa_stream *s = userdata;
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
|
|
|
|
|
2006-04-12 17:17:23 +00:00
|
|
|
pa_stream_ref(s);
|
2013-06-27 19:28:09 +02:00
|
|
|
request_auto_timing_update(s, false);
|
2006-04-12 17:17:23 +00:00
|
|
|
pa_stream_unref(s);
|
|
|
|
|
}
|
2004-10-27 00:10:12 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
static void create_stream_complete(pa_stream *s) {
|
|
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
|
|
|
|
pa_assert(s->state == PA_STREAM_CREATING);
|
|
|
|
|
|
|
|
|
|
pa_stream_set_state(s, PA_STREAM_READY);
|
|
|
|
|
|
|
|
|
|
if (s->requested_bytes > 0 && s->write_callback)
|
2009-04-01 23:05:09 +02:00
|
|
|
s->write_callback(s, (size_t) s->requested_bytes, s->write_userdata);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
|
|
|
|
if (s->flags & PA_STREAM_AUTO_TIMING_UPDATE) {
|
2009-04-01 00:36:18 +02:00
|
|
|
s->auto_timing_interval_usec = AUTO_TIMING_INTERVAL_START_USEC;
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(!s->auto_timing_update_event);
|
2009-07-24 20:13:52 +02:00
|
|
|
s->auto_timing_update_event = pa_context_rttime_new(s->context, pa_rtclock_now() + s->auto_timing_interval_usec, &auto_timing_update_callback, s);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
request_auto_timing_update(s, true);
|
2007-10-28 19:13:50 +00:00
|
|
|
}
|
2008-06-26 19:00:42 +02:00
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
check_smoother_status(s, true, false, false);
|
2007-10-28 19:13:50 +00:00
|
|
|
}
|
|
|
|
|
|
2010-01-15 01:25:42 +01:00
|
|
|
static void patch_buffer_attr(pa_stream *s, pa_buffer_attr *attr, pa_stream_flags_t *flags) {
|
|
|
|
|
const char *e;
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_assert(s);
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_assert(attr);
|
2010-01-15 01:25:42 +01:00
|
|
|
|
|
|
|
|
if ((e = getenv("PULSE_LATENCY_MSEC"))) {
|
|
|
|
|
uint32_t ms;
|
2015-06-11 11:01:30 +05:30
|
|
|
pa_sample_spec ss;
|
|
|
|
|
|
|
|
|
|
pa_sample_spec_init(&ss);
|
|
|
|
|
|
|
|
|
|
if (pa_sample_spec_valid(&s->sample_spec))
|
|
|
|
|
ss = s->sample_spec;
|
|
|
|
|
else if (s->n_formats == 1)
|
|
|
|
|
pa_format_info_to_sample_spec(s->req_formats[0], &ss, NULL);
|
2010-01-15 01:25:42 +01:00
|
|
|
|
|
|
|
|
if (pa_atou(e, &ms) < 0 || ms <= 0)
|
|
|
|
|
pa_log_debug("Failed to parse $PULSE_LATENCY_MSEC: %s", e);
|
2015-06-10 17:29:54 +02:00
|
|
|
else if (!pa_sample_spec_valid(&s->sample_spec))
|
|
|
|
|
pa_log_debug("Ignoring $PULSE_LATENCY_MSEC: %s (invalid sample spec)", e);
|
2010-01-15 01:25:42 +01:00
|
|
|
else {
|
|
|
|
|
attr->maxlength = (uint32_t) -1;
|
2015-06-11 11:01:30 +05:30
|
|
|
attr->tlength = pa_usec_to_bytes(ms * PA_USEC_PER_MSEC, &ss);
|
2010-01-15 01:25:42 +01:00
|
|
|
attr->minreq = (uint32_t) -1;
|
|
|
|
|
attr->prebuf = (uint32_t) -1;
|
|
|
|
|
attr->fragsize = attr->tlength;
|
|
|
|
|
|
2015-06-10 17:29:54 +02:00
|
|
|
if (flags)
|
|
|
|
|
*flags |= PA_STREAM_ADJUST_LATENCY;
|
|
|
|
|
}
|
2010-01-15 01:25:42 +01:00
|
|
|
}
|
2007-11-21 01:30:40 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (s->context->version >= 13)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* Version older than 0.9.10 didn't do server side buffer_attr
|
2008-06-25 02:28:33 +02:00
|
|
|
* selection, hence we have to fake it on the client side. */
|
|
|
|
|
|
|
|
|
|
/* We choose fairly conservative values here, to not confuse
|
|
|
|
|
* old clients with extremely large playback buffers */
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2008-06-27 00:34:17 +02:00
|
|
|
if (attr->maxlength == (uint32_t) -1)
|
2008-05-15 23:34:41 +00:00
|
|
|
attr->maxlength = 4*1024*1024; /* 4MB is the maximum queue length PulseAudio <= 0.9.9 supported. */
|
|
|
|
|
|
2008-06-27 00:34:17 +02:00
|
|
|
if (attr->tlength == (uint32_t) -1)
|
2010-01-15 01:25:42 +01:00
|
|
|
attr->tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, &s->sample_spec); /* 250ms of buffering */
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2008-06-27 00:34:17 +02:00
|
|
|
if (attr->minreq == (uint32_t) -1)
|
2008-06-25 02:28:33 +02:00
|
|
|
attr->minreq = (attr->tlength)/5; /* Ask for more data when there are only 200ms left in the playback buffer */
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2008-06-27 00:34:17 +02:00
|
|
|
if (attr->prebuf == (uint32_t) -1)
|
2008-05-15 23:34:41 +00:00
|
|
|
attr->prebuf = attr->tlength; /* Start to play only when the playback is fully filled up once */
|
|
|
|
|
|
2008-06-27 00:34:17 +02:00
|
|
|
if (attr->fragsize == (uint32_t) -1)
|
|
|
|
|
attr->fragsize = attr->tlength; /* Pass data to the app only when the buffer is filled up once */
|
2007-11-21 01:30:40 +00:00
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_stream *s = userdata;
|
2009-07-20 13:53:17 +03:00
|
|
|
uint32_t requested_bytes = 0;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
|
|
|
|
pa_assert(s->state == PA_STREAM_CREATING);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
pa_stream_ref(s);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-08-13 13:22:44 +00:00
|
|
|
if (command != PA_COMMAND_REPLY) {
|
2013-06-27 19:28:09 +02:00
|
|
|
if (pa_context_handle_error(s->context, command, t, false) < 0)
|
2004-08-14 20:25:32 +00:00
|
|
|
goto finish;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
pa_stream_set_state(s, PA_STREAM_FAILED);
|
|
|
|
|
goto finish;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_getu32(t, &s->channel) < 0 ||
|
2008-05-15 23:34:41 +00:00
|
|
|
s->channel == PA_INVALID_INDEX ||
|
2011-03-12 19:45:02 +01:00
|
|
|
((s->direction != PA_STREAM_UPLOAD) && (pa_tagstruct_getu32(t, &s->stream_index) < 0 || s->stream_index == PA_INVALID_INDEX)) ||
|
2009-04-01 23:05:09 +02:00
|
|
|
((s->direction != PA_STREAM_RECORD) && pa_tagstruct_getu32(t, &requested_bytes) < 0)) {
|
2006-02-20 04:05:16 +00:00
|
|
|
pa_context_fail(s->context, PA_ERR_PROTOCOL);
|
2004-08-14 20:25:32 +00:00
|
|
|
goto finish;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2009-04-01 23:05:09 +02:00
|
|
|
s->requested_bytes = (int64_t) requested_bytes;
|
|
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
if (s->context->version >= 9) {
|
2006-05-25 23:20:28 +00:00
|
|
|
if (s->direction == PA_STREAM_PLAYBACK) {
|
|
|
|
|
if (pa_tagstruct_getu32(t, &s->buffer_attr.maxlength) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &s->buffer_attr.tlength) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &s->buffer_attr.prebuf) < 0 ||
|
2006-05-26 07:24:25 +00:00
|
|
|
pa_tagstruct_getu32(t, &s->buffer_attr.minreq) < 0) {
|
2006-05-25 23:20:28 +00:00
|
|
|
pa_context_fail(s->context, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
} else if (s->direction == PA_STREAM_RECORD) {
|
|
|
|
|
if (pa_tagstruct_getu32(t, &s->buffer_attr.maxlength) < 0 ||
|
2006-05-26 07:24:25 +00:00
|
|
|
pa_tagstruct_getu32(t, &s->buffer_attr.fragsize) < 0) {
|
2006-05-25 23:20:28 +00:00
|
|
|
pa_context_fail(s->context, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2007-11-22 15:09:00 +00:00
|
|
|
if (s->context->version >= 12 && s->direction != PA_STREAM_UPLOAD) {
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_sample_spec ss;
|
|
|
|
|
pa_channel_map cm;
|
|
|
|
|
const char *dn = NULL;
|
2013-06-27 19:28:09 +02:00
|
|
|
bool suspended;
|
2007-11-21 01:30:40 +00:00
|
|
|
|
|
|
|
|
if (pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
|
|
|
|
|
pa_tagstruct_get_channel_map(t, &cm) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &s->device_index) < 0 ||
|
|
|
|
|
pa_tagstruct_gets(t, &dn) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &suspended) < 0) {
|
|
|
|
|
pa_context_fail(s->context, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!dn || s->device_index == PA_INVALID_INDEX ||
|
|
|
|
|
ss.channels != cm.channels ||
|
|
|
|
|
!pa_channel_map_valid(&cm) ||
|
|
|
|
|
!pa_sample_spec_valid(&ss) ||
|
2011-02-28 13:23:23 +05:30
|
|
|
(s->n_formats == 0 && (
|
|
|
|
|
(!(s->flags & PA_STREAM_FIX_FORMAT) && ss.format != s->sample_spec.format) ||
|
|
|
|
|
(!(s->flags & PA_STREAM_FIX_RATE) && ss.rate != s->sample_spec.rate) ||
|
|
|
|
|
(!(s->flags & PA_STREAM_FIX_CHANNELS) && !pa_channel_map_equal(&cm, &s->channel_map))))) {
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_context_fail(s->context, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_xfree(s->device_name);
|
|
|
|
|
s->device_name = pa_xstrdup(dn);
|
|
|
|
|
s->suspended = suspended;
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
s->channel_map = cm;
|
|
|
|
|
s->sample_spec = ss;
|
|
|
|
|
}
|
2007-11-21 01:30:40 +00:00
|
|
|
|
2020-01-15 20:37:12 +01:00
|
|
|
#ifdef USE_SMOOTHER_2
|
|
|
|
|
if (s->flags & PA_STREAM_INTERPOLATE_TIMING)
|
|
|
|
|
pa_smoother_2_set_sample_spec(s->smoother, pa_rtclock_now(), &s->sample_spec);
|
|
|
|
|
#endif
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (s->context->version >= 13 && s->direction != PA_STREAM_UPLOAD) {
|
|
|
|
|
pa_usec_t usec;
|
2007-11-21 01:30:40 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (pa_tagstruct_get_usec(t, &usec) < 0) {
|
|
|
|
|
pa_context_fail(s->context, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
2007-11-21 01:30:40 +00:00
|
|
|
}
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (s->direction == PA_STREAM_RECORD)
|
|
|
|
|
s->timing_info.configured_source_usec = usec;
|
|
|
|
|
else
|
|
|
|
|
s->timing_info.configured_sink_usec = usec;
|
2007-11-21 01:30:40 +00:00
|
|
|
}
|
|
|
|
|
|
2011-05-17 21:56:10 +01:00
|
|
|
if ((s->context->version >= 21 && s->direction == PA_STREAM_PLAYBACK)
|
|
|
|
|
|| s->context->version >= 22) {
|
|
|
|
|
|
2011-02-28 13:23:23 +05:30
|
|
|
pa_format_info *f = pa_format_info_new();
|
|
|
|
|
|
2015-09-15 23:11:29 +02:00
|
|
|
if (pa_tagstruct_get_format_info(t, f) < 0 || !pa_format_info_valid(f)) {
|
2011-02-28 13:23:23 +05:30
|
|
|
pa_format_info_free(f);
|
2011-03-02 12:54:02 +05:30
|
|
|
if (s->n_formats > 0) {
|
|
|
|
|
/* We used the extended API, so we should have got back a proper format */
|
|
|
|
|
pa_context_fail(s->context, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
2015-09-15 23:11:29 +02:00
|
|
|
} else
|
|
|
|
|
s->format = f;
|
2011-02-28 13:23:23 +05:30
|
|
|
}
|
|
|
|
|
|
2006-05-26 07:24:25 +00:00
|
|
|
if (!pa_tagstruct_eof(t)) {
|
|
|
|
|
pa_context_fail(s->context, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-17 15:42:47 +00:00
|
|
|
if (s->direction == PA_STREAM_RECORD) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(!s->record_memblockq);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
s->record_memblockq = pa_memblockq_new(
|
2011-09-29 18:54:03 +03:00
|
|
|
"client side record memblockq",
|
2007-01-04 13:43:45 +00:00
|
|
|
0,
|
2006-02-20 04:05:16 +00:00
|
|
|
s->buffer_attr.maxlength,
|
|
|
|
|
0,
|
2011-09-29 18:54:03 +03:00
|
|
|
&s->sample_spec,
|
2006-02-20 04:05:16 +00:00
|
|
|
1,
|
|
|
|
|
0,
|
2008-05-15 23:34:41 +00:00
|
|
|
0,
|
2006-08-18 19:55:18 +00:00
|
|
|
NULL);
|
2006-02-17 15:42:47 +00:00
|
|
|
}
|
|
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
s->channel_valid = true;
|
2009-12-03 13:22:05 +02:00
|
|
|
pa_hashmap_put((s->direction == PA_STREAM_RECORD) ? s->context->record_streams : s->context->playback_streams, PA_UINT32_TO_PTR(s->channel), s);
|
2004-08-14 20:25:32 +00:00
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
create_stream_complete(s);
|
2004-09-10 22:35:12 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
finish:
|
|
|
|
|
pa_stream_unref(s);
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
static int create_stream(
|
|
|
|
|
pa_stream_direction_t direction,
|
|
|
|
|
pa_stream *s,
|
|
|
|
|
const char *dev,
|
|
|
|
|
const pa_buffer_attr *attr,
|
|
|
|
|
pa_stream_flags_t flags,
|
|
|
|
|
const pa_cvolume *volume,
|
|
|
|
|
pa_stream *sync_stream) {
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_tagstruct *t;
|
2004-08-13 13:22:44 +00:00
|
|
|
uint32_t tag;
|
2013-06-27 19:28:09 +02:00
|
|
|
bool volume_set = !!volume;
|
2011-05-17 22:31:10 +01:00
|
|
|
pa_cvolume cv;
|
2011-02-28 13:23:23 +05:30
|
|
|
uint32_t i;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_assert(direction == PA_STREAM_PLAYBACK || direction == PA_STREAM_RECORD);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
|
2006-02-20 04:05:16 +00:00
|
|
|
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE);
|
2008-06-13 21:56:19 +00:00
|
|
|
PA_CHECK_VALIDITY(s->context, s->direct_on_input == PA_INVALID_INDEX || direction == PA_STREAM_RECORD, PA_ERR_BADSTATE);
|
2008-05-15 23:34:41 +00:00
|
|
|
PA_CHECK_VALIDITY(s->context, !(flags & ~(PA_STREAM_START_CORKED|
|
|
|
|
|
PA_STREAM_INTERPOLATE_TIMING|
|
2009-02-02 02:02:31 +01:00
|
|
|
PA_STREAM_NOT_MONOTONIC|
|
2008-05-15 23:34:41 +00:00
|
|
|
PA_STREAM_AUTO_TIMING_UPDATE|
|
|
|
|
|
PA_STREAM_NO_REMAP_CHANNELS|
|
|
|
|
|
PA_STREAM_NO_REMIX_CHANNELS|
|
|
|
|
|
PA_STREAM_FIX_FORMAT|
|
|
|
|
|
PA_STREAM_FIX_RATE|
|
|
|
|
|
PA_STREAM_FIX_CHANNELS|
|
|
|
|
|
PA_STREAM_DONT_MOVE|
|
|
|
|
|
PA_STREAM_VARIABLE_RATE|
|
|
|
|
|
PA_STREAM_PEAK_DETECT|
|
|
|
|
|
PA_STREAM_START_MUTED|
|
2008-09-03 18:31:46 +02:00
|
|
|
PA_STREAM_ADJUST_LATENCY|
|
2008-10-26 19:32:04 +01:00
|
|
|
PA_STREAM_EARLY_REQUESTS|
|
|
|
|
|
PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND|
|
2009-02-03 02:23:46 +01:00
|
|
|
PA_STREAM_START_UNMUTED|
|
2009-10-29 00:46:22 +01:00
|
|
|
PA_STREAM_FAIL_ON_SUSPEND|
|
2010-07-16 16:46:28 -05:00
|
|
|
PA_STREAM_RELATIVE_VOLUME|
|
|
|
|
|
PA_STREAM_PASSTHROUGH)), PA_ERR_INVALID);
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
PA_CHECK_VALIDITY(s->context, s->context->version >= 12 || !(flags & PA_STREAM_VARIABLE_RATE), PA_ERR_NOTSUPPORTED);
|
|
|
|
|
PA_CHECK_VALIDITY(s->context, s->context->version >= 13 || !(flags & PA_STREAM_PEAK_DETECT), PA_ERR_NOTSUPPORTED);
|
2009-03-16 16:39:28 +02:00
|
|
|
PA_CHECK_VALIDITY(s->context, s->context->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
|
2011-08-24 18:24:46 +02:00
|
|
|
/* Although some of the other flags are not supported on older
|
2008-05-15 23:34:41 +00:00
|
|
|
* version, we don't check for them here, because it doesn't hurt
|
|
|
|
|
* when they are passed but actually not supported. This makes
|
|
|
|
|
* client development easier */
|
|
|
|
|
|
2008-05-17 09:09:29 +00:00
|
|
|
PA_CHECK_VALIDITY(s->context, direction == PA_STREAM_RECORD || !(flags & (PA_STREAM_PEAK_DETECT)), PA_ERR_INVALID);
|
2006-02-20 22:41:02 +00:00
|
|
|
PA_CHECK_VALIDITY(s->context, !sync_stream || (direction == PA_STREAM_PLAYBACK && sync_stream->direction == PA_STREAM_PLAYBACK), PA_ERR_INVALID);
|
2008-09-03 18:31:46 +02:00
|
|
|
PA_CHECK_VALIDITY(s->context, (flags & (PA_STREAM_ADJUST_LATENCY|PA_STREAM_EARLY_REQUESTS)) != (PA_STREAM_ADJUST_LATENCY|PA_STREAM_EARLY_REQUESTS), PA_ERR_INVALID);
|
2004-08-14 20:25:32 +00:00
|
|
|
|
|
|
|
|
pa_stream_ref(s);
|
2004-09-17 19:45:44 +00:00
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
s->direction = direction;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
if (sync_stream)
|
|
|
|
|
s->syncid = sync_stream->syncid;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (attr)
|
2004-08-14 20:25:32 +00:00
|
|
|
s->buffer_attr = *attr;
|
2010-01-15 01:25:42 +01:00
|
|
|
patch_buffer_attr(s, &s->buffer_attr, &flags);
|
|
|
|
|
|
|
|
|
|
s->flags = flags;
|
|
|
|
|
s->corked = !!(flags & PA_STREAM_START_CORKED);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
if (flags & PA_STREAM_INTERPOLATE_TIMING) {
|
|
|
|
|
pa_usec_t x;
|
|
|
|
|
|
2009-04-04 22:56:38 +03:00
|
|
|
x = pa_rtclock_now();
|
2009-04-05 02:26:02 +02:00
|
|
|
|
|
|
|
|
pa_assert(!s->smoother);
|
2020-01-15 20:37:12 +01:00
|
|
|
#ifdef USE_SMOOTHER_2
|
|
|
|
|
s->smoother = pa_smoother_2_new(SMOOTHER_HISTORY_TIME, x, 0, 0);
|
|
|
|
|
#else
|
2009-04-05 02:26:02 +02:00
|
|
|
s->smoother = pa_smoother_new(
|
|
|
|
|
SMOOTHER_ADJUST_TIME,
|
|
|
|
|
SMOOTHER_HISTORY_TIME,
|
|
|
|
|
!(flags & PA_STREAM_NOT_MONOTONIC),
|
2013-06-27 19:28:09 +02:00
|
|
|
true,
|
2009-04-05 02:26:02 +02:00
|
|
|
SMOOTHER_MIN_HISTORY,
|
|
|
|
|
x,
|
2013-06-27 19:28:09 +02:00
|
|
|
true);
|
2020-01-15 20:37:12 +01:00
|
|
|
#endif
|
2004-08-14 20:25:32 +00:00
|
|
|
}
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2006-02-20 22:41:02 +00:00
|
|
|
if (!dev)
|
|
|
|
|
dev = s->direction == PA_STREAM_PLAYBACK ? s->context->conf->default_sink : s->context->conf->default_source;
|
2006-02-23 01:24:16 +00:00
|
|
|
|
|
|
|
|
t = pa_tagstruct_command(
|
|
|
|
|
s->context,
|
2008-08-19 22:39:54 +02:00
|
|
|
(uint32_t) (s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CREATE_PLAYBACK_STREAM : PA_COMMAND_CREATE_RECORD_STREAM),
|
2006-02-23 01:24:16 +00:00
|
|
|
&tag);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (s->context->version < 13)
|
|
|
|
|
pa_tagstruct_puts(t, pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME));
|
|
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
pa_tagstruct_put(
|
|
|
|
|
t,
|
|
|
|
|
PA_TAG_SAMPLE_SPEC, &s->sample_spec,
|
|
|
|
|
PA_TAG_CHANNEL_MAP, &s->channel_map,
|
|
|
|
|
PA_TAG_U32, PA_INVALID_INDEX,
|
|
|
|
|
PA_TAG_STRING, dev,
|
|
|
|
|
PA_TAG_U32, s->buffer_attr.maxlength,
|
2008-06-26 00:42:09 +02:00
|
|
|
PA_TAG_BOOLEAN, s->corked,
|
2006-02-20 04:05:16 +00:00
|
|
|
PA_TAG_INVALID);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2011-05-17 22:31:10 +01:00
|
|
|
if (!volume) {
|
|
|
|
|
if (pa_sample_spec_valid(&s->sample_spec))
|
|
|
|
|
volume = pa_cvolume_reset(&cv, s->sample_spec.channels);
|
|
|
|
|
else {
|
|
|
|
|
/* This is not really relevant, since no volume was set, and
|
|
|
|
|
* the real number of channels is embedded in the format_info
|
|
|
|
|
* structure */
|
|
|
|
|
volume = pa_cvolume_reset(&cv, PA_CHANNELS_MAX);
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-02-20 22:41:02 +00:00
|
|
|
|
2011-05-17 22:31:10 +01:00
|
|
|
if (s->direction == PA_STREAM_PLAYBACK) {
|
2006-02-20 04:05:16 +00:00
|
|
|
pa_tagstruct_put(
|
|
|
|
|
t,
|
|
|
|
|
PA_TAG_U32, s->buffer_attr.tlength,
|
|
|
|
|
PA_TAG_U32, s->buffer_attr.prebuf,
|
|
|
|
|
PA_TAG_U32, s->buffer_attr.minreq,
|
|
|
|
|
PA_TAG_U32, s->syncid,
|
|
|
|
|
PA_TAG_INVALID);
|
2006-01-27 16:25:31 +00:00
|
|
|
|
|
|
|
|
pa_tagstruct_put_cvolume(t, volume);
|
2004-08-13 13:22:44 +00:00
|
|
|
} else
|
|
|
|
|
pa_tagstruct_putu32(t, s->buffer_attr.fragsize);
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (s->context->version >= 12) {
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_tagstruct_put(
|
|
|
|
|
t,
|
|
|
|
|
PA_TAG_BOOLEAN, flags & PA_STREAM_NO_REMAP_CHANNELS,
|
|
|
|
|
PA_TAG_BOOLEAN, flags & PA_STREAM_NO_REMIX_CHANNELS,
|
|
|
|
|
PA_TAG_BOOLEAN, flags & PA_STREAM_FIX_FORMAT,
|
|
|
|
|
PA_TAG_BOOLEAN, flags & PA_STREAM_FIX_RATE,
|
|
|
|
|
PA_TAG_BOOLEAN, flags & PA_STREAM_FIX_CHANNELS,
|
|
|
|
|
PA_TAG_BOOLEAN, flags & PA_STREAM_DONT_MOVE,
|
|
|
|
|
PA_TAG_BOOLEAN, flags & PA_STREAM_VARIABLE_RATE,
|
|
|
|
|
PA_TAG_INVALID);
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (s->context->version >= 13) {
|
|
|
|
|
|
|
|
|
|
if (s->direction == PA_STREAM_PLAYBACK)
|
|
|
|
|
pa_tagstruct_put_boolean(t, flags & PA_STREAM_START_MUTED);
|
|
|
|
|
else
|
|
|
|
|
pa_tagstruct_put_boolean(t, flags & PA_STREAM_PEAK_DETECT);
|
|
|
|
|
|
|
|
|
|
pa_tagstruct_put(
|
|
|
|
|
t,
|
|
|
|
|
PA_TAG_BOOLEAN, flags & PA_STREAM_ADJUST_LATENCY,
|
|
|
|
|
PA_TAG_PROPLIST, s->proplist,
|
|
|
|
|
PA_TAG_INVALID);
|
2008-06-13 21:56:19 +00:00
|
|
|
|
|
|
|
|
if (s->direction == PA_STREAM_RECORD)
|
|
|
|
|
pa_tagstruct_putu32(t, s->direct_on_input);
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
|
|
|
|
|
2008-09-03 18:31:46 +02:00
|
|
|
if (s->context->version >= 14) {
|
2008-08-18 17:49:18 +02:00
|
|
|
|
2008-09-03 18:31:46 +02:00
|
|
|
if (s->direction == PA_STREAM_PLAYBACK)
|
|
|
|
|
pa_tagstruct_put_boolean(t, volume_set);
|
|
|
|
|
|
|
|
|
|
pa_tagstruct_put_boolean(t, flags & PA_STREAM_EARLY_REQUESTS);
|
2008-08-18 17:49:18 +02:00
|
|
|
}
|
|
|
|
|
|
2008-10-26 19:32:04 +01:00
|
|
|
if (s->context->version >= 15) {
|
|
|
|
|
|
|
|
|
|
if (s->direction == PA_STREAM_PLAYBACK)
|
|
|
|
|
pa_tagstruct_put_boolean(t, flags & (PA_STREAM_START_MUTED|PA_STREAM_START_UNMUTED));
|
|
|
|
|
|
|
|
|
|
pa_tagstruct_put_boolean(t, flags & PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND);
|
2009-02-03 02:23:46 +01:00
|
|
|
pa_tagstruct_put_boolean(t, flags & PA_STREAM_FAIL_ON_SUSPEND);
|
2008-10-26 19:32:04 +01:00
|
|
|
}
|
|
|
|
|
|
2011-05-17 21:56:10 +01:00
|
|
|
if (s->context->version >= 17 && s->direction == PA_STREAM_PLAYBACK)
|
|
|
|
|
pa_tagstruct_put_boolean(t, flags & PA_STREAM_RELATIVE_VOLUME);
|
2010-07-16 16:46:28 -05:00
|
|
|
|
2011-05-17 21:56:10 +01:00
|
|
|
if (s->context->version >= 18 && s->direction == PA_STREAM_PLAYBACK)
|
|
|
|
|
pa_tagstruct_put_boolean(t, flags & (PA_STREAM_PASSTHROUGH));
|
2010-07-16 16:46:28 -05:00
|
|
|
|
2011-05-17 21:56:10 +01:00
|
|
|
if ((s->context->version >= 21 && s->direction == PA_STREAM_PLAYBACK)
|
|
|
|
|
|| s->context->version >= 22) {
|
2011-02-28 13:23:23 +05:30
|
|
|
|
2011-05-17 21:56:10 +01:00
|
|
|
pa_tagstruct_putu8(t, s->n_formats);
|
|
|
|
|
for (i = 0; i < s->n_formats; i++)
|
|
|
|
|
pa_tagstruct_put_format_info(t, s->req_formats[i]);
|
2011-02-28 13:23:23 +05:30
|
|
|
}
|
|
|
|
|
|
2011-05-17 22:31:10 +01:00
|
|
|
if (s->context->version >= 22 && s->direction == PA_STREAM_RECORD) {
|
|
|
|
|
pa_tagstruct_put_cvolume(t, volume);
|
|
|
|
|
pa_tagstruct_put_boolean(t, flags & PA_STREAM_START_MUTED);
|
|
|
|
|
pa_tagstruct_put_boolean(t, volume_set);
|
|
|
|
|
pa_tagstruct_put_boolean(t, flags & (PA_STREAM_START_MUTED|PA_STREAM_START_UNMUTED));
|
|
|
|
|
pa_tagstruct_put_boolean(t, flags & PA_STREAM_RELATIVE_VOLUME);
|
|
|
|
|
pa_tagstruct_put_boolean(t, flags & (PA_STREAM_PASSTHROUGH));
|
|
|
|
|
}
|
|
|
|
|
|
2004-08-13 13:22:44 +00:00
|
|
|
pa_pstream_send_tagstruct(s->context->pstream, t);
|
2006-04-24 19:29:15 +00:00
|
|
|
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL);
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2006-02-20 22:41:02 +00:00
|
|
|
pa_stream_set_state(s, PA_STREAM_CREATING);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
pa_stream_unref(s);
|
2006-02-20 04:05:16 +00:00
|
|
|
return 0;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
int pa_stream_connect_playback(
|
|
|
|
|
pa_stream *s,
|
|
|
|
|
const char *dev,
|
|
|
|
|
const pa_buffer_attr *attr,
|
|
|
|
|
pa_stream_flags_t flags,
|
2009-07-24 20:20:34 +02:00
|
|
|
const pa_cvolume *volume,
|
2006-02-20 04:05:16 +00:00
|
|
|
pa_stream *sync_stream) {
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
return create_stream(PA_STREAM_PLAYBACK, s, dev, attr, flags, volume, sync_stream);
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
int pa_stream_connect_record(
|
|
|
|
|
pa_stream *s,
|
|
|
|
|
const char *dev,
|
|
|
|
|
const pa_buffer_attr *attr,
|
|
|
|
|
pa_stream_flags_t flags) {
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2006-02-20 04:05:16 +00:00
|
|
|
|
|
|
|
|
return create_stream(PA_STREAM_RECORD, s, dev, attr, flags, NULL, NULL);
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2009-07-20 15:49:33 +01:00
|
|
|
int pa_stream_begin_write(
|
|
|
|
|
pa_stream *s,
|
|
|
|
|
void **data,
|
|
|
|
|
size_t *nbytes) {
|
|
|
|
|
|
|
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
|
|
|
|
|
|
|
|
|
PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
|
|
|
|
|
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY(s->context, data, PA_ERR_INVALID);
|
|
|
|
|
PA_CHECK_VALIDITY(s->context, nbytes && *nbytes != 0, PA_ERR_INVALID);
|
|
|
|
|
|
2009-07-24 20:21:30 +02:00
|
|
|
if (*nbytes != (size_t) -1) {
|
|
|
|
|
size_t m, fs;
|
|
|
|
|
|
|
|
|
|
m = pa_mempool_block_size_max(s->context->mempool);
|
|
|
|
|
fs = pa_frame_size(&s->sample_spec);
|
|
|
|
|
|
|
|
|
|
m = (m / fs) * fs;
|
|
|
|
|
if (*nbytes > m)
|
|
|
|
|
*nbytes = m;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-20 15:49:33 +01:00
|
|
|
if (!s->write_memblock) {
|
|
|
|
|
s->write_memblock = pa_memblock_new(s->context->mempool, *nbytes);
|
|
|
|
|
s->write_data = pa_memblock_acquire(s->write_memblock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*data = s->write_data;
|
|
|
|
|
*nbytes = pa_memblock_get_length(s->write_memblock);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int pa_stream_cancel_write(
|
|
|
|
|
pa_stream *s) {
|
|
|
|
|
|
|
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
|
|
|
|
|
|
|
|
|
PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
|
|
|
|
|
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY(s->context, s->write_memblock, PA_ERR_BADSTATE);
|
|
|
|
|
|
|
|
|
|
pa_assert(s->write_data);
|
|
|
|
|
|
|
|
|
|
pa_memblock_release(s->write_memblock);
|
|
|
|
|
pa_memblock_unref(s->write_memblock);
|
|
|
|
|
s->write_memblock = NULL;
|
|
|
|
|
s->write_data = NULL;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-15 02:08:23 +02:00
|
|
|
int pa_stream_write_ext_free(
|
2006-02-20 04:05:16 +00:00
|
|
|
pa_stream *s,
|
|
|
|
|
const void *data,
|
|
|
|
|
size_t length,
|
2009-07-20 15:49:33 +01:00
|
|
|
pa_free_cb_t free_cb,
|
2014-04-15 02:08:23 +02:00
|
|
|
void *free_cb_data,
|
2006-02-20 04:05:16 +00:00
|
|
|
int64_t offset,
|
|
|
|
|
pa_seek_mode_t seek) {
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
|
|
|
|
pa_assert(data);
|
2006-02-20 04:05:16 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
|
2006-02-20 04:05:16 +00:00
|
|
|
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY(s->context, seek <= PA_SEEK_RELATIVE_END, PA_ERR_INVALID);
|
|
|
|
|
PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || (seek == PA_SEEK_RELATIVE && offset == 0), PA_ERR_INVALID);
|
2009-07-20 15:49:33 +01:00
|
|
|
PA_CHECK_VALIDITY(s->context,
|
|
|
|
|
!s->write_memblock ||
|
|
|
|
|
((data >= s->write_data) &&
|
|
|
|
|
((const char*) data + length <= (const char*) s->write_data + pa_memblock_get_length(s->write_memblock))),
|
|
|
|
|
PA_ERR_INVALID);
|
2014-10-16 11:41:18 +02:00
|
|
|
PA_CHECK_VALIDITY(s->context, offset % pa_frame_size(&s->sample_spec) == 0, PA_ERR_INVALID);
|
|
|
|
|
PA_CHECK_VALIDITY(s->context, length % pa_frame_size(&s->sample_spec) == 0, PA_ERR_INVALID);
|
2009-07-20 15:49:33 +01:00
|
|
|
PA_CHECK_VALIDITY(s->context, !free_cb || !s->write_memblock, PA_ERR_INVALID);
|
2006-02-20 04:05:16 +00:00
|
|
|
|
2009-07-20 15:49:33 +01:00
|
|
|
if (s->write_memblock) {
|
|
|
|
|
pa_memchunk chunk;
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2009-07-20 15:49:33 +01:00
|
|
|
/* pa_stream_write_begin() was called before */
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2009-07-20 15:49:33 +01:00
|
|
|
pa_memblock_release(s->write_memblock);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2009-07-20 15:49:33 +01:00
|
|
|
chunk.memblock = s->write_memblock;
|
|
|
|
|
chunk.index = (const char *) data - (const char *) s->write_data;
|
|
|
|
|
chunk.length = length;
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2009-07-20 15:49:33 +01:00
|
|
|
s->write_memblock = NULL;
|
|
|
|
|
s->write_data = NULL;
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2023-02-09 13:28:29 +03:00
|
|
|
pa_pstream_send_memblock(s->context->pstream, s->channel, offset, seek, &chunk, pa_frame_size(&s->sample_spec));
|
2009-07-20 15:49:33 +01:00
|
|
|
pa_memblock_unref(chunk.memblock);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2009-07-20 15:49:33 +01:00
|
|
|
} else {
|
|
|
|
|
pa_seek_mode_t t_seek = seek;
|
|
|
|
|
int64_t t_offset = offset;
|
|
|
|
|
size_t t_length = length;
|
|
|
|
|
const void *t_data = data;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-07-20 15:49:33 +01:00
|
|
|
/* pa_stream_write_begin() was not called before */
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2009-07-20 15:49:33 +01:00
|
|
|
while (t_length > 0) {
|
|
|
|
|
pa_memchunk chunk;
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2009-07-20 15:49:33 +01:00
|
|
|
chunk.index = 0;
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2009-07-20 15:49:33 +01:00
|
|
|
if (free_cb && !pa_pstream_get_shm(s->context->pstream)) {
|
2014-04-15 02:08:23 +02:00
|
|
|
chunk.memblock = pa_memblock_new_user(s->context->mempool, (void*) t_data, t_length, free_cb, free_cb_data, 1);
|
2009-07-20 15:49:33 +01:00
|
|
|
chunk.length = t_length;
|
|
|
|
|
} else {
|
|
|
|
|
void *d;
|
2016-11-22 22:18:56 +02:00
|
|
|
size_t blk_size_max;
|
2009-07-20 15:49:33 +01:00
|
|
|
|
2016-11-22 22:18:56 +02:00
|
|
|
/* Break large audio streams into _aligned_ blocks or the
|
|
|
|
|
* other endpoint will happily discard them upon arrival. */
|
|
|
|
|
blk_size_max = pa_frame_align(pa_mempool_block_size_max(s->context->mempool), &s->sample_spec);
|
|
|
|
|
chunk.length = PA_MIN(t_length, blk_size_max);
|
2009-07-20 15:49:33 +01:00
|
|
|
chunk.memblock = pa_memblock_new(s->context->mempool, chunk.length);
|
|
|
|
|
|
|
|
|
|
d = pa_memblock_acquire(chunk.memblock);
|
|
|
|
|
memcpy(d, t_data, chunk.length);
|
|
|
|
|
pa_memblock_release(chunk.memblock);
|
|
|
|
|
}
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2023-02-09 13:28:29 +03:00
|
|
|
pa_pstream_send_memblock(s->context->pstream, s->channel, t_offset, t_seek, &chunk, pa_frame_size(&s->sample_spec));
|
2009-07-20 15:49:33 +01:00
|
|
|
|
|
|
|
|
t_offset = 0;
|
|
|
|
|
t_seek = PA_SEEK_RELATIVE;
|
|
|
|
|
|
|
|
|
|
t_data = (const uint8_t*) t_data + chunk.length;
|
|
|
|
|
t_length -= chunk.length;
|
|
|
|
|
|
|
|
|
|
pa_memblock_unref(chunk.memblock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (free_cb && pa_pstream_get_shm(s->context->pstream))
|
2014-04-15 02:08:23 +02:00
|
|
|
free_cb(free_cb_data);
|
2009-07-20 15:49:33 +01:00
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-04-01 23:05:09 +02:00
|
|
|
/* This is obviously wrong since we ignore the seeking index . But
|
|
|
|
|
* that's OK, the server side applies the same error */
|
|
|
|
|
s->requested_bytes -= (seek == PA_SEEK_RELATIVE ? offset : 0) + (int64_t) length;
|
2008-06-26 19:02:14 +02:00
|
|
|
|
2012-11-22 09:58:08 +05:30
|
|
|
#ifdef STREAM_DEBUG
|
|
|
|
|
pa_log_debug("wrote %lli, now at %lli", (long long) length, (long long) s->requested_bytes);
|
|
|
|
|
#endif
|
2010-01-08 20:07:34 +01:00
|
|
|
|
2006-04-07 21:55:55 +00:00
|
|
|
if (s->direction == PA_STREAM_PLAYBACK) {
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-04-07 21:55:55 +00:00
|
|
|
/* Update latency request correction */
|
2006-04-12 17:17:23 +00:00
|
|
|
if (s->write_index_corrections[s->current_write_index_correction].valid) {
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-04-07 21:55:55 +00:00
|
|
|
if (seek == PA_SEEK_ABSOLUTE) {
|
2013-06-27 19:28:09 +02:00
|
|
|
s->write_index_corrections[s->current_write_index_correction].corrupt = false;
|
|
|
|
|
s->write_index_corrections[s->current_write_index_correction].absolute = true;
|
2008-08-19 22:39:54 +02:00
|
|
|
s->write_index_corrections[s->current_write_index_correction].value = offset + (int64_t) length;
|
2006-04-07 21:55:55 +00:00
|
|
|
} else if (seek == PA_SEEK_RELATIVE) {
|
2006-04-12 17:17:23 +00:00
|
|
|
if (!s->write_index_corrections[s->current_write_index_correction].corrupt)
|
2008-08-19 22:39:54 +02:00
|
|
|
s->write_index_corrections[s->current_write_index_correction].value += offset + (int64_t) length;
|
2006-04-07 21:55:55 +00:00
|
|
|
} else
|
2013-06-27 19:28:09 +02:00
|
|
|
s->write_index_corrections[s->current_write_index_correction].corrupt = true;
|
2006-04-07 21:55:55 +00:00
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-04-07 21:55:55 +00:00
|
|
|
/* Update the write index in the already available latency data */
|
|
|
|
|
if (s->timing_info_valid) {
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-04-07 21:55:55 +00:00
|
|
|
if (seek == PA_SEEK_ABSOLUTE) {
|
2013-06-27 19:28:09 +02:00
|
|
|
s->timing_info.write_index_corrupt = false;
|
2008-08-19 22:39:54 +02:00
|
|
|
s->timing_info.write_index = offset + (int64_t) length;
|
2006-04-07 21:55:55 +00:00
|
|
|
} else if (seek == PA_SEEK_RELATIVE) {
|
|
|
|
|
if (!s->timing_info.write_index_corrupt)
|
2008-08-19 22:39:54 +02:00
|
|
|
s->timing_info.write_index += offset + (int64_t) length;
|
2006-04-07 21:55:55 +00:00
|
|
|
} else
|
2013-06-27 19:28:09 +02:00
|
|
|
s->timing_info.write_index_corrupt = true;
|
2006-04-07 21:55:55 +00:00
|
|
|
}
|
2006-04-12 17:17:23 +00:00
|
|
|
|
|
|
|
|
if (!s->timing_info_valid || s->timing_info.write_index_corrupt)
|
2013-06-27 19:28:09 +02:00
|
|
|
request_auto_timing_update(s, true);
|
2006-04-07 00:24:48 +00:00
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
return 0;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2014-04-15 02:08:23 +02:00
|
|
|
int pa_stream_write(
|
|
|
|
|
pa_stream *s,
|
|
|
|
|
const void *data,
|
|
|
|
|
size_t length,
|
|
|
|
|
pa_free_cb_t free_cb,
|
|
|
|
|
int64_t offset,
|
|
|
|
|
pa_seek_mode_t seek) {
|
|
|
|
|
|
|
|
|
|
return pa_stream_write_ext_free(s, data, length, free_cb, (void*) data, offset, seek);
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
int pa_stream_peek(pa_stream *s, const void **data, size_t *length) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
|
|
|
|
pa_assert(data);
|
|
|
|
|
pa_assert(length);
|
2006-02-17 15:42:47 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
|
2006-02-20 04:05:16 +00:00
|
|
|
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-02-17 15:42:47 +00:00
|
|
|
if (!s->peek_memchunk.memblock) {
|
|
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
if (pa_memblockq_peek(s->record_memblockq, &s->peek_memchunk) < 0) {
|
pulse: Fix hole handling in pa_stream_peek().
Previously, if there was a hole in a recording stream,
pa_stream_peek() would crash. Holes could be handled silently inside
pa_stream_peek() by generating silence (wouldn't work for compressed
streams, though) or by skipping any holes. However, I think it's
better to let the caller decide how the holes should be handled, so
in case of holes, pa_stream_peek() will return NULL data pointer and
the length of the hole in the nbytes argument.
This change is technically an interface break, because previously the
documentation didn't mention the possibility of holes that need
special handling. However, since holes caused crashing anyway in the
past, it's not a regression if applications keep misbehaving due to
not handing holes properly.
Some words about when holes can appear in recording streams: I think
it would be reasonable behavior if overruns due to the application
reading data too slowly would cause holes. Currently that's not the
case - overruns will just cause audio to be skipped. But the point is
that this might change some day. I'm not sure how holes can occur
with the current code, but as the linked bug shows, they can happen.
It's most likely due to recording from a monitor source where the
thing being monitored has holes in its playback stream.
BugLink: http://bugs.launchpad.net/bugs/1058200
2012-11-07 16:52:37 +02:00
|
|
|
/* record_memblockq is empty. */
|
2006-02-20 04:05:16 +00:00
|
|
|
*data = NULL;
|
|
|
|
|
*length = 0;
|
|
|
|
|
return 0;
|
pulse: Fix hole handling in pa_stream_peek().
Previously, if there was a hole in a recording stream,
pa_stream_peek() would crash. Holes could be handled silently inside
pa_stream_peek() by generating silence (wouldn't work for compressed
streams, though) or by skipping any holes. However, I think it's
better to let the caller decide how the holes should be handled, so
in case of holes, pa_stream_peek() will return NULL data pointer and
the length of the hole in the nbytes argument.
This change is technically an interface break, because previously the
documentation didn't mention the possibility of holes that need
special handling. However, since holes caused crashing anyway in the
past, it's not a regression if applications keep misbehaving due to
not handing holes properly.
Some words about when holes can appear in recording streams: I think
it would be reasonable behavior if overruns due to the application
reading data too slowly would cause holes. Currently that's not the
case - overruns will just cause audio to be skipped. But the point is
that this might change some day. I'm not sure how holes can occur
with the current code, but as the linked bug shows, they can happen.
It's most likely due to recording from a monitor source where the
thing being monitored has holes in its playback stream.
BugLink: http://bugs.launchpad.net/bugs/1058200
2012-11-07 16:52:37 +02:00
|
|
|
|
|
|
|
|
} else if (!s->peek_memchunk.memblock) {
|
|
|
|
|
/* record_memblockq isn't empty, but it doesn't have any data at
|
|
|
|
|
* the current read index. */
|
|
|
|
|
*data = NULL;
|
|
|
|
|
*length = s->peek_memchunk.length;
|
|
|
|
|
return 0;
|
2006-02-20 04:05:16 +00:00
|
|
|
}
|
2007-10-28 19:13:50 +00:00
|
|
|
|
|
|
|
|
s->peek_data = pa_memblock_acquire(s->peek_memchunk.memblock);
|
2006-02-17 15:42:47 +00:00
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s->peek_data);
|
|
|
|
|
*data = (uint8_t*) s->peek_data + s->peek_memchunk.index;
|
2006-02-17 15:42:47 +00:00
|
|
|
*length = s->peek_memchunk.length;
|
2006-02-20 04:05:16 +00:00
|
|
|
return 0;
|
2006-02-17 15:42:47 +00:00
|
|
|
}
|
|
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
int pa_stream_drop(pa_stream *s) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2006-02-17 15:42:47 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
|
2006-02-20 04:05:16 +00:00
|
|
|
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE);
|
pulse: Fix hole handling in pa_stream_peek().
Previously, if there was a hole in a recording stream,
pa_stream_peek() would crash. Holes could be handled silently inside
pa_stream_peek() by generating silence (wouldn't work for compressed
streams, though) or by skipping any holes. However, I think it's
better to let the caller decide how the holes should be handled, so
in case of holes, pa_stream_peek() will return NULL data pointer and
the length of the hole in the nbytes argument.
This change is technically an interface break, because previously the
documentation didn't mention the possibility of holes that need
special handling. However, since holes caused crashing anyway in the
past, it's not a regression if applications keep misbehaving due to
not handing holes properly.
Some words about when holes can appear in recording streams: I think
it would be reasonable behavior if overruns due to the application
reading data too slowly would cause holes. Currently that's not the
case - overruns will just cause audio to be skipped. But the point is
that this might change some day. I'm not sure how holes can occur
with the current code, but as the linked bug shows, they can happen.
It's most likely due to recording from a monitor source where the
thing being monitored has holes in its playback stream.
BugLink: http://bugs.launchpad.net/bugs/1058200
2012-11-07 16:52:37 +02:00
|
|
|
PA_CHECK_VALIDITY(s->context, s->peek_memchunk.length > 0, PA_ERR_BADSTATE);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_memblockq_drop(s->record_memblockq, s->peek_memchunk.length);
|
2006-04-12 23:57:25 +00:00
|
|
|
|
|
|
|
|
/* Fix the simulated local read index */
|
|
|
|
|
if (s->timing_info_valid && !s->timing_info.read_index_corrupt)
|
2008-08-19 22:39:54 +02:00
|
|
|
s->timing_info.read_index += (int64_t) s->peek_memchunk.length;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
pulse: Fix hole handling in pa_stream_peek().
Previously, if there was a hole in a recording stream,
pa_stream_peek() would crash. Holes could be handled silently inside
pa_stream_peek() by generating silence (wouldn't work for compressed
streams, though) or by skipping any holes. However, I think it's
better to let the caller decide how the holes should be handled, so
in case of holes, pa_stream_peek() will return NULL data pointer and
the length of the hole in the nbytes argument.
This change is technically an interface break, because previously the
documentation didn't mention the possibility of holes that need
special handling. However, since holes caused crashing anyway in the
past, it's not a regression if applications keep misbehaving due to
not handing holes properly.
Some words about when holes can appear in recording streams: I think
it would be reasonable behavior if overruns due to the application
reading data too slowly would cause holes. Currently that's not the
case - overruns will just cause audio to be skipped. But the point is
that this might change some day. I'm not sure how holes can occur
with the current code, but as the linked bug shows, they can happen.
It's most likely due to recording from a monitor source where the
thing being monitored has holes in its playback stream.
BugLink: http://bugs.launchpad.net/bugs/1058200
2012-11-07 16:52:37 +02:00
|
|
|
if (s->peek_memchunk.memblock) {
|
|
|
|
|
pa_assert(s->peek_data);
|
|
|
|
|
s->peek_data = NULL;
|
|
|
|
|
pa_memblock_release(s->peek_memchunk.memblock);
|
|
|
|
|
pa_memblock_unref(s->peek_memchunk.memblock);
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_memchunk_reset(&s->peek_memchunk);
|
2006-02-20 04:05:16 +00:00
|
|
|
|
|
|
|
|
return 0;
|
2006-02-17 15:42:47 +00:00
|
|
|
}
|
|
|
|
|
|
2018-05-27 05:57:52 +01:00
|
|
|
size_t pa_stream_writable_size(const pa_stream *s) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2006-02-20 04:05:16 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_ANY(s->context, !pa_detect_fork(), PA_ERR_FORKED, (size_t) -1);
|
2006-02-20 04:05:16 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, (size_t) -1);
|
2006-04-10 17:39:24 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction != PA_STREAM_RECORD, PA_ERR_BADSTATE, (size_t) -1);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-04-01 23:05:09 +02:00
|
|
|
return s->requested_bytes > 0 ? (size_t) s->requested_bytes : 0;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2018-05-27 05:57:52 +01:00
|
|
|
size_t pa_stream_readable_size(const pa_stream *s) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2006-02-17 15:42:47 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_ANY(s->context, !pa_detect_fork(), PA_ERR_FORKED, (size_t) -1);
|
2006-02-20 04:05:16 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, (size_t) -1);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE, (size_t) -1);
|
2006-02-17 15:42:47 +00:00
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
return pa_memblockq_get_length(s->record_memblockq);
|
2006-02-17 15:42:47 +00:00
|
|
|
}
|
|
|
|
|
|
2006-02-20 22:41:02 +00:00
|
|
|
pa_operation * pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_operation *o;
|
|
|
|
|
pa_tagstruct *t;
|
2004-08-14 20:25:32 +00:00
|
|
|
uint32_t tag;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2006-02-20 04:05:16 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
|
2006-02-20 04:05:16 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE);
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2009-11-20 19:55:47 +01:00
|
|
|
/* Ask for a timing update before we cork/uncork to get the best
|
|
|
|
|
* accuracy for the transport latency suitable for the
|
|
|
|
|
* check_smoother_status() call in the started callback */
|
2013-06-27 19:28:09 +02:00
|
|
|
request_auto_timing_update(s, true);
|
2009-11-20 19:55:47 +01:00
|
|
|
|
2006-02-20 22:41:02 +00:00
|
|
|
o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2006-02-23 01:24:16 +00:00
|
|
|
t = pa_tagstruct_command(s->context, PA_COMMAND_DRAIN_PLAYBACK_STREAM, &tag);
|
2004-08-14 20:25:32 +00:00
|
|
|
pa_tagstruct_putu32(t, s->channel);
|
|
|
|
|
pa_pstream_send_tagstruct(s->context->pstream, t);
|
2006-04-24 19:29:15 +00:00
|
|
|
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2011-07-14 13:59:38 -07:00
|
|
|
/* This might cause the read index to continue again, hence
|
2009-11-20 19:55:47 +01:00
|
|
|
* let's request a timing update */
|
2013-06-27 19:28:09 +02:00
|
|
|
request_auto_timing_update(s, true);
|
2009-11-20 19:55:47 +01:00
|
|
|
|
2006-04-24 19:29:15 +00:00
|
|
|
return o;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2018-06-07 03:15:41 +01:00
|
|
|
static pa_usec_t calc_time(const pa_stream *s, bool ignore_transport) {
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_usec_t usec;
|
|
|
|
|
|
|
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
|
|
|
|
pa_assert(s->state == PA_STREAM_READY);
|
|
|
|
|
pa_assert(s->direction != PA_STREAM_UPLOAD);
|
|
|
|
|
pa_assert(s->timing_info_valid);
|
|
|
|
|
pa_assert(s->direction != PA_STREAM_PLAYBACK || !s->timing_info.read_index_corrupt);
|
|
|
|
|
pa_assert(s->direction != PA_STREAM_RECORD || !s->timing_info.write_index_corrupt);
|
|
|
|
|
|
|
|
|
|
if (s->direction == PA_STREAM_PLAYBACK) {
|
|
|
|
|
/* The last byte that was written into the output device
|
|
|
|
|
* had this time value associated */
|
|
|
|
|
usec = pa_bytes_to_usec(s->timing_info.read_index < 0 ? 0 : (uint64_t) s->timing_info.read_index, &s->sample_spec);
|
|
|
|
|
|
|
|
|
|
if (!s->corked && !s->suspended) {
|
|
|
|
|
|
|
|
|
|
if (!ignore_transport)
|
|
|
|
|
/* Because the latency info took a little time to come
|
|
|
|
|
* to us, we assume that the real output time is actually
|
|
|
|
|
* a little ahead */
|
|
|
|
|
usec += s->timing_info.transport_usec;
|
|
|
|
|
|
|
|
|
|
/* However, the output device usually maintains a buffer
|
|
|
|
|
too, hence the real sample currently played is a little
|
|
|
|
|
back */
|
|
|
|
|
if (s->timing_info.sink_usec >= usec)
|
|
|
|
|
usec = 0;
|
|
|
|
|
else
|
|
|
|
|
usec -= s->timing_info.sink_usec;
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-09 17:04:27 +02:00
|
|
|
} else {
|
|
|
|
|
pa_assert(s->direction == PA_STREAM_RECORD);
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
/* The last byte written into the server side queue had
|
|
|
|
|
* this time value associated */
|
|
|
|
|
usec = pa_bytes_to_usec(s->timing_info.write_index < 0 ? 0 : (uint64_t) s->timing_info.write_index, &s->sample_spec);
|
|
|
|
|
|
|
|
|
|
if (!s->corked && !s->suspended) {
|
|
|
|
|
|
|
|
|
|
if (!ignore_transport)
|
|
|
|
|
/* Add transport latency */
|
|
|
|
|
usec += s->timing_info.transport_usec;
|
|
|
|
|
|
|
|
|
|
/* Add latency of data in device buffer */
|
|
|
|
|
usec += s->timing_info.source_usec;
|
|
|
|
|
|
|
|
|
|
/* If this is a monitor source, we need to correct the
|
|
|
|
|
* time by the playback device buffer */
|
|
|
|
|
if (s->timing_info.sink_usec >= usec)
|
|
|
|
|
usec = 0;
|
|
|
|
|
else
|
|
|
|
|
usec -= s->timing_info.sink_usec;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return usec;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-15 20:37:12 +01:00
|
|
|
#ifdef USE_SMOOTHER_2
|
2022-05-13 15:05:36 +02:00
|
|
|
static inline uint64_t calc_bytes(pa_stream *s, bool ignore_transport) {
|
|
|
|
|
return (uint64_t)(calc_time(s, ignore_transport) * s->sample_spec.rate / PA_USEC_PER_SEC * pa_frame_size(&s->sample_spec));
|
2020-01-15 20:37:12 +01:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2006-04-07 01:29:33 +00:00
|
|
|
static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_operation *o = userdata;
|
2004-09-10 22:35:12 +00:00
|
|
|
struct timeval local, remote, now;
|
2006-04-07 01:29:33 +00:00
|
|
|
pa_timing_info *i;
|
2013-06-27 19:28:09 +02:00
|
|
|
bool playing = false;
|
2008-05-15 23:34:41 +00:00
|
|
|
uint64_t underrun_for = 0, playing_for = 0;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(o);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(o) >= 1);
|
2006-04-24 19:29:15 +00:00
|
|
|
|
2006-05-09 15:16:12 +00:00
|
|
|
if (!o->context || !o->stream)
|
2006-04-24 19:29:15 +00:00
|
|
|
goto finish;
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2006-04-07 01:29:33 +00:00
|
|
|
i = &o->stream->timing_info;
|
2006-04-12 17:17:23 +00:00
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
o->stream->timing_info_valid = false;
|
|
|
|
|
i->write_index_corrupt = true;
|
|
|
|
|
i->read_index_corrupt = true;
|
2006-03-02 16:32:36 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
if (command != PA_COMMAND_REPLY) {
|
2013-06-27 19:28:09 +02:00
|
|
|
if (pa_context_handle_error(o->context, command, t, false) < 0)
|
2004-08-14 20:25:32 +00:00
|
|
|
goto finish;
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2004-09-13 23:28:30 +00:00
|
|
|
} else {
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
if (pa_tagstruct_get_usec(t, &i->sink_usec) < 0 ||
|
|
|
|
|
pa_tagstruct_get_usec(t, &i->source_usec) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &playing) < 0 ||
|
|
|
|
|
pa_tagstruct_get_timeval(t, &local) < 0 ||
|
|
|
|
|
pa_tagstruct_get_timeval(t, &remote) < 0 ||
|
|
|
|
|
pa_tagstruct_gets64(t, &i->write_index) < 0 ||
|
|
|
|
|
pa_tagstruct_gets64(t, &i->read_index) < 0) {
|
|
|
|
|
|
|
|
|
|
pa_context_fail(o->context, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (o->context->version >= 13 &&
|
|
|
|
|
o->stream->direction == PA_STREAM_PLAYBACK)
|
|
|
|
|
if (pa_tagstruct_getu64(t, &underrun_for) < 0 ||
|
|
|
|
|
pa_tagstruct_getu64(t, &playing_for) < 0) {
|
|
|
|
|
|
|
|
|
|
pa_context_fail(o->context, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!pa_tagstruct_eof(t)) {
|
|
|
|
|
pa_context_fail(o->context, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
2013-06-27 19:28:09 +02:00
|
|
|
o->stream->timing_info_valid = true;
|
|
|
|
|
i->write_index_corrupt = false;
|
|
|
|
|
i->read_index_corrupt = false;
|
2008-06-26 19:01:51 +02:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
i->playing = (int) playing;
|
2008-08-19 22:39:54 +02:00
|
|
|
i->since_underrun = (int64_t) (playing ? playing_for : underrun_for);
|
2006-04-12 17:17:23 +00:00
|
|
|
|
2006-01-10 17:51:06 +00:00
|
|
|
pa_gettimeofday(&now);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2011-08-24 18:24:46 +02:00
|
|
|
/* Calculate timestamps */
|
2006-01-09 12:38:06 +00:00
|
|
|
if (pa_timeval_cmp(&local, &remote) <= 0 && pa_timeval_cmp(&remote, &now) <= 0) {
|
2004-10-27 00:10:12 +00:00
|
|
|
/* local and remote seem to have synchronized clocks */
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-10-27 00:10:12 +00:00
|
|
|
if (o->stream->direction == PA_STREAM_PLAYBACK)
|
2006-04-07 00:24:48 +00:00
|
|
|
i->transport_usec = pa_timeval_diff(&remote, &local);
|
2004-10-27 00:10:12 +00:00
|
|
|
else
|
2006-04-07 00:24:48 +00:00
|
|
|
i->transport_usec = pa_timeval_diff(&now, &remote);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
i->synchronized_clocks = true;
|
2006-04-07 00:24:48 +00:00
|
|
|
i->timestamp = remote;
|
2004-10-27 00:10:12 +00:00
|
|
|
} else {
|
|
|
|
|
/* clocks are not synchronized, let's estimate latency then */
|
2006-04-07 00:24:48 +00:00
|
|
|
i->transport_usec = pa_timeval_diff(&now, &local)/2;
|
2013-06-27 19:28:09 +02:00
|
|
|
i->synchronized_clocks = false;
|
2006-04-07 00:24:48 +00:00
|
|
|
i->timestamp = local;
|
|
|
|
|
pa_timeval_add(&i->timestamp, i->transport_usec);
|
2004-10-27 00:10:12 +00:00
|
|
|
}
|
2006-04-07 00:24:48 +00:00
|
|
|
|
2006-04-12 17:17:23 +00:00
|
|
|
/* Invalidate read and write indexes if necessary */
|
|
|
|
|
if (tag < o->stream->read_index_not_before)
|
2013-06-27 19:28:09 +02:00
|
|
|
i->read_index_corrupt = true;
|
2006-04-12 17:17:23 +00:00
|
|
|
|
|
|
|
|
if (tag < o->stream->write_index_not_before)
|
2013-06-27 19:28:09 +02:00
|
|
|
i->write_index_corrupt = true;
|
2006-04-12 17:17:23 +00:00
|
|
|
|
2006-04-07 00:24:48 +00:00
|
|
|
if (o->stream->direction == PA_STREAM_PLAYBACK) {
|
|
|
|
|
/* Write index correction */
|
|
|
|
|
|
|
|
|
|
int n, j;
|
|
|
|
|
uint32_t ctag = tag;
|
|
|
|
|
|
2008-06-26 19:03:16 +02:00
|
|
|
/* Go through the saved correction values and add up the
|
|
|
|
|
* total correction.*/
|
2025-09-15 11:38:16 -04:00
|
|
|
for (n = 0, j = o->stream->current_write_index_correction + 1;
|
2006-04-12 17:17:23 +00:00
|
|
|
n < PA_MAX_WRITE_INDEX_CORRECTIONS;
|
2025-08-07 15:45:26 -06:00
|
|
|
n++, j++) {
|
|
|
|
|
|
|
|
|
|
/* First fix up the index to be within the array */
|
2025-09-15 11:38:16 -04:00
|
|
|
j = j % PA_MAX_WRITE_INDEX_CORRECTIONS;
|
2006-04-07 00:24:48 +00:00
|
|
|
|
|
|
|
|
/* Step over invalid data or out-of-date data */
|
2006-04-12 17:17:23 +00:00
|
|
|
if (!o->stream->write_index_corrections[j].valid ||
|
|
|
|
|
o->stream->write_index_corrections[j].tag < ctag)
|
2006-04-07 00:24:48 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* Make sure that everything is in order */
|
2006-04-12 17:17:23 +00:00
|
|
|
ctag = o->stream->write_index_corrections[j].tag+1;
|
2006-04-07 00:24:48 +00:00
|
|
|
|
|
|
|
|
/* Now fix the write index */
|
2006-04-12 17:17:23 +00:00
|
|
|
if (o->stream->write_index_corrections[j].corrupt) {
|
2006-04-07 00:24:48 +00:00
|
|
|
/* A corrupting seek was made */
|
2013-06-27 19:28:09 +02:00
|
|
|
i->write_index_corrupt = true;
|
2006-04-12 17:17:23 +00:00
|
|
|
} else if (o->stream->write_index_corrections[j].absolute) {
|
2006-04-07 00:24:48 +00:00
|
|
|
/* An absolute seek was made */
|
2006-04-12 17:17:23 +00:00
|
|
|
i->write_index = o->stream->write_index_corrections[j].value;
|
2013-06-27 19:28:09 +02:00
|
|
|
i->write_index_corrupt = false;
|
2006-04-07 00:24:48 +00:00
|
|
|
} else if (!i->write_index_corrupt) {
|
|
|
|
|
/* A relative seek was made */
|
2006-04-12 17:17:23 +00:00
|
|
|
i->write_index += o->stream->write_index_corrections[j].value;
|
2006-04-07 00:24:48 +00:00
|
|
|
}
|
|
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-06-26 19:03:16 +02:00
|
|
|
/* Clear old correction entries */
|
2008-05-15 23:34:41 +00:00
|
|
|
for (n = 0; n < PA_MAX_WRITE_INDEX_CORRECTIONS; n++) {
|
|
|
|
|
if (!o->stream->write_index_corrections[n].valid)
|
|
|
|
|
continue;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (o->stream->write_index_corrections[n].tag <= tag)
|
2013-06-27 19:28:09 +02:00
|
|
|
o->stream->write_index_corrections[n].valid = false;
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
2006-04-07 00:24:48 +00:00
|
|
|
}
|
2006-05-15 20:17:11 +00:00
|
|
|
|
2008-06-26 19:03:16 +02:00
|
|
|
if (o->stream->direction == PA_STREAM_RECORD) {
|
|
|
|
|
/* Read index correction */
|
|
|
|
|
|
|
|
|
|
if (!i->read_index_corrupt)
|
2008-08-19 22:39:54 +02:00
|
|
|
i->read_index -= (int64_t) pa_memblockq_get_length(o->stream->record_memblockq);
|
2008-06-26 19:03:16 +02:00
|
|
|
}
|
|
|
|
|
|
2011-01-11 23:58:25 +05:30
|
|
|
/* Update smoother if we're not corked */
|
|
|
|
|
if (o->stream->smoother && !o->stream->corked) {
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_usec_t u, x;
|
|
|
|
|
|
2009-04-04 22:56:38 +03:00
|
|
|
u = x = pa_rtclock_now() - i->transport_usec;
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2008-06-26 19:03:53 +02:00
|
|
|
if (o->stream->direction == PA_STREAM_PLAYBACK && o->context->version >= 13) {
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_usec_t su;
|
|
|
|
|
|
|
|
|
|
/* If we weren't playing then it will take some time
|
|
|
|
|
* until the audio will actually come out through the
|
|
|
|
|
* speakers. Since we follow that timing here, we need
|
|
|
|
|
* to try to fix this up */
|
|
|
|
|
|
2008-08-19 22:39:54 +02:00
|
|
|
su = pa_bytes_to_usec((uint64_t) i->since_underrun, &o->stream->sample_spec);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
if (su < i->sink_usec)
|
|
|
|
|
x += i->sink_usec - su;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!i->playing)
|
2020-01-15 20:37:12 +01:00
|
|
|
#ifdef USE_SMOOTHER_2
|
|
|
|
|
pa_smoother_2_pause(o->stream->smoother, x);
|
|
|
|
|
#else
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_smoother_pause(o->stream->smoother, x);
|
2020-01-15 20:37:12 +01:00
|
|
|
#endif
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
/* Update the smoother */
|
|
|
|
|
if ((o->stream->direction == PA_STREAM_PLAYBACK && !i->read_index_corrupt) ||
|
|
|
|
|
(o->stream->direction == PA_STREAM_RECORD && !i->write_index_corrupt))
|
2020-01-15 20:37:12 +01:00
|
|
|
#ifdef USE_SMOOTHER_2
|
|
|
|
|
pa_smoother_2_put(o->stream->smoother, u, calc_bytes(o->stream, true));
|
|
|
|
|
#else
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_smoother_put(o->stream->smoother, u, calc_time(o->stream, true));
|
2020-01-15 20:37:12 +01:00
|
|
|
#endif
|
2008-05-15 23:34:41 +00:00
|
|
|
|
|
|
|
|
if (i->playing)
|
2020-01-15 20:37:12 +01:00
|
|
|
#ifdef USE_SMOOTHER_2
|
|
|
|
|
pa_smoother_2_resume(o->stream->smoother, x);
|
|
|
|
|
#else
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_smoother_resume(o->stream->smoother, x, true);
|
2020-01-15 20:37:12 +01:00
|
|
|
#endif
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
2007-11-21 01:30:40 +00:00
|
|
|
}
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
o->stream->auto_timing_update_requested = false;
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2006-05-15 20:17:11 +00:00
|
|
|
if (o->stream->latency_update_callback)
|
|
|
|
|
o->stream->latency_update_callback(o->stream, o->stream->latency_update_userdata);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-05-15 20:17:11 +00:00
|
|
|
if (o->callback && o->stream && o->stream->state == PA_STREAM_READY) {
|
2006-04-07 00:24:48 +00:00
|
|
|
pa_stream_success_cb_t cb = (pa_stream_success_cb_t) o->callback;
|
2006-04-07 01:29:33 +00:00
|
|
|
cb(o->stream, o->stream->timing_info_valid, o->userdata);
|
2004-08-14 20:25:32 +00:00
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
finish:
|
2006-04-07 00:24:48 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
pa_operation_done(o);
|
|
|
|
|
pa_operation_unref(o);
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2006-04-07 01:29:33 +00:00
|
|
|
pa_operation* pa_stream_update_timing_info(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) {
|
2004-08-14 20:25:32 +00:00
|
|
|
uint32_t tag;
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_operation *o;
|
|
|
|
|
pa_tagstruct *t;
|
2004-09-10 22:35:12 +00:00
|
|
|
struct timeval now;
|
2006-05-24 13:23:15 +00:00
|
|
|
int cidx = 0;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2004-08-14 20:25:32 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
|
2006-02-20 04:05:16 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-04-07 21:55:55 +00:00
|
|
|
if (s->direction == PA_STREAM_PLAYBACK) {
|
|
|
|
|
/* Find a place to store the write_index correction data for this entry */
|
2006-04-12 17:17:23 +00:00
|
|
|
cidx = (s->current_write_index_correction + 1) % PA_MAX_WRITE_INDEX_CORRECTIONS;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-04-07 21:55:55 +00:00
|
|
|
/* Check if we could allocate a correction slot. If not, there are too many outstanding queries */
|
2006-04-12 17:17:23 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, !s->write_index_corrections[cidx].valid, PA_ERR_INTERNAL);
|
2006-04-07 21:55:55 +00:00
|
|
|
}
|
2006-04-24 19:29:15 +00:00
|
|
|
o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-02-23 01:24:16 +00:00
|
|
|
t = pa_tagstruct_command(
|
|
|
|
|
s->context,
|
2008-08-19 22:39:54 +02:00
|
|
|
(uint32_t) (s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_GET_PLAYBACK_LATENCY : PA_COMMAND_GET_RECORD_LATENCY),
|
2006-02-23 01:24:16 +00:00
|
|
|
&tag);
|
2004-08-14 20:25:32 +00:00
|
|
|
pa_tagstruct_putu32(t, s->channel);
|
2006-04-07 00:24:48 +00:00
|
|
|
pa_tagstruct_put_timeval(t, pa_gettimeofday(&now));
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
pa_pstream_send_tagstruct(s->context->pstream, t);
|
2006-04-24 19:29:15 +00:00
|
|
|
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_timing_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2006-04-07 21:55:55 +00:00
|
|
|
if (s->direction == PA_STREAM_PLAYBACK) {
|
|
|
|
|
/* Fill in initial correction data */
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
s->current_write_index_correction = cidx;
|
|
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
s->write_index_corrections[cidx].valid = true;
|
|
|
|
|
s->write_index_corrections[cidx].absolute = false;
|
|
|
|
|
s->write_index_corrections[cidx].corrupt = false;
|
2008-05-15 23:34:41 +00:00
|
|
|
s->write_index_corrections[cidx].tag = tag;
|
|
|
|
|
s->write_index_corrections[cidx].value = 0;
|
|
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-04-24 19:29:15 +00:00
|
|
|
return o;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
void pa_stream_disconnect_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_stream *s = userdata;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
pa_stream_ref(s);
|
2004-08-13 13:22:44 +00:00
|
|
|
|
|
|
|
|
if (command != PA_COMMAND_REPLY) {
|
2013-06-27 19:28:09 +02:00
|
|
|
if (pa_context_handle_error(s->context, command, t, false) < 0)
|
2004-08-14 20:25:32 +00:00
|
|
|
goto finish;
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
pa_stream_set_state(s, PA_STREAM_FAILED);
|
|
|
|
|
goto finish;
|
|
|
|
|
} else if (!pa_tagstruct_eof(t)) {
|
2006-02-20 04:05:16 +00:00
|
|
|
pa_context_fail(s->context, PA_ERR_PROTOCOL);
|
2004-08-14 20:25:32 +00:00
|
|
|
goto finish;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
pa_stream_set_state(s, PA_STREAM_TERMINATED);
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
finish:
|
|
|
|
|
pa_stream_unref(s);
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
int pa_stream_disconnect(pa_stream *s) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_tagstruct *t;
|
2004-08-13 13:22:44 +00:00
|
|
|
uint32_t tag;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2006-02-20 04:05:16 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
|
2006-02-20 04:05:16 +00:00
|
|
|
PA_CHECK_VALIDITY(s->context, s->channel_valid, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY(s->context, s->context->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
pa_stream_ref(s);
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2006-02-23 01:24:16 +00:00
|
|
|
t = pa_tagstruct_command(
|
|
|
|
|
s->context,
|
2008-08-19 22:39:54 +02:00
|
|
|
(uint32_t) (s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_DELETE_PLAYBACK_STREAM :
|
|
|
|
|
(s->direction == PA_STREAM_RECORD ? PA_COMMAND_DELETE_RECORD_STREAM : PA_COMMAND_DELETE_UPLOAD_STREAM)),
|
2006-02-23 01:24:16 +00:00
|
|
|
&tag);
|
2004-08-13 13:22:44 +00:00
|
|
|
pa_tagstruct_putu32(t, s->channel);
|
|
|
|
|
pa_pstream_send_tagstruct(s->context->pstream, t);
|
2006-04-24 19:29:15 +00:00
|
|
|
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_disconnect_callback, s, NULL);
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
pa_stream_unref(s);
|
2006-02-20 04:05:16 +00:00
|
|
|
return 0;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
void pa_stream_set_read_callback(pa_stream *s, pa_stream_request_cb_t cb, void *userdata) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2006-02-20 04:05:16 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
if (pa_detect_fork())
|
|
|
|
|
return;
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
|
|
|
|
|
return;
|
|
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
s->read_callback = cb;
|
|
|
|
|
s->read_userdata = userdata;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
void pa_stream_set_write_callback(pa_stream *s, pa_stream_request_cb_t cb, void *userdata) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2006-02-20 04:05:16 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
if (pa_detect_fork())
|
|
|
|
|
return;
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
|
|
|
|
|
return;
|
|
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
s->write_callback = cb;
|
|
|
|
|
s->write_userdata = userdata;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2006-02-20 04:05:16 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
if (pa_detect_fork())
|
|
|
|
|
return;
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
|
|
|
|
|
return;
|
|
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
s->state_callback = cb;
|
|
|
|
|
s->state_userdata = userdata;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
void pa_stream_set_overflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
if (pa_detect_fork())
|
|
|
|
|
return;
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
|
|
|
|
|
return;
|
|
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
s->overflow_callback = cb;
|
|
|
|
|
s->overflow_userdata = userdata;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void pa_stream_set_underflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
if (pa_detect_fork())
|
|
|
|
|
return;
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
|
|
|
|
|
return;
|
|
|
|
|
|
2006-02-20 04:05:16 +00:00
|
|
|
s->underflow_callback = cb;
|
|
|
|
|
s->underflow_userdata = userdata;
|
|
|
|
|
}
|
|
|
|
|
|
2006-05-15 20:17:11 +00:00
|
|
|
void pa_stream_set_latency_update_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
if (pa_detect_fork())
|
|
|
|
|
return;
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
|
|
|
|
|
return;
|
|
|
|
|
|
2006-05-15 20:17:11 +00:00
|
|
|
s->latency_update_callback = cb;
|
|
|
|
|
s->latency_update_userdata = userdata;
|
|
|
|
|
}
|
|
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
void pa_stream_set_moved_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
|
|
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
|
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
if (pa_detect_fork())
|
|
|
|
|
return;
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
|
|
|
|
|
return;
|
|
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
s->moved_callback = cb;
|
|
|
|
|
s->moved_userdata = userdata;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void pa_stream_set_suspended_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
|
|
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
|
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
if (pa_detect_fork())
|
|
|
|
|
return;
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
|
|
|
|
|
return;
|
|
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
s->suspended_callback = cb;
|
|
|
|
|
s->suspended_userdata = userdata;
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
void pa_stream_set_started_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
|
|
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
|
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
if (pa_detect_fork())
|
|
|
|
|
return;
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
s->started_callback = cb;
|
|
|
|
|
s->started_userdata = userdata;
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-12 03:18:05 +01:00
|
|
|
void pa_stream_set_event_callback(pa_stream *s, pa_stream_event_cb_t cb, void *userdata) {
|
|
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
|
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
if (pa_detect_fork())
|
|
|
|
|
return;
|
|
|
|
|
|
2009-02-12 03:18:05 +01:00
|
|
|
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
s->event_callback = cb;
|
|
|
|
|
s->event_userdata = userdata;
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-30 18:46:12 +02:00
|
|
|
void pa_stream_set_buffer_attr_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
|
|
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
|
|
|
|
|
|
|
|
|
if (pa_detect_fork())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
s->buffer_attr_callback = cb;
|
|
|
|
|
s->buffer_attr_userdata = userdata;
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_operation *o = userdata;
|
2004-08-14 20:25:32 +00:00
|
|
|
int success = 1;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(o);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(o) >= 1);
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2006-04-24 19:29:15 +00:00
|
|
|
if (!o->context)
|
|
|
|
|
goto finish;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-08-13 13:22:44 +00:00
|
|
|
if (command != PA_COMMAND_REPLY) {
|
2013-06-27 19:28:09 +02:00
|
|
|
if (pa_context_handle_error(o->context, command, t, false) < 0)
|
2004-08-14 20:25:32 +00:00
|
|
|
goto finish;
|
2004-08-13 13:22:44 +00:00
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
success = 0;
|
|
|
|
|
} else if (!pa_tagstruct_eof(t)) {
|
2006-02-20 04:05:16 +00:00
|
|
|
pa_context_fail(o->context, PA_ERR_PROTOCOL);
|
2004-08-14 20:25:32 +00:00
|
|
|
goto finish;
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
if (o->callback) {
|
2006-02-20 22:41:02 +00:00
|
|
|
pa_stream_success_cb_t cb = (pa_stream_success_cb_t) o->callback;
|
2004-08-14 20:25:32 +00:00
|
|
|
cb(o->stream, success, o->userdata);
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
|
|
|
|
|
2004-08-14 20:25:32 +00:00
|
|
|
finish:
|
|
|
|
|
pa_operation_done(o);
|
|
|
|
|
pa_operation_unref(o);
|
2004-08-13 13:22:44 +00:00
|
|
|
}
|
2004-08-22 21:13:58 +00:00
|
|
|
|
2006-02-20 22:41:02 +00:00
|
|
|
pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_operation *o;
|
|
|
|
|
pa_tagstruct *t;
|
2004-08-22 21:13:58 +00:00
|
|
|
uint32_t tag;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
|
2006-02-20 22:41:02 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
|
2004-08-22 21:13:58 +00:00
|
|
|
|
2009-11-20 19:55:47 +01:00
|
|
|
/* Ask for a timing update before we cork/uncork to get the best
|
|
|
|
|
* accuracy for the transport latency suitable for the
|
|
|
|
|
* check_smoother_status() call in the started callback */
|
2013-06-27 19:28:09 +02:00
|
|
|
request_auto_timing_update(s, true);
|
2009-11-20 19:55:47 +01:00
|
|
|
|
2004-10-27 00:10:12 +00:00
|
|
|
s->corked = b;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-02-20 22:41:02 +00:00
|
|
|
o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
|
2004-08-22 21:13:58 +00:00
|
|
|
|
2006-02-23 01:24:16 +00:00
|
|
|
t = pa_tagstruct_command(
|
|
|
|
|
s->context,
|
2008-08-19 22:39:54 +02:00
|
|
|
(uint32_t) (s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CORK_PLAYBACK_STREAM : PA_COMMAND_CORK_RECORD_STREAM),
|
2006-02-23 01:24:16 +00:00
|
|
|
&tag);
|
2004-08-22 21:13:58 +00:00
|
|
|
pa_tagstruct_putu32(t, s->channel);
|
2004-09-26 17:02:26 +00:00
|
|
|
pa_tagstruct_put_boolean(t, !!b);
|
2004-08-22 21:13:58 +00:00
|
|
|
pa_pstream_send_tagstruct(s->context->pstream, t);
|
2006-04-24 19:29:15 +00:00
|
|
|
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
|
2004-08-22 21:13:58 +00:00
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
check_smoother_status(s, false, false, false);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2009-11-20 19:55:47 +01:00
|
|
|
/* This might cause the indexes to hang/start again, hence let's
|
|
|
|
|
* request a timing update, after the cork/uncork, too */
|
2013-06-27 19:28:09 +02:00
|
|
|
request_auto_timing_update(s, true);
|
2006-04-12 17:17:23 +00:00
|
|
|
|
2006-04-24 19:29:15 +00:00
|
|
|
return o;
|
2004-08-22 21:13:58 +00:00
|
|
|
}
|
|
|
|
|
|
2006-02-20 22:41:02 +00:00
|
|
|
static pa_operation* stream_send_simple_command(pa_stream *s, uint32_t command, pa_stream_success_cb_t cb, void *userdata) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_tagstruct *t;
|
|
|
|
|
pa_operation *o;
|
2004-08-22 21:13:58 +00:00
|
|
|
uint32_t tag;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2006-02-20 22:41:02 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
|
2006-02-20 22:41:02 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-02-20 22:41:02 +00:00
|
|
|
o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
|
2004-08-22 21:13:58 +00:00
|
|
|
|
2006-02-23 01:24:16 +00:00
|
|
|
t = pa_tagstruct_command(s->context, command, &tag);
|
2004-08-22 21:13:58 +00:00
|
|
|
pa_tagstruct_putu32(t, s->channel);
|
|
|
|
|
pa_pstream_send_tagstruct(s->context->pstream, t);
|
2006-04-24 19:29:15 +00:00
|
|
|
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
|
2004-08-22 21:13:58 +00:00
|
|
|
|
2006-04-24 19:29:15 +00:00
|
|
|
return o;
|
2004-08-27 01:29:49 +00:00
|
|
|
}
|
|
|
|
|
|
2006-02-20 22:41:02 +00:00
|
|
|
pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_operation *o;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
|
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
|
2008-05-15 23:34:41 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
|
2006-02-20 22:41:02 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-11-20 19:55:47 +01:00
|
|
|
/* Ask for a timing update *before* the flush, so that the
|
|
|
|
|
* transport usec is as up to date as possible when we get the
|
|
|
|
|
* underflow message and update the smoother status*/
|
2013-06-27 19:28:09 +02:00
|
|
|
request_auto_timing_update(s, true);
|
2009-11-20 19:55:47 +01:00
|
|
|
|
2008-08-19 22:39:54 +02:00
|
|
|
if (!(o = stream_send_simple_command(s, (uint32_t) (s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_FLUSH_PLAYBACK_STREAM : PA_COMMAND_FLUSH_RECORD_STREAM), cb, userdata)))
|
2008-06-26 19:03:53 +02:00
|
|
|
return NULL;
|
2006-04-12 17:17:23 +00:00
|
|
|
|
2008-06-26 19:03:53 +02:00
|
|
|
if (s->direction == PA_STREAM_PLAYBACK) {
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2008-06-26 19:03:53 +02:00
|
|
|
if (s->write_index_corrections[s->current_write_index_correction].valid)
|
2013-06-27 19:28:09 +02:00
|
|
|
s->write_index_corrections[s->current_write_index_correction].corrupt = true;
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2008-06-26 19:03:53 +02:00
|
|
|
if (s->buffer_attr.prebuf > 0)
|
2013-06-27 19:28:09 +02:00
|
|
|
check_smoother_status(s, false, false, true);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2008-06-26 19:03:53 +02:00
|
|
|
/* This will change the write index, but leave the
|
|
|
|
|
* read index untouched. */
|
2013-06-27 19:28:09 +02:00
|
|
|
invalidate_indexes(s, false, true);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2008-06-26 19:03:53 +02:00
|
|
|
} else
|
|
|
|
|
/* For record streams this has no influence on the write
|
|
|
|
|
* index, but the read index might jump. */
|
2013-06-27 19:28:09 +02:00
|
|
|
invalidate_indexes(s, true, false);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2010-01-18 01:33:04 +01:00
|
|
|
/* Note that we do not update requested_bytes here. This is
|
|
|
|
|
* because we cannot really know how data actually was dropped
|
|
|
|
|
* from the write index due to this. This 'error' will be applied
|
|
|
|
|
* by both client and server and hence we should be fine. */
|
|
|
|
|
|
2004-10-27 00:10:12 +00:00
|
|
|
return o;
|
2004-09-26 17:02:26 +00:00
|
|
|
}
|
|
|
|
|
|
2006-02-20 22:41:02 +00:00
|
|
|
pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_operation *o;
|
2006-02-20 22:41:02 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
|
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
|
2008-05-15 23:34:41 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
|
2006-02-20 22:41:02 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE);
|
2006-04-12 17:17:23 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE);
|
2006-02-20 22:41:02 +00:00
|
|
|
|
2009-11-20 19:55:47 +01:00
|
|
|
/* Ask for a timing update before we cork/uncork to get the best
|
|
|
|
|
* accuracy for the transport latency suitable for the
|
|
|
|
|
* check_smoother_status() call in the started callback */
|
2013-06-27 19:28:09 +02:00
|
|
|
request_auto_timing_update(s, true);
|
2009-11-20 19:55:47 +01:00
|
|
|
|
2008-06-26 19:03:53 +02:00
|
|
|
if (!(o = stream_send_simple_command(s, PA_COMMAND_PREBUF_PLAYBACK_STREAM, cb, userdata)))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
/* This might cause the read index to hang again, hence
|
|
|
|
|
* let's request a timing update */
|
2013-06-27 19:28:09 +02:00
|
|
|
request_auto_timing_update(s, true);
|
2006-04-07 00:24:48 +00:00
|
|
|
|
2004-10-27 00:10:12 +00:00
|
|
|
return o;
|
2004-08-27 01:29:49 +00:00
|
|
|
}
|
|
|
|
|
|
2006-02-20 22:41:02 +00:00
|
|
|
pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_operation *o;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
|
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
|
2008-05-15 23:34:41 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
|
2006-02-20 22:41:02 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE);
|
2006-04-12 17:17:23 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE);
|
2006-02-20 22:41:02 +00:00
|
|
|
|
2009-11-20 19:55:47 +01:00
|
|
|
/* Ask for a timing update before we cork/uncork to get the best
|
|
|
|
|
* accuracy for the transport latency suitable for the
|
|
|
|
|
* check_smoother_status() call in the started callback */
|
2013-06-27 19:28:09 +02:00
|
|
|
request_auto_timing_update(s, true);
|
2009-11-20 19:55:47 +01:00
|
|
|
|
2008-06-26 19:03:53 +02:00
|
|
|
if (!(o = stream_send_simple_command(s, PA_COMMAND_TRIGGER_PLAYBACK_STREAM, cb, userdata)))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
/* This might cause the read index to start moving again, hence
|
|
|
|
|
* let's request a timing update */
|
2013-06-27 19:28:09 +02:00
|
|
|
request_auto_timing_update(s, true);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-10-27 00:10:12 +00:00
|
|
|
return o;
|
2004-08-22 21:13:58 +00:00
|
|
|
}
|
2004-09-15 19:16:57 +00:00
|
|
|
|
2006-02-20 22:41:02 +00:00
|
|
|
pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_operation *o;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
|
|
|
|
pa_assert(name);
|
2004-09-15 19:16:57 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
|
2006-02-20 22:41:02 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (s->context->version >= 13) {
|
|
|
|
|
pa_proplist *p = pa_proplist_new();
|
2004-09-15 19:16:57 +00:00
|
|
|
|
2008-09-10 17:07:50 -04:00
|
|
|
pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name);
|
2008-05-15 23:34:41 +00:00
|
|
|
o = pa_stream_proplist_update(s, PA_UPDATE_REPLACE, p, cb, userdata);
|
|
|
|
|
pa_proplist_free(p);
|
|
|
|
|
} else {
|
|
|
|
|
pa_tagstruct *t;
|
|
|
|
|
uint32_t tag;
|
|
|
|
|
|
|
|
|
|
o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
|
|
|
|
|
t = pa_tagstruct_command(
|
|
|
|
|
s->context,
|
2008-08-19 22:39:54 +02:00
|
|
|
(uint32_t) (s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_NAME : PA_COMMAND_SET_PLAYBACK_STREAM_NAME),
|
2008-05-15 23:34:41 +00:00
|
|
|
&tag);
|
|
|
|
|
pa_tagstruct_putu32(t, s->channel);
|
|
|
|
|
pa_tagstruct_puts(t, name);
|
|
|
|
|
pa_pstream_send_tagstruct(s->context->pstream, t);
|
|
|
|
|
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
|
|
|
|
|
}
|
2004-09-15 19:16:57 +00:00
|
|
|
|
2006-04-24 19:29:15 +00:00
|
|
|
return o;
|
2004-09-15 19:16:57 +00:00
|
|
|
}
|
2004-09-26 17:02:26 +00:00
|
|
|
|
2006-04-07 00:24:48 +00:00
|
|
|
int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) {
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_usec_t usec;
|
2006-02-20 22:41:02 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2006-02-20 22:41:02 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
|
2006-04-07 00:24:48 +00:00
|
|
|
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
|
2006-04-07 01:29:33 +00:00
|
|
|
PA_CHECK_VALIDITY(s->context, s->timing_info_valid, PA_ERR_NODATA);
|
2006-04-12 17:17:23 +00:00
|
|
|
PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_PLAYBACK || !s->timing_info.read_index_corrupt, PA_ERR_NODATA);
|
|
|
|
|
PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_RECORD || !s->timing_info.write_index_corrupt, PA_ERR_NODATA);
|
2004-09-26 17:02:26 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (s->smoother)
|
2020-01-15 20:37:12 +01:00
|
|
|
#ifdef USE_SMOOTHER_2
|
|
|
|
|
usec = pa_smoother_2_get(s->smoother, pa_rtclock_now());
|
|
|
|
|
#else
|
2009-04-04 22:56:38 +03:00
|
|
|
usec = pa_smoother_get(s->smoother, pa_rtclock_now());
|
2020-01-15 20:37:12 +01:00
|
|
|
#endif
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
else
|
2013-06-27 19:28:09 +02:00
|
|
|
usec = calc_time(s, false);
|
2006-01-10 17:51:06 +00:00
|
|
|
|
2006-04-07 00:24:48 +00:00
|
|
|
/* Make sure the time runs monotonically */
|
2009-02-02 02:02:31 +01:00
|
|
|
if (!(s->flags & PA_STREAM_NOT_MONOTONIC)) {
|
2006-04-07 00:24:48 +00:00
|
|
|
if (usec < s->previous_time)
|
|
|
|
|
usec = s->previous_time;
|
|
|
|
|
else
|
|
|
|
|
s->previous_time = usec;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (r_usec)
|
|
|
|
|
*r_usec = usec;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-04-07 00:24:48 +00:00
|
|
|
return 0;
|
2004-09-26 17:02:26 +00:00
|
|
|
}
|
|
|
|
|
|
2018-06-07 03:15:41 +01:00
|
|
|
static pa_usec_t time_counter_diff(const pa_stream *s, pa_usec_t a, pa_usec_t b, int *negative) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-10-27 14:14:30 +00:00
|
|
|
if (negative)
|
|
|
|
|
*negative = 0;
|
|
|
|
|
|
2007-01-04 13:43:45 +00:00
|
|
|
if (a >= b)
|
2006-04-07 00:24:48 +00:00
|
|
|
return a-b;
|
|
|
|
|
else {
|
|
|
|
|
if (negative && s->direction == PA_STREAM_RECORD) {
|
|
|
|
|
*negative = 1;
|
|
|
|
|
return b-a;
|
2004-10-27 14:14:30 +00:00
|
|
|
} else
|
|
|
|
|
return 0;
|
2006-04-07 00:24:48 +00:00
|
|
|
}
|
2004-10-27 14:14:30 +00:00
|
|
|
}
|
|
|
|
|
|
2006-04-07 00:24:48 +00:00
|
|
|
int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative) {
|
2004-10-27 00:10:12 +00:00
|
|
|
pa_usec_t t, c;
|
2006-04-07 00:24:48 +00:00
|
|
|
int r;
|
|
|
|
|
int64_t cindex;
|
2004-09-26 17:02:26 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
|
|
|
|
pa_assert(r_usec);
|
2006-02-20 22:41:02 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
|
2006-04-07 00:24:48 +00:00
|
|
|
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
|
2006-04-07 01:29:33 +00:00
|
|
|
PA_CHECK_VALIDITY(s->context, s->timing_info_valid, PA_ERR_NODATA);
|
|
|
|
|
PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_PLAYBACK || !s->timing_info.write_index_corrupt, PA_ERR_NODATA);
|
2006-04-12 17:17:23 +00:00
|
|
|
PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_RECORD || !s->timing_info.read_index_corrupt, PA_ERR_NODATA);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-04-07 00:24:48 +00:00
|
|
|
if ((r = pa_stream_get_time(s, &t)) < 0)
|
|
|
|
|
return r;
|
2004-09-26 17:02:26 +00:00
|
|
|
|
2006-04-07 00:24:48 +00:00
|
|
|
if (s->direction == PA_STREAM_PLAYBACK)
|
2006-04-07 01:29:33 +00:00
|
|
|
cindex = s->timing_info.write_index;
|
2006-04-07 00:24:48 +00:00
|
|
|
else
|
2006-04-07 01:29:33 +00:00
|
|
|
cindex = s->timing_info.read_index;
|
2006-02-20 22:41:02 +00:00
|
|
|
|
2006-04-07 00:24:48 +00:00
|
|
|
if (cindex < 0)
|
|
|
|
|
cindex = 0;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-08-19 22:39:54 +02:00
|
|
|
c = pa_bytes_to_usec((uint64_t) cindex, &s->sample_spec);
|
2004-10-27 00:10:12 +00:00
|
|
|
|
2006-04-07 00:24:48 +00:00
|
|
|
if (s->direction == PA_STREAM_PLAYBACK)
|
|
|
|
|
*r_usec = time_counter_diff(s, c, t, negative);
|
|
|
|
|
else
|
|
|
|
|
*r_usec = time_counter_diff(s, t, c, negative);
|
2006-02-20 17:09:39 +00:00
|
|
|
|
2006-04-07 00:24:48 +00:00
|
|
|
return 0;
|
2006-02-20 17:09:39 +00:00
|
|
|
}
|
|
|
|
|
|
2006-04-07 01:29:33 +00:00
|
|
|
const pa_timing_info* pa_stream_get_timing_info(pa_stream *s) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2004-10-27 00:10:12 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
|
2006-04-07 00:24:48 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
|
2008-08-26 15:45:18 +02:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->timing_info_valid, PA_ERR_NODATA);
|
2004-10-27 00:10:12 +00:00
|
|
|
|
2006-04-07 01:29:33 +00:00
|
|
|
return &s->timing_info;
|
2004-10-27 00:10:12 +00:00
|
|
|
}
|
|
|
|
|
|
2006-04-07 00:24:48 +00:00
|
|
|
const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2004-10-27 00:10:12 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
|
|
|
|
|
|
2006-04-07 00:24:48 +00:00
|
|
|
return &s->sample_spec;
|
2004-10-27 00:10:12 +00:00
|
|
|
}
|
|
|
|
|
|
2006-04-07 00:24:48 +00:00
|
|
|
const pa_channel_map* pa_stream_get_channel_map(pa_stream *s) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2006-02-20 22:41:02 +00:00
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
|
|
|
|
|
|
2006-04-07 00:24:48 +00:00
|
|
|
return &s->channel_map;
|
2004-10-27 00:10:12 +00:00
|
|
|
}
|
2006-05-25 23:20:28 +00:00
|
|
|
|
2018-05-28 00:03:14 +01:00
|
|
|
const pa_format_info* pa_stream_get_format_info(const pa_stream *s) {
|
2011-03-08 20:16:00 +05:30
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
|
|
|
|
|
|
|
|
|
/* We don't have the format till routing is done */
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
|
|
|
|
|
|
|
|
|
|
return s->format;
|
|
|
|
|
}
|
2006-05-25 23:20:28 +00:00
|
|
|
const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
2006-05-25 23:20:28 +00:00
|
|
|
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
|
2008-05-15 23:34:41 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 9, PA_ERR_NOTSUPPORTED);
|
2006-05-25 23:20:28 +00:00
|
|
|
|
|
|
|
|
return &s->buffer_attr;
|
|
|
|
|
}
|
2007-11-21 01:30:40 +00:00
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void stream_set_buffer_attr_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_operation *o = userdata;
|
|
|
|
|
int success = 1;
|
|
|
|
|
|
|
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(o);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(o) >= 1);
|
|
|
|
|
|
|
|
|
|
if (!o->context)
|
|
|
|
|
goto finish;
|
|
|
|
|
|
|
|
|
|
if (command != PA_COMMAND_REPLY) {
|
2013-06-27 19:28:09 +02:00
|
|
|
if (pa_context_handle_error(o->context, command, t, false) < 0)
|
2007-11-21 01:30:40 +00:00
|
|
|
goto finish;
|
|
|
|
|
|
|
|
|
|
success = 0;
|
|
|
|
|
} else {
|
|
|
|
|
if (o->stream->direction == PA_STREAM_PLAYBACK) {
|
|
|
|
|
if (pa_tagstruct_getu32(t, &o->stream->buffer_attr.maxlength) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &o->stream->buffer_attr.tlength) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &o->stream->buffer_attr.prebuf) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &o->stream->buffer_attr.minreq) < 0) {
|
|
|
|
|
pa_context_fail(o->context, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
} else if (o->stream->direction == PA_STREAM_RECORD) {
|
|
|
|
|
if (pa_tagstruct_getu32(t, &o->stream->buffer_attr.maxlength) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &o->stream->buffer_attr.fragsize) < 0) {
|
|
|
|
|
pa_context_fail(o->context, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-10-02 01:15:58 +02:00
|
|
|
if (o->stream->context->version >= 13) {
|
|
|
|
|
pa_usec_t usec;
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_get_usec(t, &usec) < 0) {
|
|
|
|
|
pa_context_fail(o->context, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (o->stream->direction == PA_STREAM_RECORD)
|
|
|
|
|
o->stream->timing_info.configured_source_usec = usec;
|
|
|
|
|
else
|
|
|
|
|
o->stream->timing_info.configured_sink_usec = usec;
|
|
|
|
|
}
|
|
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
if (!pa_tagstruct_eof(t)) {
|
|
|
|
|
pa_context_fail(o->context, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (o->callback) {
|
|
|
|
|
pa_stream_success_cb_t cb = (pa_stream_success_cb_t) o->callback;
|
|
|
|
|
cb(o->stream, success, o->userdata);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
finish:
|
|
|
|
|
pa_operation_done(o);
|
|
|
|
|
pa_operation_unref(o);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_operation* pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr, pa_stream_success_cb_t cb, void *userdata) {
|
|
|
|
|
pa_operation *o;
|
|
|
|
|
pa_tagstruct *t;
|
|
|
|
|
uint32_t tag;
|
2010-01-15 01:25:42 +01:00
|
|
|
pa_buffer_attr copy;
|
2007-11-21 01:30:40 +00:00
|
|
|
|
|
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
|
|
|
|
pa_assert(attr);
|
|
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
|
2007-11-21 01:30:40 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED);
|
|
|
|
|
|
2009-11-20 19:55:47 +01:00
|
|
|
/* Ask for a timing update before we cork/uncork to get the best
|
|
|
|
|
* accuracy for the transport latency suitable for the
|
|
|
|
|
* check_smoother_status() call in the started callback */
|
2013-06-27 19:28:09 +02:00
|
|
|
request_auto_timing_update(s, true);
|
2009-11-20 19:55:47 +01:00
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
|
|
|
|
|
|
|
|
|
|
t = pa_tagstruct_command(
|
|
|
|
|
s->context,
|
2008-08-19 22:39:54 +02:00
|
|
|
(uint32_t) (s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR : PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR),
|
2007-11-21 01:30:40 +00:00
|
|
|
&tag);
|
|
|
|
|
pa_tagstruct_putu32(t, s->channel);
|
|
|
|
|
|
2010-01-15 01:25:42 +01:00
|
|
|
copy = *attr;
|
|
|
|
|
patch_buffer_attr(s, ©, NULL);
|
|
|
|
|
attr = ©
|
|
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_tagstruct_putu32(t, attr->maxlength);
|
|
|
|
|
|
|
|
|
|
if (s->direction == PA_STREAM_PLAYBACK)
|
|
|
|
|
pa_tagstruct_put(
|
|
|
|
|
t,
|
|
|
|
|
PA_TAG_U32, attr->tlength,
|
|
|
|
|
PA_TAG_U32, attr->prebuf,
|
|
|
|
|
PA_TAG_U32, attr->minreq,
|
|
|
|
|
PA_TAG_INVALID);
|
|
|
|
|
else
|
|
|
|
|
pa_tagstruct_putu32(t, attr->fragsize);
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (s->context->version >= 13)
|
|
|
|
|
pa_tagstruct_put_boolean(t, !!(s->flags & PA_STREAM_ADJUST_LATENCY));
|
|
|
|
|
|
2008-09-03 18:31:46 +02:00
|
|
|
if (s->context->version >= 14)
|
|
|
|
|
pa_tagstruct_put_boolean(t, !!(s->flags & PA_STREAM_EARLY_REQUESTS));
|
|
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_pstream_send_tagstruct(s->context->pstream, t);
|
|
|
|
|
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_set_buffer_attr_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
|
|
|
|
|
|
2011-08-24 18:24:46 +02:00
|
|
|
/* This might cause changes in the read/write index, hence let's
|
2008-06-26 19:03:53 +02:00
|
|
|
* request a timing update */
|
2013-06-27 19:28:09 +02:00
|
|
|
request_auto_timing_update(s, true);
|
2008-06-26 19:03:53 +02:00
|
|
|
|
2007-11-21 01:30:40 +00:00
|
|
|
return o;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-27 05:51:30 +01:00
|
|
|
uint32_t pa_stream_get_device_index(const pa_stream *s) {
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
|
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_ANY(s->context, !pa_detect_fork(), PA_ERR_FORKED, PA_INVALID_INDEX);
|
2007-11-21 01:30:40 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE, PA_INVALID_INDEX);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED, PA_INVALID_INDEX);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->device_index != PA_INVALID_INDEX, PA_ERR_BADSTATE, PA_INVALID_INDEX);
|
|
|
|
|
|
|
|
|
|
return s->device_index;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-27 05:51:30 +01:00
|
|
|
const char *pa_stream_get_device_name(const pa_stream *s) {
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
|
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
|
2007-11-21 01:30:40 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->device_name, PA_ERR_BADSTATE);
|
|
|
|
|
|
|
|
|
|
return s->device_name;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-27 05:54:02 +01:00
|
|
|
int pa_stream_is_suspended(const pa_stream *s) {
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
|
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
|
2007-11-21 01:30:40 +00:00
|
|
|
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED);
|
|
|
|
|
|
|
|
|
|
return s->suspended;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-27 05:54:02 +01:00
|
|
|
int pa_stream_is_corked(const pa_stream *s) {
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
|
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
|
2008-05-15 23:34:41 +00:00
|
|
|
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
|
|
|
|
|
|
|
|
|
|
return s->corked;
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-09 16:20:29 +02:00
|
|
|
static void stream_update_sample_rate_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_operation *o = userdata;
|
|
|
|
|
int success = 1;
|
|
|
|
|
|
|
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(o);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(o) >= 1);
|
|
|
|
|
|
|
|
|
|
if (!o->context)
|
|
|
|
|
goto finish;
|
|
|
|
|
|
|
|
|
|
if (command != PA_COMMAND_REPLY) {
|
2013-06-27 19:28:09 +02:00
|
|
|
if (pa_context_handle_error(o->context, command, t, false) < 0)
|
2007-11-21 01:30:40 +00:00
|
|
|
goto finish;
|
|
|
|
|
|
|
|
|
|
success = 0;
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
if (!pa_tagstruct_eof(t)) {
|
|
|
|
|
pa_context_fail(o->context, PA_ERR_PROTOCOL);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
o->stream->sample_spec.rate = PA_PTR_TO_UINT(o->private);
|
2020-01-15 20:37:12 +01:00
|
|
|
#ifdef USE_SMOOTHER_2
|
|
|
|
|
if (o->stream->smoother)
|
|
|
|
|
pa_smoother_2_set_rate(o->stream->smoother, pa_rtclock_now(), o->stream->sample_spec.rate);
|
|
|
|
|
#endif
|
2007-11-21 01:30:40 +00:00
|
|
|
pa_assert(pa_sample_spec_valid(&o->stream->sample_spec));
|
|
|
|
|
|
|
|
|
|
if (o->callback) {
|
|
|
|
|
pa_stream_success_cb_t cb = (pa_stream_success_cb_t) o->callback;
|
|
|
|
|
cb(o->stream, success, o->userdata);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
finish:
|
|
|
|
|
pa_operation_done(o);
|
|
|
|
|
pa_operation_unref(o);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_operation *pa_stream_update_sample_rate(pa_stream *s, uint32_t rate, pa_stream_success_cb_t cb, void *userdata) {
|
|
|
|
|
pa_operation *o;
|
|
|
|
|
pa_tagstruct *t;
|
|
|
|
|
uint32_t tag;
|
|
|
|
|
|
|
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
|
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
|
2013-12-04 09:50:10 +02:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, pa_sample_rate_valid(rate), PA_ERR_INVALID);
|
2007-11-21 01:30:40 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->flags & PA_STREAM_VARIABLE_RATE, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 12, PA_ERR_NOTSUPPORTED);
|
|
|
|
|
|
|
|
|
|
o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
|
|
|
|
|
o->private = PA_UINT_TO_PTR(rate);
|
|
|
|
|
|
|
|
|
|
t = pa_tagstruct_command(
|
|
|
|
|
s->context,
|
2008-08-19 22:39:54 +02:00
|
|
|
(uint32_t) (s->direction == PA_STREAM_RECORD ? PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE : PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE),
|
2007-11-21 01:30:40 +00:00
|
|
|
&tag);
|
|
|
|
|
pa_tagstruct_putu32(t, s->channel);
|
|
|
|
|
pa_tagstruct_putu32(t, rate);
|
|
|
|
|
|
|
|
|
|
pa_pstream_send_tagstruct(s->context->pstream, t);
|
|
|
|
|
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_update_sample_rate_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
|
|
|
|
|
|
|
|
|
|
return o;
|
2008-05-15 23:34:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_operation *pa_stream_proplist_update(pa_stream *s, pa_update_mode_t mode, pa_proplist *p, pa_stream_success_cb_t cb, void *userdata) {
|
|
|
|
|
pa_operation *o;
|
|
|
|
|
pa_tagstruct *t;
|
|
|
|
|
uint32_t tag;
|
|
|
|
|
|
|
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
|
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
|
2008-05-15 23:34:41 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE, PA_ERR_INVALID);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 13, PA_ERR_NOTSUPPORTED);
|
|
|
|
|
|
|
|
|
|
o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
|
|
|
|
|
|
|
|
|
|
t = pa_tagstruct_command(
|
|
|
|
|
s->context,
|
2008-08-19 22:39:54 +02:00
|
|
|
(uint32_t) (s->direction == PA_STREAM_RECORD ? PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST : PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST),
|
2008-05-15 23:34:41 +00:00
|
|
|
&tag);
|
|
|
|
|
pa_tagstruct_putu32(t, s->channel);
|
|
|
|
|
pa_tagstruct_putu32(t, (uint32_t) mode);
|
|
|
|
|
pa_tagstruct_put_proplist(t, p);
|
|
|
|
|
|
|
|
|
|
pa_pstream_send_tagstruct(s->context->pstream, t);
|
|
|
|
|
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
|
|
|
|
|
|
|
|
|
|
/* Please note that we don't update s->proplist here, because we
|
|
|
|
|
* don't export that field */
|
|
|
|
|
|
|
|
|
|
return o;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_operation *pa_stream_proplist_remove(pa_stream *s, const char *const keys[], pa_stream_success_cb_t cb, void *userdata) {
|
|
|
|
|
pa_operation *o;
|
|
|
|
|
pa_tagstruct *t;
|
|
|
|
|
uint32_t tag;
|
|
|
|
|
const char * const*k;
|
|
|
|
|
|
|
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
|
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
|
2008-05-15 23:34:41 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, keys && keys[0], PA_ERR_INVALID);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 13, PA_ERR_NOTSUPPORTED);
|
2007-11-21 01:30:40 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
|
|
|
|
|
|
|
|
|
|
t = pa_tagstruct_command(
|
|
|
|
|
s->context,
|
2008-08-19 22:39:54 +02:00
|
|
|
(uint32_t) (s->direction == PA_STREAM_RECORD ? PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST : PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST),
|
2008-05-15 23:34:41 +00:00
|
|
|
&tag);
|
|
|
|
|
pa_tagstruct_putu32(t, s->channel);
|
|
|
|
|
|
|
|
|
|
for (k = keys; *k; k++)
|
|
|
|
|
pa_tagstruct_puts(t, *k);
|
|
|
|
|
|
|
|
|
|
pa_tagstruct_puts(t, NULL);
|
|
|
|
|
|
|
|
|
|
pa_pstream_send_tagstruct(s->context->pstream, t);
|
|
|
|
|
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
|
|
|
|
|
|
|
|
|
|
/* Please note that we don't update s->proplist here, because we
|
|
|
|
|
* don't export that field */
|
|
|
|
|
|
|
|
|
|
return o;
|
2007-11-21 01:30:40 +00:00
|
|
|
}
|
2008-06-13 21:56:19 +00:00
|
|
|
|
|
|
|
|
int pa_stream_set_monitor_stream(pa_stream *s, uint32_t sink_input_idx) {
|
|
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
|
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
|
2008-06-13 21:56:19 +00:00
|
|
|
PA_CHECK_VALIDITY(s->context, sink_input_idx != PA_INVALID_INDEX, PA_ERR_INVALID);
|
|
|
|
|
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE);
|
|
|
|
|
PA_CHECK_VALIDITY(s->context, s->context->version >= 13, PA_ERR_NOTSUPPORTED);
|
|
|
|
|
|
|
|
|
|
s->direct_on_input = sink_input_idx;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-27 06:08:16 +01:00
|
|
|
uint32_t pa_stream_get_monitor_stream(const pa_stream *s) {
|
2008-06-13 21:56:19 +00:00
|
|
|
pa_assert(s);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(s) >= 1);
|
|
|
|
|
|
2009-03-05 04:33:40 +01:00
|
|
|
PA_CHECK_VALIDITY_RETURN_ANY(s->context, !pa_detect_fork(), PA_ERR_FORKED, PA_INVALID_INDEX);
|
2008-06-13 21:56:19 +00:00
|
|
|
PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direct_on_input != PA_INVALID_INDEX, PA_ERR_BADSTATE, PA_INVALID_INDEX);
|
|
|
|
|
PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->context->version >= 13, PA_ERR_NOTSUPPORTED, PA_INVALID_INDEX);
|
|
|
|
|
|
|
|
|
|
return s->direct_on_input;
|
|
|
|
|
}
|