1 /**
2 * @file pulse/recorder.c Pulseaudio sound driver - recorder
3 *
4 * Copyright (C) 2010 - 2016 Creytiv.com
5 */
6 #include <pulse/pulseaudio.h>
7 #include <pulse/simple.h>
8 #include <pthread.h>
9 #include <re.h>
10 #include <rem.h>
11 #include <baresip.h>
12 #include "pulse.h"
13
14
15 struct ausrc_st {
16 const struct ausrc *as; /* inheritance */
17
18 pa_simple *s;
19 pthread_t thread;
20 bool run;
21 void *sampv;
22 size_t sampc;
23 size_t sampsz;
24 uint32_t ptime;
25 ausrc_read_h *rh;
26 void *arg;
27 };
28
29
ausrc_destructor(void * arg)30 static void ausrc_destructor(void *arg)
31 {
32 struct ausrc_st *st = arg;
33
34 /* Wait for termination of other thread */
35 if (st->run) {
36 debug("pulse: stopping record thread\n");
37 st->run = false;
38 (void)pthread_join(st->thread, NULL);
39 }
40
41 if (st->s)
42 pa_simple_free(st->s);
43
44 mem_deref(st->sampv);
45 }
46
47
read_thread(void * arg)48 static void *read_thread(void *arg)
49 {
50 struct ausrc_st *st = arg;
51 const size_t num_bytes = st->sampc * st->sampsz;
52 int ret, pa_error = 0;
53 uint64_t now, last_read, diff;
54 unsigned dropped = 0;
55 bool init = true;
56
57 if (pa_simple_flush(st->s, &pa_error)) {
58 warning("pulse: pa_simple_flush error (%s)\n",
59 pa_strerror(pa_error));
60 }
61
62 last_read = tmr_jiffies();
63
64 while (st->run) {
65
66 ret = pa_simple_read(st->s, st->sampv, num_bytes, &pa_error);
67 if (ret < 0) {
68 warning("pulse: pa_simple_write error (%s)\n",
69 pa_strerror(pa_error));
70 continue;
71 }
72
73 /* Some devices might send a burst of samples right after the
74 initialization - filter them out */
75 if (init) {
76 now = tmr_jiffies();
77 diff = (now > last_read)? now - last_read : 0;
78
79 if (diff < st->ptime / 2) {
80 last_read = now;
81 ++dropped;
82 continue;
83 }
84 else {
85 init = false;
86
87 if (dropped)
88 debug("pulse: dropped %u frames of "
89 "garbage at the beginning of "
90 "the recording\n", dropped);
91 }
92 }
93
94 st->rh(st->sampv, st->sampc, st->arg);
95 }
96
97 return NULL;
98 }
99
100
aufmt_to_pulse_format(enum aufmt fmt)101 static int aufmt_to_pulse_format(enum aufmt fmt)
102 {
103 switch (fmt) {
104
105 case AUFMT_S16LE: return PA_SAMPLE_S16NE;
106 case AUFMT_FLOAT: return PA_SAMPLE_FLOAT32NE;
107 default: return 0;
108 }
109 }
110
111
pulse_recorder_alloc(struct ausrc_st ** stp,const struct ausrc * as,struct media_ctx ** ctx,struct ausrc_prm * prm,const char * device,ausrc_read_h * rh,ausrc_error_h * errh,void * arg)112 int pulse_recorder_alloc(struct ausrc_st **stp, const struct ausrc *as,
113 struct media_ctx **ctx,
114 struct ausrc_prm *prm, const char *device,
115 ausrc_read_h *rh, ausrc_error_h *errh, void *arg)
116 {
117 struct ausrc_st *st;
118 pa_sample_spec ss;
119 pa_buffer_attr attr;
120 int pa_error;
121 int err;
122
123 (void)ctx;
124 (void)device;
125 (void)errh;
126
127 if (!stp || !as || !prm)
128 return EINVAL;
129
130 debug("pulse: opening recorder (%u Hz, %d channels, device '%s')\n",
131 prm->srate, prm->ch, device);
132
133 st = mem_zalloc(sizeof(*st), ausrc_destructor);
134 if (!st)
135 return ENOMEM;
136
137 st->as = as;
138 st->rh = rh;
139 st->arg = arg;
140
141 st->sampc = prm->srate * prm->ch * prm->ptime / 1000;
142 st->sampsz = aufmt_sample_size(prm->fmt);
143 st->ptime = prm->ptime;
144
145 st->sampv = mem_alloc(st->sampsz * st->sampc, NULL);
146 if (!st->sampv) {
147 err = ENOMEM;
148 goto out;
149 }
150
151 ss.format = aufmt_to_pulse_format(prm->fmt);
152 ss.channels = prm->ch;
153 ss.rate = prm->srate;
154
155 attr.maxlength = (uint32_t)-1;
156 attr.tlength = (uint32_t)-1;
157 attr.prebuf = (uint32_t)-1;
158 attr.minreq = (uint32_t)-1;
159 attr.fragsize = (uint32_t)pa_usec_to_bytes(prm->ptime * 1000, &ss);
160
161 st->s = pa_simple_new(NULL,
162 "Baresip",
163 PA_STREAM_RECORD,
164 str_isset(device) ? device : 0,
165 "VoIP Record",
166 &ss,
167 NULL,
168 &attr,
169 &pa_error);
170 if (!st->s) {
171 warning("pulse: could not connect to server (%s)\n",
172 pa_strerror(pa_error));
173 err = ENODEV;
174 goto out;
175 }
176
177 st->run = true;
178 err = pthread_create(&st->thread, NULL, read_thread, st);
179 if (err) {
180 st->run = false;
181 goto out;
182 }
183
184 debug("pulse: recording started\n");
185
186 out:
187 if (err)
188 mem_deref(st);
189 else
190 *stp = st;
191
192 return err;
193 }
194