Initial import

This commit is contained in:
Wim Taymans 2018-06-01 11:28:31 +02:00
commit faa5984fce
39 changed files with 9955 additions and 0 deletions

64
src/bitset.c Normal file
View file

@ -0,0 +1,64 @@
/***
This file is part of PulseAudio.
Copyright 2009 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
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.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
#include <stdbool.h>
#include "internal.h"
#include "bitset.h"
void pa_bitset_set(pa_bitset_t *b, unsigned k, bool v) {
pa_assert(b);
if (v)
b[k >> 5] |= 1 << (k & 31);
else
b[k >> 5] &= ~((uint32_t) (1 << (k & 31)));
}
bool pa_bitset_get(const pa_bitset_t *b, unsigned k) {
return !!(b[k >> 5] & (1 << (k & 31)));
}
bool pa_bitset_equals(const pa_bitset_t *b, unsigned n, ...) {
va_list ap;
pa_bitset_t *a;
bool equal;
a = alloca(PA_BITSET_ELEMENTS(n));
va_start(ap, n);
for (;;) {
int j = va_arg(ap, int);
if (j < 0)
break;
pa_bitset_set(a, j, true);
}
va_end(ap);
equal = memcmp(a, b, PA_BITSET_SIZE(n)) == 0;
return equal;
}

34
src/bitset.h Normal file
View file

@ -0,0 +1,34 @@
#ifndef foopulsecorebitsethfoo
#define foopulsecorebitsethfoo
/***
This file is part of PulseAudio.
Copyright 2009 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
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.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#include <inttypes.h>
#define PA_BITSET_ELEMENTS(n) (((n)+31)/32)
#define PA_BITSET_SIZE(n) (PA_BITSET_ELEMENTS(n)*4)
typedef uint32_t pa_bitset_t;
void pa_bitset_set(pa_bitset_t *b, unsigned k, bool v);
bool pa_bitset_get(const pa_bitset_t *b, unsigned k);
bool pa_bitset_equals(const pa_bitset_t *b, unsigned n, ...);
#endif

830
src/channelmap.c Normal file
View file

@ -0,0 +1,830 @@
/***
This file is part of PulseAudio.
Copyright 2005-2006 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
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.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pulse/xmalloc.h>
#include <pulse/channelmap.h>
#include "internal.h"
#include "bitset.h"
#include "sample-util.h"
const char *const table[PA_CHANNEL_POSITION_MAX] = {
[PA_CHANNEL_POSITION_MONO] = "mono",
[PA_CHANNEL_POSITION_FRONT_CENTER] = "front-center",
[PA_CHANNEL_POSITION_FRONT_LEFT] = "front-left",
[PA_CHANNEL_POSITION_FRONT_RIGHT] = "front-right",
[PA_CHANNEL_POSITION_REAR_CENTER] = "rear-center",
[PA_CHANNEL_POSITION_REAR_LEFT] = "rear-left",
[PA_CHANNEL_POSITION_REAR_RIGHT] = "rear-right",
[PA_CHANNEL_POSITION_LFE] = "lfe",
[PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = "front-left-of-center",
[PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = "front-right-of-center",
[PA_CHANNEL_POSITION_SIDE_LEFT] = "side-left",
[PA_CHANNEL_POSITION_SIDE_RIGHT] = "side-right",
[PA_CHANNEL_POSITION_AUX0] = "aux0",
[PA_CHANNEL_POSITION_AUX1] = "aux1",
[PA_CHANNEL_POSITION_AUX2] = "aux2",
[PA_CHANNEL_POSITION_AUX3] = "aux3",
[PA_CHANNEL_POSITION_AUX4] = "aux4",
[PA_CHANNEL_POSITION_AUX5] = "aux5",
[PA_CHANNEL_POSITION_AUX6] = "aux6",
[PA_CHANNEL_POSITION_AUX7] = "aux7",
[PA_CHANNEL_POSITION_AUX8] = "aux8",
[PA_CHANNEL_POSITION_AUX9] = "aux9",
[PA_CHANNEL_POSITION_AUX10] = "aux10",
[PA_CHANNEL_POSITION_AUX11] = "aux11",
[PA_CHANNEL_POSITION_AUX12] = "aux12",
[PA_CHANNEL_POSITION_AUX13] = "aux13",
[PA_CHANNEL_POSITION_AUX14] = "aux14",
[PA_CHANNEL_POSITION_AUX15] = "aux15",
[PA_CHANNEL_POSITION_AUX16] = "aux16",
[PA_CHANNEL_POSITION_AUX17] = "aux17",
[PA_CHANNEL_POSITION_AUX18] = "aux18",
[PA_CHANNEL_POSITION_AUX19] = "aux19",
[PA_CHANNEL_POSITION_AUX20] = "aux20",
[PA_CHANNEL_POSITION_AUX21] = "aux21",
[PA_CHANNEL_POSITION_AUX22] = "aux22",
[PA_CHANNEL_POSITION_AUX23] = "aux23",
[PA_CHANNEL_POSITION_AUX24] = "aux24",
[PA_CHANNEL_POSITION_AUX25] = "aux25",
[PA_CHANNEL_POSITION_AUX26] = "aux26",
[PA_CHANNEL_POSITION_AUX27] = "aux27",
[PA_CHANNEL_POSITION_AUX28] = "aux28",
[PA_CHANNEL_POSITION_AUX29] = "aux29",
[PA_CHANNEL_POSITION_AUX30] = "aux30",
[PA_CHANNEL_POSITION_AUX31] = "aux31",
[PA_CHANNEL_POSITION_TOP_CENTER] = "top-center",
[PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = "top-front-center",
[PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = "top-front-left",
[PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = "top-front-right",
[PA_CHANNEL_POSITION_TOP_REAR_CENTER] = "top-rear-center",
[PA_CHANNEL_POSITION_TOP_REAR_LEFT] = "top-rear-left",
[PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = "top-rear-right"
};
const char *const pretty_table[PA_CHANNEL_POSITION_MAX] = {
[PA_CHANNEL_POSITION_MONO] = N_("Mono"),
[PA_CHANNEL_POSITION_FRONT_CENTER] = N_("Front Center"),
[PA_CHANNEL_POSITION_FRONT_LEFT] = N_("Front Left"),
[PA_CHANNEL_POSITION_FRONT_RIGHT] = N_("Front Right"),
[PA_CHANNEL_POSITION_REAR_CENTER] = N_("Rear Center"),
[PA_CHANNEL_POSITION_REAR_LEFT] = N_("Rear Left"),
[PA_CHANNEL_POSITION_REAR_RIGHT] = N_("Rear Right"),
[PA_CHANNEL_POSITION_LFE] = N_("Subwoofer"),
[PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = N_("Front Left-of-center"),
[PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = N_("Front Right-of-center"),
[PA_CHANNEL_POSITION_SIDE_LEFT] = N_("Side Left"),
[PA_CHANNEL_POSITION_SIDE_RIGHT] = N_("Side Right"),
[PA_CHANNEL_POSITION_AUX0] = N_("Auxiliary 0"),
[PA_CHANNEL_POSITION_AUX1] = N_("Auxiliary 1"),
[PA_CHANNEL_POSITION_AUX2] = N_("Auxiliary 2"),
[PA_CHANNEL_POSITION_AUX3] = N_("Auxiliary 3"),
[PA_CHANNEL_POSITION_AUX4] = N_("Auxiliary 4"),
[PA_CHANNEL_POSITION_AUX5] = N_("Auxiliary 5"),
[PA_CHANNEL_POSITION_AUX6] = N_("Auxiliary 6"),
[PA_CHANNEL_POSITION_AUX7] = N_("Auxiliary 7"),
[PA_CHANNEL_POSITION_AUX8] = N_("Auxiliary 8"),
[PA_CHANNEL_POSITION_AUX9] = N_("Auxiliary 9"),
[PA_CHANNEL_POSITION_AUX10] = N_("Auxiliary 10"),
[PA_CHANNEL_POSITION_AUX11] = N_("Auxiliary 11"),
[PA_CHANNEL_POSITION_AUX12] = N_("Auxiliary 12"),
[PA_CHANNEL_POSITION_AUX13] = N_("Auxiliary 13"),
[PA_CHANNEL_POSITION_AUX14] = N_("Auxiliary 14"),
[PA_CHANNEL_POSITION_AUX15] = N_("Auxiliary 15"),
[PA_CHANNEL_POSITION_AUX16] = N_("Auxiliary 16"),
[PA_CHANNEL_POSITION_AUX17] = N_("Auxiliary 17"),
[PA_CHANNEL_POSITION_AUX18] = N_("Auxiliary 18"),
[PA_CHANNEL_POSITION_AUX19] = N_("Auxiliary 19"),
[PA_CHANNEL_POSITION_AUX20] = N_("Auxiliary 20"),
[PA_CHANNEL_POSITION_AUX21] = N_("Auxiliary 21"),
[PA_CHANNEL_POSITION_AUX22] = N_("Auxiliary 22"),
[PA_CHANNEL_POSITION_AUX23] = N_("Auxiliary 23"),
[PA_CHANNEL_POSITION_AUX24] = N_("Auxiliary 24"),
[PA_CHANNEL_POSITION_AUX25] = N_("Auxiliary 25"),
[PA_CHANNEL_POSITION_AUX26] = N_("Auxiliary 26"),
[PA_CHANNEL_POSITION_AUX27] = N_("Auxiliary 27"),
[PA_CHANNEL_POSITION_AUX28] = N_("Auxiliary 28"),
[PA_CHANNEL_POSITION_AUX29] = N_("Auxiliary 29"),
[PA_CHANNEL_POSITION_AUX30] = N_("Auxiliary 30"),
[PA_CHANNEL_POSITION_AUX31] = N_("Auxiliary 31"),
[PA_CHANNEL_POSITION_TOP_CENTER] = N_("Top Center"),
[PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = N_("Top Front Center"),
[PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = N_("Top Front Left"),
[PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = N_("Top Front Right"),
[PA_CHANNEL_POSITION_TOP_REAR_CENTER] = N_("Top Rear Center"),
[PA_CHANNEL_POSITION_TOP_REAR_LEFT] = N_("Top Rear Left"),
[PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = N_("Top Rear Right")
};
pa_channel_map* pa_channel_map_init(pa_channel_map *m) {
unsigned c;
pa_assert(m);
m->channels = 0;
for (c = 0; c < PA_CHANNELS_MAX; c++)
m->map[c] = PA_CHANNEL_POSITION_INVALID;
return m;
}
pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m) {
pa_assert(m);
pa_channel_map_init(m);
m->channels = 1;
m->map[0] = PA_CHANNEL_POSITION_MONO;
return m;
}
pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m) {
pa_assert(m);
pa_channel_map_init(m);
m->channels = 2;
m->map[0] = PA_CHANNEL_POSITION_LEFT;
m->map[1] = PA_CHANNEL_POSITION_RIGHT;
return m;
}
pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def) {
pa_assert(m);
pa_assert(pa_channels_valid(channels));
pa_assert(def < PA_CHANNEL_MAP_DEF_MAX);
pa_channel_map_init(m);
m->channels = (uint8_t) channels;
switch (def) {
case PA_CHANNEL_MAP_AIFF:
/* This is somewhat compatible with RFC3551 */
switch (channels) {
case 1:
m->map[0] = PA_CHANNEL_POSITION_MONO;
return m;
case 6:
m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
m->map[1] = PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
m->map[3] = PA_CHANNEL_POSITION_FRONT_RIGHT;
m->map[4] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
m->map[5] = PA_CHANNEL_POSITION_REAR_CENTER;
return m;
case 5:
m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
m->map[3] = PA_CHANNEL_POSITION_REAR_LEFT;
m->map[4] = PA_CHANNEL_POSITION_REAR_RIGHT;
/* Fall through */
case 2:
m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
return m;
case 3:
m->map[0] = PA_CHANNEL_POSITION_LEFT;
m->map[1] = PA_CHANNEL_POSITION_RIGHT;
m->map[2] = PA_CHANNEL_POSITION_CENTER;
return m;
case 4:
m->map[0] = PA_CHANNEL_POSITION_LEFT;
m->map[1] = PA_CHANNEL_POSITION_CENTER;
m->map[2] = PA_CHANNEL_POSITION_RIGHT;
m->map[3] = PA_CHANNEL_POSITION_REAR_CENTER;
return m;
default:
return NULL;
}
case PA_CHANNEL_MAP_ALSA:
switch (channels) {
case 1:
m->map[0] = PA_CHANNEL_POSITION_MONO;
return m;
case 8:
m->map[6] = PA_CHANNEL_POSITION_SIDE_LEFT;
m->map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT;
/* Fall through */
case 6:
m->map[5] = PA_CHANNEL_POSITION_LFE;
/* Fall through */
case 5:
m->map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
/* Fall through */
case 4:
m->map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
m->map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
/* Fall through */
case 2:
m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
return m;
default:
return NULL;
}
case PA_CHANNEL_MAP_AUX: {
unsigned i;
for (i = 0; i < channels; i++)
m->map[i] = PA_CHANNEL_POSITION_AUX0 + i;
return m;
}
case PA_CHANNEL_MAP_WAVEEX:
/* Following http://www.microsoft.com/whdc/device/audio/multichaud.mspx#EKLAC */
switch (channels) {
case 1:
m->map[0] = PA_CHANNEL_POSITION_MONO;
return m;
case 18:
m->map[15] = PA_CHANNEL_POSITION_TOP_REAR_LEFT;
m->map[16] = PA_CHANNEL_POSITION_TOP_REAR_CENTER;
m->map[17] = PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
/* Fall through */
case 15:
m->map[12] = PA_CHANNEL_POSITION_TOP_FRONT_LEFT;
m->map[13] = PA_CHANNEL_POSITION_TOP_FRONT_CENTER;
m->map[14] = PA_CHANNEL_POSITION_TOP_FRONT_RIGHT;
/* Fall through */
case 12:
m->map[11] = PA_CHANNEL_POSITION_TOP_CENTER;
/* Fall through */
case 11:
m->map[9] = PA_CHANNEL_POSITION_SIDE_LEFT;
m->map[10] = PA_CHANNEL_POSITION_SIDE_RIGHT;
/* Fall through */
case 9:
m->map[8] = PA_CHANNEL_POSITION_REAR_CENTER;
/* Fall through */
case 8:
m->map[6] = PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
m->map[7] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
/* Fall through */
case 6:
m->map[4] = PA_CHANNEL_POSITION_REAR_LEFT;
m->map[5] = PA_CHANNEL_POSITION_REAR_RIGHT;
/* Fall through */
case 4:
m->map[3] = PA_CHANNEL_POSITION_LFE;
/* Fall through */
case 3:
m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
/* Fall through */
case 2:
m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
return m;
default:
return NULL;
}
case PA_CHANNEL_MAP_OSS:
switch (channels) {
case 1:
m->map[0] = PA_CHANNEL_POSITION_MONO;
return m;
case 8:
m->map[6] = PA_CHANNEL_POSITION_REAR_LEFT;
m->map[7] = PA_CHANNEL_POSITION_REAR_RIGHT;
/* Fall through */
case 6:
m->map[4] = PA_CHANNEL_POSITION_SIDE_LEFT;
m->map[5] = PA_CHANNEL_POSITION_SIDE_RIGHT;
/* Fall through */
case 4:
m->map[3] = PA_CHANNEL_POSITION_LFE;
/* Fall through */
case 3:
m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
/* Fall through */
case 2:
m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
return m;
default:
return NULL;
}
default:
pa_assert_not_reached();
}
}
pa_channel_map* pa_channel_map_init_extend(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def) {
unsigned c;
pa_assert(m);
pa_assert(pa_channels_valid(channels));
pa_assert(def < PA_CHANNEL_MAP_DEF_MAX);
pa_channel_map_init(m);
for (c = channels; c > 0; c--) {
if (pa_channel_map_init_auto(m, c, def)) {
unsigned i = 0;
for (; c < channels; c++) {
m->map[c] = PA_CHANNEL_POSITION_AUX0 + i;
i++;
}
m->channels = (uint8_t) channels;
return m;
}
}
return NULL;
}
const char* pa_channel_position_to_string(pa_channel_position_t pos) {
if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX)
return NULL;
return table[pos];
}
const char* pa_channel_position_to_pretty_string(pa_channel_position_t pos) {
if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX)
return NULL;
pa_init_i18n();
return _(pretty_table[pos]);
}
int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) {
unsigned c;
pa_assert(a);
pa_assert(b);
pa_return_val_if_fail(pa_channel_map_valid(a), 0);
if (PA_UNLIKELY(a == b))
return 1;
pa_return_val_if_fail(pa_channel_map_valid(b), 0);
if (a->channels != b->channels)
return 0;
for (c = 0; c < a->channels; c++)
if (a->map[c] != b->map[c])
return 0;
return 1;
}
char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map) {
unsigned channel;
bool first = true;
char *e;
pa_assert(s);
pa_assert(l > 0);
pa_assert(map);
pa_init_i18n();
if (!pa_channel_map_valid(map)) {
pa_snprintf(s, l, _("(invalid)"));
return s;
}
*(e = s) = 0;
for (channel = 0; channel < map->channels && l > 1; channel++) {
l -= pa_snprintf(e, l, "%s%s",
first ? "" : ",",
pa_channel_position_to_string(map->map[channel]));
e = strchr(e, 0);
first = false;
}
return s;
}
pa_channel_position_t pa_channel_position_from_string(const char *p) {
pa_channel_position_t i;
pa_assert(p);
/* Some special aliases */
if (pa_streq(p, "left"))
return PA_CHANNEL_POSITION_LEFT;
else if (pa_streq(p, "right"))
return PA_CHANNEL_POSITION_RIGHT;
else if (pa_streq(p, "center"))
return PA_CHANNEL_POSITION_CENTER;
else if (pa_streq(p, "subwoofer"))
return PA_CHANNEL_POSITION_SUBWOOFER;
for (i = 0; i < PA_CHANNEL_POSITION_MAX; i++)
if (pa_streq(p, table[i]))
return i;
return PA_CHANNEL_POSITION_INVALID;
}
pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) {
pa_channel_map map;
char **tokens;
int i, n_tokens;
pa_assert(rmap);
pa_assert(s);
pa_channel_map_init(&map);
/* We don't need to match against the well known channel mapping
* "mono" here explicitly, because that can be understood as
* listing with one channel called "mono". */
if (pa_streq(s, "stereo")) {
map.channels = 2;
map.map[0] = PA_CHANNEL_POSITION_LEFT;
map.map[1] = PA_CHANNEL_POSITION_RIGHT;
goto finish;
} else if (pa_streq(s, "surround-21")) {
map.channels = 3;
map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
map.map[2] = PA_CHANNEL_POSITION_LFE;
goto finish;
} else if (pa_streq(s, "surround-40")) {
map.channels = 4;
map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
goto finish;
} else if (pa_streq(s, "surround-41")) {
map.channels = 5;
map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
map.map[4] = PA_CHANNEL_POSITION_LFE;
goto finish;
} else if (pa_streq(s, "surround-50")) {
map.channels = 5;
map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
goto finish;
} else if (pa_streq(s, "surround-51")) {
map.channels = 6;
map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
map.map[5] = PA_CHANNEL_POSITION_LFE;
goto finish;
} else if (pa_streq(s, "surround-71")) {
map.channels = 8;
map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
map.map[5] = PA_CHANNEL_POSITION_LFE;
map.map[6] = PA_CHANNEL_POSITION_SIDE_LEFT;
map.map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT;
goto finish;
}
map.channels = 0;
tokens = pw_split_strv(s, ",", -1, &n_tokens);
for (i = 0; i < n_tokens; i++) {
pa_channel_position_t f;
if (map.channels >= PA_CHANNELS_MAX) {
pw_free_strv(tokens);
return NULL;
}
if ((f = pa_channel_position_from_string(tokens[i])) == PA_CHANNEL_POSITION_INVALID) {
pw_free_strv(tokens);
return NULL;
}
map.map[map.channels++] = f;
}
pw_free_strv(tokens);
finish:
if (!pa_channel_map_valid(&map))
return NULL;
*rmap = map;
return rmap;
}
int pa_channel_map_valid(const pa_channel_map *map) {
unsigned c;
pa_assert(map);
if (!pa_channels_valid(map->channels))
return 0;
for (c = 0; c < map->channels; c++)
if (map->map[c] < 0 || map->map[c] >= PA_CHANNEL_POSITION_MAX)
return 0;
return 1;
}
int pa_channel_map_compatible(const pa_channel_map *map, const pa_sample_spec *ss) {
pa_assert(map);
pa_assert(ss);
pa_return_val_if_fail(pa_channel_map_valid(map), 0);
pa_return_val_if_fail(pa_sample_spec_valid(ss), 0);
return map->channels == ss->channels;
}
int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) {
pa_channel_position_mask_t am, bm;
pa_assert(a);
pa_assert(b);
pa_return_val_if_fail(pa_channel_map_valid(a), 0);
if (PA_UNLIKELY(a == b))
return 1;
pa_return_val_if_fail(pa_channel_map_valid(b), 0);
am = pa_channel_map_mask(a);
bm = pa_channel_map_mask(b);
return (bm & am) == bm;
}
int pa_channel_map_can_balance(const pa_channel_map *map) {
pa_channel_position_mask_t m;
pa_assert(map);
pa_return_val_if_fail(pa_channel_map_valid(map), 0);
m = pa_channel_map_mask(map);
return
(PA_CHANNEL_POSITION_MASK_LEFT & m) &&
(PA_CHANNEL_POSITION_MASK_RIGHT & m);
}
int pa_channel_map_can_fade(const pa_channel_map *map) {
pa_channel_position_mask_t m;
pa_assert(map);
pa_return_val_if_fail(pa_channel_map_valid(map), 0);
m = pa_channel_map_mask(map);
return
(PA_CHANNEL_POSITION_MASK_FRONT & m) &&
(PA_CHANNEL_POSITION_MASK_REAR & m);
}
int pa_channel_map_can_lfe_balance(const pa_channel_map *map) {
pa_channel_position_mask_t m;
pa_assert(map);
pa_return_val_if_fail(pa_channel_map_valid(map), 0);
m = pa_channel_map_mask(map);
return
(PA_CHANNEL_POSITION_MASK_LFE & m) &&
(PA_CHANNEL_POSITION_MASK_HFE & m);
}
const char* pa_channel_map_to_name(const pa_channel_map *map) {
pa_bitset_t in_map[PA_BITSET_ELEMENTS(PA_CHANNEL_POSITION_MAX)];
unsigned c;
pa_assert(map);
pa_return_val_if_fail(pa_channel_map_valid(map), NULL);
memset(in_map, 0, sizeof(in_map));
for (c = 0; c < map->channels; c++)
pa_bitset_set(in_map, map->map[c], true);
if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
PA_CHANNEL_POSITION_MONO, -1))
return "mono";
if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, -1))
return "stereo";
if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, -1))
return "surround-40";
if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
PA_CHANNEL_POSITION_LFE, -1))
return "surround-41";
if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
PA_CHANNEL_POSITION_FRONT_CENTER, -1))
return "surround-50";
if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE, -1))
return "surround-51";
if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT, -1))
return "surround-71";
return NULL;
}
const char* pa_channel_map_to_pretty_name(const pa_channel_map *map) {
pa_bitset_t in_map[PA_BITSET_ELEMENTS(PA_CHANNEL_POSITION_MAX)];
unsigned c;
pa_assert(map);
pa_return_val_if_fail(pa_channel_map_valid(map), NULL);
memset(in_map, 0, sizeof(in_map));
for (c = 0; c < map->channels; c++)
pa_bitset_set(in_map, map->map[c], true);
pa_init_i18n();
if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
PA_CHANNEL_POSITION_MONO, -1))
return _("Mono");
if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, -1))
return _("Stereo");
if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, -1))
return _("Surround 4.0");
if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
PA_CHANNEL_POSITION_LFE, -1))
return _("Surround 4.1");
if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
PA_CHANNEL_POSITION_FRONT_CENTER, -1))
return _("Surround 5.0");
if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE, -1))
return _("Surround 5.1");
if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT, -1))
return _("Surround 7.1");
return NULL;
}
int pa_channel_map_has_position(const pa_channel_map *map, pa_channel_position_t p) {
unsigned c;
pa_return_val_if_fail(pa_channel_map_valid(map), 0);
pa_return_val_if_fail(p < PA_CHANNEL_POSITION_MAX, 0);
for (c = 0; c < map->channels; c++)
if (map->map[c] == p)
return 1;
return 0;
}
pa_channel_position_mask_t pa_channel_map_mask(const pa_channel_map *map) {
unsigned c;
pa_channel_position_mask_t r = 0;
pa_return_val_if_fail(pa_channel_map_valid(map), 0);
for (c = 0; c < map->channels; c++)
r |= PA_CHANNEL_POSITION_MASK(map->map[c]);
return r;
}

453
src/context.c Normal file
View file

@ -0,0 +1,453 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <errno.h>
#include <pipewire/log.h>
#include <pipewire/properties.h>
#include <pipewire/main-loop.h>
#include <pipewire/core.h>
#include <pipewire/remote.h>
#include <pulse/context.h>
#include "internal.h"
int pa_context_set_error(pa_context *c, int error) {
spa_assert(error >= 0);
spa_assert(error < PA_ERR_MAX);
if (c)
c->error = error;
return error;
}
static void context_unlink(pa_context *c)
{
pa_stream *s, *t;
spa_list_for_each_safe(s, t, &c->streams, link) {
pa_stream_set_state(s, c->state == PA_CONTEXT_FAILED ?
PA_STREAM_FAILED : PA_STREAM_TERMINATED);
pa_stream_unref(s);
}
while(!spa_list_is_empty(&c->operations))
pa_operation_cancel(spa_list_first(&c->operations, pa_operation, link));
}
void pa_context_set_state(pa_context *c, pa_context_state_t st) {
spa_assert(c);
spa_assert(c->refcount >= 1);
if (c->state == st)
return;
pa_context_ref(c);
c->state = st;
if (c->state_callback)
c->state_callback(c, c->state_userdata);
if (st == PA_CONTEXT_FAILED || st == PA_CONTEXT_TERMINATED)
context_unlink(c);
pa_context_unref(c);
}
static void context_fail(pa_context *c, int error) {
spa_assert(c);
spa_assert(c->refcount >= 1);
pa_context_set_error(c, error);
pa_context_set_state(c, PA_CONTEXT_FAILED);
}
pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name)
{
return pa_context_new_with_proplist(mainloop, name, NULL);
}
static void registry_event_global(void *data, uint32_t id, uint32_t parent_id,
uint32_t permissions, uint32_t type, uint32_t version,
const struct spa_dict *props)
{
pa_context *c = data;
struct global *g;
pw_log_debug("global %d", id);
g = calloc(1, sizeof(struct global));
g->id = id;
g->parent_id = parent_id;
g->type = type;
g->props = props ? pw_properties_new_dict(props) : NULL;
spa_list_append(&c->globals, &g->link);
}
struct global *pa_context_find_global(pa_context *c, uint32_t id)
{
struct global *g;
spa_list_for_each(g, &c->globals, link) {
if (g->id == id)
return g;
}
return NULL;
}
static void registry_event_global_remove(void *object, uint32_t id)
{
pa_context *c = object;
struct global *g;
pw_log_debug("remove %d", id);
if ((g = pa_context_find_global(c, id)) == NULL)
return;
spa_list_remove(&g->link);
pw_properties_free(g->props);
if (g->destroy && g->info)
g->destroy(g->info);
free(g);
}
static const struct pw_registry_proxy_events registry_events =
{
PW_VERSION_REGISTRY_PROXY_EVENTS,
.global = registry_event_global,
.global_remove = registry_event_global_remove,
};
struct ready_data
{
pa_context *context;
};
static void on_ready(pa_operation *o, void *userdata)
{
struct ready_data *d = userdata;
pa_context_set_state(d->context, PA_CONTEXT_READY);
}
static void remote_state_changed(void *data, enum pw_remote_state old,
enum pw_remote_state state, const char *error)
{
pa_context *c = data;
pa_operation *o;
struct ready_data *d;
switch(state) {
case PW_REMOTE_STATE_ERROR:
context_fail(c, PA_ERR_CONNECTIONTERMINATED);
break;
case PW_REMOTE_STATE_UNCONNECTED:
break;
case PW_REMOTE_STATE_CONNECTING:
pa_context_set_state(c, PA_CONTEXT_CONNECTING);
break;
case PW_REMOTE_STATE_CONNECTED:
pa_context_set_state(c, PA_CONTEXT_AUTHORIZING);
pa_context_set_state(c, PA_CONTEXT_SETTING_NAME);
c->core_proxy = pw_remote_get_core_proxy(c->remote);
c->registry_proxy = pw_core_proxy_get_registry(c->core_proxy,
c->t->registry,
PW_VERSION_REGISTRY, 0);
pw_registry_proxy_add_listener(c->registry_proxy,
&c->registry_listener,
&registry_events, c);
o = pa_operation_new(c, NULL, on_ready, sizeof(struct ready_data));
d = o->userdata;
d->context = c;
break;
}
}
static void remote_sync_reply(void *data, uint32_t seq)
{
pa_context *c = data;
pa_operation *o;
pw_log_debug("done %d", seq);
spa_list_for_each(o, &c->operations, link) {
if (o->seq != seq)
continue;
pa_operation_ref(o);
if (o->callback)
o->callback(o, o->userdata);
pa_operation_unref(o);
break;
}
}
static const struct pw_remote_events remote_events = {
PW_VERSION_REMOTE_EVENTS,
.state_changed = remote_state_changed,
.sync_reply = remote_sync_reply
};
pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *name, pa_proplist *p)
{
struct pw_core *core;
struct pw_loop *loop;
struct pw_remote *r;
struct pw_properties *props;
pa_context *c;
spa_assert(mainloop);
props = pw_properties_new(NULL, NULL);
if (name)
pw_properties_set(props, PA_PROP_APPLICATION_NAME, name);
loop = mainloop->userdata;
core = pw_core_new(loop, NULL);
r = pw_remote_new(core, props, sizeof(struct pa_context));
if (r == NULL)
return NULL;
c = pw_remote_get_user_data(r);
c->loop = loop;
c->core = core;
c->t = pw_core_get_type(core);
c->remote = r;
pw_remote_add_listener(r, &c->remote_listener, &remote_events, c);
c->proplist = p ? pa_proplist_copy(p) : pa_proplist_new();
c->refcount = 1;
c->client_index = PA_INVALID_INDEX;
if (name)
pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name);
c->mainloop = mainloop;
c->error = 0;
c->state = PA_CONTEXT_UNCONNECTED;
spa_list_init(&c->globals);
spa_list_init(&c->streams);
spa_list_init(&c->operations);
return c;
}
static void context_free(pa_context *c)
{
context_unlink(c);
if (c->proplist)
pa_proplist_free(c->proplist);
free(c);
}
void pa_context_unref(pa_context *c)
{
spa_assert(c);
spa_assert(c->refcount >= 1);
if (--c->refcount == 0)
context_free(c);
}
pa_context* pa_context_ref(pa_context *c)
{
spa_assert(c);
spa_assert(c->refcount >= 1);
c->refcount++;
return c;
}
void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, void *userdata)
{
spa_assert(c);
spa_assert(c->refcount >= 1);
if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED)
return;
c->state_callback = cb;
c->state_userdata = userdata;
}
void pa_context_set_event_callback(pa_context *c, pa_context_event_cb_t cb, void *userdata)
{
spa_assert(c);
spa_assert(c->refcount >= 1);
if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED)
return;
c->event_callback = cb;
c->event_userdata = userdata;
}
int pa_context_errno(pa_context *c)
{
if (!c)
return PA_ERR_INVALID;
spa_assert(c->refcount >= 1);
return c->error;
}
int pa_context_is_pending(pa_context *c)
{
pw_log_warn("Not Implemented");
return 0;
}
pa_context_state_t pa_context_get_state(pa_context *c)
{
spa_assert(c);
spa_assert(c->refcount >= 1);
return c->state;
}
int pa_context_connect(pa_context *c, const char *server, pa_context_flags_t flags, const pa_spawn_api *api)
{
int res;
spa_assert(c);
spa_assert(c->refcount >= 1);
PA_CHECK_VALIDITY(c, c->state == PA_CONTEXT_UNCONNECTED, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(c, !(flags & ~(PA_CONTEXT_NOAUTOSPAWN|PA_CONTEXT_NOFAIL)), PA_ERR_INVALID);
PA_CHECK_VALIDITY(c, !server || *server, PA_ERR_INVALID);
pa_context_ref(c);
c->no_fail = !!(flags & PA_CONTEXT_NOFAIL);
res = pw_remote_connect(c->remote);
pa_context_unref(c);
return res;
}
void pa_context_disconnect(pa_context *c)
{
spa_assert(c);
spa_assert(c->refcount >= 1);
pw_remote_disconnect(c->remote);
if (PA_CONTEXT_IS_GOOD(c->state))
pa_context_set_state(c, PA_CONTEXT_TERMINATED);
}
pa_operation* pa_context_drain(pa_context *c, pa_context_notify_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
int pa_context_is_local(pa_context *c)
{
pw_log_warn("Not Implemented");
return 0;
}
pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
const char* pa_context_get_server(pa_context *c)
{
pw_log_warn("Not Implemented");
return NULL;
}
uint32_t pa_context_get_protocol_version(pa_context *c)
{
return PA_PROTOCOL_VERSION;
}
uint32_t pa_context_get_server_protocol_version(pa_context *c)
{
return PA_PROTOCOL_VERSION;
}
pa_operation *pa_context_proplist_update(pa_context *c, pa_update_mode_t mode, pa_proplist *p, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation *pa_context_proplist_remove(pa_context *c, const char *const keys[], pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
uint32_t pa_context_get_index(pa_context *c)
{
return c->client_index;
}
pa_time_event* pa_context_rttime_new(pa_context *c, pa_usec_t usec, pa_time_event_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
void pa_context_rttime_restart(pa_context *c, pa_time_event *e, pa_usec_t usec)
{
pw_log_warn("Not Implemented");
}
size_t pa_context_get_tile_size(pa_context *c, const pa_sample_spec *ss)
{
pw_log_warn("Not Implemented");
return 1024;
}
int pa_context_load_cookie_from_file(pa_context *c, const char *cookie_file_path)
{
pw_log_warn("Not Implemented");
return -ENOTSUP;
}

241
src/core-format.c Normal file
View file

@ -0,0 +1,241 @@
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
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.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "core-format.h"
#include <pulse/def.h>
#include <pulse/xmalloc.h>
#include "internal.h"
int pa_format_info_get_sample_format(const pa_format_info *f, pa_sample_format_t *sf) {
int r;
char *sf_str;
pa_sample_format_t sf_local;
pa_assert(f);
pa_assert(sf);
r = pa_format_info_get_prop_string(f, PA_PROP_FORMAT_SAMPLE_FORMAT, &sf_str);
if (r < 0)
return r;
sf_local = pa_parse_sample_format(sf_str);
pa_xfree(sf_str);
if (!pa_sample_format_valid(sf_local)) {
pa_log_debug("Invalid sample format.");
return -PA_ERR_INVALID;
}
*sf = sf_local;
return 0;
}
int pa_format_info_get_rate(const pa_format_info *f, uint32_t *rate) {
int r;
int rate_local;
pa_assert(f);
pa_assert(rate);
r = pa_format_info_get_prop_int(f, PA_PROP_FORMAT_RATE, &rate_local);
if (r < 0)
return r;
if (!pa_sample_rate_valid(rate_local)) {
pa_log_debug("Invalid sample rate: %i", rate_local);
return -PA_ERR_INVALID;
}
*rate = rate_local;
return 0;
}
int pa_format_info_get_channels(const pa_format_info *f, uint8_t *channels) {
int r;
int channels_local;
pa_assert(f);
pa_assert(channels);
r = pa_format_info_get_prop_int(f, PA_PROP_FORMAT_CHANNELS, &channels_local);
if (r < 0)
return r;
if (!pa_channels_valid(channels_local)) {
pa_log_debug("Invalid channel count: %i", channels_local);
return -PA_ERR_INVALID;
}
*channels = channels_local;
return 0;
}
int pa_format_info_get_channel_map(const pa_format_info *f, pa_channel_map *map) {
int r;
char *map_str;
pa_assert(f);
pa_assert(map);
r = pa_format_info_get_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, &map_str);
if (r < 0)
return r;
map = pa_channel_map_parse(map, map_str);
pa_xfree(map_str);
if (!map) {
pa_log_debug("Failed to parse channel map.");
return -PA_ERR_INVALID;
}
return 0;
}
pa_format_info *pa_format_info_from_sample_spec2(const pa_sample_spec *ss, const pa_channel_map *map, bool set_format,
bool set_rate, bool set_channels) {
pa_format_info *format = NULL;
pa_assert(ss);
format = pa_format_info_new();
format->encoding = PA_ENCODING_PCM;
if (set_format)
pa_format_info_set_sample_format(format, ss->format);
if (set_rate)
pa_format_info_set_rate(format, ss->rate);
if (set_channels) {
pa_format_info_set_channels(format, ss->channels);
if (map) {
if (map->channels != ss->channels) {
pa_log_debug("Channel map is incompatible with the sample spec.");
goto fail;
}
pa_format_info_set_channel_map(format, map);
}
}
return format;
fail:
if (format)
pa_format_info_free(format);
return NULL;
}
int pa_format_info_to_sample_spec2(const pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map,
const pa_sample_spec *fallback_ss, const pa_channel_map *fallback_map) {
int r, r2;
pa_sample_spec ss_local;
pa_channel_map map_local;
pa_assert(f);
pa_assert(ss);
pa_assert(map);
pa_assert(fallback_ss);
pa_assert(fallback_map);
if (!pa_format_info_is_pcm(f))
return pa_format_info_to_sample_spec_fake(f, ss, map);
r = pa_format_info_get_sample_format(f, &ss_local.format);
if (r == -PA_ERR_NOENTITY)
ss_local.format = fallback_ss->format;
else if (r < 0)
return r;
pa_assert(pa_sample_format_valid(ss_local.format));
r = pa_format_info_get_rate(f, &ss_local.rate);
if (r == -PA_ERR_NOENTITY)
ss_local.rate = fallback_ss->rate;
else if (r < 0)
return r;
pa_assert(pa_sample_rate_valid(ss_local.rate));
r = pa_format_info_get_channels(f, &ss_local.channels);
r2 = pa_format_info_get_channel_map(f, &map_local);
if (r == -PA_ERR_NOENTITY && r2 >= 0)
ss_local.channels = map_local.channels;
else if (r == -PA_ERR_NOENTITY)
ss_local.channels = fallback_ss->channels;
else if (r < 0)
return r;
pa_assert(pa_channels_valid(ss_local.channels));
if (r2 >= 0 && map_local.channels != ss_local.channels) {
pa_log_debug("Channel map is not compatible with the sample spec.");
return -PA_ERR_INVALID;
}
if (r2 == -PA_ERR_NOENTITY) {
if (fallback_map->channels == ss_local.channels)
map_local = *fallback_map;
else
pa_channel_map_init_extend(&map_local, ss_local.channels, PA_CHANNEL_MAP_DEFAULT);
} else if (r2 < 0)
return r2;
pa_assert(pa_channel_map_valid(&map_local));
pa_assert(ss_local.channels == map_local.channels);
*ss = ss_local;
*map = map_local;
return 0;
}
int pa_format_info_to_sample_spec_fake(const pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map) {
int rate;
pa_assert(f);
pa_assert(ss);
/* Note: When we add support for non-IEC61937 encapsulated compressed
* formats, this function should return a non-zero values for these. */
ss->format = PA_SAMPLE_S16LE;
ss->channels = 2;
if (map)
pa_channel_map_init_stereo(map);
pa_return_val_if_fail(pa_format_info_get_prop_int(f, PA_PROP_FORMAT_RATE, &rate) == 0, -PA_ERR_INVALID);
ss->rate = (uint32_t) rate;
if (f->encoding == PA_ENCODING_EAC3_IEC61937)
ss->rate *= 4;
return 0;
}

79
src/core-format.h Normal file
View file

@ -0,0 +1,79 @@
#ifndef foocoreformathfoo
#define foocoreformathfoo
/***
This file is part of PulseAudio.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
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.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#include <pulse/format.h>
#include <stdbool.h>
/* Gets the sample format stored in the format info. Returns a negative error
* code on failure. If the sample format property is not set at all, returns
* -PA_ERR_NOENTITY. */
int pa_format_info_get_sample_format(const pa_format_info *f, pa_sample_format_t *sf);
/* Gets the sample rate stored in the format info. Returns a negative error
* code on failure. If the sample rate property is not set at all, returns
* -PA_ERR_NOENTITY. */
int pa_format_info_get_rate(const pa_format_info *f, uint32_t *rate);
/* Gets the channel count stored in the format info. Returns a negative error
* code on failure. If the channels property is not set at all, returns
* -PA_ERR_NOENTITY. */
int pa_format_info_get_channels(const pa_format_info *f, uint8_t *channels);
/* Gets the channel map stored in the format info. Returns a negative error
* code on failure. If the channel map property is not set at all, returns
* -PA_ERR_NOENTITY. */
int pa_format_info_get_channel_map(const pa_format_info *f, pa_channel_map *map);
/* Convert a sample spec and an optional channel map to a new PCM format info
* object (remember to free it). If map is NULL, then the channel map will be
* left unspecified. If some fields of the sample spec should be ignored, pass
* false for set_format, set_rate and set_channels as appropriate, then those
* fields will be left unspecified. This function returns NULL if the input is
* invalid (for example, setting the sample rate was requested, but the rate
* in ss is invalid).
*
* pa_format_info_from_sample_spec() exists too. This "version 2" was created,
* because the original function doesn't provide the possibility of ignoring
* some of the sample spec fields. That functionality can't be added to the
* original function, because the function is a part of the public API and
* adding parameters to it would break the API. */
pa_format_info *pa_format_info_from_sample_spec2(const pa_sample_spec *ss, const pa_channel_map *map, bool set_format,
bool set_rate, bool set_channels);
/* Convert the format info into a sample spec and a channel map. If the format
* info doesn't contain some information, the fallback sample spec and channel
* map are used to populate the output.
*
* pa_format_info_to_sample_spec() exists too. This "version 2" was created,
* because the original function doesn't provide the possibility of specifying
* a fallback sample spec and channel map. That functionality can't be added to
* the original function, because the function is part of the public API and
* adding parameters to it would break the API. */
int pa_format_info_to_sample_spec2(const pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map,
const pa_sample_spec *fallback_ss, const pa_channel_map *fallback_map);
/* For compressed formats. Converts the format info into a sample spec and a
* channel map that an ALSA device can use as its configuration parameters when
* playing back the compressed data. That is, the returned sample spec doesn't
* describe the audio content, but the device parameters. */
int pa_format_info_to_sample_spec_fake(const pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map);
#endif

47
src/direction.c Normal file
View file

@ -0,0 +1,47 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <errno.h>
#include <pulse/direction.h>
#define pa_init_i18n()
#define _(String) (String)
int pa_direction_valid(pa_direction_t direction)
{
if (direction != PA_DIRECTION_INPUT
&& direction != PA_DIRECTION_OUTPUT
&& direction != (PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT))
return 0;
return 1;
}
const char *pa_direction_to_string(pa_direction_t direction) {
pa_init_i18n();
if (direction == PA_DIRECTION_INPUT)
return _("input");
if (direction == PA_DIRECTION_OUTPUT)
return _("output");
if (direction == (PA_DIRECTION_INPUT | PA_DIRECTION_OUTPUT))
return _("bidirectional");
return _("invalid");
}

74
src/error.c Normal file
View file

@ -0,0 +1,74 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <unistd.h>
#include <errno.h>
#include <spa/utils/defs.h>
#include <pulse/def.h>
#include <pulse/error.h>
#define N_(String) (String)
#define _(String) (String)
#define pa_init_i18n()
const char*pa_strerror(int error)
{
static const char* const errortab[PA_ERR_MAX] = {
[PA_OK] = N_("OK"),
[PA_ERR_ACCESS] = N_("Access denied"),
[PA_ERR_COMMAND] = N_("Unknown command"),
[PA_ERR_INVALID] = N_("Invalid argument"),
[PA_ERR_EXIST] = N_("Entity exists"),
[PA_ERR_NOENTITY] = N_("No such entity"),
[PA_ERR_CONNECTIONREFUSED] = N_("Connection refused"),
[PA_ERR_PROTOCOL] = N_("Protocol error"),
[PA_ERR_TIMEOUT] = N_("Timeout"),
[PA_ERR_AUTHKEY] = N_("No authentication key"),
[PA_ERR_INTERNAL] = N_("Internal error"),
[PA_ERR_CONNECTIONTERMINATED] = N_("Connection terminated"),
[PA_ERR_KILLED] = N_("Entity killed"),
[PA_ERR_INVALIDSERVER] = N_("Invalid server"),
[PA_ERR_MODINITFAILED] = N_("Module initialization failed"),
[PA_ERR_BADSTATE] = N_("Bad state"),
[PA_ERR_NODATA] = N_("No data"),
[PA_ERR_VERSION] = N_("Incompatible protocol version"),
[PA_ERR_TOOLARGE] = N_("Too large"),
[PA_ERR_NOTSUPPORTED] = N_("Not supported"),
[PA_ERR_UNKNOWN] = N_("Unknown error code"),
[PA_ERR_NOEXTENSION] = N_("No such extension"),
[PA_ERR_OBSOLETE] = N_("Obsolete functionality"),
[PA_ERR_NOTIMPLEMENTED] = N_("Missing implementation"),
[PA_ERR_FORKED] = N_("Client forked"),
[PA_ERR_IO] = N_("Input/Output error"),
[PA_ERR_BUSY] = N_("Device or resource busy")
};
pa_init_i18n();
if (error < 0)
error = -error;
if (error >= PA_ERR_MAX)
return NULL;
return _(errortab[error]);
}

105
src/ext-device-manager.c Normal file
View file

@ -0,0 +1,105 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <errno.h>
#include <pipewire/log.h>
#include <pulse/ext-device-manager.h>
#include "internal.h"
pa_operation *pa_ext_device_manager_test(
pa_context *c,
pa_ext_device_manager_test_cb_t cb,
void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation *pa_ext_device_manager_read(
pa_context *c,
pa_ext_device_manager_read_cb_t cb,
void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation *pa_ext_device_manager_set_device_description(
pa_context *c,
const char* device,
const char* description,
pa_context_success_cb_t cb,
void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation *pa_ext_device_manager_delete(
pa_context *c,
const char *const s[],
pa_context_success_cb_t cb,
void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation *pa_ext_device_manager_enable_role_device_priority_routing(
pa_context *c,
int enable,
pa_context_success_cb_t cb,
void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation *pa_ext_device_manager_reorder_devices_for_role(
pa_context *c,
const char* role,
const char** devices,
pa_context_success_cb_t cb,
void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation *pa_ext_device_manager_subscribe(
pa_context *c,
int enable,
pa_context_success_cb_t cb,
void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
void pa_ext_device_manager_set_subscribe_cb(
pa_context *c,
pa_ext_device_manager_subscribe_cb_t cb,
void *userdata)
{
pw_log_warn("Not Implemented");
}

84
src/ext-device-restore.c Normal file
View file

@ -0,0 +1,84 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <pipewire/log.h>
#include <pulse/ext-device-restore.h>
#include "internal.h"
pa_operation *pa_ext_device_restore_test(
pa_context *c,
pa_ext_device_restore_test_cb_t cb,
void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation *pa_ext_device_restore_subscribe(
pa_context *c,
int enable,
pa_context_success_cb_t cb,
void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
void pa_ext_device_restore_set_subscribe_cb(
pa_context *c,
pa_ext_device_restore_subscribe_cb_t cb,
void *userdata)
{
pw_log_warn("Not Implemented");
}
pa_operation *pa_ext_device_restore_read_formats_all(
pa_context *c,
pa_ext_device_restore_read_device_formats_cb_t cb,
void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation *pa_ext_device_restore_read_formats(
pa_context *c,
pa_device_type_t type,
uint32_t idx,
pa_ext_device_restore_read_device_formats_cb_t cb,
void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation *pa_ext_device_restore_save_formats(
pa_context *c,
pa_device_type_t type,
uint32_t idx,
uint8_t n_formats,
pa_format_info **formats,
pa_context_success_cb_t cb,
void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}

679
src/format.c Normal file
View file

@ -0,0 +1,679 @@
/***
This file is part of PulseAudio.
Copyright 2011 Intel Corporation
Copyright 2011 Collabora Multimedia
Copyright 2011 Arun Raghavan <arun.raghavan@collabora.co.uk>
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
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.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <pulse/xmalloc.h>
#include <pulse/format.h>
#include "core-format.h"
#include "json.h"
#include "internal.h"
#include "strbuf.h"
#define PA_JSON_MIN_KEY "min"
#define PA_JSON_MAX_KEY "max"
static int pa_format_info_prop_compatible(const char *one, const char *two);
static const char* const _encoding_str_table[]= {
[PA_ENCODING_PCM] = "pcm",
[PA_ENCODING_AC3_IEC61937] = "ac3-iec61937",
[PA_ENCODING_EAC3_IEC61937] = "eac3-iec61937",
[PA_ENCODING_MPEG_IEC61937] = "mpeg-iec61937",
[PA_ENCODING_DTS_IEC61937] = "dts-iec61937",
[PA_ENCODING_MPEG2_AAC_IEC61937] = "mpeg2-aac-iec61937",
[PA_ENCODING_ANY] = "any",
};
const char *pa_encoding_to_string(pa_encoding_t e) {
if (e < 0 || e >= PA_ENCODING_MAX)
return NULL;
return _encoding_str_table[e];
}
pa_encoding_t pa_encoding_from_string(const char *encoding) {
pa_encoding_t e;
for (e = PA_ENCODING_ANY; e < PA_ENCODING_MAX; e++)
if (pa_streq(_encoding_str_table[e], encoding))
return e;
return PA_ENCODING_INVALID;
}
pa_format_info* pa_format_info_new(void) {
pa_format_info *f = pa_xnew(pa_format_info, 1);
f->encoding = PA_ENCODING_INVALID;
f->plist = pa_proplist_new();
return f;
}
pa_format_info* pa_format_info_copy(const pa_format_info *src) {
pa_format_info *dest;
pa_assert(src);
dest = pa_xnew(pa_format_info, 1);
dest->encoding = src->encoding;
if (src->plist)
dest->plist = pa_proplist_copy(src->plist);
else
dest->plist = NULL;
return dest;
}
void pa_format_info_free(pa_format_info *f) {
pa_assert(f);
pa_proplist_free(f->plist);
pa_xfree(f);
}
int pa_format_info_valid(const pa_format_info *f) {
return (f->encoding >= 0 && f->encoding < PA_ENCODING_MAX && f->plist != NULL);
}
int pa_format_info_is_pcm(const pa_format_info *f) {
return f->encoding == PA_ENCODING_PCM;
}
char *pa_format_info_snprint(char *s, size_t l, const pa_format_info *f) {
char *tmp;
pa_assert(s);
pa_assert(l > 0);
pa_assert(f);
pa_init_i18n();
if (!pa_format_info_valid(f))
pa_snprintf(s, l, _("(invalid)"));
else {
tmp = pa_proplist_to_string_sep(f->plist, " ");
if (tmp[0])
pa_snprintf(s, l, "%s, %s", pa_encoding_to_string(f->encoding), tmp);
else
pa_snprintf(s, l, "%s", pa_encoding_to_string(f->encoding));
pa_xfree(tmp);
}
return s;
}
pa_format_info* pa_format_info_from_string(const char *str) {
pa_format_info *f = pa_format_info_new();
char *encoding = NULL, *properties = NULL;
size_t pos;
pos = strcspn(str, ",");
encoding = pa_xstrndup(str, pos);
f->encoding = pa_encoding_from_string(pa_strip(encoding));
if (f->encoding == PA_ENCODING_INVALID)
goto error;
if (pos != strlen(str)) {
pa_proplist *plist;
properties = pa_xstrdup(&str[pos+1]);
plist = pa_proplist_from_string(properties);
if (!plist)
goto error;
pa_proplist_free(f->plist);
f->plist = plist;
}
out:
if (encoding)
pa_xfree(encoding);
if (properties)
pa_xfree(properties);
return f;
error:
pa_format_info_free(f);
f = NULL;
goto out;
}
int pa_format_info_is_compatible(const pa_format_info *first, const pa_format_info *second) {
const char *key;
void *state = NULL;
pa_assert(first);
pa_assert(second);
if (first->encoding != second->encoding)
return false;
while ((key = pa_proplist_iterate(first->plist, &state))) {
const char *value_one, *value_two;
value_one = pa_proplist_gets(first->plist, key);
value_two = pa_proplist_gets(second->plist, key);
if (!value_two || !pa_format_info_prop_compatible(value_one, value_two))
return false;
}
return true;
}
pa_format_info* pa_format_info_from_sample_spec(const pa_sample_spec *ss, const pa_channel_map *map) {
char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
pa_format_info *f;
pa_assert(ss && pa_sample_spec_valid(ss));
pa_assert(!map || pa_channel_map_valid(map));
f = pa_format_info_new();
f->encoding = PA_ENCODING_PCM;
pa_format_info_set_sample_format(f, ss->format);
pa_format_info_set_rate(f, ss->rate);
pa_format_info_set_channels(f, ss->channels);
if (map) {
pa_channel_map_snprint(cm, sizeof(cm), map);
pa_format_info_set_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, cm);
}
return f;
}
/* For PCM streams */
int pa_format_info_to_sample_spec(const pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map) {
pa_assert(f);
pa_assert(ss);
if (!pa_format_info_is_pcm(f))
return pa_format_info_to_sample_spec_fake(f, ss, map);
if (pa_format_info_get_sample_format(f, &ss->format) < 0)
return -PA_ERR_INVALID;
if (pa_format_info_get_rate(f, &ss->rate) < 0)
return -PA_ERR_INVALID;
if (pa_format_info_get_channels(f, &ss->channels) < 0)
return -PA_ERR_INVALID;
if (map && pa_format_info_get_channel_map(f, map) < 0)
return -PA_ERR_INVALID;
return 0;
}
pa_prop_type_t pa_format_info_get_prop_type(const pa_format_info *f, const char *key) {
const char *str;
pa_json_object *o;
const pa_json_object *o1;
pa_prop_type_t type;
pa_assert(f);
pa_assert(key);
str = pa_proplist_gets(f->plist, key);
if (!str)
return PA_PROP_TYPE_INVALID;
o = pa_json_parse(str);
if (!o)
return PA_PROP_TYPE_INVALID;
switch (pa_json_object_get_type(o)) {
case PA_JSON_TYPE_INT:
type = PA_PROP_TYPE_INT;
break;
case PA_JSON_TYPE_STRING:
type = PA_PROP_TYPE_STRING;
break;
case PA_JSON_TYPE_ARRAY:
if (pa_json_object_get_array_length(o) == 0) {
/* Unlikely, but let's account for this anyway. We need at
* least one element to figure out the array type. */
type = PA_PROP_TYPE_INVALID;
break;
}
o1 = pa_json_object_get_array_member(o, 0);
if (pa_json_object_get_type(o1) == PA_JSON_TYPE_INT)
type = PA_PROP_TYPE_INT_ARRAY;
else if (pa_json_object_get_type(o1) == PA_JSON_TYPE_STRING)
type = PA_PROP_TYPE_STRING_ARRAY;
else
type = PA_PROP_TYPE_INVALID;
break;
case PA_JSON_TYPE_OBJECT:
/* We actually know at this point that it's a int range, but let's
* confirm. */
if (!pa_json_object_get_object_member(o, PA_JSON_MIN_KEY)) {
type = PA_PROP_TYPE_INVALID;
break;
}
if (!pa_json_object_get_object_member(o, PA_JSON_MAX_KEY)) {
type = PA_PROP_TYPE_INVALID;
break;
}
type = PA_PROP_TYPE_INT_RANGE;
break;
default:
type = PA_PROP_TYPE_INVALID;
break;
}
pa_json_object_free(o);
return type;
}
int pa_format_info_get_prop_int(const pa_format_info *f, const char *key, int *v) {
const char *str;
pa_json_object *o;
pa_assert(f);
pa_assert(key);
pa_assert(v);
str = pa_proplist_gets(f->plist, key);
if (!str)
return -PA_ERR_NOENTITY;
o = pa_json_parse(str);
if (!o) {
pa_log_debug("Failed to parse format info property '%s'.", key);
return -PA_ERR_INVALID;
}
if (pa_json_object_get_type(o) != PA_JSON_TYPE_INT) {
pa_log_debug("Format info property '%s' type is not int.", key);
pa_json_object_free(o);
return -PA_ERR_INVALID;
}
*v = pa_json_object_get_int(o);
pa_json_object_free(o);
return 0;
}
int pa_format_info_get_prop_int_range(const pa_format_info *f, const char *key, int *min, int *max) {
const char *str;
pa_json_object *o;
const pa_json_object *o1;
int ret = -PA_ERR_INVALID;
pa_assert(f);
pa_assert(key);
pa_assert(min);
pa_assert(max);
str = pa_proplist_gets(f->plist, key);
if (!str)
return -PA_ERR_NOENTITY;
o = pa_json_parse(str);
if (!o) {
pa_log_debug("Failed to parse format info property '%s'.", key);
return -PA_ERR_INVALID;
}
if (pa_json_object_get_type(o) != PA_JSON_TYPE_OBJECT)
goto out;
if (!(o1 = pa_json_object_get_object_member(o, PA_JSON_MIN_KEY)) ||
(pa_json_object_get_type(o1) != PA_JSON_TYPE_INT))
goto out;
*min = pa_json_object_get_int(o1);
if (!(o1 = pa_json_object_get_object_member(o, PA_JSON_MAX_KEY)) ||
(pa_json_object_get_type(o1) != PA_JSON_TYPE_INT))
goto out;
*max = pa_json_object_get_int(o1);
ret = 0;
out:
if (ret < 0)
pa_log_debug("Format info property '%s' is not a valid int range.", key);
pa_json_object_free(o);
return ret;
}
int pa_format_info_get_prop_int_array(const pa_format_info *f, const char *key, int **values, int *n_values) {
const char *str;
pa_json_object *o;
const pa_json_object *o1;
int i, ret = -PA_ERR_INVALID;
pa_assert(f);
pa_assert(key);
pa_assert(values);
pa_assert(n_values);
str = pa_proplist_gets(f->plist, key);
if (!str)
return -PA_ERR_NOENTITY;
o = pa_json_parse(str);
if (!o) {
pa_log_debug("Failed to parse format info property '%s'.", key);
return -PA_ERR_INVALID;
}
if (pa_json_object_get_type(o) != PA_JSON_TYPE_ARRAY)
goto out;
*n_values = pa_json_object_get_array_length(o);
*values = pa_xnew(int, *n_values);
for (i = 0; i < *n_values; i++) {
o1 = pa_json_object_get_array_member(o, i);
if (pa_json_object_get_type(o1) != PA_JSON_TYPE_INT) {
goto out;
}
(*values)[i] = pa_json_object_get_int(o1);
}
ret = 0;
out:
if (ret < 0)
pa_log_debug("Format info property '%s' is not a valid int array.", key);
pa_json_object_free(o);
return ret;
}
int pa_format_info_get_prop_string(const pa_format_info *f, const char *key, char **v) {
const char *str = NULL;
pa_json_object *o;
pa_assert(f);
pa_assert(key);
pa_assert(v);
str = pa_proplist_gets(f->plist, key);
if (!str)
return -PA_ERR_NOENTITY;
o = pa_json_parse(str);
if (!o) {
pa_log_debug("Failed to parse format info property '%s'.", key);
return -PA_ERR_INVALID;
}
if (pa_json_object_get_type(o) != PA_JSON_TYPE_STRING) {
pa_log_debug("Format info property '%s' type is not string.", key);
pa_json_object_free(o);
return -PA_ERR_INVALID;
}
*v = pa_xstrdup(pa_json_object_get_string(o));
pa_json_object_free(o);
return 0;
}
int pa_format_info_get_prop_string_array(const pa_format_info *f, const char *key, char ***values, int *n_values) {
const char *str;
pa_json_object *o;
const pa_json_object *o1;
int i, ret = -PA_ERR_INVALID;
pa_assert(f);
pa_assert(key);
pa_assert(values);
pa_assert(n_values);
str = pa_proplist_gets(f->plist, key);
if (!str)
return -PA_ERR_NOENTITY;
o = pa_json_parse(str);
if (!o) {
pa_log_debug("Failed to parse format info property '%s'.", key);
return -PA_ERR_INVALID;
}
if (pa_json_object_get_type(o) != PA_JSON_TYPE_ARRAY)
goto out;
*n_values = pa_json_object_get_array_length(o);
*values = pa_xnew(char *, *n_values);
for (i = 0; i < *n_values; i++) {
o1 = pa_json_object_get_array_member(o, i);
if (pa_json_object_get_type(o1) != PA_JSON_TYPE_STRING) {
goto out;
}
(*values)[i] = pa_xstrdup(pa_json_object_get_string(o1));
}
ret = 0;
out:
if (ret < 0)
pa_log_debug("Format info property '%s' is not a valid string array.", key);
pa_json_object_free(o);
return ret;
}
void pa_format_info_free_string_array(char **values, int n_values) {
int i;
for (i = 0; i < n_values; i++)
pa_xfree(values[i]);
pa_xfree(values);
}
void pa_format_info_set_sample_format(pa_format_info *f, pa_sample_format_t sf) {
pa_format_info_set_prop_string(f, PA_PROP_FORMAT_SAMPLE_FORMAT, pa_sample_format_to_string(sf));
}
void pa_format_info_set_rate(pa_format_info *f, int rate) {
pa_format_info_set_prop_int(f, PA_PROP_FORMAT_RATE, rate);
}
void pa_format_info_set_channels(pa_format_info *f, int channels) {
pa_format_info_set_prop_int(f, PA_PROP_FORMAT_CHANNELS, channels);
}
void pa_format_info_set_channel_map(pa_format_info *f, const pa_channel_map *map) {
char map_str[PA_CHANNEL_MAP_SNPRINT_MAX];
pa_channel_map_snprint(map_str, sizeof(map_str), map);
pa_format_info_set_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, map_str);
}
void pa_format_info_set_prop_int(pa_format_info *f, const char *key, int value) {
pa_assert(f);
pa_assert(key);
pa_proplist_setf(f->plist, key, "%d", value);
}
void pa_format_info_set_prop_int_array(pa_format_info *f, const char *key, const int *values, int n_values) {
pa_strbuf *buf;
char *str;
int i;
pa_assert(f);
pa_assert(key);
pa_assert(n_values > 0);
buf = pa_strbuf_new();
pa_strbuf_printf(buf, "[ %d", values[0]);
for (i = 1; i < n_values; i++)
pa_strbuf_printf(buf, ", %d", values[i]);
pa_strbuf_printf(buf, " ]");
str = pa_strbuf_to_string_free(buf);
pa_proplist_sets(f->plist, key, str);
pa_xfree (str);
}
void pa_format_info_set_prop_int_range(pa_format_info *f, const char *key, int min, int max) {
pa_assert(f);
pa_assert(key);
pa_proplist_setf(f->plist, key, "{ \"" PA_JSON_MIN_KEY "\": %d, \"" PA_JSON_MAX_KEY "\": %d }",
min, max);
}
void pa_format_info_set_prop_string(pa_format_info *f, const char *key, const char *value) {
pa_assert(f);
pa_assert(key);
pa_proplist_setf(f->plist, key, "\"%s\"", value);
}
void pa_format_info_set_prop_string_array(pa_format_info *f, const char *key, const char **values, int n_values) {
pa_strbuf *buf;
char *str;
int i;
pa_assert(f);
pa_assert(key);
buf = pa_strbuf_new();
pa_strbuf_printf(buf, "[ \"%s\"", values[0]);
for (i = 1; i < n_values; i++)
pa_strbuf_printf(buf, ", \"%s\"", values[i]);
pa_strbuf_printf(buf, " ]");
str = pa_strbuf_to_string_free(buf);
pa_proplist_sets(f->plist, key, str);
pa_xfree (str);
}
static bool pa_json_is_fixed_type(pa_json_object *o) {
switch(pa_json_object_get_type(o)) {
case PA_JSON_TYPE_OBJECT:
case PA_JSON_TYPE_ARRAY:
return false;
default:
return true;
}
}
static int pa_format_info_prop_compatible(const char *one, const char *two) {
pa_json_object *o1 = NULL, *o2 = NULL;
int i, ret = 0;
o1 = pa_json_parse(one);
if (!o1)
goto out;
o2 = pa_json_parse(two);
if (!o2)
goto out;
/* We don't deal with both values being non-fixed - just because there is no immediate need (FIXME) */
pa_return_val_if_fail(pa_json_is_fixed_type(o1) || pa_json_is_fixed_type(o2), false);
if (pa_json_is_fixed_type(o1) && pa_json_is_fixed_type(o2)) {
ret = pa_json_object_equal(o1, o2);
goto out;
}
if (pa_json_is_fixed_type(o1)) {
pa_json_object *tmp = o2;
o2 = o1;
o1 = tmp;
}
/* o2 is now a fixed type, and o1 is not */
if (pa_json_object_get_type(o1) == PA_JSON_TYPE_ARRAY) {
for (i = 0; i < pa_json_object_get_array_length(o1); i++) {
if (pa_json_object_equal(pa_json_object_get_array_member(o1, i), o2)) {
ret = 1;
break;
}
}
} else if (pa_json_object_get_type(o1) == PA_JSON_TYPE_OBJECT) {
/* o1 should be a range type */
int min, max, v;
const pa_json_object *o_min = NULL, *o_max = NULL;
if (pa_json_object_get_type(o2) != PA_JSON_TYPE_INT) {
/* We don't support non-integer ranges */
goto out;
}
if (!(o_min = pa_json_object_get_object_member(o1, PA_JSON_MIN_KEY)) ||
pa_json_object_get_type(o_min) != PA_JSON_TYPE_INT)
goto out;
if (!(o_max = pa_json_object_get_object_member(o1, PA_JSON_MAX_KEY)) ||
pa_json_object_get_type(o_max) != PA_JSON_TYPE_INT)
goto out;
v = pa_json_object_get_int(o2);
min = pa_json_object_get_int(o_min);
max = pa_json_object_get_int(o_max);
ret = v >= min && v <= max;
} else {
pa_log_warn("Got a format type that we don't support");
}
out:
if (o1)
pa_json_object_free(o1);
if (o2)
pa_json_object_free(o2);
return ret;
}

348
src/hashmap.c Normal file
View file

@ -0,0 +1,348 @@
/***
This file is part of PulseAudio.
Copyright 2004-2008 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
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.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <pulse/xmalloc.h>
#include "internal.h"
#include "hashmap.h"
#define NBUCKETS 127
struct hashmap_entry {
void *key;
void *value;
struct hashmap_entry *bucket_next, *bucket_previous;
struct hashmap_entry *iterate_next, *iterate_previous;
};
struct pa_hashmap {
pa_hash_func_t hash_func;
pa_compare_func_t compare_func;
pa_free_cb_t key_free_func;
pa_free_cb_t value_free_func;
struct hashmap_entry *iterate_list_head, *iterate_list_tail;
unsigned n_entries;
};
#define BY_HASH(h) ((struct hashmap_entry**) ((uint8_t*) (h) + PA_ALIGN(sizeof(pa_hashmap))))
PA_STATIC_FLIST_DECLARE(entries, 0, pa_xfree);
static unsigned pa_idxset_trivial_hash_func(const void *p) {
return PA_PTR_TO_UINT(p);
}
static int pa_idxset_trivial_compare_func(const void *a, const void *b) {
return a < b ? -1 : (a > b ? 1 : 0);
}
pa_hashmap *pa_hashmap_new_full(pa_hash_func_t hash_func, pa_compare_func_t compare_func, pa_free_cb_t key_free_func, pa_free_cb_t value_free_func) {
pa_hashmap *h;
h = pa_xmalloc0(PA_ALIGN(sizeof(pa_hashmap)) + NBUCKETS*sizeof(struct hashmap_entry*));
h->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func;
h->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func;
h->key_free_func = key_free_func;
h->value_free_func = value_free_func;
h->n_entries = 0;
h->iterate_list_head = h->iterate_list_tail = NULL;
return h;
}
pa_hashmap *pa_hashmap_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func) {
return pa_hashmap_new_full(hash_func, compare_func, NULL, NULL);
}
static void remove_entry(pa_hashmap *h, struct hashmap_entry *e) {
pa_assert(h);
pa_assert(e);
/* Remove from iteration list */
if (e->iterate_next)
e->iterate_next->iterate_previous = e->iterate_previous;
else
h->iterate_list_tail = e->iterate_previous;
if (e->iterate_previous)
e->iterate_previous->iterate_next = e->iterate_next;
else
h->iterate_list_head = e->iterate_next;
/* Remove from hash table bucket list */
if (e->bucket_next)
e->bucket_next->bucket_previous = e->bucket_previous;
if (e->bucket_previous)
e->bucket_previous->bucket_next = e->bucket_next;
else {
unsigned hash = h->hash_func(e->key) % NBUCKETS;
BY_HASH(h)[hash] = e->bucket_next;
}
if (h->key_free_func)
h->key_free_func(e->key);
if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0)
pa_xfree(e);
pa_assert(h->n_entries >= 1);
h->n_entries--;
}
void pa_hashmap_free(pa_hashmap *h) {
pa_assert(h);
pa_hashmap_remove_all(h);
pa_xfree(h);
}
static struct hashmap_entry *hash_scan(pa_hashmap *h, unsigned hash, const void *key) {
struct hashmap_entry *e;
pa_assert(h);
pa_assert(hash < NBUCKETS);
for (e = BY_HASH(h)[hash]; e; e = e->bucket_next)
if (h->compare_func(e->key, key) == 0)
return e;
return NULL;
}
int pa_hashmap_put(pa_hashmap *h, void *key, void *value) {
struct hashmap_entry *e;
unsigned hash;
pa_assert(h);
hash = h->hash_func(key) % NBUCKETS;
if (hash_scan(h, hash, key))
return -1;
if (!(e = pa_flist_pop(PA_STATIC_FLIST_GET(entries))))
e = pa_xnew(struct hashmap_entry, 1);
e->key = key;
e->value = value;
/* Insert into hash table */
e->bucket_next = BY_HASH(h)[hash];
e->bucket_previous = NULL;
if (BY_HASH(h)[hash])
BY_HASH(h)[hash]->bucket_previous = e;
BY_HASH(h)[hash] = e;
/* Insert into iteration list */
e->iterate_previous = h->iterate_list_tail;
e->iterate_next = NULL;
if (h->iterate_list_tail) {
pa_assert(h->iterate_list_head);
h->iterate_list_tail->iterate_next = e;
} else {
pa_assert(!h->iterate_list_head);
h->iterate_list_head = e;
}
h->iterate_list_tail = e;
h->n_entries++;
pa_assert(h->n_entries >= 1);
return 0;
}
void* pa_hashmap_get(pa_hashmap *h, const void *key) {
unsigned hash;
struct hashmap_entry *e;
pa_assert(h);
hash = h->hash_func(key) % NBUCKETS;
if (!(e = hash_scan(h, hash, key)))
return NULL;
return e->value;
}
void* pa_hashmap_remove(pa_hashmap *h, const void *key) {
struct hashmap_entry *e;
unsigned hash;
void *data;
pa_assert(h);
hash = h->hash_func(key) % NBUCKETS;
if (!(e = hash_scan(h, hash, key)))
return NULL;
data = e->value;
remove_entry(h, e);
return data;
}
int pa_hashmap_remove_and_free(pa_hashmap *h, const void *key) {
void *data;
pa_assert(h);
data = pa_hashmap_remove(h, key);
if (data && h->value_free_func)
h->value_free_func(data);
return data ? 0 : -1;
}
void pa_hashmap_remove_all(pa_hashmap *h) {
pa_assert(h);
while (h->iterate_list_head) {
void *data;
data = h->iterate_list_head->value;
remove_entry(h, h->iterate_list_head);
if (h->value_free_func)
h->value_free_func(data);
}
}
void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void **key) {
struct hashmap_entry *e;
pa_assert(h);
pa_assert(state);
if (*state == (void*) -1)
goto at_end;
if (!*state && !h->iterate_list_head)
goto at_end;
e = *state ? *state : h->iterate_list_head;
if (e->iterate_next)
*state = e->iterate_next;
else
*state = (void*) -1;
if (key)
*key = e->key;
return e->value;
at_end:
*state = (void *) -1;
if (key)
*key = NULL;
return NULL;
}
void *pa_hashmap_iterate_backwards(pa_hashmap *h, void **state, const void **key) {
struct hashmap_entry *e;
pa_assert(h);
pa_assert(state);
if (*state == (void*) -1)
goto at_beginning;
if (!*state && !h->iterate_list_tail)
goto at_beginning;
e = *state ? *state : h->iterate_list_tail;
if (e->iterate_previous)
*state = e->iterate_previous;
else
*state = (void*) -1;
if (key)
*key = e->key;
return e->value;
at_beginning:
*state = (void *) -1;
if (key)
*key = NULL;
return NULL;
}
void* pa_hashmap_first(pa_hashmap *h) {
pa_assert(h);
if (!h->iterate_list_head)
return NULL;
return h->iterate_list_head->value;
}
void* pa_hashmap_last(pa_hashmap *h) {
pa_assert(h);
if (!h->iterate_list_tail)
return NULL;
return h->iterate_list_tail->value;
}
void* pa_hashmap_steal_first(pa_hashmap *h) {
void *data;
pa_assert(h);
if (!h->iterate_list_head)
return NULL;
data = h->iterate_list_head->value;
remove_entry(h, h->iterate_list_head);
return data;
}
unsigned pa_hashmap_size(pa_hashmap *h) {
pa_assert(h);
return h->n_entries;
}
bool pa_hashmap_isempty(pa_hashmap *h) {
pa_assert(h);
return h->n_entries == 0;
}

105
src/hashmap.h Normal file
View file

@ -0,0 +1,105 @@
#ifndef foopulsecorehashmaphfoo
#define foopulsecorehashmaphfoo
/***
This file is part of PulseAudio.
Copyright 2004-2008 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
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.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#include <spa/utils/defs.h>
#include <pulse/def.h>
typedef unsigned (*pa_hash_func_t)(const void *p);
typedef int (*pa_compare_func_t)(const void *a, const void *b);
typedef void *(*pa_copy_func_t)(const void *p);
/* Simple Implementation of a hash table. Memory management is the
* user's job. It's a good idea to have the key pointer point to a
* string in the value data. The insertion order is preserved when
* iterating. */
typedef struct pa_hashmap pa_hashmap;
/* Create a new hashmap. Use the specified functions for hashing and comparing objects in the map */
pa_hashmap *pa_hashmap_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func);
/* Create a new hashmap. Use the specified functions for hashing and comparing objects in the map, and functions to free the key
* and value (either or both can be NULL). */
pa_hashmap *pa_hashmap_new_full(pa_hash_func_t hash_func, pa_compare_func_t compare_func, pa_free_cb_t key_free_func, pa_free_cb_t value_free_func);
/* Free the hash table. */
void pa_hashmap_free(pa_hashmap*);
/* Add an entry to the hashmap. Returns non-zero when the entry already exists */
int pa_hashmap_put(pa_hashmap *h, void *key, void *value);
/* Return an entry from the hashmap */
void* pa_hashmap_get(pa_hashmap *h, const void *key);
/* Returns the data of the entry while removing */
void* pa_hashmap_remove(pa_hashmap *h, const void *key);
/* Removes the entry and frees the entry data. Returns a negative value if the
* entry is not found. FIXME: This function shouldn't be needed.
* pa_hashmap_remove() should free the entry data, and the current semantics of
* pa_hashmap_remove() should be implemented by a function called
* pa_hashmap_steal(). */
int pa_hashmap_remove_and_free(pa_hashmap *h, const void *key);
/* Remove all entries but don't free the hashmap */
void pa_hashmap_remove_all(pa_hashmap *h);
/* Return the current number of entries of the hashmap */
unsigned pa_hashmap_size(pa_hashmap *h);
/* Return true if the hashmap is empty */
bool pa_hashmap_isempty(pa_hashmap *h);
/* May be used to iterate through the hashmap. Initially the opaque
pointer *state has to be set to NULL. The hashmap may not be
modified during iteration -- except for deleting the current entry
via pa_hashmap_remove(). The key of the entry is returned in *key,
if key is non-NULL. After the last entry in the hashmap NULL is
returned. */
void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void**key);
/* Same as pa_hashmap_iterate() but goes backwards */
void *pa_hashmap_iterate_backwards(pa_hashmap *h, void **state, const void**key);
/* Remove the oldest entry in the hashmap and return it */
void *pa_hashmap_steal_first(pa_hashmap *h);
/* Return the oldest entry in the hashmap */
void* pa_hashmap_first(pa_hashmap *h);
/* Return the newest entry in the hashmap */
void* pa_hashmap_last(pa_hashmap *h);
/* A macro to ease iteration through all entries */
#define PA_HASHMAP_FOREACH(e, h, state) \
for ((state) = NULL, (e) = pa_hashmap_iterate((h), &(state), NULL); (e); (e) = pa_hashmap_iterate((h), &(state), NULL))
/* A macro to ease itration through all key, value pairs */
#define PA_HASHMAP_FOREACH_KV(k, e, h, state) \
for ((state) = NULL, (e) = pa_hashmap_iterate((h), &(state), (const void **) &(k)); (e); (e) = pa_hashmap_iterate((h), &(state), (const void **) &(k)))
/* A macro to ease iteration through all entries, backwards */
#define PA_HASHMAP_FOREACH_BACKWARDS(e, h, state) \
for ((state) = NULL, (e) = pa_hashmap_iterate_backwards((h), &(state), NULL); (e); (e) = pa_hashmap_iterate_backwards((h), &(state), NULL))
#endif

306
src/internal.h Normal file
View file

@ -0,0 +1,306 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __PIPEWIRE_PULSEAUDIO_INTERNAL_H__
#define __PIPEWIRE_PULSEAUDIO_INTERNAL_H__
#include <string.h>
#include <spa/utils/defs.h>
#include <spa/utils/hook.h>
#include <spa/param/format-utils.h>
#include <spa/param/audio/format-utils.h>
#include <pulse/stream.h>
#include <pulse/format.h>
#include <pipewire/utils.h>
#include <pipewire/interfaces.h>
#include <pipewire/log.h>
#define PA_MAX_FORMATS (PA_ENCODING_MAX)
#ifdef __cplusplus
extern "C" {
#endif
#define pa_streq(a,b) (!strcmp((a),(b)))
#define pa_strneq(a,b,n) (!strncmp((a),(b),(n)))
#define PA_UNLIKELY SPA_UNLIKELY
#define PA_MIN SPA_MIN
#define PA_MAX SPA_MAX
#define pa_assert spa_assert
#define pa_return_val_if_fail spa_return_val_if_fail
#define pa_assert_not_reached spa_assert_not_reached
#define PA_USEC_PER_MSEC SPA_USEC_PER_MSEC
#ifdef __GNUC__
#define PA_CLAMP_UNLIKELY(x, low, high) \
__extension__ ({ \
typeof(x) _x = (x); \
typeof(low) _low = (low); \
typeof(high) _high = (high); \
(PA_UNLIKELY(_x > _high) ? _high : (PA_UNLIKELY(_x < _low) ? _low : _x)); \
})
#else
#define PA_CLAMP_UNLIKELY(x, low, high) (PA_UNLIKELY((x) > (high)) ? (high) : (PA_UNLIKELY((x) < (low)) ? (low) : (x)))
#endif
#define pa_init_i18n()
#define _(String) (String)
#define N_(String) (String)
#define pa_snprintf snprintf
#define pa_strip(n) pw_strip(n,"\n\r \t")
#define pa_log pw_log_info
#define pa_log_debug pw_log_debug
#define pa_log_warn pw_log_warn
static inline void* PA_ALIGN_PTR(const void *p) {
return (void*) (((size_t) p) & ~(sizeof(void*) - 1));
}
/* Rounds up */
static inline size_t PA_ALIGN(size_t l) {
return ((l + sizeof(void*) - 1) & ~(sizeof(void*) - 1));
}
static inline const char *pa_strnull(const char *x) {
return x ? x : "(null)";
}
int pa_context_set_error(pa_context *c, int error);
#define PA_CHECK_VALIDITY(context, expression, error) \
do { \
if (!(expression)) \
return -pa_context_set_error((context), (error)); \
} while(false)
#define PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, value) \
do { \
if (!(expression)) { \
pa_context_set_error((context), (error)); \
return value; \
} \
} while(false)
#define PA_CHECK_VALIDITY_RETURN_NULL(context, expression, error) \
PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, NULL)
#define PA_FAIL(context, error) \
do { \
return -pa_context_set_error((context), (error)); \
} while(false)
#define PA_FAIL_RETURN_ANY(context, error, value) \
do { \
pa_context_set_error((context), (error)); \
return value; \
} while(false)
#define PA_FAIL_RETURN_NULL(context, error) \
PA_FAIL_RETURN_ANY(context, error, NULL)
struct pa_proplist {
struct pw_properties *props;
};
pa_proplist* pa_proplist_new_props(struct pw_properties *props);
pa_proplist* pa_proplist_new_dict(struct spa_dict *dict);
struct pa_mainloop {
struct pw_loop *loop;
struct spa_source *event;
pa_mainloop_api api;
bool quit;
int retval;
int timeout;
int n_events;
};
struct global {
struct spa_list link;
uint32_t id;
uint32_t parent_id;
uint32_t type;
struct pw_properties *props;
void *info;
pw_destroy_t destroy;
struct pw_proxy *proxy;
struct spa_hook proxy_listener;
struct spa_hook proxy_proxy_listener;
};
struct pa_context {
int refcount;
struct pw_loop *loop;
struct pw_core *core;
struct pw_type *t;
struct pw_remote *remote;
struct spa_hook remote_listener;
struct pw_core_proxy *core_proxy;
struct pw_registry_proxy *registry_proxy;
struct spa_hook registry_listener;
pa_proplist *proplist;
pa_mainloop_api *mainloop;
uint32_t seq;
int error;
pa_context_state_t state;
pa_context_notify_cb_t state_callback;
void *state_userdata;
pa_context_event_cb_t event_callback;
void *event_userdata;
bool no_fail;
uint32_t client_index;
struct spa_list globals;
struct spa_list streams;
struct spa_list operations;
};
struct global *pa_context_find_global(pa_context *c, uint32_t id);
struct type {
struct spa_type_media_type media_type;
struct spa_type_media_subtype media_subtype;
struct spa_type_format_audio format_audio;
struct spa_type_audio_format audio_format;
};
static inline void init_type(struct type *type, struct spa_type_map *map)
{
spa_type_media_type_map(map, &type->media_type);
spa_type_media_subtype_map(map, &type->media_subtype);
spa_type_format_audio_map(map, &type->format_audio);
spa_type_audio_format_map(map, &type->audio_format);
}
struct pa_stream {
struct spa_list link;
int refcount;
struct pw_stream *stream;
struct spa_hook stream_listener;
struct type type;
pa_context *context;
pa_proplist *proplist;
pa_stream_direction_t direction;
pa_stream_state_t state;
pa_stream_flags_t flags;
pa_sample_spec sample_spec;
pa_channel_map channel_map;
uint8_t n_formats;
pa_format_info *req_formats[PA_MAX_FORMATS];
pa_format_info *format;
uint32_t stream_index;
pa_buffer_attr buffer_attr;
uint32_t device_index;
char *device_name;
pa_timing_info timing_info;
uint32_t direct_on_input;
bool suspended:1;
bool corked:1;
bool timing_info_valid:1;
pa_stream_notify_cb_t state_callback;
void *state_userdata;
pa_stream_request_cb_t read_callback;
void *read_userdata;
pa_stream_request_cb_t write_callback;
void *write_userdata;
pa_stream_notify_cb_t overflow_callback;
void *overflow_userdata;
pa_stream_notify_cb_t underflow_callback;
void *underflow_userdata;
pa_stream_notify_cb_t latency_update_callback;
void *latency_update_userdata;
pa_stream_notify_cb_t moved_callback;
void *moved_userdata;
pa_stream_notify_cb_t suspended_callback;
void *suspended_userdata;
pa_stream_notify_cb_t started_callback;
void *started_userdata;
pa_stream_event_cb_t event_callback;
void *event_userdata;
pa_stream_notify_cb_t buffer_attr_callback;
void *buffer_attr_userdata;
struct pw_buffer *buffer;
};
void pa_stream_set_state(pa_stream *s, pa_stream_state_t st);
typedef void (*pa_operation_cb_t)(pa_operation *o, void *userdata);
struct pa_operation
{
struct spa_list link;
int refcount;
pa_context *context;
pa_stream *stream;
uint32_t seq;
pa_operation_state_t state;
pa_operation_cb_t callback;
void *userdata;
pa_operation_notify_cb_t state_callback;
void *state_userdata;
};
pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t cb, size_t userdata_size);
void pa_operation_done(pa_operation *o);
#ifdef __cplusplus
}
#endif
#endif /* __PIPEWIRE_PULSEAUDIO_INTERNAL_H__ */

839
src/introspect.c Normal file
View file

@ -0,0 +1,839 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <errno.h>
#include <pipewire/log.h>
#include <pulse/introspect.h>
#include "internal.h"
typedef int (*global_filter_t)(pa_context *c, struct global *g);
static void node_event_info(void *object, struct pw_node_info *info)
{
struct global *g = object;
pw_log_debug("update");
g->info = pw_node_info_update(g->info, info);
}
static const struct pw_node_proxy_events node_events = {
PW_VERSION_NODE_PROXY_EVENTS,
.info = node_event_info,
};
static void module_event_info(void *object, struct pw_module_info *info)
{
struct global *g = object;
pw_log_debug("update");
g->info = pw_module_info_update(g->info, info);
}
static const struct pw_module_proxy_events module_events = {
PW_VERSION_MODULE_PROXY_EVENTS,
.info = module_event_info,
};
static void client_event_info(void *object, struct pw_client_info *info)
{
struct global *g = object;
pw_log_debug("update");
g->info = pw_client_info_update(g->info, info);
}
static const struct pw_client_proxy_events client_events = {
PW_VERSION_CLIENT_PROXY_EVENTS,
.info = client_event_info,
};
static int ensure_global(pa_context *c, struct global *g)
{
uint32_t client_version;
const void *events;
pw_destroy_t destroy;
struct pw_type *t = c->t;
if (g->proxy != NULL)
return 0;
if (g->type == t->node) {
events = &node_events;
client_version = PW_VERSION_NODE;
destroy = (pw_destroy_t) pw_node_info_free;
}
else if (g->type == t->module) {
events = &module_events;
client_version = PW_VERSION_MODULE;
destroy = (pw_destroy_t) pw_module_info_free;
}
else if (g->type == t->client) {
events = &client_events;
client_version = PW_VERSION_CLIENT;
destroy = (pw_destroy_t) pw_client_info_free;
}
else
return -EINVAL;
g->proxy = pw_registry_proxy_bind(c->registry_proxy, g->id, g->type,
client_version, 0);
if (g->proxy == NULL)
return -ENOMEM;
pw_proxy_add_proxy_listener(g->proxy, &g->proxy_proxy_listener, events, g);
g->destroy = destroy;
return 0;
}
static void ensure_types(pa_context *c, uint32_t type, global_filter_t filter)
{
struct global *g;
spa_list_for_each(g, &c->globals, link) {
if (!filter(c, g))
continue;
ensure_global(c, g);
}
}
struct sink_data {
pa_context *context;
pa_sink_info_cb_t cb;
void *userdata;
struct global *global;
};
static void sink_callback(struct sink_data *d)
{
struct global *g = d->global;
struct pw_node_info *info = g->info;
pa_sink_info i;
spa_zero(i);
i.index = g->id;
i.name = info->name;
i.proplist = pa_proplist_new_dict(info->props);
i.owner_module = g->parent_id;
i.base_volume = PA_VOLUME_NORM;
i.n_volume_steps = PA_VOLUME_NORM+1;
d->cb(d->context, &i, 0, d->userdata);
}
static void sink_info(pa_operation *o, void *userdata)
{
struct sink_data *d = userdata;
sink_callback(d);
d->cb(d->context, NULL, 1, d->userdata);
}
static int sink_filter(pa_context *c, struct global *g)
{
const char *str;
if (g->type != c->t->node)
return 0;
if (g->props == NULL)
return 0;
if ((str = pw_properties_get(g->props, "media.class")) == NULL)
return 0;
if (strcmp(str, "Audio/Sink") != 0)
return 0;
return 1;
}
pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, pa_sink_info_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, pa_sink_info_cb_t cb, void *userdata)
{
pa_operation *o;
struct global *g;
struct sink_data *d;
pa_assert(c);
pa_assert(c->refcount >= 1);
pa_assert(cb);
PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
if ((g = pa_context_find_global(c, idx)) == NULL)
return NULL;
if (!sink_filter(c, g))
return NULL;
ensure_global(c, g);
o = pa_operation_new(c, NULL, sink_info, sizeof(struct sink_data));
d = o->userdata;
d->global = g;
return o;
}
static void sink_info_list(pa_operation *o, void *userdata)
{
struct sink_data *d = userdata;
pa_context *c = d->context;
struct global *g;
spa_list_for_each(g, &c->globals, link) {
if (!sink_filter(c, g))
continue;
d->global = g;
sink_callback(d);
}
d->cb(c, NULL, 1, d->userdata);
}
pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, void *userdata)
{
pa_operation *o;
struct sink_data *d;
pa_assert(c);
pa_assert(c->refcount >= 1);
pa_assert(cb);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
ensure_types(c, c->t->node, sink_filter);
o = pa_operation_new(c, NULL, sink_info_list, sizeof(struct sink_data));
d = o->userdata;
d->context = c;
d->cb = cb;
d->userdata = userdata;
return o;
}
pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_suspend_sink_by_name(pa_context *c, const char *sink_name, int suspend, pa_context_success_cb_t cb, void* userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_suspend_sink_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_sink_port_by_index(pa_context *c, uint32_t idx, const char*port, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_sink_port_by_name(pa_context *c, const char*name, const char*port, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
struct source_data {
pa_context *context;
pa_source_info_cb_t cb;
void *userdata;
struct global *global;
};
static void source_callback(struct source_data *d)
{
struct global *g = d->global;
struct pw_node_info *info = g->info;
pa_source_info i;
spa_zero(i);
i.index = g->id;
i.name = info->name;
i.proplist = pa_proplist_new_dict(info->props);
i.owner_module = g->parent_id;
i.base_volume = PA_VOLUME_NORM;
i.n_volume_steps = PA_VOLUME_NORM+1;
d->cb(d->context, &i, 0, d->userdata);
}
static void source_info(pa_operation *o, void *userdata)
{
struct source_data *d = userdata;
source_callback(d);
d->cb(d->context, NULL, 1, d->userdata);
}
static int source_filter(pa_context *c, struct global *g)
{
const char *str;
if (g->type != c->t->node)
return 0;
if (g->props == NULL)
return 0;
if ((str = pw_properties_get(g->props, "media.class")) == NULL)
return 0;
if (strcmp(str, "Audio/Source") != 0)
return 0;
return 1;
}
pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name, pa_source_info_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t idx, pa_source_info_cb_t cb, void *userdata)
{
pa_operation *o;
struct global *g;
struct source_data *d;
pa_assert(c);
pa_assert(c->refcount >= 1);
pa_assert(cb);
PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
if ((g = pa_context_find_global(c, idx)) == NULL)
return NULL;
if (!source_filter(c, g))
return NULL;
ensure_global(c, g);
o = pa_operation_new(c, NULL, source_info, sizeof(struct source_data));
d = o->userdata;
d->global = g;
return o;
}
static void source_info_list(pa_operation *o, void *userdata)
{
struct source_data *d = userdata;
pa_context *c = d->context;
struct global *g;
spa_list_for_each(g, &c->globals, link) {
if (!source_filter(c, g))
continue;
d->global = g;
source_callback(d);
}
d->cb(c, NULL, 1, d->userdata);
}
pa_operation* pa_context_get_source_info_list(pa_context *c, pa_source_info_cb_t cb, void *userdata)
{
pa_operation *o;
struct source_data *d;
pa_assert(c);
pa_assert(c->refcount >= 1);
pa_assert(cb);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
ensure_types(c, c->t->node, source_filter);
o = pa_operation_new(c, NULL, source_info_list, sizeof(struct source_data));
d = o->userdata;
d->context = c;
d->cb = cb;
d->userdata = userdata;
return o;
}
pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_suspend_source_by_name(pa_context *c, const char *source_name, int suspend, pa_context_success_cb_t cb, void* userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_suspend_source_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_source_port_by_index(pa_context *c, uint32_t idx, const char*port, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_source_port_by_name(pa_context *c, const char*name, const char*port, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_get_server_info(pa_context *c, pa_server_info_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
struct module_data {
pa_context *context;
pa_module_info_cb_t cb;
void *userdata;
struct global *global;
};
static void module_callback(struct module_data *d)
{
struct global *g = d->global;
struct pw_module_info *info = g->info;
pa_module_info i;
spa_zero(i);
i.proplist = pa_proplist_new_dict(info->props);
i.index = g->id;
i.name = info->name;
i.argument = info->args;
i.n_used = -1;
i.auto_unload = false;
d->cb(d->context, &i, 0, d->userdata);
}
static void module_info(pa_operation *o, void *userdata)
{
struct module_data *d = userdata;
module_callback(d);
d->cb(d->context, NULL, 1, d->userdata);
}
static int module_filter(pa_context *c, struct global *g)
{
if (g->type != c->t->module)
return 0;
return 1;
}
pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, pa_module_info_cb_t cb, void *userdata)
{
pa_operation *o;
struct global *g;
struct module_data *d;
pa_assert(c);
pa_assert(c->refcount >= 1);
pa_assert(cb);
PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
if ((g = pa_context_find_global(c, idx)) == NULL)
return NULL;
if (!module_filter(c, g))
return NULL;
ensure_global(c, g);
o = pa_operation_new(c, NULL, module_info, sizeof(struct module_data));
d = o->userdata;
d->global = g;
return o;
}
static void module_info_list(pa_operation *o, void *userdata)
{
struct module_data *d = userdata;
pa_context *c = d->context;
struct global *g;
spa_list_for_each(g, &c->globals, link) {
if (!module_filter(c, g))
continue;
d->global = g;
module_callback(d);
}
d->cb(c, NULL, 1, d->userdata);
}
pa_operation* pa_context_get_module_info_list(pa_context *c, pa_module_info_cb_t cb, void *userdata)
{
pa_operation *o;
struct module_data *d;
pa_assert(c);
pa_assert(c->refcount >= 1);
pa_assert(cb);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
ensure_types(c, c->t->module, module_filter);
o = pa_operation_new(c, NULL, module_info_list, sizeof(struct module_data));
d = o->userdata;
d->context = c;
d->cb = cb;
d->userdata = userdata;
return o;
}
pa_operation* pa_context_load_module(pa_context *c, const char*name, const char *argument, pa_context_index_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
struct client_data {
pa_context *context;
pa_client_info_cb_t cb;
void *userdata;
struct global *global;
};
static void client_callback(struct client_data *d)
{
struct global *g = d->global;
struct pw_client_info *info = g->info;
pa_client_info i;
spa_zero(i);
i.proplist = pa_proplist_new_dict(info->props);
i.index = g->id;
i.name = info->props ?
spa_dict_lookup(info->props, "application.prgname") : NULL;
i.owner_module = g->parent_id;
i.driver = info->props ?
spa_dict_lookup(info->props, PW_CLIENT_PROP_PROTOCOL) : NULL;
d->cb(d->context, &i, 0, d->userdata);
}
static void client_info(pa_operation *o, void *userdata)
{
struct client_data *d = userdata;
client_callback(d);
d->cb(d->context, NULL, 1, d->userdata);
}
static int client_filter(pa_context *c, struct global *g)
{
if (g->type != c->t->client)
return 0;
return 1;
}
pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, pa_client_info_cb_t cb, void *userdata)
{
pa_operation *o;
struct global *g;
struct client_data *d;
pa_assert(c);
pa_assert(c->refcount >= 1);
pa_assert(cb);
PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
if ((g = pa_context_find_global(c, idx)) == NULL)
return NULL;
if (!client_filter(c, g))
return NULL;
ensure_global(c, g);
o = pa_operation_new(c, NULL, client_info, sizeof(struct client_data));
d = o->userdata;
d->global = g;
return o;
}
static void client_info_list(pa_operation *o, void *userdata)
{
struct client_data *d = userdata;
pa_context *c = d->context;
struct global *g;
spa_list_for_each(g, &c->globals, link) {
if (!client_filter(c, g))
continue;
d->global = g;
client_callback(d);
}
d->cb(c, NULL, 1, d->userdata);
}
pa_operation* pa_context_get_client_info_list(pa_context *c, pa_client_info_cb_t cb, void *userdata)
{
pa_operation *o;
struct client_data *d;
pa_assert(c);
pa_assert(c->refcount >= 1);
pa_assert(cb);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
ensure_types(c, c->t->client, client_filter);
o = pa_operation_new(c, NULL, client_info_list, sizeof(struct client_data));
d = o->userdata;
d->context = c;
d->cb = cb;
d->userdata = userdata;
return o;
}
pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_get_card_info_by_index(pa_context *c, uint32_t idx, pa_card_info_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_get_card_info_by_name(pa_context *c, const char *name, pa_card_info_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_get_card_info_list(pa_context *c, pa_card_info_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_card_profile_by_index(pa_context *c, uint32_t idx, const char*profile, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_card_profile_by_name(pa_context *c, const char*name, const char*profile, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_port_latency_offset(pa_context *c, const char *card_name, const char *port_name, int64_t offset, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, pa_sink_input_info_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_get_sink_input_info_list(pa_context *c, pa_sink_input_info_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, const char *sink_name, pa_context_success_cb_t cb, void* userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_move_sink_input_by_index(pa_context *c, uint32_t idx, uint32_t sink_idx, pa_context_success_cb_t cb, void* userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, pa_source_output_info_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_get_source_output_info_list(pa_context *c, pa_source_output_info_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx, const char *source_name, pa_context_success_cb_t cb, void* userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_move_source_output_by_index(pa_context *c, uint32_t idx, uint32_t source_idx, pa_context_success_cb_t cb, void* userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_source_output_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_set_source_output_mute(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_stat(pa_context *c, pa_stat_info_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_get_sample_info_by_name(pa_context *c, const char *name, pa_sample_info_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_get_sample_info_by_index(pa_context *c, uint32_t idx, pa_sample_info_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_get_sample_info_list(pa_context *c, pa_sample_info_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_autoload_info_cb_t cb, void *userdata)
{
pw_log_warn("Deprecated: Not Implemented");
return NULL;
}
pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_autoload_info_cb_t cb, void *userdata)
{
pw_log_warn("Deprecated: Not Implemented");
return NULL;
}
pa_operation* pa_context_get_autoload_info_list(pa_context *c, pa_autoload_info_cb_t cb, void *userdata)
{
pw_log_warn("Deprecated: Not Implemented");
return NULL;
}
pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, pa_context_index_cb_t cb, void* userdata)
{
pw_log_warn("Deprecated: Not Implemented");
return NULL;
}
pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_context_success_cb_t cb, void* userdata)
{
pw_log_warn("Deprecated: Not Implemented");
return NULL;
}
pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata)
{
pw_log_warn("Deprecated: Not Implemented");
return NULL;
}

648
src/json.c Normal file
View file

@ -0,0 +1,648 @@
/***
This file is part of PulseAudio.
Copyright 2016 Arun Raghavan <mail@arunraghavan.net>
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
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.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <math.h>
#include <pulse/xmalloc.h>
#include <pipewire/array.h>
#include <pipewire/properties.h>
#include "internal.h"
#include "json.h"
#include "strbuf.h"
#define MAX_NESTING_DEPTH 20 /* Arbitrary number to make sure we don't have a stack overflow */
typedef struct pa_json_item {
char *key;
pa_json_object *value;
} pa_json_item;
struct pa_json_object {
pa_json_type type;
union {
int int_value;
double double_value;
bool bool_value;
char *string_value;
struct pw_array values; /* objects */
};
};
static void clear_array(struct pw_array *array)
{
pa_json_object *value;
pw_array_for_each(value, array)
pa_json_object_free(value);
pw_array_clear(array);
}
static void clear_item(pa_json_item *item)
{
free(item->key);
pa_json_object_free(item->value);
}
static void clear_object(struct pw_array *array)
{
pa_json_item *item;
pw_array_for_each(item, array)
clear_item(item);
pw_array_clear(array);
}
static const char* parse_value(const char *str, const char *end, pa_json_object **obj, unsigned int depth);
static pa_json_object* json_object_new(void) {
pa_json_object *obj;
obj = pa_xnew0(pa_json_object, 1);
return obj;
}
static bool is_whitespace(char c) {
return c == '\t' || c == '\n' || c == '\r' || c == ' ';
}
static bool is_digit(char c) {
return c >= '0' && c <= '9';
}
static bool is_end(const char c, const char *end) {
if (!end)
return c == '\0';
else {
while (*end) {
if (c == *end)
return true;
end++;
}
}
return false;
}
static const char* consume_string(const char *str, const char *expect) {
while (*expect) {
if (*str != *expect)
return NULL;
str++;
expect++;
}
return str;
}
static const char* parse_null(const char *str, pa_json_object *obj) {
str = consume_string(str, "null");
if (str)
obj->type = PA_JSON_TYPE_NULL;
return str;
}
static const char* parse_boolean(const char *str, pa_json_object *obj) {
const char *tmp;
tmp = consume_string(str, "true");
if (tmp) {
obj->type = PA_JSON_TYPE_BOOL;
obj->bool_value = true;
} else {
tmp = consume_string(str, "false");
if (str) {
obj->type = PA_JSON_TYPE_BOOL;
obj->bool_value = false;
}
}
return tmp;
}
static const char* parse_string(const char *str, pa_json_object *obj) {
pa_strbuf *buf = pa_strbuf_new();
str++; /* Consume leading '"' */
while (*str && *str != '"') {
if (*str != '\\') {
/* We only accept ASCII printable characters. */
if (*str < 0x20 || *str > 0x7E) {
pa_log("Invalid non-ASCII character: 0x%x", (unsigned int) *str);
goto error;
}
/* Normal character, juts consume */
pa_strbuf_putc(buf, *str);
} else {
/* Need to unescape */
str++;
switch (*str) {
case '"':
case '\\':
case '/':
pa_strbuf_putc(buf, *str);
break;
case 'b':
pa_strbuf_putc(buf, '\b' /* backspace */);
break;
case 'f':
pa_strbuf_putc(buf, '\f' /* form feed */);
break;
case 'n':
pa_strbuf_putc(buf, '\n' /* new line */);
break;
case 'r':
pa_strbuf_putc(buf, '\r' /* carriage return */);
break;
case 't':
pa_strbuf_putc(buf, '\t' /* horizontal tab */);
break;
case 'u':
pa_log("Unicode code points are currently unsupported");
goto error;
default:
pa_log("Unexepcted escape value: %c", *str);
goto error;
}
}
str++;
}
if (*str != '"') {
pa_log("Failed to parse remainder of string: %s", str);
goto error;
}
str++;
obj->type = PA_JSON_TYPE_STRING;
obj->string_value = pa_strbuf_to_string_free(buf);
return str;
error:
pa_strbuf_free(buf);
return NULL;
}
static const char* parse_number(const char *str, pa_json_object *obj) {
bool negative = false, has_fraction = false, has_exponent = false, valid = false;
unsigned int integer = 0;
unsigned int fraction = 0;
unsigned int fraction_digits = 0;
int exponent = 0;
if (*str == '-') {
negative = true;
str++;
}
if (*str == '0') {
valid = true;
str++;
goto fraction;
}
while (is_digit(*str)) {
valid = true;
if (integer > ((negative ? INT_MAX : UINT_MAX) / 10)) {
pa_log("Integer overflow while parsing number");
goto error;
}
integer = (integer * 10) + (*str - '0');
str++;
}
fraction:
if (!valid) {
pa_log("Missing digits while parsing number");
goto error;
}
if (*str == '.') {
has_fraction = true;
str++;
valid = false;
while (is_digit(*str)) {
valid = true;
if (fraction > (UINT_MAX / 10)) {
pa_log("Integer overflow while parsing fractional part of number");
goto error;
}
fraction = (fraction * 10) + (*str - '0');
fraction_digits++;
str++;
}
if (!valid) {
pa_log("No digit after '.' while parsing fraction");
goto error;
}
}
if (*str == 'e' || *str == 'E') {
bool exponent_negative = false;
has_exponent = true;
str++;
valid = false;
if (*str == '-') {
exponent_negative = true;
str++;
} else if (*str == '+')
str++;
while (is_digit(*str)) {
valid = true;
if (exponent > (INT_MAX / 10)) {
pa_log("Integer overflow while parsing exponent part of number");
goto error;
}
exponent = (exponent * 10) + (*str - '0');
str++;
}
if (!valid) {
pa_log("No digit in exponent while parsing fraction");
goto error;
}
if (exponent_negative)
exponent *= -1;
}
if (has_fraction || has_exponent) {
obj->type = PA_JSON_TYPE_DOUBLE;
obj->double_value =
(negative ? -1.0 : 1.0) * (integer + (double) fraction / pow(10, fraction_digits)) * pow(10, exponent);
} else {
obj->type = PA_JSON_TYPE_INT;
obj->int_value = (negative ? -1 : 1) * integer;
}
return str;
error:
return NULL;
}
static const char *parse_object(const char *str, pa_json_object *obj, unsigned int depth) {
pa_json_object *name = NULL, *value = NULL;
pa_json_item *item;
obj->values = PW_ARRAY_INIT(64);
while (*str != '}') {
str++; /* Consume leading '{' or ',' */
str = parse_value(str, ":", &name, depth + 1);
if (!str || pa_json_object_get_type(name) != PA_JSON_TYPE_STRING) {
pa_log("Could not parse key for object");
goto error;
}
/* Consume the ':' */
str++;
str = parse_value(str, ",}", &value, depth + 1);
if (!str) {
pa_log("Could not parse value for object");
goto error;
}
item = pw_array_add(&obj->values, sizeof(pa_json_item));
item->key = strdup(pa_json_object_get_string(name));
item->value = value;
pa_json_object_free(name);
name = NULL;
value = NULL;
}
/* Drop trailing '}' */
str++;
/* We now know the value was correctly parsed */
obj->type = PA_JSON_TYPE_OBJECT;
return str;
error:
clear_object(&obj->values);
if (name)
pa_json_object_free(name);
if (value)
pa_json_object_free(value);
return NULL;
}
static const char *parse_array(const char *str, pa_json_object *obj, unsigned int depth) {
pa_json_object *value;
obj->values = PW_ARRAY_INIT(64);
while (*str != ']') {
str++; /* Consume leading '[' or ',' */
/* Need to chew up whitespaces as a special case to deal with the
* possibility of an empty array */
while (is_whitespace(*str))
str++;
if (*str == ']')
break;
str = parse_value(str, ",]", &value, depth + 1);
if (!str) {
pa_log("Could not parse value for array");
goto error;
}
pw_array_add_ptr(&obj->values, value);
}
/* Drop trailing ']' */
str++;
/* We now know the value was correctly parsed */
obj->type = PA_JSON_TYPE_ARRAY;
return str;
error:
clear_array(&obj->values);
return NULL;
}
typedef enum {
JSON_PARSER_STATE_INIT,
JSON_PARSER_STATE_FINISH,
} json_parser_state;
static const char* parse_value(const char *str, const char *end, pa_json_object **obj, unsigned int depth) {
json_parser_state state = JSON_PARSER_STATE_INIT;
pa_json_object *o;
pa_assert(str != NULL);
o = json_object_new();
if (depth > MAX_NESTING_DEPTH) {
pa_log("Exceeded maximum permitted nesting depth of objects (%u)", MAX_NESTING_DEPTH);
goto error;
}
while (!is_end(*str, end)) {
switch (state) {
case JSON_PARSER_STATE_INIT:
if (is_whitespace(*str)) {
str++;
} else if (*str == 'n') {
str = parse_null(str, o);
state = JSON_PARSER_STATE_FINISH;
} else if (*str == 't' || *str == 'f') {
str = parse_boolean(str, o);
state = JSON_PARSER_STATE_FINISH;
} else if (*str == '"') {
str = parse_string(str, o);
state = JSON_PARSER_STATE_FINISH;
} else if (is_digit(*str) || *str == '-') {
str = parse_number(str, o);
state = JSON_PARSER_STATE_FINISH;
} else if (*str == '{') {
str = parse_object(str, o, depth);
state = JSON_PARSER_STATE_FINISH;
} else if (*str == '[') {
str = parse_array(str, o, depth);
state = JSON_PARSER_STATE_FINISH;
} else {
pa_log("Invalid JSON string: %s", str);
goto error;
}
if (!str)
goto error;
break;
case JSON_PARSER_STATE_FINISH:
/* Consume trailing whitespaces */
if (is_whitespace(*str)) {
str++;
} else {
goto error;
}
}
}
if (pa_json_object_get_type(o) == PA_JSON_TYPE_INIT) {
/* We didn't actually get any data */
pa_log("No data while parsing json string: '%s' till '%s'", str, pa_strnull(end));
goto error;
}
*obj = o;
return str;
error:
pa_json_object_free(o);
return NULL;
}
pa_json_object* pa_json_parse(const char *str) {
pa_json_object *obj;
str = parse_value(str, NULL, &obj, 0);
if (!str) {
pa_log("JSON parsing failed");
return NULL;
}
if (*str != '\0') {
pa_log("Unable to parse complete JSON string, remainder is: %s", str);
pa_json_object_free(obj);
return NULL;
}
return obj;
}
pa_json_type pa_json_object_get_type(const pa_json_object *obj) {
return obj->type;
}
void pa_json_object_free(pa_json_object *obj) {
switch (pa_json_object_get_type(obj)) {
case PA_JSON_TYPE_INIT:
case PA_JSON_TYPE_INT:
case PA_JSON_TYPE_DOUBLE:
case PA_JSON_TYPE_BOOL:
case PA_JSON_TYPE_NULL:
break;
case PA_JSON_TYPE_STRING:
pa_xfree(obj->string_value);
break;
case PA_JSON_TYPE_OBJECT:
clear_object(&obj->values);
break;
case PA_JSON_TYPE_ARRAY:
clear_array(&obj->values);
break;
default:
pa_assert_not_reached();
}
pa_xfree(obj);
}
int pa_json_object_get_int(const pa_json_object *o) {
pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_INT);
return o->int_value;
}
double pa_json_object_get_double(const pa_json_object *o) {
pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_DOUBLE);
return o->double_value;
}
bool pa_json_object_get_bool(const pa_json_object *o) {
pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_BOOL);
return o->bool_value;
}
const char* pa_json_object_get_string(const pa_json_object *o) {
pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_STRING);
return o->string_value;
}
const pa_json_object* pa_json_object_get_object_member(const pa_json_object *o, const char *name) {
pa_json_item *item;
pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT);
pw_array_for_each(item, &o->values) {
if (pa_streq(item->key, name))
return item->value;
}
return NULL;
}
int pa_json_object_get_array_length(const pa_json_object *o) {
pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY);
return pw_array_get_len(&o->values, const pa_json_object*);
}
const pa_json_object* pa_json_object_get_array_member(const pa_json_object *o, int index) {
pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY);
return pw_array_get_unchecked_s(&o->values, index, sizeof(pa_json_object*),
const pa_json_object);
}
bool pa_json_object_equal(const pa_json_object *o1, const pa_json_object *o2) {
int i;
if (pa_json_object_get_type(o1) != pa_json_object_get_type(o2))
return false;
switch (pa_json_object_get_type(o1)) {
case PA_JSON_TYPE_NULL:
return true;
case PA_JSON_TYPE_BOOL:
return o1->bool_value == o2->bool_value;
case PA_JSON_TYPE_INT:
return o1->int_value == o2->int_value;
case PA_JSON_TYPE_DOUBLE:
return PA_DOUBLE_IS_EQUAL(o1->double_value, o2->double_value);
case PA_JSON_TYPE_STRING:
return pa_streq(o1->string_value, o2->string_value);
case PA_JSON_TYPE_ARRAY:
if (pa_json_object_get_array_length(o1) != pa_json_object_get_array_length(o2))
return false;
for (i = 0; i < pa_json_object_get_array_length(o1); i++) {
if (!pa_json_object_equal(pa_json_object_get_array_member(o1, i),
pa_json_object_get_array_member(o2, i)))
return false;
}
return true;
case PA_JSON_TYPE_OBJECT: {
const pa_json_object *value;
const pa_json_item *item;
if (o1->values.size != o2->values.size)
return false;
pw_array_for_each(item, &o1->values) {
value = pa_json_object_get_object_member(o2, item->key);
if (!value || !pa_json_object_equal(item->value, value))
return false;
}
return true;
}
default:
pa_assert_not_reached();
}
}

53
src/json.h Normal file
View file

@ -0,0 +1,53 @@
/***
This file is part of PulseAudio.
Copyright 2016 Arun Raghavan <mail@arunraghavan.net>
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
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.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#include <stdbool.h>
#define PA_DOUBLE_IS_EQUAL(x, y) (((x) - (y)) < 0.000001 && ((x) - (y)) > -0.000001)
typedef enum {
PA_JSON_TYPE_INIT = 0,
PA_JSON_TYPE_NULL,
PA_JSON_TYPE_INT,
PA_JSON_TYPE_DOUBLE,
PA_JSON_TYPE_BOOL,
PA_JSON_TYPE_STRING,
PA_JSON_TYPE_ARRAY,
PA_JSON_TYPE_OBJECT,
} pa_json_type;
typedef struct pa_json_object pa_json_object;
pa_json_object* pa_json_parse(const char *str);
pa_json_type pa_json_object_get_type(const pa_json_object *obj);
void pa_json_object_free(pa_json_object *obj);
/* All pointer members that are returned are valid while the corresponding object is valid */
int pa_json_object_get_int(const pa_json_object *o);
double pa_json_object_get_double(const pa_json_object *o);
bool pa_json_object_get_bool(const pa_json_object *o);
const char* pa_json_object_get_string(const pa_json_object *o);
const pa_json_object* pa_json_object_get_object_member(const pa_json_object *o, const char *name);
int pa_json_object_get_array_length(const pa_json_object *o);
const pa_json_object* pa_json_object_get_array_member(const pa_json_object *o, int index);
bool pa_json_object_equal(const pa_json_object *o1, const pa_json_object *o2);

109
src/mainloop-signal.c Normal file
View file

@ -0,0 +1,109 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <errno.h>
#include <spa/utils/list.h>
#include <spa/support/loop.h>
#include <pipewire/log.h>
#include <pipewire/loop.h>
#include <pulse/mainloop-signal.h>
#include "internal.h"
static pa_mainloop_api *api = NULL;
static struct spa_list signals;
static struct pw_loop *loop = NULL;
struct pa_signal_event {
struct spa_list link;
struct spa_source *source;
pa_signal_cb_t callback;
pa_signal_destroy_cb_t destroy;
void *userdata;
};
int pa_signal_init(pa_mainloop_api *a)
{
pa_assert(a);
pa_assert(!api);
api = a;
spa_list_init(&signals);
loop = a->userdata;
return 0;
}
void pa_signal_done(void)
{
pa_signal_event *ev, *t;
pa_assert(api);
spa_list_for_each_safe(ev, t, &signals, link)
pa_signal_free(ev);
spa_list_init(&signals);
api = NULL;
}
static void source_signal_func (void *data, int signal_number)
{
pa_signal_event *ev = data;
if (ev->callback)
ev->callback(api, ev, signal_number, ev->userdata);
}
pa_signal_event* pa_signal_new(int sig, pa_signal_cb_t callback, void *userdata)
{
pa_signal_event *ev;
pa_assert(sig > 0);
pa_assert(callback);
ev = calloc(1, sizeof(pa_signal_event));
ev->source = spa_loop_utils_add_signal(loop->utils, sig, source_signal_func, ev);
ev->callback = callback;
ev->userdata = userdata;
spa_list_append(&signals, &ev->link);
return ev;
}
void pa_signal_free(pa_signal_event *e)
{
pa_assert(e);
spa_list_remove(&e->link);
spa_loop_utils_destroy_source(loop->utils, e->source);
if (e->destroy)
e->destroy(api, e, e->userdata);
free(e);
}
void pa_signal_set_destroy(pa_signal_event *e, pa_signal_destroy_cb_t callback)
{
pa_assert(e);
e->destroy = callback;
}

251
src/mainloop.c Normal file
View file

@ -0,0 +1,251 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <errno.h>
#include <pipewire/log.h>
#include <pipewire/loop.h>
#include <pulse/mainloop.h>
#include "internal.h"
static void do_stop(void *data, uint64_t count)
{
struct pa_mainloop *this = data;
this->quit = true;
}
static pa_io_event* api_io_new(pa_mainloop_api*a, int fd, pa_io_event_flags_t events, pa_io_event_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
static void api_io_enable(pa_io_event* e, pa_io_event_flags_t events)
{
pw_log_warn("Not Implemented");
}
static void api_io_free(pa_io_event* e)
{
pw_log_warn("Not Implemented");
}
static void api_io_set_destroy(pa_io_event *e, pa_io_event_destroy_cb_t cb)
{
pw_log_warn("Not Implemented");
}
static pa_time_event* api_time_new(pa_mainloop_api*a, const struct timeval *tv, pa_time_event_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
static void api_time_restart(pa_time_event* e, const struct timeval *tv)
{
pw_log_warn("Not Implemented");
}
static void api_time_free(pa_time_event* e)
{
pw_log_warn("Not Implemented");
}
static void api_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t cb)
{
pw_log_warn("Not Implemented");
}
static pa_defer_event* api_defer_new(pa_mainloop_api*a, pa_defer_event_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
static void api_defer_enable(pa_defer_event* e, int b)
{
pw_log_warn("Not Implemented");
}
static void api_defer_free(pa_defer_event* e)
{
pw_log_warn("Not Implemented");
}
static void api_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_t cb)
{
pw_log_warn("Not Implemented");
}
static void api_quit(pa_mainloop_api*a, int retval)
{
pa_mainloop *m = SPA_CONTAINER_OF(a, pa_mainloop, api);
m->quit = true;
m->retval = retval;
pa_mainloop_wakeup(m);
}
static const pa_mainloop_api api =
{
.io_new = api_io_new,
.io_enable = api_io_enable,
.io_free = api_io_free,
.io_set_destroy = api_io_set_destroy,
.time_new = api_time_new,
.time_restart = api_time_restart,
.time_free = api_time_free,
.time_set_destroy = api_time_set_destroy,
.defer_new = api_defer_new,
.defer_enable = api_defer_enable,
.defer_free = api_defer_free,
.defer_set_destroy = api_defer_set_destroy,
.quit = api_quit,
};
pa_mainloop *pa_mainloop_new(void)
{
pa_mainloop *loop;
loop = calloc(1, sizeof(pa_mainloop));
if (loop == NULL)
return NULL;
loop->loop = pw_loop_new(NULL);
if (loop->loop == NULL)
goto no_loop;
loop->event = pw_loop_add_event(loop->loop, do_stop, loop);
loop->api = api;
loop->api.userdata = loop->loop;
return loop;
no_loop:
free(loop);
return NULL;
}
void pa_mainloop_free(pa_mainloop* m)
{
pw_loop_destroy(m->loop);
free(m);
}
int pa_mainloop_prepare(pa_mainloop *m, int timeout)
{
if (m->quit)
return -2;
m->timeout = timeout;
m->n_events = -EIO;
return 0;
}
/** Execute the previously prepared poll. Returns a negative value on error.*/
int pa_mainloop_poll(pa_mainloop *m)
{
if (m->quit)
return -2;
return m->n_events = pw_loop_iterate(m->loop, m->timeout);
}
int pa_mainloop_dispatch(pa_mainloop *m)
{
if (m->quit)
return -2;
return m->n_events;
}
int pa_mainloop_get_retval(pa_mainloop *m)
{
return m->retval;
}
/** Run a single iteration of the main loop. This is a convenience function
for pa_mainloop_prepare(), pa_mainloop_poll() and pa_mainloop_dispatch().
Returns a negative value on error or exit request. If block is nonzero,
block for events if none are queued. Optionally return the return value as
specified with the main loop's quit() routine in the integer variable retval points
to. On success returns the number of sources dispatched in this iteration. */
int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval)
{
int r;
pa_assert(m);
if ((r = pa_mainloop_prepare(m, block ? -1 : 0)) < 0)
goto quit;
if ((r = pa_mainloop_poll(m)) < 0)
goto quit;
if ((r = pa_mainloop_dispatch(m)) < 0)
goto quit;
return r;
quit:
if ((r == -2) && retval)
*retval = pa_mainloop_get_retval(m);
return r;
}
int pa_mainloop_run(pa_mainloop *m, int *retval)
{
int r;
while ((r = pa_mainloop_iterate(m, 1, retval)) >= 0)
;
if (r == -2)
return 1;
else
return -1;
}
pa_mainloop_api* pa_mainloop_get_api(pa_mainloop *m)
{
pa_assert(m);
return &m->api;
}
void pa_mainloop_quit(pa_mainloop *m, int retval)
{
pa_assert(m);
m->api.quit(&m->api, retval);
}
void pa_mainloop_wakeup(pa_mainloop *m)
{
pa_assert(m);
pw_loop_signal_event(m->loop, m->event);
}
void pa_mainloop_set_poll_func(pa_mainloop *m, pa_poll_func poll_func, void *userdata)
{
pw_log_warn("Not Implemented");
}

386
src/map-file Normal file
View file

@ -0,0 +1,386 @@
PULSE_0 {
global:
pa_ascii_filter;
pa_ascii_valid;
pa_bytes_per_second;
pa_bytes_snprint;
pa_bytes_to_usec;
pa_channel_map_can_balance;
pa_channel_map_can_fade;
pa_channel_map_can_lfe_balance;
pa_channel_map_compatible;
pa_channel_map_equal;
pa_channel_map_has_position;
pa_channel_map_init;
pa_channel_map_init_auto;
pa_channel_map_init_extend;
pa_channel_map_init_mono;
pa_channel_map_init_stereo;
pa_channel_map_mask;
pa_channel_map_parse;
pa_channel_map_snprint;
pa_channel_map_superset;
pa_channel_map_to_name;
pa_channel_map_to_pretty_name;
pa_channel_map_valid;
pa_channel_position_from_string;
pa_channel_position_to_pretty_string;
pa_channel_position_to_string;
pa_channels_valid;
pa_context_add_autoload;
pa_context_connect;
pa_context_disconnect;
pa_context_drain;
pa_context_errno;
pa_context_exit_daemon;
pa_context_get_autoload_info_by_index;
pa_context_get_autoload_info_by_name;
pa_context_get_autoload_info_list;
pa_context_get_card_info_by_index;
pa_context_get_card_info_by_name;
pa_context_get_card_info_list;
pa_context_get_client_info;
pa_context_get_client_info_list;
pa_context_get_index;
pa_context_get_module_info;
pa_context_get_module_info_list;
pa_context_get_protocol_version;
pa_context_get_sample_info_by_index;
pa_context_get_sample_info_by_name;
pa_context_get_sample_info_list;
pa_context_get_server;
pa_context_get_server_info;
pa_context_get_server_protocol_version;
pa_context_get_sink_info_by_index;
pa_context_get_sink_info_by_name;
pa_context_get_sink_info_list;
pa_context_get_sink_input_info;
pa_context_get_sink_input_info_list;
pa_context_get_source_info_by_index;
pa_context_get_source_info_by_name;
pa_context_get_source_info_list;
pa_context_get_source_output_info;
pa_context_get_source_output_info_list;
pa_context_set_port_latency_offset;
pa_context_get_state;
pa_context_get_tile_size;
pa_context_is_local;
pa_context_is_pending;
pa_context_kill_client;
pa_context_kill_sink_input;
pa_context_kill_source_output;
pa_context_load_cookie_from_file;
pa_context_load_module;
pa_context_move_sink_input_by_index;
pa_context_move_sink_input_by_name;
pa_context_move_source_output_by_index;
pa_context_move_source_output_by_name;
pa_context_new;
pa_context_new_with_proplist;
pa_context_play_sample;
pa_context_play_sample_with_proplist;
pa_context_proplist_remove;
pa_context_proplist_update;
pa_context_ref;
pa_context_remove_autoload_by_index;
pa_context_remove_autoload_by_name;
pa_context_remove_sample;
pa_context_rttime_new;
pa_context_rttime_restart;
pa_context_set_card_profile_by_index;
pa_context_set_card_profile_by_name;
pa_context_set_default_sink;
pa_context_set_default_source;
pa_context_set_event_callback;
pa_context_set_name;
pa_context_set_sink_input_mute;
pa_context_set_sink_input_volume;
pa_context_set_sink_mute_by_index;
pa_context_set_sink_mute_by_name;
pa_context_set_sink_port_by_index;
pa_context_set_sink_port_by_name;
pa_context_set_sink_volume_by_index;
pa_context_set_sink_volume_by_name;
pa_context_set_source_output_mute;
pa_context_set_source_output_volume;
pa_context_set_source_mute_by_index;
pa_context_set_source_mute_by_name;
pa_context_set_source_port_by_index;
pa_context_set_source_port_by_name;
pa_context_set_source_volume_by_index;
pa_context_set_source_volume_by_name;
pa_context_set_state_callback;
pa_context_set_subscribe_callback;
pa_context_stat;
pa_context_subscribe;
pa_context_suspend_sink_by_index;
pa_context_suspend_sink_by_name;
pa_context_suspend_source_by_index;
pa_context_suspend_source_by_name;
pa_context_unload_module;
pa_context_unref;
pa_cvolume_avg;
pa_cvolume_avg_mask;
pa_cvolume_channels_equal_to;
pa_cvolume_compatible;
pa_cvolume_compatible_with_channel_map;
pa_cvolume_dec;
pa_cvolume_equal;
pa_cvolume_get_balance;
pa_cvolume_get_fade;
pa_cvolume_get_lfe_balance;
pa_cvolume_get_position;
pa_cvolume_inc;
pa_cvolume_inc_clamp;
pa_cvolume_init;
pa_cvolume_max;
pa_cvolume_max_mask;
pa_cvolume_merge;
pa_cvolume_min;
pa_cvolume_min_mask;
pa_cvolume_remap;
pa_cvolume_scale;
pa_cvolume_scale_mask;
pa_cvolume_set;
pa_cvolume_set_balance;
pa_cvolume_set_fade;
pa_cvolume_set_lfe_balance;
pa_cvolume_set_position;
pa_cvolume_snprint;
pa_cvolume_snprint_verbose;
pa_cvolume_valid;
pa_direction_to_string;
pa_direction_valid;
pa_encoding_from_string;
pa_encoding_to_string;
pa_ext_device_manager_delete;
pa_ext_device_manager_enable_role_device_priority_routing;
pa_ext_device_manager_read;
pa_ext_device_manager_reorder_devices_for_role;
pa_ext_device_manager_set_device_description;
pa_ext_device_manager_set_subscribe_cb;
pa_ext_device_manager_subscribe;
pa_ext_device_manager_test;
pa_ext_device_restore_read_formats;
pa_ext_device_restore_read_formats_all;
pa_ext_device_restore_save_formats;
pa_ext_device_restore_set_subscribe_cb;
pa_ext_device_restore_subscribe;
pa_ext_device_restore_test;
pa_ext_stream_restore_delete;
pa_ext_stream_restore_read;
pa_ext_stream_restore_set_subscribe_cb;
pa_ext_stream_restore_subscribe;
pa_ext_stream_restore_test;
pa_ext_stream_restore_write;
pa_format_info_copy;
pa_format_info_free;
pa_format_info_from_string;
pa_format_info_from_sample_spec;
pa_format_info_get_prop_type;
pa_format_info_get_prop_int;
pa_format_info_get_prop_int_range;
pa_format_info_get_prop_int_array;
pa_format_info_get_prop_string;
pa_format_info_get_prop_string_array;
pa_format_info_free_string_array;
pa_format_info_is_compatible;
pa_format_info_is_pcm;
pa_format_info_new;
pa_format_info_set_channel_map;
pa_format_info_set_channels;
pa_format_info_set_prop_int;
pa_format_info_set_prop_int_array;
pa_format_info_set_prop_int_range;
pa_format_info_set_prop_string;
pa_format_info_set_prop_string_array;
pa_format_info_set_rate;
pa_format_info_set_sample_format;
pa_format_info_snprint;
pa_format_info_to_sample_spec;
pa_format_info_valid;
pa_frame_size;
pa_get_binary_name;
pa_get_fqdn;
pa_get_home_dir;
pa_get_host_name;
pa_get_library_version;
pa_gettimeofday;
pa_get_user_name;
pa_glib_mainloop_free;
pa_glib_mainloop_get_api;
pa_glib_mainloop_new;
pa_locale_to_utf8;
pa_mainloop_api_once;
pa_mainloop_dispatch;
pa_mainloop_free;
pa_mainloop_get_api;
pa_mainloop_get_retval;
pa_mainloop_iterate;
pa_mainloop_new;
pa_mainloop_poll;
pa_mainloop_prepare;
pa_mainloop_quit;
pa_mainloop_run;
pa_mainloop_set_poll_func;
pa_mainloop_wakeup;
pa_msleep;
pa_operation_cancel;
pa_operation_get_state;
pa_operation_ref;
pa_operation_set_state_callback;
pa_operation_unref;
pa_parse_sample_format;
pa_path_get_filename;
pa_proplist_clear;
pa_proplist_contains;
pa_proplist_copy;
pa_proplist_equal;
pa_proplist_free;
pa_proplist_from_string;
pa_proplist_get;
pa_proplist_gets;
pa_proplist_isempty;
pa_proplist_iterate;
pa_proplist_key_valid;
pa_proplist_new;
pa_proplist_set;
pa_proplist_setf;
pa_proplist_setp;
pa_proplist_sets;
pa_proplist_size;
pa_proplist_to_string;
pa_proplist_to_string_sep;
pa_proplist_unset;
pa_proplist_unset_many;
pa_proplist_update;
pa_rtclock_now;
pa_sample_format_is_be;
pa_sample_format_is_le;
pa_sample_format_to_string;
pa_sample_format_valid;
pa_sample_rate_valid;
pa_sample_size;
pa_sample_size_of_format;
pa_sample_spec_equal;
pa_sample_spec_init;
pa_sample_spec_snprint;
pa_sample_spec_valid;
pa_signal_done;
pa_signal_free;
pa_signal_init;
pa_signal_new;
pa_signal_set_destroy;
pa_simple_drain;
pa_simple_flush;
pa_simple_free;
pa_simple_get_latency;
pa_simple_new;
pa_simple_read;
pa_simple_write;
pa_stream_begin_write;
pa_stream_cancel_write;
pa_stream_connect_playback;
pa_stream_connect_record;
pa_stream_connect_upload;
pa_stream_cork;
pa_stream_disconnect;
pa_stream_drain;
pa_stream_drop;
pa_stream_finish_upload;
pa_stream_flush;
pa_stream_get_buffer_attr;
pa_stream_get_channel_map;
pa_stream_get_context;
pa_stream_get_device_index;
pa_stream_get_device_name;
pa_stream_get_format_info;
pa_stream_get_index;
pa_stream_get_latency;
pa_stream_get_monitor_stream;
pa_stream_get_sample_spec;
pa_stream_get_state;
pa_stream_get_time;
pa_stream_get_timing_info;
pa_stream_get_underflow_index;
pa_stream_is_corked;
pa_stream_is_suspended;
pa_stream_new;
pa_stream_new_extended;
pa_stream_new_with_proplist;
pa_stream_peek;
pa_stream_prebuf;
pa_stream_proplist_remove;
pa_stream_proplist_update;
pa_stream_readable_size;
pa_stream_ref;
pa_stream_set_buffer_attr;
pa_stream_set_buffer_attr_callback;
pa_stream_set_event_callback;
pa_stream_set_latency_update_callback;
pa_stream_set_monitor_stream;
pa_stream_set_moved_callback;
pa_stream_set_name;
pa_stream_set_overflow_callback;
pa_stream_set_read_callback;
pa_stream_set_started_callback;
pa_stream_set_state_callback;
pa_stream_set_suspended_callback;
pa_stream_set_underflow_callback;
pa_stream_set_write_callback;
pa_stream_trigger;
pa_stream_unref;
pa_stream_update_sample_rate;
pa_stream_update_timing_info;
pa_stream_writable_size;
pa_stream_write;
pa_stream_write_ext_free;
pa_strerror;
pa_sw_cvolume_divide;
pa_sw_cvolume_divide_scalar;
pa_sw_cvolume_multiply;
pa_sw_cvolume_multiply_scalar;
pa_sw_cvolume_snprint_dB;
pa_sw_volume_divide;
pa_sw_volume_from_dB;
pa_sw_volume_from_linear;
pa_sw_volume_multiply;
pa_sw_volume_snprint_dB;
pa_sw_volume_to_dB;
pa_sw_volume_to_linear;
pa_threaded_mainloop_accept;
pa_threaded_mainloop_free;
pa_threaded_mainloop_get_api;
pa_threaded_mainloop_get_retval;
pa_threaded_mainloop_in_thread;
pa_threaded_mainloop_lock;
pa_threaded_mainloop_new;
pa_threaded_mainloop_set_name;
pa_threaded_mainloop_signal;
pa_threaded_mainloop_start;
pa_threaded_mainloop_stop;
pa_threaded_mainloop_unlock;
pa_threaded_mainloop_wait;
pa_timeval_add;
pa_timeval_age;
pa_timeval_cmp;
pa_timeval_diff;
pa_timeval_load;
pa_timeval_store;
pa_timeval_sub;
pa_usec_to_bytes;
pa_utf8_filter;
pa_utf8_to_locale;
pa_utf8_valid;
pa_volume_snprint;
pa_volume_snprint_verbose;
pa_xfree;
pa_xmalloc;
pa_xmalloc0;
pa_xmemdup;
pa_xrealloc;
pa_xstrdup;
pa_xstrndup;
local:
*;
};

47
src/meson.build Normal file
View file

@ -0,0 +1,47 @@
pipewire_pulseaudio_sources = [
'bitset.c',
'channelmap.c',
'context.c',
'core-format.c',
'direction.c',
'error.c',
'ext-device-manager.c',
'ext-device-restore.c',
'format.c',
'introspect.c',
'json.c',
'mainloop.c',
'mainloop-signal.c',
'operation.c',
'proplist.c',
'rtclock.c',
'sample.c',
'scache.c',
'stream.c',
'strbuf.c',
'subscribe.c',
'utf8.c',
'util.c',
'version.c',
'volume.c',
'xmalloc.c',
'pipewire-pulseaudio.c',
]
pipewire_pulseaudio_c_args = [
'-DHAVE_CONFIG_H',
'-DPIC',
]
mapfile = 'map-file'
vflag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), mapfile)
pipewire_pulseaudio = shared_library('pulse',
pipewire_pulseaudio_sources,
soversion : '0',
c_args : pipewire_pulseaudio_c_args,
link_args : vflag,
include_directories : [configinc],
dependencies : [pipewire_dep, pulseaudio_dep, mathlib],
install : false,
)

153
src/operation.c Normal file
View file

@ -0,0 +1,153 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <errno.h>
#include <spa/utils/list.h>
#include <pipewire/log.h>
#include <pulse/operation.h>
#include "internal.h"
pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t cb, size_t userdata_size) {
pa_operation *o;
pa_assert(c);
o = calloc(1, sizeof(pa_operation) + userdata_size);
o->refcount = 1;
o->context = c;
o->stream = s;
o->seq = ++c->seq;
o->state = PA_OPERATION_RUNNING;
o->callback = cb;
o->userdata = SPA_MEMBER(o, sizeof(pa_operation), void);
spa_list_append(&c->operations, &o->link);
pa_operation_ref(o);
pw_log_debug("new %p %d", o, o->seq);
pw_core_proxy_sync(c->core_proxy, o->seq);
return o;
}
pa_operation *pa_operation_ref(pa_operation *o)
{
pa_assert(o);
pa_assert(o->refcount >= 1);
o->refcount++;
return o;
}
static void operation_free(pa_operation *o)
{
pa_assert(!o->context);
pa_assert(!o->stream);
pw_log_debug("%p %d", o, o->seq);
free(o);
}
static void operation_unlink(pa_operation *o) {
pa_assert(o);
pw_log_debug("%p %d", o, o->seq);
if (o->context) {
pa_assert(o->refcount >= 2);
spa_list_remove(&o->link);
pa_operation_unref(o);
o->context = NULL;
}
o->stream = NULL;
o->callback = NULL;
o->userdata = NULL;
o->state_callback = NULL;
o->state_userdata = NULL;
}
void pa_operation_unref(pa_operation *o)
{
pa_assert(o);
pa_assert(o->refcount >= 1);
if (--o->refcount == 0)
operation_free(o);
}
static void operation_set_state(pa_operation *o, pa_operation_state_t st) {
pa_assert(o);
pa_assert(o->refcount >= 1);
if (st == o->state)
return;
pa_operation_ref(o);
pw_log_debug("new state %p %d %d", o, o->seq, st);
o->state = st;
if (o->state_callback)
o->state_callback(o, o->state_userdata);
if ((o->state == PA_OPERATION_DONE) || (o->state == PA_OPERATION_CANCELED))
operation_unlink(o);
pa_operation_unref(o);
}
void pa_operation_cancel(pa_operation *o)
{
pa_assert(o);
pa_assert(o->refcount >= 1);
operation_set_state(o, PA_OPERATION_CANCELED);
}
void pa_operation_done(pa_operation *o) {
pa_assert(o);
pa_assert(o->refcount >= 1);
operation_set_state(o, PA_OPERATION_DONE);
}
pa_operation_state_t pa_operation_get_state(pa_operation *o)
{
pa_assert(o);
pa_assert(o->refcount >= 1);
return o->state;
}
void pa_operation_set_state_callback(pa_operation *o, pa_operation_notify_cb_t cb, void *userdata)
{
pa_assert(o);
pa_assert(o->refcount >= 1);
if (o->state == PA_OPERATION_DONE || o->state == PA_OPERATION_CANCELED)
return;
o->state_callback = cb;
o->state_userdata = userdata;
}

30
src/pipewire-pulseaudio.c Normal file
View file

@ -0,0 +1,30 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <pipewire/pipewire.h>
static void reg(void) __attribute__ ((constructor));
static void reg(void)
{
pw_init(NULL, NULL);
}

271
src/proplist.c Normal file
View file

@ -0,0 +1,271 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <string.h>
#include <pipewire/log.h>
#include <pipewire/properties.h>
#include <pulse/proplist.h>
#include "internal.h"
#include "strbuf.h"
pa_proplist* pa_proplist_new_dict(struct spa_dict *dict)
{
pa_proplist *p;
p = calloc(1, sizeof(struct pa_proplist));
if (p == NULL)
return NULL;
if (dict)
p->props = pw_properties_new_dict(dict);
else
p->props = pw_properties_new(NULL, NULL);
return p;
}
pa_proplist* pa_proplist_new_props(struct pw_properties *props)
{
return pa_proplist_new_dict(&props->dict);
}
pa_proplist* pa_proplist_new(void)
{
return pa_proplist_new_dict(NULL);
}
void pa_proplist_free(pa_proplist* p)
{
pw_properties_free(p->props);
free(p);
}
int pa_proplist_key_valid(const char *key)
{
const char *p;
for (p = key; *p; p++)
if ((unsigned char) *p >= 128)
return 0;
if (strlen(key) < 1)
return 0;
return 1;
}
int pa_proplist_sets(pa_proplist *p, const char *key, const char *value)
{
pw_properties_set(p->props, key, value);
return 0;
}
int pa_proplist_setp(pa_proplist *p, const char *pair)
{
pw_log_warn("Not Implemented");
return -1;
}
int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...)
{
pw_log_warn("Not Implemented");
return -1;
}
int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nbytes)
{
pw_log_warn("Not Implemented");
return -1;
}
const char *pa_proplist_gets(pa_proplist *p, const char *key)
{
return pw_properties_get(p->props, key);
}
int pa_proplist_get(pa_proplist *p, const char *key, const void **data, size_t *nbytes)
{
const char *val;
spa_assert(p);
spa_assert(key);
val = pw_properties_get(p->props, key);
*data = val;
*nbytes = val ? strlen(val) : 0;
return 0;
}
void pa_proplist_update(pa_proplist *p, pa_update_mode_t mode, const pa_proplist *other)
{
spa_assert(p);
pw_log_warn("Not Implemented");
}
int pa_proplist_unset(pa_proplist *p, const char *key)
{
spa_assert(p);
spa_assert(key);
if (!pa_proplist_key_valid(key))
return -1;
return pw_properties_set(p->props, key, NULL);
}
int pa_proplist_unset_many(pa_proplist *p, const char * const keys[])
{
const char * const * k;
int n = 0;
spa_assert(p);
spa_assert(keys);
for (k = keys; *k; k++)
if (!pa_proplist_key_valid(*k))
return -1;
for (k = keys; *k; k++)
if (pa_proplist_unset(p, *k) >= 0)
n++;
return n;
}
const char *pa_proplist_iterate(pa_proplist *p, void **state)
{
spa_assert(p);
spa_assert(state);
return pw_properties_iterate(p->props, state);
}
char *pa_proplist_to_string(pa_proplist *p)
{
spa_assert(p);
pw_log_warn("Not Implemented");
return NULL;
}
char *pa_proplist_to_string_sep(pa_proplist *p, const char *sep)
{
const char *key;
void *state = NULL;
pa_strbuf *buf;
spa_assert(p);
spa_assert(sep);
buf = pa_strbuf_new();
while ((key = pa_proplist_iterate(p, &state))) {
const char *v;
const char *t;
if (!pa_strbuf_isempty(buf))
pa_strbuf_puts(buf, sep);
if ((v = pa_proplist_gets(p, key)) == NULL)
continue;
pa_strbuf_printf(buf, "%s = \"", key);
for (t = v;;) {
size_t h;
h = strcspn(t, "\"");
if (h > 0)
pa_strbuf_putsn(buf, t, h);
t += h;
if (*t == 0)
break;
pa_assert(*t == '"');
pa_strbuf_puts(buf, "\\\"");
t++;
}
pa_strbuf_puts(buf, "\"");
}
return pa_strbuf_to_string_free(buf);
}
pa_proplist *pa_proplist_from_string(const char *str)
{
spa_assert(str);
pw_log_warn("Not Implemented");
return NULL;
}
int pa_proplist_contains(pa_proplist *p, const char *key)
{
spa_assert(p);
spa_assert(key);
if (!pa_proplist_key_valid(key))
return -1;
if (pw_properties_get(p->props, key) == NULL)
return 0;
return 1;
}
void pa_proplist_clear(pa_proplist *p)
{
spa_assert(p);
pw_log_warn("Not Implemented");
}
pa_proplist* pa_proplist_copy(const pa_proplist *p)
{
pa_proplist *c;
spa_assert(p);
c = calloc(1, sizeof(struct pa_proplist));
if (c == NULL)
return NULL;
c->props = pw_properties_copy(p->props);
return c;
}
unsigned pa_proplist_size(pa_proplist *p)
{
spa_assert(p);
return p->props->dict.n_items;
}
int pa_proplist_isempty(pa_proplist *p)
{
spa_assert(p);
return p->props->dict.n_items == 0 ? 1 : 0;
}
int pa_proplist_equal(pa_proplist *a, pa_proplist *b)
{
spa_assert(a);
spa_assert(b);
pw_log_warn("Not Implemented");
return 0;
}

34
src/rtclock.c Normal file
View file

@ -0,0 +1,34 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <time.h>
#include <spa/utils/defs.h>
#include <pulse/rtclock.h>
pa_usec_t pa_rtclock_now(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec * SPA_USEC_PER_SEC +
ts.tv_nsec * SPA_NSEC_PER_USEC;
}

134
src/sample-util.h Normal file
View file

@ -0,0 +1,134 @@
#ifndef foosampleutilhfoo
#define foosampleutilhfoo
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
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.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#include <inttypes.h>
#include <limits.h>
#include <pulse/gccmacro.h>
#include <pulse/sample.h>
#include <pulse/volume.h>
#include <pulse/channelmap.h>
size_t pa_frame_align(size_t l, const pa_sample_spec *ss) PA_GCC_PURE;
bool pa_frame_aligned(size_t l, const pa_sample_spec *ss) PA_GCC_PURE;
void pa_interleave(const void *src[], unsigned channels, void *dst, size_t ss, unsigned n);
void pa_deinterleave(const void *src, void *dst[], unsigned channels, size_t ss, unsigned n);
void pa_sample_clamp(pa_sample_format_t format, void *dst, size_t dstr, const void *src, size_t sstr, unsigned n);
static inline int32_t pa_mult_s16_volume(int16_t v, int32_t cv) {
#ifdef HAVE_FAST_64BIT_OPERATIONS
/* Multiply with 64 bit integers on 64 bit platforms */
return (v * (int64_t) cv) >> 16;
#else
/* Multiplying the 32 bit volume factor with the
* 16 bit sample might result in an 48 bit value. We
* want to do without 64 bit integers and hence do
* the multiplication independently for the HI and
* LO part of the volume. */
int32_t hi = cv >> 16;
int32_t lo = cv & 0xFFFF;
return ((v * lo) >> 16) + (v * hi);
#endif
}
pa_usec_t pa_bytes_to_usec_round_up(uint64_t length, const pa_sample_spec *spec);
size_t pa_usec_to_bytes_round_up(pa_usec_t t, const pa_sample_spec *spec);
typedef void (*pa_do_volume_func_t) (void *samples, const void *volumes, unsigned channels, unsigned length);
pa_do_volume_func_t pa_get_volume_func(pa_sample_format_t f);
void pa_set_volume_func(pa_sample_format_t f, pa_do_volume_func_t func);
size_t pa_convert_size(size_t size, const pa_sample_spec *from, const pa_sample_spec *to);
#define PA_CHANNEL_POSITION_MASK_LEFT \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT)) \
#define PA_CHANNEL_POSITION_MASK_RIGHT \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT))
#define PA_CHANNEL_POSITION_MASK_CENTER \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER))
#define PA_CHANNEL_POSITION_MASK_FRONT \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER))
#define PA_CHANNEL_POSITION_MASK_REAR \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER))
#define PA_CHANNEL_POSITION_MASK_LFE \
PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE)
#define PA_CHANNEL_POSITION_MASK_HFE \
(PA_CHANNEL_POSITION_MASK_REAR | PA_CHANNEL_POSITION_MASK_FRONT \
| PA_CHANNEL_POSITION_MASK_LEFT | PA_CHANNEL_POSITION_MASK_RIGHT \
| PA_CHANNEL_POSITION_MASK_CENTER)
#define PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER))
#define PA_CHANNEL_POSITION_MASK_TOP \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER))
#define PA_CHANNEL_POSITION_MASK_ALL \
((pa_channel_position_mask_t) (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_MAX)-1))
#endif

319
src/sample.c Normal file
View file

@ -0,0 +1,319 @@
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
Copyright 2018 Wim Taymans <wim.taymans@gmail.com>
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
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.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <string.h>
#include <spa/utils/defs.h>
#include <pulse/timeval.h>
#include <pulse/sample.h>
#define pa_init_i18n()
#define _(String) (String)
static const size_t size_table[] = {
[PA_SAMPLE_U8] = 1,
[PA_SAMPLE_ULAW] = 1,
[PA_SAMPLE_ALAW] = 1,
[PA_SAMPLE_S16LE] = 2,
[PA_SAMPLE_S16BE] = 2,
[PA_SAMPLE_FLOAT32LE] = 4,
[PA_SAMPLE_FLOAT32BE] = 4,
[PA_SAMPLE_S32LE] = 4,
[PA_SAMPLE_S32BE] = 4,
[PA_SAMPLE_S24LE] = 3,
[PA_SAMPLE_S24BE] = 3,
[PA_SAMPLE_S24_32LE] = 4,
[PA_SAMPLE_S24_32BE] = 4
};
size_t pa_sample_size_of_format(pa_sample_format_t f)
{
spa_assert(pa_sample_format_valid(f));
return size_table[f];
}
size_t pa_sample_size(const pa_sample_spec * spec)
{
spa_assert(spec);
spa_assert(pa_sample_spec_valid(spec));
return size_table[spec->format];
}
size_t pa_frame_size(const pa_sample_spec * spec)
{
spa_assert(spec);
spa_assert(pa_sample_spec_valid(spec));
return size_table[spec->format] * spec->channels;
}
size_t pa_bytes_per_second(const pa_sample_spec * spec)
{
spa_assert(spec);
spa_assert(pa_sample_spec_valid(spec));
return spec->rate * size_table[spec->format] * spec->channels;
}
pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec * spec)
{
spa_assert(spec);
spa_assert(pa_sample_spec_valid(spec));
return (((pa_usec_t)
(length / (size_table[spec->format] * spec->channels))
* PA_USEC_PER_SEC) / spec->rate);
}
size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec * spec)
{
spa_assert(spec);
spa_assert(pa_sample_spec_valid(spec));
return (size_t) (((t * spec->rate) / PA_USEC_PER_SEC)) *
(size_table[spec->format] * spec->channels);
}
pa_sample_spec *pa_sample_spec_init(pa_sample_spec * spec)
{
spa_assert(spec);
spec->format = PA_SAMPLE_INVALID;
spec->rate = 0;
spec->channels = 0;
return spec;
}
int pa_sample_format_valid(unsigned format)
{
return format < PA_SAMPLE_MAX;
}
int pa_sample_rate_valid(uint32_t rate)
{
/* The extra 1% is due to module-loopback: it temporarily sets
* a higher-than-nominal rate to get rid of excessive buffer
* latency */
return rate > 0 && rate <= PA_RATE_MAX * 101 / 100;
}
int pa_channels_valid(uint8_t channels)
{
return channels > 0 && channels <= PA_CHANNELS_MAX;
}
int pa_sample_spec_valid(const pa_sample_spec * spec)
{
spa_assert(spec);
if (SPA_UNLIKELY(!pa_sample_rate_valid(spec->rate) ||
!pa_channels_valid(spec->channels) ||
!pa_sample_format_valid(spec->format)))
return 0;
return 1;
}
int pa_sample_spec_equal(const pa_sample_spec * a, const pa_sample_spec * b)
{
spa_assert(a);
spa_assert(b);
spa_return_val_if_fail(pa_sample_spec_valid(a), 0);
if (SPA_UNLIKELY(a == b))
return 1;
spa_return_val_if_fail(pa_sample_spec_valid(b), 0);
return
(a->format == b->format) &&
(a->rate == b->rate) && (a->channels == b->channels);
}
const char *pa_sample_format_to_string(pa_sample_format_t f)
{
static const char *const table[] = {
[PA_SAMPLE_U8] = "u8",
[PA_SAMPLE_ALAW] = "aLaw",
[PA_SAMPLE_ULAW] = "uLaw",
[PA_SAMPLE_S16LE] = "s16le",
[PA_SAMPLE_S16BE] = "s16be",
[PA_SAMPLE_FLOAT32LE] = "float32le",
[PA_SAMPLE_FLOAT32BE] = "float32be",
[PA_SAMPLE_S32LE] = "s32le",
[PA_SAMPLE_S32BE] = "s32be",
[PA_SAMPLE_S24LE] = "s24le",
[PA_SAMPLE_S24BE] = "s24be",
[PA_SAMPLE_S24_32LE] = "s24-32le",
[PA_SAMPLE_S24_32BE] = "s24-32be",
};
if (!pa_sample_format_valid(f))
return NULL;
return table[f];
}
char *pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec * spec)
{
spa_assert(s);
spa_assert(l > 0);
spa_assert(spec);
pa_init_i18n();
if (!pa_sample_spec_valid(spec))
snprintf(s, l, _("(invalid)"));
else
snprintf(s, l, _("%s %uch %uHz"),
pa_sample_format_to_string(spec->format),
spec->channels, spec->rate);
return s;
}
char *pa_bytes_snprint(char *s, size_t l, unsigned v)
{
spa_assert(s);
spa_assert(l > 0);
pa_init_i18n();
if (v >= ((unsigned)1024) * 1024 * 1024)
snprintf(s, l, _("%0.1f GiB"),
((double)v) / 1024 / 1024 / 1024);
else if (v >= ((unsigned)1024) * 1024)
snprintf(s, l, _("%0.1f MiB"), ((double)v) / 1024 / 1024);
else if (v >= (unsigned)1024)
snprintf(s, l, _("%0.1f KiB"), ((double)v) / 1024);
else
snprintf(s, l, _("%u B"), (unsigned)v);
return s;
}
pa_sample_format_t pa_parse_sample_format(const char *format)
{
spa_assert(format);
if (strcasecmp(format, "s16le") == 0)
return PA_SAMPLE_S16LE;
else if (strcasecmp(format, "s16be") == 0)
return PA_SAMPLE_S16BE;
else if (strcasecmp(format, "s16ne") == 0
|| strcasecmp(format, "s16") == 0
|| strcasecmp(format, "16") == 0)
return PA_SAMPLE_S16NE;
else if (strcasecmp(format, "s16re") == 0)
return PA_SAMPLE_S16RE;
else if (strcasecmp(format, "u8") == 0 || strcasecmp(format, "8") == 0)
return PA_SAMPLE_U8;
else if (strcasecmp(format, "float32") == 0
|| strcasecmp(format, "float32ne") == 0
|| strcasecmp(format, "float") == 0)
return PA_SAMPLE_FLOAT32NE;
else if (strcasecmp(format, "float32re") == 0)
return PA_SAMPLE_FLOAT32RE;
else if (strcasecmp(format, "float32le") == 0)
return PA_SAMPLE_FLOAT32LE;
else if (strcasecmp(format, "float32be") == 0)
return PA_SAMPLE_FLOAT32BE;
else if (strcasecmp(format, "ulaw") == 0
|| strcasecmp(format, "mulaw") == 0)
return PA_SAMPLE_ULAW;
else if (strcasecmp(format, "alaw") == 0)
return PA_SAMPLE_ALAW;
else if (strcasecmp(format, "s32le") == 0)
return PA_SAMPLE_S32LE;
else if (strcasecmp(format, "s32be") == 0)
return PA_SAMPLE_S32BE;
else if (strcasecmp(format, "s32ne") == 0
|| strcasecmp(format, "s32") == 0
|| strcasecmp(format, "32") == 0)
return PA_SAMPLE_S32NE;
else if (strcasecmp(format, "s32re") == 0)
return PA_SAMPLE_S24RE;
else if (strcasecmp(format, "s24le") == 0)
return PA_SAMPLE_S24LE;
else if (strcasecmp(format, "s24be") == 0)
return PA_SAMPLE_S24BE;
else if (strcasecmp(format, "s24ne") == 0
|| strcasecmp(format, "s24") == 0
|| strcasecmp(format, "24") == 0)
return PA_SAMPLE_S24NE;
else if (strcasecmp(format, "s24re") == 0)
return PA_SAMPLE_S24RE;
else if (strcasecmp(format, "s24-32le") == 0)
return PA_SAMPLE_S24_32LE;
else if (strcasecmp(format, "s24-32be") == 0)
return PA_SAMPLE_S24_32BE;
else if (strcasecmp(format, "s24-32ne") == 0
|| strcasecmp(format, "s24-32") == 0)
return PA_SAMPLE_S24_32NE;
else if (strcasecmp(format, "s24-32re") == 0)
return PA_SAMPLE_S24_32RE;
return PA_SAMPLE_INVALID;
}
int pa_sample_format_is_le(pa_sample_format_t f)
{
spa_assert(pa_sample_format_valid(f));
switch (f) {
case PA_SAMPLE_S16LE:
case PA_SAMPLE_S24LE:
case PA_SAMPLE_S32LE:
case PA_SAMPLE_S24_32LE:
case PA_SAMPLE_FLOAT32LE:
return 1;
case PA_SAMPLE_S16BE:
case PA_SAMPLE_S24BE:
case PA_SAMPLE_S32BE:
case PA_SAMPLE_S24_32BE:
case PA_SAMPLE_FLOAT32BE:
return 0;
default:
return -1;
}
}
int pa_sample_format_is_be(pa_sample_format_t f)
{
int r;
if ((r = pa_sample_format_is_le(f)) < 0)
return r;
return !r;
}

57
src/scache.c Normal file
View file

@ -0,0 +1,57 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <pipewire/log.h>
#include <pulse/scache.h>
#include "internal.h"
int pa_stream_connect_upload(pa_stream *s, size_t length)
{
pw_log_warn("Not Implemented");
return 0;
}
int pa_stream_finish_upload(pa_stream *s)
{
pw_log_warn("Not Implemented");
return 0;
}
pa_operation* pa_context_remove_sample(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_play_sample(pa_context *c, const char *name, const char *dev,
pa_volume_t volume, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_context_play_sample_with_proplist(pa_context *c, const char *name,
const char *dev, pa_volume_t volume, pa_proplist *proplist,
pa_context_play_sample_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}

193
src/strbuf.c Normal file
View file

@ -0,0 +1,193 @@
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
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.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <pulse/xmalloc.h>
#include "internal.h"
#include "strbuf.h"
/* A chunk of the linked list that makes up the string */
struct chunk {
struct chunk *next;
size_t length;
};
#define CHUNK_TO_TEXT(c) ((char*) (c) + PA_ALIGN(sizeof(struct chunk)))
struct pa_strbuf {
size_t length;
struct chunk *head, *tail;
};
pa_strbuf *pa_strbuf_new(void) {
pa_strbuf *sb;
sb = pa_xnew(pa_strbuf, 1);
sb->length = 0;
sb->head = sb->tail = NULL;
return sb;
}
void pa_strbuf_free(pa_strbuf *sb) {
pa_assert(sb);
while (sb->head) {
struct chunk *c = sb->head;
sb->head = sb->head->next;
pa_xfree(c);
}
pa_xfree(sb);
}
/* Make a C string from the string buffer. The caller has to free
* string with pa_xfree(). */
char *pa_strbuf_to_string(pa_strbuf *sb) {
char *t, *e;
struct chunk *c;
pa_assert(sb);
e = t = pa_xmalloc(sb->length+1);
for (c = sb->head; c; c = c->next) {
pa_assert((size_t) (e-t) <= sb->length);
memcpy(e, CHUNK_TO_TEXT(c), c->length);
e += c->length;
}
/* Trailing NUL */
*e = 0;
pa_assert(e == t+sb->length);
return t;
}
/* Combination of pa_strbuf_free() and pa_strbuf_to_string() */
char *pa_strbuf_to_string_free(pa_strbuf *sb) {
char *t;
pa_assert(sb);
t = pa_strbuf_to_string(sb);
pa_strbuf_free(sb);
return t;
}
/* Append a string to the string buffer */
void pa_strbuf_puts(pa_strbuf *sb, const char *t) {
pa_assert(sb);
pa_assert(t);
pa_strbuf_putsn(sb, t, strlen(t));
}
/* Append a character to the string buffer */
void pa_strbuf_putc(pa_strbuf *sb, char c) {
pa_assert(sb);
pa_strbuf_putsn(sb, &c, 1);
}
/* Append a new chunk to the linked list */
static void append(pa_strbuf *sb, struct chunk *c) {
pa_assert(sb);
pa_assert(c);
if (sb->tail) {
pa_assert(sb->head);
sb->tail->next = c;
} else {
pa_assert(!sb->head);
sb->head = c;
}
sb->tail = c;
sb->length += c->length;
c->next = NULL;
}
/* Append up to l bytes of a string to the string buffer */
void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t l) {
struct chunk *c;
pa_assert(sb);
pa_assert(t);
if (!l)
return;
c = pa_xmalloc(PA_ALIGN(sizeof(struct chunk)) + l);
c->length = l;
memcpy(CHUNK_TO_TEXT(c), t, l);
append(sb, c);
}
/* Append a printf() style formatted string to the string buffer. */
/* The following is based on an example from the GNU libc documentation */
size_t pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) {
size_t size = 100;
struct chunk *c = NULL;
pa_assert(sb);
pa_assert(format);
for(;;) {
va_list ap;
int r;
c = pa_xrealloc(c, PA_ALIGN(sizeof(struct chunk)) + size);
va_start(ap, format);
r = vsnprintf(CHUNK_TO_TEXT(c), size, format, ap);
CHUNK_TO_TEXT(c)[size-1] = 0;
va_end(ap);
if (r > -1 && (size_t) r < size) {
c->length = (size_t) r;
append(sb, c);
return (size_t) r;
}
if (r > -1) /* glibc 2.1 */
size = (size_t) r+1;
else /* glibc 2.0 */
size *= 2;
}
}
bool pa_strbuf_isempty(pa_strbuf *sb) {
pa_assert(sb);
return sb->length <= 0;
}

39
src/strbuf.h Normal file
View file

@ -0,0 +1,39 @@
#ifndef foostrbufhfoo
#define foostrbufhfoo
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
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.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#include <spa/utils/defs.h>
typedef struct pa_strbuf pa_strbuf;
pa_strbuf *pa_strbuf_new(void);
void pa_strbuf_free(pa_strbuf *sb);
char *pa_strbuf_to_string(pa_strbuf *sb);
char *pa_strbuf_to_string_free(pa_strbuf *sb);
size_t pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3);
void pa_strbuf_puts(pa_strbuf *sb, const char *t);
void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t m);
void pa_strbuf_putc(pa_strbuf *sb, char c);
bool pa_strbuf_isempty(pa_strbuf *sb);
#endif

908
src/stream.c Normal file
View file

@ -0,0 +1,908 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <unistd.h>
#include <errno.h>
#include <spa/utils/defs.h>
#include <pulse/stream.h>
#include <pipewire/stream.h>
#include "internal.h"
static void stream_state_changed(void *data, enum pw_stream_state old,
enum pw_stream_state state, const char *error)
{
pa_stream *s = data;
switch(state) {
case PW_STREAM_STATE_ERROR:
pa_stream_set_state(s, PA_STREAM_FAILED);
break;
case PW_STREAM_STATE_UNCONNECTED:
pa_stream_set_state(s, PA_STREAM_UNCONNECTED);
break;
case PW_STREAM_STATE_CONNECTING:
pa_stream_set_state(s, PA_STREAM_CREATING);
break;
case PW_STREAM_STATE_CONFIGURE:
case PW_STREAM_STATE_READY:
break;
case PW_STREAM_STATE_PAUSED:
pa_stream_set_state(s, PA_STREAM_READY);
break;
case PW_STREAM_STATE_STREAMING:
break;
}
}
static void stream_format_changed(void *data, const struct spa_pod *format)
{
}
static void stream_process(void *data)
{
pa_stream *s = data;
if (s->direction == PA_STREAM_PLAYBACK) {
if (s->write_callback)
s->write_callback(s, 4096, s->write_userdata);
}
else {
if (s->read_callback)
s->read_callback(s, 4096, s->read_userdata);
}
}
static const struct pw_stream_events stream_events =
{
PW_VERSION_STREAM_EVENTS,
.state_changed = stream_state_changed,
.format_changed = stream_format_changed,
.process = stream_process,
};
pa_stream* stream_new(pa_context *c, const char *name,
const pa_sample_spec *ss, const pa_channel_map *map,
pa_format_info * const * formats, unsigned int n_formats,
pa_proplist *p)
{
pa_stream *s;
int i;
spa_assert(c);
spa_assert(c->refcount >= 1);
pa_assert((ss == NULL && map == NULL) || (formats == NULL && n_formats == 0));
pa_assert(n_formats < PA_MAX_FORMATS);
PA_CHECK_VALIDITY_RETURN_NULL(c, name ||
(p && pa_proplist_contains(p, PA_PROP_MEDIA_NAME)), PA_ERR_INVALID);
s = calloc(1, sizeof(pa_stream));
if (s == NULL)
return NULL;
s->stream = pw_stream_new(c->remote, name, NULL);
s->refcount = 1;
s->context = c;
init_type(&s->type, pw_core_get_type(c->core)->map);
pw_stream_add_listener(s->stream, &s->stream_listener, &stream_events, s);
s->direction = PA_STREAM_NODIRECTION;
s->state = PA_STREAM_UNCONNECTED;
s->flags = 0;
if (ss)
s->sample_spec = *ss;
else
pa_sample_spec_init(&s->sample_spec);
if (map)
s->channel_map = *map;
else
pa_channel_map_init(&s->channel_map);
s->n_formats = 0;
if (formats) {
s->n_formats = n_formats;
for (i = 0; i < n_formats; i++)
s->req_formats[i] = pa_format_info_copy(formats[i]);
}
s->format = NULL;
s->direct_on_input = PA_INVALID_INDEX;
s->proplist = p ? pa_proplist_copy(p) : pa_proplist_new();
if (name)
pa_proplist_sets(s->proplist, PA_PROP_MEDIA_NAME, name);
s->stream_index = PA_INVALID_INDEX;
s->buffer_attr.maxlength = (uint32_t) -1;
if (ss)
s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, ss); /* 250ms of buffering */
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 */
}
s->buffer_attr.minreq = (uint32_t) -1;
s->buffer_attr.prebuf = (uint32_t) -1;
s->buffer_attr.fragsize = (uint32_t) -1;
s->device_index = PA_INVALID_INDEX;
s->timing_info_valid = false;
spa_list_append(&c->streams, &s->link);
pa_stream_ref(s);
return s;
}
pa_stream* pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss,
const pa_channel_map *map)
{
return stream_new(c, name, ss, map, NULL, 0, NULL);
}
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)
{
return stream_new(c, name, ss, map, NULL, 0, p);
}
pa_stream *pa_stream_new_extended(pa_context *c, const char *name,
pa_format_info * const * formats, unsigned int n_formats, pa_proplist *p)
{
return stream_new(c, name, NULL, NULL, formats, n_formats, p);
}
static void stream_unlink(pa_stream *s)
{
spa_list_remove(&s->link);
}
static void stream_free(pa_stream *s)
{
int i;
if (s->proplist)
pa_proplist_free(s->proplist);
for (i = 0; i < s->n_formats; i++)
pa_format_info_free(s->req_formats[i]);
if (s->format)
pa_format_info_free(s->format);
free(s->device_name);
free(s);
}
void pa_stream_unref(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
if (--s->refcount == 0)
stream_free(s);
}
pa_stream *pa_stream_ref(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
s->refcount++;
return s;
}
pa_stream_state_t pa_stream_get_state(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
return s->state;
}
pa_context* pa_stream_get_context(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
return s->context;
}
uint32_t pa_stream_get_index(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
PA_CHECK_VALIDITY_RETURN_ANY(s->context,
s->state == PA_STREAM_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX);
return s->stream_index;
}
void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) {
spa_assert(s);
spa_assert(s->refcount >= 1);
if (s->state == st)
return;
pa_stream_ref(s);
s->state = st;
if (s->state_callback)
s->state_callback(s, s->state_userdata);
if ((st == PA_STREAM_FAILED || st == PA_STREAM_TERMINATED))
stream_unlink(s);
pa_stream_unref(s);
}
uint32_t pa_stream_get_device_index(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
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->device_index != PA_INVALID_INDEX,
PA_ERR_BADSTATE, PA_INVALID_INDEX);
return s->device_index;
}
const char *pa_stream_get_device_name(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
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->device_name, PA_ERR_BADSTATE);
return s->device_name;
}
int pa_stream_is_suspended(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
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->suspended;
}
int pa_stream_is_corked(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
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;
}
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)
{
int res;
enum pw_stream_flags fl;
const struct spa_pod *params[1];
uint8_t buffer[1024];
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
struct pw_type *t;
spa_assert(s);
spa_assert(s->refcount >= 1);
s->direction = direction;
t = pw_core_get_type(s->context->core);
pa_stream_set_state(s, PA_STREAM_CREATING);
fl = PW_STREAM_FLAG_AUTOCONNECT |
PW_STREAM_FLAG_MAP_BUFFERS |
PW_STREAM_FLAG_RT_PROCESS;
if (flags & PA_STREAM_PASSTHROUGH)
fl |= PW_STREAM_FLAG_EXCLUSIVE;
params[0] = spa_pod_builder_object(&b,
t->param.idEnumFormat, t->spa_format,
"I", s->type.media_type.audio,
"I", s->type.media_subtype.raw,
":", s->type.format_audio.format, "I", s->type.audio_format.S16,
":", s->type.format_audio.layout, "i", SPA_AUDIO_LAYOUT_INTERLEAVED,
":", s->type.format_audio.channels, "i", 2,
":", s->type.format_audio.rate, "i", 44100);
res = pw_stream_connect(s->stream,
direction == PA_STREAM_PLAYBACK ?
PW_DIRECTION_OUTPUT :
PW_DIRECTION_INPUT,
dev,
fl,
params, 1);
return res;
}
int pa_stream_connect_playback(
pa_stream *s,
const char *dev,
const pa_buffer_attr *attr,
pa_stream_flags_t flags,
const pa_cvolume *volume,
pa_stream *sync_stream)
{
return create_stream(PA_STREAM_PLAYBACK, s, dev, attr, flags, volume, sync_stream);
}
int pa_stream_connect_record(
pa_stream *s,
const char *dev,
const pa_buffer_attr *attr,
pa_stream_flags_t flags)
{
return create_stream(PA_STREAM_RECORD, s, dev, attr, flags, NULL, NULL);
}
int pa_stream_disconnect(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
PA_CHECK_VALIDITY(s->context, s->context->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
pa_stream_set_state(s, PA_STREAM_TERMINATED);
return 0;
}
int pa_stream_begin_write(
pa_stream *s,
void **data,
size_t *nbytes)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
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);
if (s->buffer == NULL) {
s->buffer = pw_stream_dequeue_buffer(s->stream);
}
if (s->buffer == NULL) {
*data = NULL;
*nbytes = 0;
}
else {
*data = s->buffer->buffer->datas[0].data;
*nbytes = s->buffer->buffer->datas[0].maxsize;
}
return 0;
}
int pa_stream_cancel_write(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
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);
pw_log_warn("Not Implemented");
return 0;
}
int pa_stream_write(pa_stream *s,
const void *data,
size_t nbytes,
pa_free_cb_t free_cb,
int64_t offset,
pa_seek_mode_t seek)
{
return pa_stream_write_ext_free(s, data, nbytes, free_cb, (void*) data, offset, seek);
}
int pa_stream_write_ext_free(pa_stream *s,
const void *data,
size_t nbytes,
pa_free_cb_t free_cb,
void *free_cb_data,
int64_t offset,
pa_seek_mode_t seek)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
spa_assert(data);
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);
PA_CHECK_VALIDITY(s->context, offset % pa_frame_size(&s->sample_spec) == 0, PA_ERR_INVALID);
PA_CHECK_VALIDITY(s->context, nbytes % pa_frame_size(&s->sample_spec) == 0, PA_ERR_INVALID);
if (s->buffer == NULL) {
pw_log_warn("Not Implemented");
return PA_ERR_INVALID;
}
else {
s->buffer->buffer->datas[0].chunk->offset = 0;
s->buffer->buffer->datas[0].chunk->size = nbytes;
}
pw_stream_queue_buffer(s->stream, s->buffer);
return 0;
}
int pa_stream_peek(pa_stream *s,
const void **data,
size_t *nbytes)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
spa_assert(data);
spa_assert(nbytes);
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);
pw_log_warn("Not Implemented");
return 0;
}
int pa_stream_drop(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
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);
pw_log_warn("Not Implemented");
return 0;
}
size_t pa_stream_writable_size(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
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);
pw_log_warn("Not Implemented");
return 0;
}
size_t pa_stream_readable_size(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
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);
pw_log_warn("Not Implemented");
return 0;
}
pa_operation* pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
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);
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_stream_update_timing_info(pa_stream *s, pa_stream_success_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
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);
pw_log_warn("Not Implemented");
return NULL;
}
void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
return;
s->state_callback = cb;
s->state_userdata = userdata;
}
void pa_stream_set_write_callback(pa_stream *s, pa_stream_request_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
return;
s->write_callback = cb;
s->write_userdata = userdata;
}
void pa_stream_set_read_callback(pa_stream *s, pa_stream_request_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
return;
s->read_callback = cb;
s->read_userdata = userdata;
}
void pa_stream_set_overflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
return;
s->overflow_callback = cb;
s->overflow_userdata = userdata;
}
int64_t pa_stream_get_underflow_index(pa_stream *s)
{
pw_log_warn("Not Implemented");
return 0;
}
void pa_stream_set_underflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
return;
s->underflow_callback = cb;
s->underflow_userdata = userdata;
}
void pa_stream_set_started_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
return;
s->started_callback = cb;
s->started_userdata = userdata;
}
void pa_stream_set_latency_update_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
return;
s->latency_update_callback = cb;
s->latency_update_userdata = userdata;
}
void pa_stream_set_moved_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
return;
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)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
return;
s->suspended_callback = cb;
s->suspended_userdata = userdata;
}
void pa_stream_set_event_callback(pa_stream *s, pa_stream_event_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
return;
s->event_callback = cb;
s->event_userdata = userdata;
}
void pa_stream_set_buffer_attr_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
return;
s->buffer_attr_callback = cb;
s->buffer_attr_userdata = userdata;
}
pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
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);
s->corked = b;
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
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);
pw_log_warn("Not Implemented");
return NULL;
}
pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
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);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE);
return NULL;
}
pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
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);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE);
return NULL;
}
pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
spa_assert(name);
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);
return NULL;
}
int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
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->timing_info_valid, PA_ERR_NODATA);
return 0;
}
int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
spa_assert(r_usec);
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->timing_info_valid, PA_ERR_NODATA);
return 0;
}
const pa_timing_info* pa_stream_get_timing_info(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
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->timing_info_valid, PA_ERR_NODATA);
return &s->timing_info;
}
const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
return &s->sample_spec;
}
const pa_channel_map* pa_stream_get_channel_map(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
return &s->channel_map;
}
const pa_format_info* pa_stream_get_format_info(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
return s->format;
}
const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
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);
return &s->buffer_attr;
}
pa_operation *pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr, pa_stream_success_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
spa_assert(attr);
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);
return NULL;
}
pa_operation *pa_stream_update_sample_rate(pa_stream *s, uint32_t rate, pa_stream_success_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, pa_sample_rate_valid(rate), 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->flags & PA_STREAM_VARIABLE_RATE, PA_ERR_BADSTATE);
return NULL;
}
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)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
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);
return NULL;
}
pa_operation *pa_stream_proplist_remove(pa_stream *s, const char *const keys[], pa_stream_success_cb_t cb, void *userdata)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
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);
return NULL;
}
int pa_stream_set_monitor_stream(pa_stream *s, uint32_t sink_input_idx)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
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);
s->direct_on_input = sink_input_idx;
return 0;
}
uint32_t pa_stream_get_monitor_stream(pa_stream *s)
{
spa_assert(s);
spa_assert(s->refcount >= 1);
PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direct_on_input != PA_INVALID_INDEX,
PA_ERR_BADSTATE, PA_INVALID_INDEX);
return s->direct_on_input;
}

35
src/subscribe.c Normal file
View file

@ -0,0 +1,35 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <pipewire/log.h>
#include <pulse/subscribe.h>
#include "internal.h"
pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, pa_context_success_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
return NULL;
}
void pa_context_set_subscribe_callback(pa_context *c, pa_context_subscribe_cb_t cb, void *userdata)
{
pw_log_warn("Not Implemented");
}

287
src/utf8.c Normal file
View file

@ -0,0 +1,287 @@
/***
This file is part of PulseAudio.
Copyright 2006 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
/* This file is based on the GLIB utf8 validation functions. The
* original license text follows. */
/* gutf8.c - Operations on UTF-8 strings.
*
* Copyright (C) 1999 Tom Tromey
* Copyright (C) 2000 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <errno.h>
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#ifdef HAVE_ICONV
#include <iconv.h>
#endif
#include <pulse/utf8.h>
#include <pulse/xmalloc.h>
#include "internal.h"
#define FILTER_CHAR '_'
static inline bool is_unicode_valid(uint32_t ch) {
if (ch >= 0x110000) /* End of unicode space */
return false;
if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */
return false;
if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */
return false;
if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */
return false;
return true;
}
static inline bool is_continuation_char(uint8_t ch) {
if ((ch & 0xc0) != 0x80) /* 10xxxxxx */
return false;
return true;
}
static inline void merge_continuation_char(uint32_t *u_ch, uint8_t ch) {
*u_ch <<= 6;
*u_ch |= ch & 0x3f;
}
static char* utf8_validate(const char *str, char *output) {
uint32_t val = 0;
uint32_t min = 0;
const uint8_t *p, *last;
int size;
uint8_t *o;
pa_assert(str);
o = (uint8_t*) output;
for (p = (const uint8_t*) str; *p; p++) {
if (*p < 128) {
if (o)
*o = *p;
} else {
last = p;
if ((*p & 0xe0) == 0xc0) { /* 110xxxxx two-char seq. */
size = 2;
min = 128;
val = (uint32_t) (*p & 0x1e);
goto ONE_REMAINING;
} else if ((*p & 0xf0) == 0xe0) { /* 1110xxxx three-char seq.*/
size = 3;
min = (1 << 11);
val = (uint32_t) (*p & 0x0f);
goto TWO_REMAINING;
} else if ((*p & 0xf8) == 0xf0) { /* 11110xxx four-char seq */
size = 4;
min = (1 << 16);
val = (uint32_t) (*p & 0x07);
} else
goto error;
p++;
if (!is_continuation_char(*p))
goto error;
merge_continuation_char(&val, *p);
TWO_REMAINING:
p++;
if (!is_continuation_char(*p))
goto error;
merge_continuation_char(&val, *p);
ONE_REMAINING:
p++;
if (!is_continuation_char(*p))
goto error;
merge_continuation_char(&val, *p);
if (val < min)
goto error;
if (!is_unicode_valid(val))
goto error;
if (o) {
memcpy(o, last, (size_t) size);
o += size;
}
continue;
error:
if (o) {
*o = FILTER_CHAR;
p = last; /* We retry at the next character */
} else
goto failure;
}
if (o)
o++;
}
if (o) {
*o = '\0';
return output;
}
return (char*) str;
failure:
return NULL;
}
char* pa_utf8_valid (const char *str) {
return utf8_validate(str, NULL);
}
char* pa_utf8_filter (const char *str) {
char *new_str;
pa_assert(str);
new_str = pa_xmalloc(strlen(str) + 1);
return utf8_validate(str, new_str);
}
#ifdef HAVE_ICONV
static char* iconv_simple(const char *str, const char *to, const char *from) {
char *new_str;
size_t len, inlen;
iconv_t cd;
ICONV_CONST char *inbuf;
char *outbuf;
size_t res, inbytes, outbytes;
pa_assert(str);
pa_assert(to);
pa_assert(from);
cd = iconv_open(to, from);
if (cd == (iconv_t)-1)
return NULL;
inlen = len = strlen(str) + 1;
new_str = pa_xmalloc(len);
for (;;) {
inbuf = (ICONV_CONST char*) str; /* Brain dead prototype for iconv() */
inbytes = inlen;
outbuf = new_str;
outbytes = len;
res = iconv(cd, &inbuf, &inbytes, &outbuf, &outbytes);
if (res != (size_t)-1)
break;
if (errno != E2BIG) {
pa_xfree(new_str);
new_str = NULL;
break;
}
pa_assert(inbytes != 0);
len += inbytes;
new_str = pa_xrealloc(new_str, len);
}
iconv_close(cd);
return new_str;
}
char* pa_utf8_to_locale (const char *str) {
return iconv_simple(str, "", "UTF-8");
}
char* pa_locale_to_utf8 (const char *str) {
return iconv_simple(str, "UTF-8", "");
}
#else
char* pa_utf8_to_locale (const char *str) {
pa_assert(str);
return pa_ascii_filter(str);
}
char* pa_locale_to_utf8 (const char *str) {
pa_assert(str);
if (pa_utf8_valid(str))
return pa_xstrdup(str);
return NULL;
}
#endif
char *pa_ascii_valid(const char *str) {
const char *p;
pa_assert(str);
for (p = str; *p; p++)
if ((unsigned char) *p >= 128)
return NULL;
return (char*) str;
}
char *pa_ascii_filter(const char *str) {
char *r, *s, *d;
pa_assert(str);
r = pa_xstrdup(str);
for (s = r, d = r; *s; s++)
if ((unsigned char) *s < 128)
*(d++) = *s;
*d = 0;
return r;
}

77
src/util.c Normal file
View file

@ -0,0 +1,77 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <time.h>
#include <pipewire/log.h>
#include <pipewire/pipewire.h>
#include <pulse/util.h>
#define PA_PATH_SEP_CHAR '/'
char *pa_get_user_name(char *s, size_t l)
{
return strncpy(s, pw_get_user_name(), l);
}
char *pa_get_host_name(char *s, size_t l)
{
return strncpy(s, pw_get_host_name(), l);
}
char *pa_get_fqdn(char *s, size_t l)
{
return strncpy(s, pw_get_host_name(), l);
}
char *pa_get_home_dir(char *s, size_t l)
{
pw_log_warn("Not Implemented");
return NULL;
}
char *pa_get_binary_name(char *s, size_t l)
{
return strncpy(s, pw_get_prgname(), l);
}
char *pa_path_get_filename(const char *p)
{
char *fn;
if (!p)
return NULL;
if ((fn = strrchr(p, PA_PATH_SEP_CHAR)))
return fn+1;
return (char*) p;
}
int pa_msleep(unsigned long t)
{
struct timespec ts;
ts.tv_sec = (time_t) (t / SPA_MSEC_PER_SEC);
ts.tv_nsec = (long) ((t % SPA_MSEC_PER_SEC) * SPA_NSEC_PER_MSEC);
return nanosleep(&ts, NULL);
}

26
src/version.c Normal file
View file

@ -0,0 +1,26 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <pulse/version.h>
const char* pa_get_library_version(void)
{
return pa_get_headers_version();
}

989
src/volume.c Normal file
View file

@ -0,0 +1,989 @@
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
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.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <pulse/volume.h>
#include "internal.h"
#include "sample-util.h"
int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) {
int i;
pa_assert(a);
pa_assert(b);
pa_return_val_if_fail(pa_cvolume_valid(a), 0);
if (PA_UNLIKELY(a == b))
return 1;
pa_return_val_if_fail(pa_cvolume_valid(b), 0);
if (a->channels != b->channels)
return 0;
for (i = 0; i < a->channels; i++)
if (a->values[i] != b->values[i])
return 0;
return 1;
}
pa_cvolume* pa_cvolume_init(pa_cvolume *a) {
unsigned c;
pa_assert(a);
a->channels = 0;
for (c = 0; c < PA_CHANNELS_MAX; c++)
a->values[c] = PA_VOLUME_INVALID;
return a;
}
pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v) {
int i;
pa_assert(a);
pa_assert(pa_channels_valid(channels));
a->channels = (uint8_t) channels;
for (i = 0; i < a->channels; i++)
/* Clamp in case there is stale data that exceeds the current
* PA_VOLUME_MAX */
a->values[i] = PA_CLAMP_VOLUME(v);
return a;
}
pa_volume_t pa_cvolume_avg(const pa_cvolume *a) {
uint64_t sum = 0;
unsigned c;
pa_assert(a);
pa_return_val_if_fail(pa_cvolume_valid(a), PA_VOLUME_MUTED);
for (c = 0; c < a->channels; c++)
sum += a->values[c];
sum /= a->channels;
return (pa_volume_t) sum;
}
pa_volume_t pa_cvolume_avg_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) {
uint64_t sum = 0;
unsigned c, n;
pa_assert(a);
if (!cm)
return pa_cvolume_avg(a);
pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(a, cm), PA_VOLUME_MUTED);
for (c = n = 0; c < a->channels; c++) {
if (!(PA_CHANNEL_POSITION_MASK(cm->map[c]) & mask))
continue;
sum += a->values[c];
n ++;
}
if (n > 0)
sum /= n;
return (pa_volume_t) sum;
}
pa_volume_t pa_cvolume_max(const pa_cvolume *a) {
pa_volume_t m = PA_VOLUME_MUTED;
unsigned c;
pa_assert(a);
pa_return_val_if_fail(pa_cvolume_valid(a), PA_VOLUME_MUTED);
for (c = 0; c < a->channels; c++)
if (a->values[c] > m)
m = a->values[c];
return m;
}
pa_volume_t pa_cvolume_min(const pa_cvolume *a) {
pa_volume_t m = PA_VOLUME_MAX;
unsigned c;
pa_assert(a);
pa_return_val_if_fail(pa_cvolume_valid(a), PA_VOLUME_MUTED);
for (c = 0; c < a->channels; c++)
if (a->values[c] < m)
m = a->values[c];
return m;
}
pa_volume_t pa_cvolume_max_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) {
pa_volume_t m = PA_VOLUME_MUTED;
unsigned c;
pa_assert(a);
if (!cm)
return pa_cvolume_max(a);
pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(a, cm), PA_VOLUME_MUTED);
for (c = 0; c < a->channels; c++) {
if (!(PA_CHANNEL_POSITION_MASK(cm->map[c]) & mask))
continue;
if (a->values[c] > m)
m = a->values[c];
}
return m;
}
pa_volume_t pa_cvolume_min_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) {
pa_volume_t m = PA_VOLUME_MAX;
unsigned c;
pa_assert(a);
if (!cm)
return pa_cvolume_min(a);
pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(a, cm), PA_VOLUME_MUTED);
for (c = 0; c < a->channels; c++) {
if (!(PA_CHANNEL_POSITION_MASK(cm->map[c]) & mask))
continue;
if (a->values[c] < m)
m = a->values[c];
}
return m;
}
pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) {
uint64_t result;
pa_return_val_if_fail(PA_VOLUME_IS_VALID(a), PA_VOLUME_INVALID);
pa_return_val_if_fail(PA_VOLUME_IS_VALID(b), PA_VOLUME_INVALID);
/* cbrt((a/PA_VOLUME_NORM)^3*(b/PA_VOLUME_NORM)^3)*PA_VOLUME_NORM = a*b/PA_VOLUME_NORM */
result = ((uint64_t) a * (uint64_t) b + (uint64_t) PA_VOLUME_NORM / 2ULL) / (uint64_t) PA_VOLUME_NORM;
if (result > (uint64_t)PA_VOLUME_MAX)
pa_log_warn("pa_sw_volume_multiply: Volume exceeds maximum allowed value and will be clipped. Please check your volume settings.");
return (pa_volume_t) PA_CLAMP_VOLUME(result);
}
pa_volume_t pa_sw_volume_divide(pa_volume_t a, pa_volume_t b) {
uint64_t result;
pa_return_val_if_fail(PA_VOLUME_IS_VALID(a), PA_VOLUME_INVALID);
pa_return_val_if_fail(PA_VOLUME_IS_VALID(b), PA_VOLUME_INVALID);
if (b <= PA_VOLUME_MUTED)
return 0;
result = ((uint64_t) a * (uint64_t) PA_VOLUME_NORM + (uint64_t) b / 2ULL) / (uint64_t) b;
if (result > (uint64_t)PA_VOLUME_MAX)
pa_log_warn("pa_sw_volume_divide: Volume exceeds maximum allowed value and will be clipped. Please check your volume settings.");
return (pa_volume_t) PA_CLAMP_VOLUME(result);
}
/* Amplitude, not power */
static double linear_to_dB(double v) {
return 20.0 * log10(v);
}
static double dB_to_linear(double v) {
return pow(10.0, v / 20.0);
}
pa_volume_t pa_sw_volume_from_dB(double dB) {
if (isinf(dB) < 0 || dB <= PA_DECIBEL_MININFTY)
return PA_VOLUME_MUTED;
return pa_sw_volume_from_linear(dB_to_linear(dB));
}
double pa_sw_volume_to_dB(pa_volume_t v) {
pa_return_val_if_fail(PA_VOLUME_IS_VALID(v), PA_DECIBEL_MININFTY);
if (v <= PA_VOLUME_MUTED)
return PA_DECIBEL_MININFTY;
return linear_to_dB(pa_sw_volume_to_linear(v));
}
pa_volume_t pa_sw_volume_from_linear(double v) {
if (v <= 0.0)
return PA_VOLUME_MUTED;
/*
* We use a cubic mapping here, as suggested and discussed here:
*
* http://www.robotplanet.dk/audio/audio_gui_design/
* http://lists.linuxaudio.org/pipermail/linux-audio-dev/2009-May/thread.html#23151
*
* We make sure that the conversion to linear and back yields the
* same volume value! That's why we need the lround() below!
*/
return (pa_volume_t) PA_CLAMP_VOLUME((uint64_t) lround(cbrt(v) * PA_VOLUME_NORM));
}
double pa_sw_volume_to_linear(pa_volume_t v) {
double f;
pa_return_val_if_fail(PA_VOLUME_IS_VALID(v), 0.0);
if (v <= PA_VOLUME_MUTED)
return 0.0;
if (v == PA_VOLUME_NORM)
return 1.0;
f = ((double) v / PA_VOLUME_NORM);
return f*f*f;
}
char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c) {
unsigned channel;
bool first = true;
char *e;
pa_assert(s);
pa_assert(l > 0);
pa_assert(c);
pa_init_i18n();
if (!pa_cvolume_valid(c)) {
pa_snprintf(s, l, _("(invalid)"));
return s;
}
*(e = s) = 0;
for (channel = 0; channel < c->channels && l > 1; channel++) {
l -= pa_snprintf(e, l, "%s%u: %3u%%",
first ? "" : " ",
channel,
(unsigned)(((uint64_t)c->values[channel] * 100 + (uint64_t)PA_VOLUME_NORM / 2) / (uint64_t)PA_VOLUME_NORM));
e = strchr(e, 0);
first = false;
}
return s;
}
char *pa_volume_snprint(char *s, size_t l, pa_volume_t v) {
pa_assert(s);
pa_assert(l > 0);
pa_init_i18n();
if (!PA_VOLUME_IS_VALID(v)) {
pa_snprintf(s, l, _("(invalid)"));
return s;
}
pa_snprintf(s, l, "%3u%%", (unsigned)(((uint64_t)v * 100 + (uint64_t)PA_VOLUME_NORM / 2) / (uint64_t)PA_VOLUME_NORM));
return s;
}
char *pa_sw_cvolume_snprint_dB(char *s, size_t l, const pa_cvolume *c) {
unsigned channel;
bool first = true;
char *e;
pa_assert(s);
pa_assert(l > 0);
pa_assert(c);
pa_init_i18n();
if (!pa_cvolume_valid(c)) {
pa_snprintf(s, l, _("(invalid)"));
return s;
}
*(e = s) = 0;
for (channel = 0; channel < c->channels && l > 1; channel++) {
double f = pa_sw_volume_to_dB(c->values[channel]);
l -= pa_snprintf(e, l, "%s%u: %0.2f dB",
first ? "" : " ",
channel,
isinf(f) < 0 || f <= PA_DECIBEL_MININFTY ? -INFINITY : f);
e = strchr(e, 0);
first = false;
}
return s;
}
char *pa_cvolume_snprint_verbose(char *s, size_t l, const pa_cvolume *c, const pa_channel_map *map, int print_dB) {
char *current = s;
bool first = true;
pa_assert(s);
pa_assert(l > 0);
pa_assert(c);
pa_init_i18n();
if (!pa_cvolume_valid(c)) {
pa_snprintf(s, l, _("(invalid)"));
return s;
}
pa_assert(!map || (map->channels == c->channels));
pa_assert(!map || pa_channel_map_valid(map));
current[0] = 0;
for (unsigned channel = 0; channel < c->channels && l > 1; channel++) {
char channel_position[32];
size_t bytes_printed;
char buf[PA_VOLUME_SNPRINT_VERBOSE_MAX];
if (map)
pa_snprintf(channel_position, sizeof(channel_position), "%s", pa_channel_position_to_string(map->map[channel]));
else
pa_snprintf(channel_position, sizeof(channel_position), "%u", channel);
bytes_printed = pa_snprintf(current, l, "%s%s: %s",
first ? "" : ", ",
channel_position,
pa_volume_snprint_verbose(buf, sizeof(buf), c->values[channel], print_dB));
l -= bytes_printed;
current += bytes_printed;
first = false;
}
return s;
}
char *pa_sw_volume_snprint_dB(char *s, size_t l, pa_volume_t v) {
double f;
pa_assert(s);
pa_assert(l > 0);
pa_init_i18n();
if (!PA_VOLUME_IS_VALID(v)) {
pa_snprintf(s, l, _("(invalid)"));
return s;
}
f = pa_sw_volume_to_dB(v);
pa_snprintf(s, l, "%0.2f dB", isinf(f) < 0 || f <= PA_DECIBEL_MININFTY ? -INFINITY : f);
return s;
}
char *pa_volume_snprint_verbose(char *s, size_t l, pa_volume_t v, int print_dB) {
char dB[PA_SW_VOLUME_SNPRINT_DB_MAX];
pa_assert(s);
pa_assert(l > 0);
pa_init_i18n();
if (!PA_VOLUME_IS_VALID(v)) {
pa_snprintf(s, l, _("(invalid)"));
return s;
}
pa_snprintf(s, l, "%" PRIu32 " / %3u%%%s%s",
v,
(unsigned)(((uint64_t)v * 100 + (uint64_t)PA_VOLUME_NORM / 2) / (uint64_t)PA_VOLUME_NORM),
print_dB ? " / " : "",
print_dB ? pa_sw_volume_snprint_dB(dB, sizeof(dB), v) : "");
return s;
}
int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) {
unsigned c;
pa_assert(a);
pa_return_val_if_fail(pa_cvolume_valid(a), 0);
pa_return_val_if_fail(PA_VOLUME_IS_VALID(v), 0);
for (c = 0; c < a->channels; c++)
if (a->values[c] != v)
return 0;
return 1;
}
pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) {
unsigned i;
pa_assert(dest);
pa_assert(a);
pa_assert(b);
pa_return_val_if_fail(pa_cvolume_valid(a), NULL);
pa_return_val_if_fail(pa_cvolume_valid(b), NULL);
dest->channels = PA_MIN(a->channels, b->channels);
for (i = 0; i < dest->channels; i++)
dest->values[i] = pa_sw_volume_multiply(a->values[i], b->values[i]);
return dest;
}
pa_cvolume *pa_sw_cvolume_multiply_scalar(pa_cvolume *dest, const pa_cvolume *a, pa_volume_t b) {
unsigned i;
pa_assert(dest);
pa_assert(a);
pa_return_val_if_fail(pa_cvolume_valid(a), NULL);
pa_return_val_if_fail(PA_VOLUME_IS_VALID(b), NULL);
for (i = 0; i < a->channels; i++)
dest->values[i] = pa_sw_volume_multiply(a->values[i], b);
dest->channels = (uint8_t) i;
return dest;
}
pa_cvolume *pa_sw_cvolume_divide(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) {
unsigned i;
pa_assert(dest);
pa_assert(a);
pa_assert(b);
pa_return_val_if_fail(pa_cvolume_valid(a), NULL);
pa_return_val_if_fail(pa_cvolume_valid(b), NULL);
dest->channels = PA_MIN(a->channels, b->channels);
for (i = 0; i < dest->channels; i++)
dest->values[i] = pa_sw_volume_divide(a->values[i], b->values[i]);
return dest;
}
pa_cvolume *pa_sw_cvolume_divide_scalar(pa_cvolume *dest, const pa_cvolume *a, pa_volume_t b) {
unsigned i;
pa_assert(dest);
pa_assert(a);
pa_return_val_if_fail(pa_cvolume_valid(a), NULL);
pa_return_val_if_fail(PA_VOLUME_IS_VALID(b), NULL);
for (i = 0; i < a->channels; i++)
dest->values[i] = pa_sw_volume_divide(a->values[i], b);
dest->channels = (uint8_t) i;
return dest;
}
int pa_cvolume_valid(const pa_cvolume *v) {
unsigned c;
pa_assert(v);
if (!pa_channels_valid(v->channels))
return 0;
for (c = 0; c < v->channels; c++)
if (!PA_VOLUME_IS_VALID(v->values[c]))
return 0;
return 1;
}
static bool on_left(pa_channel_position_t p) {
return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_LEFT);
}
static bool on_right(pa_channel_position_t p) {
return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_RIGHT);
}
static bool on_center(pa_channel_position_t p) {
return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_CENTER);
}
static bool on_hfe(pa_channel_position_t p) {
return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_HFE);
}
static bool on_lfe(pa_channel_position_t p) {
return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_LFE);
}
static bool on_front(pa_channel_position_t p) {
return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_FRONT);
}
static bool on_rear(pa_channel_position_t p) {
return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_REAR);
}
pa_cvolume *pa_cvolume_remap(pa_cvolume *v, const pa_channel_map *from, const pa_channel_map *to) {
int a, b;
pa_cvolume result;
pa_assert(v);
pa_assert(from);
pa_assert(to);
pa_return_val_if_fail(pa_channel_map_valid(to), NULL);
pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, from), NULL);
if (pa_channel_map_equal(from, to))
return v;
result.channels = to->channels;
for (b = 0; b < to->channels; b++) {
pa_volume_t k = 0;
int n = 0;
for (a = 0; a < from->channels; a++)
if (from->map[a] == to->map[b]) {
k += v->values[a];
n ++;
}
if (n <= 0) {
for (a = 0; a < from->channels; a++)
if ((on_left(from->map[a]) && on_left(to->map[b])) ||
(on_right(from->map[a]) && on_right(to->map[b])) ||
(on_center(from->map[a]) && on_center(to->map[b])) ||
(on_lfe(from->map[a]) && on_lfe(to->map[b]))) {
k += v->values[a];
n ++;
}
}
if (n <= 0)
k = pa_cvolume_avg(v);
else
k /= n;
result.values[b] = k;
}
*v = result;
return v;
}
int pa_cvolume_compatible(const pa_cvolume *v, const pa_sample_spec *ss) {
pa_assert(v);
pa_assert(ss);
pa_return_val_if_fail(pa_cvolume_valid(v), 0);
pa_return_val_if_fail(pa_sample_spec_valid(ss), 0);
return v->channels == ss->channels;
}
int pa_cvolume_compatible_with_channel_map(const pa_cvolume *v, const pa_channel_map *cm) {
pa_assert(v);
pa_assert(cm);
pa_return_val_if_fail(pa_cvolume_valid(v), 0);
pa_return_val_if_fail(pa_channel_map_valid(cm), 0);
return v->channels == cm->channels;
}
/*
* Returns the average volume of l and r, where l and r are two disjoint sets of channels
* (e g left and right, or front and rear).
*/
static void get_avg(const pa_channel_map *map, const pa_cvolume *v, pa_volume_t *l, pa_volume_t *r,
bool (*on_l)(pa_channel_position_t), bool (*on_r)(pa_channel_position_t)) {
int c;
pa_volume_t left = 0, right = 0;
unsigned n_left = 0, n_right = 0;
pa_assert(v);
pa_assert(map);
pa_assert(map->channels == v->channels);
pa_assert(l);
pa_assert(r);
for (c = 0; c < map->channels; c++) {
if (on_l(map->map[c])) {
left += v->values[c];
n_left++;
} else if (on_r(map->map[c])) {
right += v->values[c];
n_right++;
}
}
if (n_left <= 0)
*l = PA_VOLUME_NORM;
else
*l = left / n_left;
if (n_right <= 0)
*r = PA_VOLUME_NORM;
else
*r = right / n_right;
}
float pa_cvolume_get_balance(const pa_cvolume *v, const pa_channel_map *map) {
pa_volume_t left, right;
pa_assert(v);
pa_assert(map);
pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), 0.0f);
if (!pa_channel_map_can_balance(map))
return 0.0f;
get_avg(map, v, &left, &right, on_left, on_right);
if (left == right)
return 0.0f;
/* 1.0, 0.0 => -1.0
0.0, 1.0 => 1.0
0.0, 0.0 => 0.0
0.5, 0.5 => 0.0
1.0, 0.5 => -0.5
1.0, 0.25 => -0.75
0.75, 0.25 => -0.66
0.5, 0.25 => -0.5 */
if (left > right)
return -1.0f + ((float) right / (float) left);
else
return 1.0f - ((float) left / (float) right);
}
static pa_cvolume* set_balance(pa_cvolume *v, const pa_channel_map *map, float new_balance,
bool (*on_l)(pa_channel_position_t), bool (*on_r)(pa_channel_position_t)) {
pa_volume_t left, nleft, right, nright, m;
unsigned c;
get_avg(map, v, &left, &right, on_l, on_r);
m = PA_MAX(left, right);
if (new_balance <= 0) {
nright = (new_balance + 1.0f) * m;
nleft = m;
} else {
nleft = (1.0f - new_balance) * m;
nright = m;
}
for (c = 0; c < map->channels; c++) {
if (on_l(map->map[c])) {
if (left == 0)
v->values[c] = nleft;
else
v->values[c] = (pa_volume_t) PA_CLAMP_VOLUME(((uint64_t) v->values[c] * (uint64_t) nleft) / (uint64_t) left);
} else if (on_r(map->map[c])) {
if (right == 0)
v->values[c] = nright;
else
v->values[c] = (pa_volume_t) PA_CLAMP_VOLUME(((uint64_t) v->values[c] * (uint64_t) nright) / (uint64_t) right);
}
}
return v;
}
pa_cvolume* pa_cvolume_set_balance(pa_cvolume *v, const pa_channel_map *map, float new_balance) {
pa_assert(map);
pa_assert(v);
pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), NULL);
pa_return_val_if_fail(new_balance >= -1.0f, NULL);
pa_return_val_if_fail(new_balance <= 1.0f, NULL);
if (!pa_channel_map_can_balance(map))
return v;
return set_balance(v, map, new_balance, on_left, on_right);
}
pa_cvolume* pa_cvolume_scale(pa_cvolume *v, pa_volume_t max) {
unsigned c;
pa_volume_t t = 0;
pa_assert(v);
pa_return_val_if_fail(pa_cvolume_valid(v), NULL);
pa_return_val_if_fail(PA_VOLUME_IS_VALID(max), NULL);
t = pa_cvolume_max(v);
if (t <= PA_VOLUME_MUTED)
return pa_cvolume_set(v, v->channels, max);
for (c = 0; c < v->channels; c++)
v->values[c] = (pa_volume_t) PA_CLAMP_VOLUME(((uint64_t) v->values[c] * (uint64_t) max) / (uint64_t) t);
return v;
}
pa_cvolume* pa_cvolume_scale_mask(pa_cvolume *v, pa_volume_t max, pa_channel_map *cm, pa_channel_position_mask_t mask) {
unsigned c;
pa_volume_t t = 0;
pa_assert(v);
pa_return_val_if_fail(PA_VOLUME_IS_VALID(max), NULL);
if (!cm)
return pa_cvolume_scale(v, max);
pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, cm), NULL);
t = pa_cvolume_max_mask(v, cm, mask);
if (t <= PA_VOLUME_MUTED)
return pa_cvolume_set(v, v->channels, max);
for (c = 0; c < v->channels; c++)
v->values[c] = (pa_volume_t) PA_CLAMP_VOLUME(((uint64_t) v->values[c] * (uint64_t) max) / (uint64_t) t);
return v;
}
float pa_cvolume_get_fade(const pa_cvolume *v, const pa_channel_map *map) {
pa_volume_t rear, front;
pa_assert(v);
pa_assert(map);
pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), 0.0f);
if (!pa_channel_map_can_fade(map))
return 0.0f;
get_avg(map, v, &rear, &front, on_rear, on_front);
if (front == rear)
return 0.0f;
if (rear > front)
return -1.0f + ((float) front / (float) rear);
else
return 1.0f - ((float) rear / (float) front);
}
pa_cvolume* pa_cvolume_set_fade(pa_cvolume *v, const pa_channel_map *map, float new_fade) {
pa_assert(map);
pa_assert(v);
pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), NULL);
pa_return_val_if_fail(new_fade >= -1.0f, NULL);
pa_return_val_if_fail(new_fade <= 1.0f, NULL);
if (!pa_channel_map_can_fade(map))
return v;
return set_balance(v, map, new_fade, on_rear, on_front);
}
float pa_cvolume_get_lfe_balance(const pa_cvolume *v, const pa_channel_map *map) {
pa_volume_t hfe, lfe;
pa_assert(v);
pa_assert(map);
pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), 0.0f);
if (!pa_channel_map_can_lfe_balance(map))
return 0.0f;
get_avg(map, v, &hfe, &lfe, on_hfe, on_lfe);
if (hfe == lfe)
return 0.0f;
if (hfe > lfe)
return -1.0f + ((float) lfe / (float) hfe);
else
return 1.0f - ((float) hfe / (float) lfe);
}
pa_cvolume* pa_cvolume_set_lfe_balance(pa_cvolume *v, const pa_channel_map *map, float new_balance) {
pa_assert(map);
pa_assert(v);
pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), NULL);
pa_return_val_if_fail(new_balance >= -1.0f, NULL);
pa_return_val_if_fail(new_balance <= 1.0f, NULL);
if (!pa_channel_map_can_lfe_balance(map))
return v;
return set_balance(v, map, new_balance, on_hfe, on_lfe);
}
pa_cvolume* pa_cvolume_set_position(
pa_cvolume *cv,
const pa_channel_map *map,
pa_channel_position_t t,
pa_volume_t v) {
unsigned c;
bool good = false;
pa_assert(cv);
pa_assert(map);
pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(cv, map), NULL);
pa_return_val_if_fail(t < PA_CHANNEL_POSITION_MAX, NULL);
pa_return_val_if_fail(PA_VOLUME_IS_VALID(v), NULL);
for (c = 0; c < map->channels; c++)
if (map->map[c] == t) {
cv->values[c] = v;
good = true;
}
return good ? cv : NULL;
}
pa_volume_t pa_cvolume_get_position(
pa_cvolume *cv,
const pa_channel_map *map,
pa_channel_position_t t) {
unsigned c;
pa_volume_t v = PA_VOLUME_MUTED;
pa_assert(cv);
pa_assert(map);
pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(cv, map), PA_VOLUME_MUTED);
pa_return_val_if_fail(t < PA_CHANNEL_POSITION_MAX, PA_VOLUME_MUTED);
for (c = 0; c < map->channels; c++)
if (map->map[c] == t)
if (cv->values[c] > v)
v = cv->values[c];
return v;
}
pa_cvolume* pa_cvolume_merge(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) {
unsigned i;
pa_assert(dest);
pa_assert(a);
pa_assert(b);
pa_return_val_if_fail(pa_cvolume_valid(a), NULL);
pa_return_val_if_fail(pa_cvolume_valid(b), NULL);
dest->channels = PA_MIN(a->channels, b->channels);
for (i = 0; i < dest->channels; i++)
dest->values[i] = PA_MAX(a->values[i], b->values[i]);
return dest;
}
pa_cvolume* pa_cvolume_inc_clamp(pa_cvolume *v, pa_volume_t inc, pa_volume_t limit) {
pa_volume_t m;
pa_assert(v);
pa_return_val_if_fail(pa_cvolume_valid(v), NULL);
pa_return_val_if_fail(PA_VOLUME_IS_VALID(inc), NULL);
m = pa_cvolume_max(v);
if (m >= limit - inc)
m = limit;
else
m += inc;
return pa_cvolume_scale(v, m);
}
pa_cvolume* pa_cvolume_inc(pa_cvolume *v, pa_volume_t inc) {
return pa_cvolume_inc_clamp(v, inc, PA_VOLUME_MAX);
}
pa_cvolume* pa_cvolume_dec(pa_cvolume *v, pa_volume_t dec) {
pa_volume_t m;
pa_assert(v);
pa_return_val_if_fail(pa_cvolume_valid(v), NULL);
pa_return_val_if_fail(PA_VOLUME_IS_VALID(dec), NULL);
m = pa_cvolume_max(v);
if (m <= PA_VOLUME_MUTED + dec)
m = PA_VOLUME_MUTED;
else
m -= dec;
return pa_cvolume_scale(v, m);
}

115
src/xmalloc.c Normal file
View file

@ -0,0 +1,115 @@
/* PipeWire
* Copyright (C) 2018 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <unistd.h>
#include <errno.h>
#include <spa/utils/defs.h>
#include <pulse/xmalloc.h>
#define MAX_ALLOC_SIZE (1024*1024*96) /* 96MB */
static void oom(void) {
static const char e[] = "Not enough memory\n";
write(STDERR_FILENO, e, sizeof(e)-1);
#ifdef SIGQUIT
raise(SIGQUIT);
#endif
_exit(1);
}
void* pa_xmalloc(size_t l)
{
void *p;
spa_assert(l > 0);
spa_assert(l < MAX_ALLOC_SIZE);
if (!(p = malloc(l)))
oom();
return p;
}
void *pa_xmalloc0(size_t l)
{
void *p;
spa_assert(l > 0);
spa_assert(l < MAX_ALLOC_SIZE);
if (!(p = calloc(1, l)))
oom();
return p;
}
void *pa_xrealloc(void *ptr, size_t size)
{
void *p;
spa_assert(size > 0);
spa_assert(size < MAX_ALLOC_SIZE);
if (!(p = realloc(ptr, size)))
oom();
return p;
}
void pa_xfree(void *p)
{
int saved_errno;
if (!p)
return;
saved_errno = errno;
free(p);
errno = saved_errno;
}
char *pa_xstrdup(const char *s)
{
if (!s)
return NULL;
return pa_xmemdup(s, strlen(s)+1);
}
char *pa_xstrndup(const char *s, size_t l)
{
char *e, *r;
if (!s)
return NULL;
if ((e = memchr(s, 0, l)))
return pa_xmemdup(s, (size_t) (e-s+1));
r = pa_xmalloc(l+1);
memcpy(r, s, l);
r[l] = 0;
return r;
}
void* pa_xmemdup(const void *p, size_t l)
{
if (!p)
return NULL;
else {
char *r = pa_xmalloc(l);
memcpy(r, p, l);
return r;
}
}