1 | /* $Id: conference.c 974 2007-02-19 01:13:53Z bennylp $ */
|
---|
2 | /*
|
---|
3 | * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
|
---|
4 | *
|
---|
5 | * This program is free software; you can redistribute it and/or modify
|
---|
6 | * it under the terms of the GNU General Public License as published by
|
---|
7 | * the Free Software Foundation; either version 2 of the License, or
|
---|
8 | * (at your option) any later version.
|
---|
9 | *
|
---|
10 | * This program is distributed in the hope that it will be useful,
|
---|
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
13 | * GNU General Public License for more details.
|
---|
14 | *
|
---|
15 | * You should have received a copy of the GNU General Public License
|
---|
16 | * along with this program; if not, write to the Free Software
|
---|
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
---|
18 | */
|
---|
19 | #include <pjmedia/conference.h>
|
---|
20 | #include <pjmedia/alaw_ulaw.h>
|
---|
21 | #include <pjmedia/errno.h>
|
---|
22 | #include <pjmedia/port.h>
|
---|
23 | #include <pjmedia/resample.h>
|
---|
24 | #include <pjmedia/silencedet.h>
|
---|
25 | #include <pjmedia/sound_port.h>
|
---|
26 | #include <pjmedia/stream.h>
|
---|
27 | #include <pj/array.h>
|
---|
28 | #include <pj/assert.h>
|
---|
29 | #include <pj/log.h>
|
---|
30 | #include <pj/pool.h>
|
---|
31 | #include <pj/string.h>
|
---|
32 |
|
---|
33 | // enable this to use the previous version conference.c
|
---|
34 | //#define USE_SND_THREAD 1
|
---|
35 | #ifndef USE_SND_THREAD
|
---|
36 | #include <pjmedia/clock.h>
|
---|
37 | #endif
|
---|
38 |
|
---|
39 |
|
---|
40 | /* CONF_DEBUG enables detailed operation of the conference bridge.
|
---|
41 | * Beware that it prints large amounts of logs (several lines per frame).
|
---|
42 | */
|
---|
43 | //#define CONF_DEBUG
|
---|
44 | #ifdef CONF_DEBUG
|
---|
45 | # include <stdio.h>
|
---|
46 | # define TRACE_(x) PJ_LOG(5,x)
|
---|
47 | #else
|
---|
48 | # define TRACE_(x)
|
---|
49 | #endif
|
---|
50 |
|
---|
51 |
|
---|
52 | /* REC_FILE macro enables recording of the samples written to the sound
|
---|
53 | * device. The file contains RAW PCM data with no header, and has the
|
---|
54 | * same settings (clock rate etc) as the conference bridge.
|
---|
55 | * This should only be enabled when debugging audio quality *only*.
|
---|
56 | */
|
---|
57 | //#define REC_FILE "confrec.pcm"
|
---|
58 | #ifdef REC_FILE
|
---|
59 | static FILE *fhnd_rec;
|
---|
60 | #endif
|
---|
61 |
|
---|
62 |
|
---|
63 | #define THIS_FILE "conference.c"
|
---|
64 | #define RX_BUF_COUNT PJMEDIA_SOUND_BUFFER_COUNT
|
---|
65 |
|
---|
66 | #define BYTES_PER_SAMPLE 2
|
---|
67 |
|
---|
68 | #define SIGNATURE PJMEDIA_PORT_SIGNATURE('C', 'O', 'N', 'F')
|
---|
69 | #define SIGNATURE_PORT PJMEDIA_PORT_SIGNATURE('C', 'O', 'N', 'P')
|
---|
70 | /* Normal level is hardcodec to 128 in all over places */
|
---|
71 | #define NORMAL_LEVEL 128
|
---|
72 | #define SLOT_TYPE unsigned
|
---|
73 | #define INVALID_SLOT ((SLOT_TYPE)-1)
|
---|
74 |
|
---|
75 |
|
---|
76 | /* These are settings to control the adaptivity of changes in the
|
---|
77 | * signal level of the ports, so that sudden change in signal level
|
---|
78 | * in the port does not cause misaligned signal (which causes noise).
|
---|
79 | */
|
---|
80 | #if 1
|
---|
81 | # define ATTACK_A 10
|
---|
82 | # define ATTACK_B 2
|
---|
83 | # define DECAY_A 10
|
---|
84 | # define DECAY_B 2
|
---|
85 | #else
|
---|
86 | /* To simulate old behavior */
|
---|
87 | # define ATTACK_A 0
|
---|
88 | # define ATTACK_B 1
|
---|
89 | # define DECAY_A 0
|
---|
90 | # define DECAY_B 1
|
---|
91 | #endif
|
---|
92 |
|
---|
93 |
|
---|
94 |
|
---|
95 | /*
|
---|
96 | * DON'T GET CONFUSED WITH TX/RX!!
|
---|
97 | *
|
---|
98 | * TX and RX directions are always viewed from the conference bridge's point
|
---|
99 | * of view, and NOT from the port's point of view. So TX means the bridge
|
---|
100 | * is transmitting to the port, RX means the bridge is receiving from the
|
---|
101 | * port.
|
---|
102 | */
|
---|
103 |
|
---|
104 |
|
---|
105 | /**
|
---|
106 | * This is a port connected to conference bridge.
|
---|
107 | */
|
---|
108 | struct conf_port
|
---|
109 | {
|
---|
110 | pj_str_t name; /**< Port name. */
|
---|
111 | pjmedia_port *port; /**< get_frame() and put_frame() */
|
---|
112 | pjmedia_port_op rx_setting; /**< Can we receive from this port */
|
---|
113 | pjmedia_port_op tx_setting; /**< Can we transmit to this port */
|
---|
114 | unsigned listener_cnt; /**< Number of listeners. */
|
---|
115 | SLOT_TYPE *listener_slots;/**< Array of listeners. */
|
---|
116 | unsigned transmitter_cnt;/**<Number of transmitters. */
|
---|
117 | pjmedia_silence_det *vad; /**< VAD for this port. */
|
---|
118 |
|
---|
119 | /* Shortcut for port info. */
|
---|
120 | unsigned clock_rate; /**< Port's clock rate. */
|
---|
121 | unsigned samples_per_frame; /**< Port's samples per frame. */
|
---|
122 |
|
---|
123 | /* Last level calculated from this port */
|
---|
124 | pj_int32_t last_level;
|
---|
125 |
|
---|
126 | /* Calculated signal levels: */
|
---|
127 | unsigned tx_level; /**< Last tx level to this port. */
|
---|
128 | unsigned rx_level; /**< Last rx level from this port. */
|
---|
129 |
|
---|
130 | /* The normalized signal level adjustment.
|
---|
131 | * A value of 128 (NORMAL_LEVEL) means there's no adjustment.
|
---|
132 | */
|
---|
133 | unsigned tx_adj_level; /**< Adjustment for TX. */
|
---|
134 | unsigned rx_adj_level; /**< Adjustment for RX. */
|
---|
135 |
|
---|
136 | /* Resample, for converting clock rate, if they're different. */
|
---|
137 | pjmedia_resample *rx_resample;
|
---|
138 | pjmedia_resample *tx_resample;
|
---|
139 |
|
---|
140 | /* RX buffer is temporary buffer to be used when there is mismatch
|
---|
141 | * between port's sample rate or ptime with conference's sample rate
|
---|
142 | * or ptime. The buffer is used for sampling rate conversion AND/OR to
|
---|
143 | * buffer the samples until there are enough samples to fulfill a
|
---|
144 | * complete frame to be processed by the bridge.
|
---|
145 | *
|
---|
146 | * When both sample rate AND ptime of the port match the conference
|
---|
147 | * settings, this buffer will not be created.
|
---|
148 | *
|
---|
149 | * This buffer contains samples at port's clock rate.
|
---|
150 | * The size of this buffer is the sum between port's samples per frame
|
---|
151 | * and bridge's samples per frame.
|
---|
152 | */
|
---|
153 | pj_int16_t *rx_buf; /**< The RX buffer. */
|
---|
154 | unsigned rx_buf_cap; /**< Max size, in samples */
|
---|
155 | unsigned rx_buf_count; /**< # of samples in the buf. */
|
---|
156 |
|
---|
157 | /* Mix buf is a temporary buffer used to calculate the average signal
|
---|
158 | * received by this port from all other ports. Samples from all ports
|
---|
159 | * that are transmitting to this port will be accumulated here, then
|
---|
160 | * they will be divided by the source level before the samples are put
|
---|
161 | * to the TX buffer of this port.
|
---|
162 | *
|
---|
163 | * This buffer contains samples at bridge's clock rate.
|
---|
164 | * The size of this buffer is equal to samples per frame of the bridge.
|
---|
165 | *
|
---|
166 | * Note that the samples here are unsigned 32bit.
|
---|
167 | */
|
---|
168 | unsigned src_level; /**< Sum of input levels */
|
---|
169 | unsigned src_cnt; /**< Number of sources. */
|
---|
170 | pj_uint32_t *mix_buf; /**< Total sum of signal. */
|
---|
171 |
|
---|
172 | /* Tx buffer is a temporary buffer to be used when there's mismatch
|
---|
173 | * between port's clock rate or ptime with conference's sample rate
|
---|
174 | * or ptime. This buffer is used as the source of the sampling rate
|
---|
175 | * conversion AND/OR to buffer the samples until there are enough
|
---|
176 | * samples to fulfill a complete frame to be transmitted to the port.
|
---|
177 | *
|
---|
178 | * When both sample rate and ptime of the port match the bridge's
|
---|
179 | * settings, this buffer will not be created.
|
---|
180 | *
|
---|
181 | * This buffer contains samples at port's clock rate.
|
---|
182 | * The size of this buffer is the sum between port's samples per frame
|
---|
183 | * and bridge's samples per frame.
|
---|
184 | */
|
---|
185 | pj_int16_t *tx_buf; /**< Tx buffer. */
|
---|
186 | unsigned tx_buf_cap; /**< Max size, in samples. */
|
---|
187 | unsigned tx_buf_count; /**< # of samples in the buffer. */
|
---|
188 |
|
---|
189 | /* Snd buffers is a special buffer for sound device port (port 0, master
|
---|
190 | * port). It's not used by other ports.
|
---|
191 | *
|
---|
192 | * There are multiple numbers of this buffer, because we can not expect
|
---|
193 | * the mic and speaker thread to run equally after one another. In most
|
---|
194 | * systems, each thread will run multiple times before the other thread
|
---|
195 | * gains execution time. For example, in my system, mic thread is called
|
---|
196 | * three times, then speaker thread is called three times, and so on.
|
---|
197 | */
|
---|
198 | int snd_write_pos, snd_read_pos;
|
---|
199 | pj_int16_t *snd_buf[RX_BUF_COUNT]; /**< Buffer */
|
---|
200 |
|
---|
201 | #ifndef USE_SND_THREAD
|
---|
202 | // for periodically sending RTP packets, only means for port 0
|
---|
203 | pjmedia_clock *clock;
|
---|
204 |
|
---|
205 | // similar to snd_buf, but the snd_buf is for mic thread to use
|
---|
206 | // spk_buf is just for speaker thread to use
|
---|
207 | // like snd_buf, it only means for port 0
|
---|
208 | int spk_write_pos, spk_read_pos;
|
---|
209 | pj_int16_t *spk_buf[RX_BUF_COUNT]; /**< Buffer */
|
---|
210 | #endif
|
---|
211 |
|
---|
212 | };
|
---|
213 |
|
---|
214 |
|
---|
215 | /*
|
---|
216 | * Conference bridge.
|
---|
217 | */
|
---|
218 | struct pjmedia_conf
|
---|
219 | {
|
---|
220 | unsigned options; /**< Bitmask options. */
|
---|
221 | unsigned max_ports; /**< Maximum ports. */
|
---|
222 | unsigned port_cnt; /**< Current number of ports. */
|
---|
223 | unsigned connect_cnt; /**< Total number of connections */
|
---|
224 | pjmedia_snd_port *snd_dev_port; /**< Sound device port. */
|
---|
225 | pjmedia_port *master_port; /**< Port zero's port. */
|
---|
226 | char master_name_buf[80]; /**< Port0 name buffer. */
|
---|
227 | pj_mutex_t *mutex; /**< Conference mutex. */
|
---|
228 | struct conf_port **ports; /**< Array of ports. */
|
---|
229 | pj_uint16_t *uns_buf; /**< Buf for unsigned conversion */
|
---|
230 | unsigned clock_rate; /**< Sampling rate. */
|
---|
231 | unsigned channel_count;/**< Number of channels (1=mono). */
|
---|
232 | unsigned samples_per_frame; /**< Samples per frame. */
|
---|
233 | unsigned bits_per_sample; /**< Bits per sample. */
|
---|
234 | };
|
---|
235 |
|
---|
236 |
|
---|
237 | /* Prototypes */
|
---|
238 | static pj_status_t put_frame(pjmedia_port *this_port,
|
---|
239 | const pjmedia_frame *frame);
|
---|
240 | static pj_status_t get_frame(pjmedia_port *this_port,
|
---|
241 | pjmedia_frame *frame);
|
---|
242 | static pj_status_t get_frame_pasv(pjmedia_port *this_port,
|
---|
243 | pjmedia_frame *frame);
|
---|
244 | static pj_status_t destroy_port(pjmedia_port *this_port);
|
---|
245 |
|
---|
246 | #ifndef USE_SND_THREAD
|
---|
247 | static void clock_callback(const pj_timestamp *ts, void *user_data);
|
---|
248 | #endif
|
---|
249 |
|
---|
250 | /*
|
---|
251 | * Create port.
|
---|
252 | */
|
---|
253 | static pj_status_t create_conf_port( pj_pool_t *pool,
|
---|
254 | pjmedia_conf *conf,
|
---|
255 | pjmedia_port *port,
|
---|
256 | const pj_str_t *name,
|
---|
257 | struct conf_port **p_conf_port)
|
---|
258 | {
|
---|
259 | struct conf_port *conf_port;
|
---|
260 | pj_status_t status;
|
---|
261 |
|
---|
262 | /* Create port. */
|
---|
263 | conf_port = pj_pool_zalloc(pool, sizeof(struct conf_port));
|
---|
264 | PJ_ASSERT_RETURN(conf_port, PJ_ENOMEM);
|
---|
265 |
|
---|
266 | /* Set name */
|
---|
267 | pj_strdup_with_null(pool, &conf_port->name, name);
|
---|
268 |
|
---|
269 | /* Default has tx and rx enabled. */
|
---|
270 | conf_port->rx_setting = PJMEDIA_PORT_ENABLE;
|
---|
271 | conf_port->tx_setting = PJMEDIA_PORT_ENABLE;
|
---|
272 |
|
---|
273 | /* Default level adjustment is 128 (which means no adjustment) */
|
---|
274 | conf_port->tx_adj_level = NORMAL_LEVEL;
|
---|
275 | conf_port->rx_adj_level = NORMAL_LEVEL;
|
---|
276 |
|
---|
277 | /* Create transmit flag array */
|
---|
278 | conf_port->listener_slots = pj_pool_zalloc(pool,
|
---|
279 | conf->max_ports * sizeof(SLOT_TYPE));
|
---|
280 | PJ_ASSERT_RETURN(conf_port->listener_slots, PJ_ENOMEM);
|
---|
281 |
|
---|
282 | /* Save some port's infos, for convenience. */
|
---|
283 | if (port) {
|
---|
284 | conf_port->port = port;
|
---|
285 | conf_port->clock_rate = port->info.clock_rate;
|
---|
286 | conf_port->samples_per_frame = port->info.samples_per_frame;
|
---|
287 | } else {
|
---|
288 | conf_port->port = NULL;
|
---|
289 | conf_port->clock_rate = conf->clock_rate;
|
---|
290 | conf_port->samples_per_frame = conf->samples_per_frame;
|
---|
291 | }
|
---|
292 |
|
---|
293 | /* Create and init vad. */
|
---|
294 | status = pjmedia_silence_det_create( pool,
|
---|
295 | conf_port->clock_rate,
|
---|
296 | conf_port->samples_per_frame,
|
---|
297 | &conf_port->vad);
|
---|
298 | if (status != PJ_SUCCESS)
|
---|
299 | return status;
|
---|
300 |
|
---|
301 | /* Set fixed */
|
---|
302 | pjmedia_silence_det_set_fixed(conf_port->vad, 2);
|
---|
303 |
|
---|
304 | /* Set VAD name */
|
---|
305 | pjmedia_silence_det_set_name(conf_port->vad, conf_port->name.ptr);
|
---|
306 |
|
---|
307 | /* If port's clock rate is different than conference's clock rate,
|
---|
308 | * create a resample sessions.
|
---|
309 | */
|
---|
310 | if (conf_port->clock_rate != conf->clock_rate) {
|
---|
311 |
|
---|
312 | pj_bool_t high_quality;
|
---|
313 | pj_bool_t large_filter;
|
---|
314 |
|
---|
315 | high_quality = ((conf->options & PJMEDIA_CONF_USE_LINEAR)==0);
|
---|
316 | large_filter = ((conf->options & PJMEDIA_CONF_SMALL_FILTER)==0);
|
---|
317 |
|
---|
318 | /* Create resample for rx buffer. */
|
---|
319 | status = pjmedia_resample_create( pool,
|
---|
320 | high_quality,
|
---|
321 | large_filter,
|
---|
322 | conf_port->clock_rate,/* Rate in */
|
---|
323 | conf->clock_rate, /* Rate out */
|
---|
324 | conf->samples_per_frame *
|
---|
325 | conf_port->clock_rate /
|
---|
326 | conf->clock_rate,
|
---|
327 | &conf_port->rx_resample);
|
---|
328 | if (status != PJ_SUCCESS)
|
---|
329 | return status;
|
---|
330 |
|
---|
331 |
|
---|
332 | /* Create resample for tx buffer. */
|
---|
333 | status = pjmedia_resample_create(pool,
|
---|
334 | high_quality,
|
---|
335 | large_filter,
|
---|
336 | conf->clock_rate, /* Rate in */
|
---|
337 | conf_port->clock_rate, /* Rate out */
|
---|
338 | conf->samples_per_frame,
|
---|
339 | &conf_port->tx_resample);
|
---|
340 | if (status != PJ_SUCCESS)
|
---|
341 | return status;
|
---|
342 | }
|
---|
343 |
|
---|
344 | /*
|
---|
345 | * Initialize rx and tx buffer, only when port's samples per frame or
|
---|
346 | * port's clock rate is different then the conference bridge settings.
|
---|
347 | */
|
---|
348 | if (conf_port->clock_rate != conf->clock_rate ||
|
---|
349 | conf_port->samples_per_frame != conf->samples_per_frame)
|
---|
350 | {
|
---|
351 | /* Create RX buffer. */
|
---|
352 | conf_port->rx_buf_cap = (unsigned)(conf_port->samples_per_frame +
|
---|
353 | conf->samples_per_frame *
|
---|
354 | conf_port->clock_rate * 1.0 /
|
---|
355 | conf->clock_rate);
|
---|
356 | conf_port->rx_buf_count = 0;
|
---|
357 | conf_port->rx_buf = pj_pool_alloc(pool, conf_port->rx_buf_cap *
|
---|
358 | sizeof(conf_port->rx_buf[0]));
|
---|
359 | PJ_ASSERT_RETURN(conf_port->rx_buf, PJ_ENOMEM);
|
---|
360 |
|
---|
361 | /* Create TX buffer. */
|
---|
362 | conf_port->tx_buf_cap = conf_port->rx_buf_cap;
|
---|
363 | conf_port->tx_buf_count = 0;
|
---|
364 | conf_port->tx_buf = pj_pool_alloc(pool, conf_port->tx_buf_cap *
|
---|
365 | sizeof(conf_port->tx_buf[0]));
|
---|
366 | PJ_ASSERT_RETURN(conf_port->tx_buf, PJ_ENOMEM);
|
---|
367 | }
|
---|
368 |
|
---|
369 |
|
---|
370 | /* Create mix buffer. */
|
---|
371 | conf_port->mix_buf = pj_pool_zalloc(pool, conf->samples_per_frame *
|
---|
372 | sizeof(conf_port->mix_buf[0]));
|
---|
373 | PJ_ASSERT_RETURN(conf_port->mix_buf, PJ_ENOMEM);
|
---|
374 |
|
---|
375 |
|
---|
376 | /* Done */
|
---|
377 | *p_conf_port = conf_port;
|
---|
378 | return PJ_SUCCESS;
|
---|
379 | }
|
---|
380 |
|
---|
381 |
|
---|
382 | /*
|
---|
383 | * Add passive port.
|
---|
384 | */
|
---|
385 | static pj_status_t create_pasv_port( pjmedia_conf *conf,
|
---|
386 | pj_pool_t *pool,
|
---|
387 | const pj_str_t *name,
|
---|
388 | pjmedia_port *port,
|
---|
389 | struct conf_port **p_conf_port)
|
---|
390 | {
|
---|
391 | struct conf_port *conf_port;
|
---|
392 | unsigned i;
|
---|
393 | pj_status_t status;
|
---|
394 |
|
---|
395 | /* Create port */
|
---|
396 | status = create_conf_port(pool, conf, port, name, &conf_port);
|
---|
397 | if (status != PJ_SUCCESS)
|
---|
398 | return status;
|
---|
399 |
|
---|
400 | /* Passive port has rx buffers. */
|
---|
401 | for (i=0; i<RX_BUF_COUNT; ++i) {
|
---|
402 | conf_port->snd_buf[i] = pj_pool_zalloc(pool, conf->samples_per_frame *
|
---|
403 | sizeof(conf_port->snd_buf[0][0]));
|
---|
404 | if (conf_port->snd_buf[i] == NULL) {
|
---|
405 | return PJ_ENOMEM;
|
---|
406 | }
|
---|
407 | }
|
---|
408 | conf_port->snd_write_pos = 0;
|
---|
409 | conf_port->snd_read_pos = 0;
|
---|
410 |
|
---|
411 | #ifndef USE_SND_THREAD
|
---|
412 | for (i=0; i<RX_BUF_COUNT; ++i) {
|
---|
413 | conf_port->spk_buf[i] = pj_pool_zalloc(pool, conf->samples_per_frame *
|
---|
414 | sizeof(conf_port->spk_buf[0][0]));
|
---|
415 | if (conf_port->spk_buf[i] == NULL) {
|
---|
416 | return PJ_ENOMEM;
|
---|
417 | }
|
---|
418 | }
|
---|
419 | conf_port->spk_write_pos = 0;
|
---|
420 | conf_port->spk_read_pos = 0;
|
---|
421 |
|
---|
422 | status = pjmedia_clock_create(pool, conf_port->clock_rate,
|
---|
423 | conf_port->samples_per_frame, 0, &clock_callback, conf,
|
---|
424 | &conf_port->clock);
|
---|
425 | if (status != PJ_SUCCESS)
|
---|
426 | return status;
|
---|
427 | PJ_LOG(2, (THIS_FILE, "pjmedia_clock_create succeed!"));
|
---|
428 | // pjmedia_clock_start(conf_port->clock);
|
---|
429 | #endif
|
---|
430 |
|
---|
431 | *p_conf_port = conf_port;
|
---|
432 |
|
---|
433 | return PJ_SUCCESS;
|
---|
434 | }
|
---|
435 |
|
---|
436 |
|
---|
437 | /*
|
---|
438 | * Create port zero for the sound device.
|
---|
439 | */
|
---|
440 | static pj_status_t create_sound_port( pj_pool_t *pool,
|
---|
441 | pjmedia_conf *conf )
|
---|
442 | {
|
---|
443 | struct conf_port *conf_port;
|
---|
444 | pj_str_t name = { "Master/sound", 12 };
|
---|
445 | pj_status_t status;
|
---|
446 |
|
---|
447 |
|
---|
448 | status = create_pasv_port(conf, pool, &name, NULL, &conf_port);
|
---|
449 | if (status != PJ_SUCCESS)
|
---|
450 | return status;
|
---|
451 |
|
---|
452 |
|
---|
453 | /* Create sound device port: */
|
---|
454 |
|
---|
455 | if ((conf->options & PJMEDIA_CONF_NO_DEVICE) == 0) {
|
---|
456 | pjmedia_snd_stream *strm;
|
---|
457 | pjmedia_snd_stream_info si;
|
---|
458 |
|
---|
459 | /*
|
---|
460 | * If capture is disabled then create player only port.
|
---|
461 | * Otherwise create bidirectional sound device port.
|
---|
462 | */
|
---|
463 | if (conf->options & PJMEDIA_CONF_NO_MIC) {
|
---|
464 | status = pjmedia_snd_port_create_player(pool, -1, conf->clock_rate,
|
---|
465 | conf->channel_count,
|
---|
466 | conf->samples_per_frame,
|
---|
467 | conf->bits_per_sample,
|
---|
468 | 0, /* options */
|
---|
469 | &conf->snd_dev_port);
|
---|
470 |
|
---|
471 | } else {
|
---|
472 | status = pjmedia_snd_port_create( pool, -1, -1, conf->clock_rate,
|
---|
473 | conf->channel_count,
|
---|
474 | conf->samples_per_frame,
|
---|
475 | conf->bits_per_sample,
|
---|
476 | 0, /* Options */
|
---|
477 | &conf->snd_dev_port);
|
---|
478 | }
|
---|
479 |
|
---|
480 | if (status != PJ_SUCCESS)
|
---|
481 | return status;
|
---|
482 |
|
---|
483 | strm = pjmedia_snd_port_get_snd_stream(conf->snd_dev_port);
|
---|
484 | status = pjmedia_snd_stream_get_info(strm, &si);
|
---|
485 | if (status == PJ_SUCCESS) {
|
---|
486 | const pjmedia_snd_dev_info *snd_dev_info;
|
---|
487 | if (conf->options & PJMEDIA_CONF_NO_MIC)
|
---|
488 | snd_dev_info = pjmedia_snd_get_dev_info(si.play_id);
|
---|
489 | else
|
---|
490 | snd_dev_info = pjmedia_snd_get_dev_info(si.rec_id);
|
---|
491 | pj_strdup2_with_null(pool, &conf_port->name, snd_dev_info->name);
|
---|
492 | }
|
---|
493 | }
|
---|
494 |
|
---|
495 |
|
---|
496 | /* Add the port to the bridge */
|
---|
497 | conf->ports[0] = conf_port;
|
---|
498 | conf->port_cnt++;
|
---|
499 |
|
---|
500 |
|
---|
501 | PJ_LOG(5,(THIS_FILE, "Sound device successfully created for port 0"));
|
---|
502 | return PJ_SUCCESS;
|
---|
503 | }
|
---|
504 |
|
---|
505 | /*
|
---|
506 | * Create conference bridge.
|
---|
507 | */
|
---|
508 | PJ_DEF(pj_status_t) pjmedia_conf_create( pj_pool_t *pool,
|
---|
509 | unsigned max_ports,
|
---|
510 | unsigned clock_rate,
|
---|
511 | unsigned channel_count,
|
---|
512 | unsigned samples_per_frame,
|
---|
513 | unsigned bits_per_sample,
|
---|
514 | unsigned options,
|
---|
515 | pjmedia_conf **p_conf )
|
---|
516 | {
|
---|
517 | pjmedia_conf *conf;
|
---|
518 | const pj_str_t name = { "Conf", 4 };
|
---|
519 | pj_status_t status;
|
---|
520 |
|
---|
521 | /* Can only accept 16bits per sample, for now.. */
|
---|
522 | PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL);
|
---|
523 |
|
---|
524 | PJ_LOG(5,(THIS_FILE, "Creating conference bridge with %d ports",
|
---|
525 | max_ports));
|
---|
526 |
|
---|
527 | /* Create and init conf structure. */
|
---|
528 | conf = pj_pool_zalloc(pool, sizeof(pjmedia_conf));
|
---|
529 | PJ_ASSERT_RETURN(conf, PJ_ENOMEM);
|
---|
530 |
|
---|
531 | conf->ports = pj_pool_zalloc(pool, max_ports*sizeof(void*));
|
---|
532 | PJ_ASSERT_RETURN(conf->ports, PJ_ENOMEM);
|
---|
533 |
|
---|
534 | conf->options = options;
|
---|
535 | conf->max_ports = max_ports;
|
---|
536 | conf->clock_rate = clock_rate;
|
---|
537 | conf->channel_count = channel_count;
|
---|
538 | conf->samples_per_frame = samples_per_frame;
|
---|
539 | conf->bits_per_sample = bits_per_sample;
|
---|
540 |
|
---|
541 |
|
---|
542 | /* Create and initialize the master port interface. */
|
---|
543 | conf->master_port = pj_pool_zalloc(pool, sizeof(pjmedia_port));
|
---|
544 | PJ_ASSERT_RETURN(conf->master_port, PJ_ENOMEM);
|
---|
545 |
|
---|
546 | pjmedia_port_info_init(&conf->master_port->info, &name, SIGNATURE,
|
---|
547 | clock_rate, channel_count, bits_per_sample,
|
---|
548 | samples_per_frame);
|
---|
549 |
|
---|
550 | conf->master_port->port_data.pdata = conf;
|
---|
551 | conf->master_port->port_data.ldata = 0;
|
---|
552 |
|
---|
553 | conf->master_port->get_frame = &get_frame;
|
---|
554 | conf->master_port->put_frame = &put_frame;
|
---|
555 | conf->master_port->on_destroy = &destroy_port;
|
---|
556 |
|
---|
557 |
|
---|
558 | /* Create port zero for sound device. */
|
---|
559 | status = create_sound_port(pool, conf);
|
---|
560 | if (status != PJ_SUCCESS)
|
---|
561 | return status;
|
---|
562 |
|
---|
563 | /* Create temporary buffer. */
|
---|
564 | conf->uns_buf = pj_pool_zalloc(pool, samples_per_frame *
|
---|
565 | sizeof(conf->uns_buf[0]));
|
---|
566 |
|
---|
567 | /* Create mutex. */
|
---|
568 | status = pj_mutex_create_recursive(pool, "conf", &conf->mutex);
|
---|
569 | if (status != PJ_SUCCESS)
|
---|
570 | return status;
|
---|
571 |
|
---|
572 | /* If sound device was created, connect sound device to the
|
---|
573 | * master port.
|
---|
574 | */
|
---|
575 | if (conf->snd_dev_port) {
|
---|
576 | status = pjmedia_snd_port_connect( conf->snd_dev_port,
|
---|
577 | conf->master_port );
|
---|
578 | if (status != PJ_SUCCESS) {
|
---|
579 | pjmedia_conf_destroy(conf);
|
---|
580 | return status;
|
---|
581 | }
|
---|
582 | }
|
---|
583 |
|
---|
584 |
|
---|
585 | /* Done */
|
---|
586 |
|
---|
587 | *p_conf = conf;
|
---|
588 |
|
---|
589 | return PJ_SUCCESS;
|
---|
590 | }
|
---|
591 |
|
---|
592 |
|
---|
593 | /*
|
---|
594 | * Pause sound device.
|
---|
595 | */
|
---|
596 | static pj_status_t pause_sound( pjmedia_conf *conf )
|
---|
597 | {
|
---|
598 | #ifndef USE_SND_THREAD
|
---|
599 | // supposing that the bug in clock_thread.c has been fixed
|
---|
600 | // otherwise, we should start it after it has been created,
|
---|
601 | // and keep it running forever
|
---|
602 | if (conf && conf->ports[0] && conf->ports[0]->clock) {
|
---|
603 | PJ_LOG(2, (THIS_FILE, "pjmedia_clock_stop"));
|
---|
604 | pjmedia_clock_stop(conf->ports[0]->clock);
|
---|
605 | }
|
---|
606 | #else
|
---|
607 | /* Do nothing. */
|
---|
608 | PJ_UNUSED_ARG(conf);
|
---|
609 | #endif
|
---|
610 | return PJ_SUCCESS;
|
---|
611 | }
|
---|
612 |
|
---|
613 | /*
|
---|
614 | * Resume sound device.
|
---|
615 | */
|
---|
616 | static pj_status_t resume_sound( pjmedia_conf *conf )
|
---|
617 | {
|
---|
618 | #ifndef USE_SND_THREAD
|
---|
619 | // supposing that the bug in clock_thread.c has been fixed
|
---|
620 | // otherwise, we should start it after it has been created,
|
---|
621 | // and keep it running forever
|
---|
622 | if (conf && conf->ports[0] && conf->ports[0]->clock) {
|
---|
623 | PJ_LOG(2, (THIS_FILE, "pjmedia_clock_start"));
|
---|
624 | pjmedia_clock_start(conf->ports[0]->clock);
|
---|
625 | }
|
---|
626 | #else
|
---|
627 | /* Do nothing. */
|
---|
628 | PJ_UNUSED_ARG(conf);
|
---|
629 | #endif
|
---|
630 | return PJ_SUCCESS;
|
---|
631 | }
|
---|
632 |
|
---|
633 |
|
---|
634 | /**
|
---|
635 | * Destroy conference bridge.
|
---|
636 | */
|
---|
637 | PJ_DEF(pj_status_t) pjmedia_conf_destroy( pjmedia_conf *conf )
|
---|
638 | {
|
---|
639 | #ifndef USE_SND_THREAD
|
---|
640 | if (conf && conf->ports[0] && conf->ports[0]->clock)
|
---|
641 | pjmedia_clock_destroy(conf->ports[0]->clock);
|
---|
642 | #endif
|
---|
643 |
|
---|
644 | PJ_ASSERT_RETURN(conf != NULL, PJ_EINVAL);
|
---|
645 |
|
---|
646 | /* Destroy sound device port. */
|
---|
647 | if (conf->snd_dev_port) {
|
---|
648 | pjmedia_snd_port_destroy(conf->snd_dev_port);
|
---|
649 | conf->snd_dev_port = NULL;
|
---|
650 | }
|
---|
651 |
|
---|
652 | /* Destroy mutex */
|
---|
653 | pj_mutex_destroy(conf->mutex);
|
---|
654 |
|
---|
655 | return PJ_SUCCESS;
|
---|
656 | }
|
---|
657 |
|
---|
658 |
|
---|
659 | /*
|
---|
660 | * Destroy the master port (will destroy the conference)
|
---|
661 | */
|
---|
662 | static pj_status_t destroy_port(pjmedia_port *this_port)
|
---|
663 | {
|
---|
664 | pjmedia_conf *conf = this_port->port_data.pdata;
|
---|
665 | return pjmedia_conf_destroy(conf);
|
---|
666 | }
|
---|
667 |
|
---|
668 |
|
---|
669 | /*
|
---|
670 | * Get port zero interface.
|
---|
671 | */
|
---|
672 | PJ_DEF(pjmedia_port*) pjmedia_conf_get_master_port(pjmedia_conf *conf)
|
---|
673 | {
|
---|
674 | /* Sanity check. */
|
---|
675 | PJ_ASSERT_RETURN(conf != NULL, NULL);
|
---|
676 |
|
---|
677 | /* Can only return port interface when PJMEDIA_CONF_NO_DEVICE was
|
---|
678 | * present in the option.
|
---|
679 | */
|
---|
680 | PJ_ASSERT_RETURN((conf->options & PJMEDIA_CONF_NO_DEVICE) != 0, NULL);
|
---|
681 |
|
---|
682 | return conf->master_port;
|
---|
683 | }
|
---|
684 |
|
---|
685 |
|
---|
686 | /*
|
---|
687 | * Set master port name.
|
---|
688 | */
|
---|
689 | PJ_DEF(pj_status_t) pjmedia_conf_set_port0_name(pjmedia_conf *conf,
|
---|
690 | const pj_str_t *name)
|
---|
691 | {
|
---|
692 | int len;
|
---|
693 |
|
---|
694 | /* Sanity check. */
|
---|
695 | PJ_ASSERT_RETURN(conf != NULL && name != NULL, PJ_EINVAL);
|
---|
696 |
|
---|
697 | len = name->slen;
|
---|
698 | if (len > sizeof(conf->master_name_buf))
|
---|
699 | len = sizeof(conf->master_name_buf);
|
---|
700 |
|
---|
701 | if (len > 0) pj_memcpy(conf->master_name_buf, name->ptr, len);
|
---|
702 |
|
---|
703 | conf->ports[0]->name.ptr = conf->master_name_buf;
|
---|
704 | conf->ports[0]->name.slen = len;
|
---|
705 |
|
---|
706 | if (conf->master_port)
|
---|
707 | conf->master_port->info.name = conf->ports[0]->name;
|
---|
708 |
|
---|
709 | return PJ_SUCCESS;
|
---|
710 | }
|
---|
711 |
|
---|
712 | /*
|
---|
713 | * Add stream port to the conference bridge.
|
---|
714 | */
|
---|
715 | PJ_DEF(pj_status_t) pjmedia_conf_add_port( pjmedia_conf *conf,
|
---|
716 | pj_pool_t *pool,
|
---|
717 | pjmedia_port *strm_port,
|
---|
718 | const pj_str_t *port_name,
|
---|
719 | unsigned *p_port )
|
---|
720 | {
|
---|
721 | struct conf_port *conf_port;
|
---|
722 | unsigned index;
|
---|
723 | pj_status_t status;
|
---|
724 |
|
---|
725 | PJ_ASSERT_RETURN(conf && pool && strm_port, PJ_EINVAL);
|
---|
726 |
|
---|
727 | /* If port_name is not specified, use the port's name */
|
---|
728 | if (!port_name)
|
---|
729 | port_name = &strm_port->info.name;
|
---|
730 |
|
---|
731 | /* For this version of PJMEDIA, port MUST have the same number of
|
---|
732 | * PCM channels.
|
---|
733 | */
|
---|
734 | if (strm_port->info.channel_count != conf->channel_count) {
|
---|
735 | pj_assert(!"Number of channels mismatch");
|
---|
736 | return PJMEDIA_ENCCHANNEL;
|
---|
737 | }
|
---|
738 |
|
---|
739 | pj_mutex_lock(conf->mutex);
|
---|
740 |
|
---|
741 | if (conf->port_cnt >= conf->max_ports) {
|
---|
742 | pj_assert(!"Too many ports");
|
---|
743 | pj_mutex_unlock(conf->mutex);
|
---|
744 | return PJ_ETOOMANY;
|
---|
745 | }
|
---|
746 |
|
---|
747 | /* Find empty port in the conference bridge. */
|
---|
748 | for (index=0; index < conf->max_ports; ++index) {
|
---|
749 | if (conf->ports[index] == NULL)
|
---|
750 | break;
|
---|
751 | }
|
---|
752 |
|
---|
753 | pj_assert(index != conf->max_ports);
|
---|
754 |
|
---|
755 | /* Create conf port structure. */
|
---|
756 | status = create_conf_port(pool, conf, strm_port, port_name, &conf_port);
|
---|
757 | if (status != PJ_SUCCESS) {
|
---|
758 | pj_mutex_unlock(conf->mutex);
|
---|
759 | return status;
|
---|
760 | }
|
---|
761 |
|
---|
762 | /* Put the port. */
|
---|
763 | conf->ports[index] = conf_port;
|
---|
764 | conf->port_cnt++;
|
---|
765 |
|
---|
766 | /* Done. */
|
---|
767 | if (p_port) {
|
---|
768 | *p_port = index;
|
---|
769 | }
|
---|
770 |
|
---|
771 | pj_mutex_unlock(conf->mutex);
|
---|
772 |
|
---|
773 | return PJ_SUCCESS;
|
---|
774 | }
|
---|
775 |
|
---|
776 |
|
---|
777 | /*
|
---|
778 | * Add passive port.
|
---|
779 | */
|
---|
780 | PJ_DEF(pj_status_t) pjmedia_conf_add_passive_port( pjmedia_conf *conf,
|
---|
781 | pj_pool_t *pool,
|
---|
782 | const pj_str_t *name,
|
---|
783 | unsigned clock_rate,
|
---|
784 | unsigned channel_count,
|
---|
785 | unsigned samples_per_frame,
|
---|
786 | unsigned bits_per_sample,
|
---|
787 | unsigned options,
|
---|
788 | unsigned *p_slot,
|
---|
789 | pjmedia_port **p_port )
|
---|
790 | {
|
---|
791 | struct conf_port *conf_port;
|
---|
792 | pjmedia_port *port;
|
---|
793 | unsigned index;
|
---|
794 | pj_str_t tmp;
|
---|
795 | pj_status_t status;
|
---|
796 |
|
---|
797 | PJ_ASSERT_RETURN(conf && pool, PJ_EINVAL);
|
---|
798 |
|
---|
799 | /* For this version of PJMEDIA, port MUST have the same number of
|
---|
800 | * PCM channels.
|
---|
801 | */
|
---|
802 | if (channel_count != conf->channel_count) {
|
---|
803 | pj_assert(!"Number of channels mismatch");
|
---|
804 | return PJMEDIA_ENCCHANNEL;
|
---|
805 | }
|
---|
806 |
|
---|
807 | /* For this version, options must be zero */
|
---|
808 | PJ_ASSERT_RETURN(options == 0, PJ_EINVAL);
|
---|
809 | PJ_UNUSED_ARG(options);
|
---|
810 |
|
---|
811 | pj_mutex_lock(conf->mutex);
|
---|
812 |
|
---|
813 | if (conf->port_cnt >= conf->max_ports) {
|
---|
814 | pj_assert(!"Too many ports");
|
---|
815 | pj_mutex_unlock(conf->mutex);
|
---|
816 | return PJ_ETOOMANY;
|
---|
817 | }
|
---|
818 |
|
---|
819 | /* Find empty port in the conference bridge. */
|
---|
820 | for (index=0; index < conf->max_ports; ++index) {
|
---|
821 | if (conf->ports[index] == NULL)
|
---|
822 | break;
|
---|
823 | }
|
---|
824 |
|
---|
825 | pj_assert(index != conf->max_ports);
|
---|
826 |
|
---|
827 | if (name == NULL) {
|
---|
828 | name = &tmp;
|
---|
829 |
|
---|
830 | tmp.ptr = pj_pool_alloc(pool, 32);
|
---|
831 | tmp.slen = pj_ansi_snprintf(tmp.ptr, 32, "ConfPort#%d", index);
|
---|
832 | }
|
---|
833 |
|
---|
834 | /* Create and initialize the media port structure. */
|
---|
835 | port = pj_pool_zalloc(pool, sizeof(pjmedia_port));
|
---|
836 | PJ_ASSERT_RETURN(port, PJ_ENOMEM);
|
---|
837 |
|
---|
838 | pjmedia_port_info_init(&port->info, name, SIGNATURE_PORT,
|
---|
839 | clock_rate, channel_count, bits_per_sample,
|
---|
840 | samples_per_frame);
|
---|
841 |
|
---|
842 | port->port_data.pdata = conf;
|
---|
843 | port->port_data.ldata = index;
|
---|
844 |
|
---|
845 | port->get_frame = &get_frame_pasv;
|
---|
846 | port->put_frame = &put_frame;
|
---|
847 | port->on_destroy = NULL;
|
---|
848 |
|
---|
849 |
|
---|
850 | /* Create conf port structure. */
|
---|
851 | status = create_pasv_port(conf, pool, name, port, &conf_port);
|
---|
852 | if (status != PJ_SUCCESS) {
|
---|
853 | pj_mutex_unlock(conf->mutex);
|
---|
854 | return status;
|
---|
855 | }
|
---|
856 |
|
---|
857 |
|
---|
858 | /* Put the port. */
|
---|
859 | conf->ports[index] = conf_port;
|
---|
860 | conf->port_cnt++;
|
---|
861 |
|
---|
862 | /* Done. */
|
---|
863 | if (p_slot)
|
---|
864 | *p_slot = index;
|
---|
865 | if (p_port)
|
---|
866 | *p_port = port;
|
---|
867 |
|
---|
868 | pj_mutex_unlock(conf->mutex);
|
---|
869 |
|
---|
870 | return PJ_SUCCESS;
|
---|
871 | }
|
---|
872 |
|
---|
873 |
|
---|
874 |
|
---|
875 | /*
|
---|
876 | * Change TX and RX settings for the port.
|
---|
877 | */
|
---|
878 | PJ_DECL(pj_status_t) pjmedia_conf_configure_port( pjmedia_conf *conf,
|
---|
879 | unsigned slot,
|
---|
880 | pjmedia_port_op tx,
|
---|
881 | pjmedia_port_op rx)
|
---|
882 | {
|
---|
883 | struct conf_port *conf_port;
|
---|
884 |
|
---|
885 | /* Check arguments */
|
---|
886 | PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
|
---|
887 |
|
---|
888 | /* Port must be valid. */
|
---|
889 | PJ_ASSERT_RETURN(conf->ports[slot] != NULL, PJ_EINVAL);
|
---|
890 |
|
---|
891 | conf_port = conf->ports[slot];
|
---|
892 |
|
---|
893 | if (tx != PJMEDIA_PORT_NO_CHANGE)
|
---|
894 | conf_port->tx_setting = tx;
|
---|
895 |
|
---|
896 | if (rx != PJMEDIA_PORT_NO_CHANGE)
|
---|
897 | conf_port->rx_setting = rx;
|
---|
898 |
|
---|
899 | return PJ_SUCCESS;
|
---|
900 | }
|
---|
901 |
|
---|
902 |
|
---|
903 | /*
|
---|
904 | * Connect port.
|
---|
905 | */
|
---|
906 | PJ_DEF(pj_status_t) pjmedia_conf_connect_port( pjmedia_conf *conf,
|
---|
907 | unsigned src_slot,
|
---|
908 | unsigned sink_slot,
|
---|
909 | int level )
|
---|
910 | {
|
---|
911 | struct conf_port *src_port, *dst_port;
|
---|
912 | pj_bool_t start_sound = PJ_FALSE;
|
---|
913 | unsigned i;
|
---|
914 |
|
---|
915 | /* Check arguments */
|
---|
916 | PJ_ASSERT_RETURN(conf && src_slot<conf->max_ports &&
|
---|
917 | sink_slot<conf->max_ports, PJ_EINVAL);
|
---|
918 |
|
---|
919 | /* Ports must be valid. */
|
---|
920 | PJ_ASSERT_RETURN(conf->ports[src_slot] != NULL, PJ_EINVAL);
|
---|
921 | PJ_ASSERT_RETURN(conf->ports[sink_slot] != NULL, PJ_EINVAL);
|
---|
922 |
|
---|
923 | /* For now, level MUST be zero. */
|
---|
924 | PJ_ASSERT_RETURN(level == 0, PJ_EINVAL);
|
---|
925 |
|
---|
926 | pj_mutex_lock(conf->mutex);
|
---|
927 |
|
---|
928 | src_port = conf->ports[src_slot];
|
---|
929 | dst_port = conf->ports[sink_slot];
|
---|
930 |
|
---|
931 | /* Check if connection has been made */
|
---|
932 | for (i=0; i<src_port->listener_cnt; ++i) {
|
---|
933 | if (src_port->listener_slots[i] == sink_slot)
|
---|
934 | break;
|
---|
935 | }
|
---|
936 |
|
---|
937 | if (i == src_port->listener_cnt) {
|
---|
938 | src_port->listener_slots[src_port->listener_cnt] = sink_slot;
|
---|
939 | ++conf->connect_cnt;
|
---|
940 | ++src_port->listener_cnt;
|
---|
941 | ++dst_port->transmitter_cnt;
|
---|
942 |
|
---|
943 | if (conf->connect_cnt == 1)
|
---|
944 | start_sound = 1;
|
---|
945 |
|
---|
946 | PJ_LOG(4,(THIS_FILE,"Port %d (%.*s) transmitting to port %d (%.*s)",
|
---|
947 | src_slot,
|
---|
948 | (int)src_port->name.slen,
|
---|
949 | src_port->name.ptr,
|
---|
950 | sink_slot,
|
---|
951 | (int)dst_port->name.slen,
|
---|
952 | dst_port->name.ptr));
|
---|
953 | }
|
---|
954 |
|
---|
955 | pj_mutex_unlock(conf->mutex);
|
---|
956 |
|
---|
957 | /* Sound device must be started without mutex, otherwise the
|
---|
958 | * sound thread will deadlock (?)
|
---|
959 | */
|
---|
960 | if (start_sound)
|
---|
961 | resume_sound(conf);
|
---|
962 |
|
---|
963 | return PJ_SUCCESS;
|
---|
964 | }
|
---|
965 |
|
---|
966 |
|
---|
967 | /*
|
---|
968 | * Disconnect port
|
---|
969 | */
|
---|
970 | PJ_DEF(pj_status_t) pjmedia_conf_disconnect_port( pjmedia_conf *conf,
|
---|
971 | unsigned src_slot,
|
---|
972 | unsigned sink_slot )
|
---|
973 | {
|
---|
974 | struct conf_port *src_port, *dst_port;
|
---|
975 | unsigned i;
|
---|
976 |
|
---|
977 | /* Check arguments */
|
---|
978 | PJ_ASSERT_RETURN(conf && src_slot<conf->max_ports &&
|
---|
979 | sink_slot<conf->max_ports, PJ_EINVAL);
|
---|
980 |
|
---|
981 | /* Ports must be valid. */
|
---|
982 | PJ_ASSERT_RETURN(conf->ports[src_slot] != NULL, PJ_EINVAL);
|
---|
983 | PJ_ASSERT_RETURN(conf->ports[sink_slot] != NULL, PJ_EINVAL);
|
---|
984 |
|
---|
985 | pj_mutex_lock(conf->mutex);
|
---|
986 |
|
---|
987 | src_port = conf->ports[src_slot];
|
---|
988 | dst_port = conf->ports[sink_slot];
|
---|
989 |
|
---|
990 | /* Check if connection has been made */
|
---|
991 | for (i=0; i<src_port->listener_cnt; ++i) {
|
---|
992 | if (src_port->listener_slots[i] == sink_slot)
|
---|
993 | break;
|
---|
994 | }
|
---|
995 |
|
---|
996 | if (i != src_port->listener_cnt) {
|
---|
997 | pj_assert(src_port->listener_cnt > 0 &&
|
---|
998 | src_port->listener_cnt < conf->max_ports);
|
---|
999 | pj_assert(dst_port->transmitter_cnt > 0 &&
|
---|
1000 | dst_port->transmitter_cnt < conf->max_ports);
|
---|
1001 | pj_array_erase(src_port->listener_slots, sizeof(SLOT_TYPE),
|
---|
1002 | src_port->listener_cnt, i);
|
---|
1003 | --conf->connect_cnt;
|
---|
1004 | --src_port->listener_cnt;
|
---|
1005 | --dst_port->transmitter_cnt;
|
---|
1006 |
|
---|
1007 | PJ_LOG(4,(THIS_FILE,
|
---|
1008 | "Port %d (%.*s) stop transmitting to port %d (%.*s)",
|
---|
1009 | src_slot,
|
---|
1010 | (int)src_port->name.slen,
|
---|
1011 | src_port->name.ptr,
|
---|
1012 | sink_slot,
|
---|
1013 | (int)dst_port->name.slen,
|
---|
1014 | dst_port->name.ptr));
|
---|
1015 |
|
---|
1016 |
|
---|
1017 | }
|
---|
1018 |
|
---|
1019 | pj_mutex_unlock(conf->mutex);
|
---|
1020 |
|
---|
1021 | if (conf->connect_cnt == 0) {
|
---|
1022 | pause_sound(conf);
|
---|
1023 | }
|
---|
1024 |
|
---|
1025 | return PJ_SUCCESS;
|
---|
1026 | }
|
---|
1027 |
|
---|
1028 |
|
---|
1029 | /*
|
---|
1030 | * Get total number of ports connections currently set up in the bridge.
|
---|
1031 | */
|
---|
1032 | PJ_DECL(unsigned) pjmedia_conf_get_connect_count(pjmedia_conf *conf)
|
---|
1033 | {
|
---|
1034 | return conf->connect_cnt;
|
---|
1035 | }
|
---|
1036 |
|
---|
1037 |
|
---|
1038 | /*
|
---|
1039 | * Remove the specified port.
|
---|
1040 | */
|
---|
1041 | PJ_DEF(pj_status_t) pjmedia_conf_remove_port( pjmedia_conf *conf,
|
---|
1042 | unsigned port )
|
---|
1043 | {
|
---|
1044 | struct conf_port *conf_port;
|
---|
1045 | unsigned i;
|
---|
1046 |
|
---|
1047 | /* Check arguments */
|
---|
1048 | PJ_ASSERT_RETURN(conf && port < conf->max_ports, PJ_EINVAL);
|
---|
1049 |
|
---|
1050 | /* Port must be valid. */
|
---|
1051 | PJ_ASSERT_RETURN(conf->ports[port] != NULL, PJ_EINVAL);
|
---|
1052 |
|
---|
1053 | /* Suspend the sound devices.
|
---|
1054 | * Don't want to remove port while port is being accessed by sound
|
---|
1055 | * device's threads!
|
---|
1056 | */
|
---|
1057 |
|
---|
1058 | pj_mutex_lock(conf->mutex);
|
---|
1059 |
|
---|
1060 | conf_port = conf->ports[port];
|
---|
1061 | conf_port->tx_setting = PJMEDIA_PORT_DISABLE;
|
---|
1062 | conf_port->rx_setting = PJMEDIA_PORT_DISABLE;
|
---|
1063 |
|
---|
1064 | /* Remove this port from transmit array of other ports. */
|
---|
1065 | for (i=0; i<conf->max_ports; ++i) {
|
---|
1066 | unsigned j;
|
---|
1067 |
|
---|
1068 | conf_port = conf->ports[i];
|
---|
1069 |
|
---|
1070 | if (!conf_port)
|
---|
1071 | continue;
|
---|
1072 |
|
---|
1073 | if (conf_port->listener_cnt == 0)
|
---|
1074 | continue;
|
---|
1075 |
|
---|
1076 | for (j=0; j<conf_port->listener_cnt; ++j) {
|
---|
1077 | if (conf_port->listener_slots[j] == port) {
|
---|
1078 | pj_array_erase(conf_port->listener_slots, sizeof(SLOT_TYPE),
|
---|
1079 | conf_port->listener_cnt, j);
|
---|
1080 | --conf->connect_cnt;
|
---|
1081 | --conf_port->listener_cnt;
|
---|
1082 | break;
|
---|
1083 | }
|
---|
1084 | }
|
---|
1085 | }
|
---|
1086 |
|
---|
1087 | /* Update conf's connection count. */
|
---|
1088 | conf_port = conf->ports[port];
|
---|
1089 | conf->connect_cnt -= conf_port->listener_cnt;
|
---|
1090 |
|
---|
1091 | /* Remove the port. */
|
---|
1092 | conf->ports[port] = NULL;
|
---|
1093 | --conf->port_cnt;
|
---|
1094 |
|
---|
1095 | pj_mutex_unlock(conf->mutex);
|
---|
1096 |
|
---|
1097 |
|
---|
1098 | /* Stop sound if there's no connection. */
|
---|
1099 | if (conf->connect_cnt == 0) {
|
---|
1100 | pause_sound(conf);
|
---|
1101 | }
|
---|
1102 |
|
---|
1103 | return PJ_SUCCESS;
|
---|
1104 | }
|
---|
1105 |
|
---|
1106 |
|
---|
1107 | /*
|
---|
1108 | * Enum ports.
|
---|
1109 | */
|
---|
1110 | PJ_DEF(pj_status_t) pjmedia_conf_enum_ports( pjmedia_conf *conf,
|
---|
1111 | unsigned ports[],
|
---|
1112 | unsigned *p_count )
|
---|
1113 | {
|
---|
1114 | unsigned i, count=0;
|
---|
1115 |
|
---|
1116 | PJ_ASSERT_RETURN(conf && p_count && ports, PJ_EINVAL);
|
---|
1117 |
|
---|
1118 | for (i=0; i<conf->max_ports && count<*p_count; ++i) {
|
---|
1119 | if (!conf->ports[i])
|
---|
1120 | continue;
|
---|
1121 |
|
---|
1122 | ports[count++] = i;
|
---|
1123 | }
|
---|
1124 |
|
---|
1125 | *p_count = count;
|
---|
1126 | return PJ_SUCCESS;
|
---|
1127 | }
|
---|
1128 |
|
---|
1129 | /*
|
---|
1130 | * Get port info
|
---|
1131 | */
|
---|
1132 | PJ_DEF(pj_status_t) pjmedia_conf_get_port_info( pjmedia_conf *conf,
|
---|
1133 | unsigned slot,
|
---|
1134 | pjmedia_conf_port_info *info)
|
---|
1135 | {
|
---|
1136 | struct conf_port *conf_port;
|
---|
1137 |
|
---|
1138 | /* Check arguments */
|
---|
1139 | PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
|
---|
1140 |
|
---|
1141 | /* Port must be valid. */
|
---|
1142 | PJ_ASSERT_RETURN(conf->ports[slot] != NULL, PJ_EINVAL);
|
---|
1143 |
|
---|
1144 | conf_port = conf->ports[slot];
|
---|
1145 |
|
---|
1146 | info->slot = slot;
|
---|
1147 | info->name = conf_port->name;
|
---|
1148 | info->tx_setting = conf_port->tx_setting;
|
---|
1149 | info->rx_setting = conf_port->rx_setting;
|
---|
1150 | info->listener_cnt = conf_port->listener_cnt;
|
---|
1151 | info->listener_slots = conf_port->listener_slots;
|
---|
1152 | info->clock_rate = conf_port->clock_rate;
|
---|
1153 | info->channel_count = conf->channel_count;
|
---|
1154 | info->samples_per_frame = conf_port->samples_per_frame;
|
---|
1155 | info->bits_per_sample = conf->bits_per_sample;
|
---|
1156 | info->tx_adj_level = conf_port->tx_adj_level - NORMAL_LEVEL;
|
---|
1157 | info->rx_adj_level = conf_port->rx_adj_level - NORMAL_LEVEL;
|
---|
1158 |
|
---|
1159 | return PJ_SUCCESS;
|
---|
1160 | }
|
---|
1161 |
|
---|
1162 |
|
---|
1163 | PJ_DEF(pj_status_t) pjmedia_conf_get_ports_info(pjmedia_conf *conf,
|
---|
1164 | unsigned *size,
|
---|
1165 | pjmedia_conf_port_info info[])
|
---|
1166 | {
|
---|
1167 | unsigned i, count=0;
|
---|
1168 |
|
---|
1169 | PJ_ASSERT_RETURN(conf && size && info, PJ_EINVAL);
|
---|
1170 |
|
---|
1171 | for (i=0; i<conf->max_ports && count<*size; ++i) {
|
---|
1172 | if (!conf->ports[i])
|
---|
1173 | continue;
|
---|
1174 |
|
---|
1175 | pjmedia_conf_get_port_info(conf, i, &info[count]);
|
---|
1176 | ++count;
|
---|
1177 | }
|
---|
1178 |
|
---|
1179 | *size = count;
|
---|
1180 | return PJ_SUCCESS;
|
---|
1181 | }
|
---|
1182 |
|
---|
1183 |
|
---|
1184 | /*
|
---|
1185 | * Get signal level.
|
---|
1186 | */
|
---|
1187 | PJ_DEF(pj_status_t) pjmedia_conf_get_signal_level( pjmedia_conf *conf,
|
---|
1188 | unsigned slot,
|
---|
1189 | unsigned *tx_level,
|
---|
1190 | unsigned *rx_level)
|
---|
1191 | {
|
---|
1192 | struct conf_port *conf_port;
|
---|
1193 |
|
---|
1194 | /* Check arguments */
|
---|
1195 | PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
|
---|
1196 |
|
---|
1197 | /* Port must be valid. */
|
---|
1198 | PJ_ASSERT_RETURN(conf->ports[slot] != NULL, PJ_EINVAL);
|
---|
1199 |
|
---|
1200 | conf_port = conf->ports[slot];
|
---|
1201 |
|
---|
1202 | if (tx_level != NULL) {
|
---|
1203 | *tx_level = conf_port->tx_level;
|
---|
1204 | }
|
---|
1205 |
|
---|
1206 | if (rx_level != NULL)
|
---|
1207 | *rx_level = conf_port->rx_level;
|
---|
1208 |
|
---|
1209 | return PJ_SUCCESS;
|
---|
1210 | }
|
---|
1211 |
|
---|
1212 |
|
---|
1213 | /*
|
---|
1214 | * Adjust RX level of individual port.
|
---|
1215 | */
|
---|
1216 | PJ_DEF(pj_status_t) pjmedia_conf_adjust_rx_level( pjmedia_conf *conf,
|
---|
1217 | unsigned slot,
|
---|
1218 | int adj_level )
|
---|
1219 | {
|
---|
1220 | struct conf_port *conf_port;
|
---|
1221 |
|
---|
1222 | /* Check arguments */
|
---|
1223 | PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
|
---|
1224 |
|
---|
1225 | /* Port must be valid. */
|
---|
1226 | PJ_ASSERT_RETURN(conf->ports[slot] != NULL, PJ_EINVAL);
|
---|
1227 |
|
---|
1228 | /* Value must be from -128 to +127 */
|
---|
1229 | /* Disabled, you can put more than +127, at your own risk:
|
---|
1230 | PJ_ASSERT_RETURN(adj_level >= -128 && adj_level <= 127, PJ_EINVAL);
|
---|
1231 | */
|
---|
1232 | PJ_ASSERT_RETURN(adj_level >= -128, PJ_EINVAL);
|
---|
1233 |
|
---|
1234 | conf_port = conf->ports[slot];
|
---|
1235 |
|
---|
1236 | /* Set normalized adjustment level. */
|
---|
1237 | conf_port->rx_adj_level = adj_level + NORMAL_LEVEL;
|
---|
1238 |
|
---|
1239 | return PJ_SUCCESS;
|
---|
1240 | }
|
---|
1241 |
|
---|
1242 |
|
---|
1243 | /*
|
---|
1244 | * Adjust TX level of individual port.
|
---|
1245 | */
|
---|
1246 | PJ_DEF(pj_status_t) pjmedia_conf_adjust_tx_level( pjmedia_conf *conf,
|
---|
1247 | unsigned slot,
|
---|
1248 | int adj_level )
|
---|
1249 | {
|
---|
1250 | struct conf_port *conf_port;
|
---|
1251 |
|
---|
1252 | /* Check arguments */
|
---|
1253 | PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
|
---|
1254 |
|
---|
1255 | /* Port must be valid. */
|
---|
1256 | PJ_ASSERT_RETURN(conf->ports[slot] != NULL, PJ_EINVAL);
|
---|
1257 |
|
---|
1258 | /* Value must be from -128 to +127 */
|
---|
1259 | /* Disabled, you can put more than +127,, at your own risk:
|
---|
1260 | PJ_ASSERT_RETURN(adj_level >= -128 && adj_level <= 127, PJ_EINVAL);
|
---|
1261 | */
|
---|
1262 | PJ_ASSERT_RETURN(adj_level >= -128, PJ_EINVAL);
|
---|
1263 |
|
---|
1264 | conf_port = conf->ports[slot];
|
---|
1265 |
|
---|
1266 | /* Set normalized adjustment level. */
|
---|
1267 | conf_port->tx_adj_level = adj_level + NORMAL_LEVEL;
|
---|
1268 |
|
---|
1269 | return PJ_SUCCESS;
|
---|
1270 | }
|
---|
1271 |
|
---|
1272 |
|
---|
1273 | /* Convert signed 16bit pcm sample to unsigned 16bit sample */
|
---|
1274 | static pj_uint16_t pcm2unsigned(pj_int32_t pcm)
|
---|
1275 | {
|
---|
1276 | return (pj_uint16_t)(pcm + 32768);
|
---|
1277 | }
|
---|
1278 |
|
---|
1279 | /* Convert unsigned 16bit sample to signed 16bit pcm sample */
|
---|
1280 | static pj_int16_t unsigned2pcm(pj_uint32_t uns)
|
---|
1281 | {
|
---|
1282 | return (pj_int16_t)(uns - 32768);
|
---|
1283 | }
|
---|
1284 |
|
---|
1285 |
|
---|
1286 | /*
|
---|
1287 | * Read from port.
|
---|
1288 | */
|
---|
1289 | static pj_status_t read_port( pjmedia_conf *conf,
|
---|
1290 | struct conf_port *cport, pj_int16_t *frame,
|
---|
1291 | pj_size_t count, pjmedia_frame_type *type )
|
---|
1292 | {
|
---|
1293 |
|
---|
1294 | pj_assert(count == conf->samples_per_frame);
|
---|
1295 |
|
---|
1296 | TRACE_((THIS_FILE, "read_port %.*s: count=%d",
|
---|
1297 | (int)cport->name.slen, cport->name.ptr,
|
---|
1298 | count));
|
---|
1299 |
|
---|
1300 | /* If port's samples per frame and sampling rate matches conference
|
---|
1301 | * bridge's settings, get the frame directly from the port.
|
---|
1302 | */
|
---|
1303 | if (cport->rx_buf_cap == 0) {
|
---|
1304 | pjmedia_frame f;
|
---|
1305 | pj_status_t status;
|
---|
1306 |
|
---|
1307 | f.buf = frame;
|
---|
1308 | f.size = count * BYTES_PER_SAMPLE;
|
---|
1309 |
|
---|
1310 | TRACE_((THIS_FILE, " get_frame %.*s: count=%d",
|
---|
1311 | (int)cport->name.slen, cport->name.ptr,
|
---|
1312 | count));
|
---|
1313 |
|
---|
1314 | status = pjmedia_port_get_frame(cport->port, &f);
|
---|
1315 |
|
---|
1316 | *type = f.type;
|
---|
1317 |
|
---|
1318 | return status;
|
---|
1319 |
|
---|
1320 | } else {
|
---|
1321 |
|
---|
1322 | /*
|
---|
1323 | * If we don't have enough samples in rx_buf, read from the port
|
---|
1324 | * first. Remember that rx_buf may be in different clock rate!
|
---|
1325 | */
|
---|
1326 | while (cport->rx_buf_count < count * 1.0 *
|
---|
1327 | cport->clock_rate / conf->clock_rate) {
|
---|
1328 |
|
---|
1329 | pjmedia_frame f;
|
---|
1330 | pj_status_t status;
|
---|
1331 |
|
---|
1332 | f.buf = cport->rx_buf + cport->rx_buf_count;
|
---|
1333 | f.size = cport->samples_per_frame * BYTES_PER_SAMPLE;
|
---|
1334 |
|
---|
1335 | TRACE_((THIS_FILE, " get_frame, count=%d",
|
---|
1336 | cport->samples_per_frame));
|
---|
1337 |
|
---|
1338 | status = pjmedia_port_get_frame(cport->port, &f);
|
---|
1339 |
|
---|
1340 | if (status != PJ_SUCCESS) {
|
---|
1341 | /* Fatal error! */
|
---|
1342 | return status;
|
---|
1343 | }
|
---|
1344 |
|
---|
1345 | if (f.type != PJMEDIA_FRAME_TYPE_AUDIO) {
|
---|
1346 | TRACE_((THIS_FILE, " get_frame returned non-audio"));
|
---|
1347 | pjmedia_zero_samples( cport->rx_buf + cport->rx_buf_count,
|
---|
1348 | cport->samples_per_frame);
|
---|
1349 | }
|
---|
1350 |
|
---|
1351 | cport->rx_buf_count += cport->samples_per_frame;
|
---|
1352 |
|
---|
1353 | TRACE_((THIS_FILE, " rx buffer size is now %d",
|
---|
1354 | cport->rx_buf_count));
|
---|
1355 |
|
---|
1356 | pj_assert(cport->rx_buf_count <= cport->rx_buf_cap);
|
---|
1357 | }
|
---|
1358 |
|
---|
1359 | /*
|
---|
1360 | * If port's clock_rate is different, resample.
|
---|
1361 | * Otherwise just copy.
|
---|
1362 | */
|
---|
1363 | if (cport->clock_rate != conf->clock_rate) {
|
---|
1364 |
|
---|
1365 | unsigned src_count;
|
---|
1366 |
|
---|
1367 | TRACE_((THIS_FILE, " resample, input count=%d",
|
---|
1368 | pjmedia_resample_get_input_size(cport->rx_resample)));
|
---|
1369 |
|
---|
1370 | pjmedia_resample_run( cport->rx_resample,cport->rx_buf, frame);
|
---|
1371 |
|
---|
1372 | src_count = (unsigned)(count * 1.0 * cport->clock_rate /
|
---|
1373 | conf->clock_rate);
|
---|
1374 | cport->rx_buf_count -= src_count;
|
---|
1375 | if (cport->rx_buf_count) {
|
---|
1376 | pjmedia_copy_samples(cport->rx_buf, cport->rx_buf+src_count,
|
---|
1377 | cport->rx_buf_count);
|
---|
1378 | }
|
---|
1379 |
|
---|
1380 | TRACE_((THIS_FILE, " rx buffer size is now %d",
|
---|
1381 | cport->rx_buf_count));
|
---|
1382 |
|
---|
1383 | } else {
|
---|
1384 |
|
---|
1385 | pjmedia_copy_samples(frame, cport->rx_buf, count);
|
---|
1386 | cport->rx_buf_count -= count;
|
---|
1387 | if (cport->rx_buf_count) {
|
---|
1388 | pjmedia_copy_samples(cport->rx_buf, cport->rx_buf+count,
|
---|
1389 | cport->rx_buf_count);
|
---|
1390 | }
|
---|
1391 | }
|
---|
1392 | }
|
---|
1393 |
|
---|
1394 | return PJ_SUCCESS;
|
---|
1395 | }
|
---|
1396 |
|
---|
1397 |
|
---|
1398 | /*
|
---|
1399 | * Write the mixed signal to the port.
|
---|
1400 | */
|
---|
1401 | static pj_status_t write_port(pjmedia_conf *conf, struct conf_port *cport,
|
---|
1402 | const pj_timestamp *timestamp,
|
---|
1403 | pjmedia_frame_type *frm_type)
|
---|
1404 | {
|
---|
1405 | pj_int16_t *buf;
|
---|
1406 | unsigned j, ts;
|
---|
1407 | pj_status_t status;
|
---|
1408 |
|
---|
1409 | *frm_type = PJMEDIA_FRAME_TYPE_AUDIO;
|
---|
1410 |
|
---|
1411 | /* If port is muted or nobody is transmitting to this port,
|
---|
1412 | * transmit NULL frame.
|
---|
1413 | */
|
---|
1414 | if (cport->tx_setting == PJMEDIA_PORT_MUTE || cport->transmitter_cnt==0) {
|
---|
1415 |
|
---|
1416 | pjmedia_frame frame;
|
---|
1417 |
|
---|
1418 | /* Adjust the timestamp */
|
---|
1419 | frame.timestamp.u64 = timestamp->u64 * cport->clock_rate /
|
---|
1420 | conf->clock_rate;
|
---|
1421 | frame.type = PJMEDIA_FRAME_TYPE_NONE;
|
---|
1422 | frame.buf = NULL;
|
---|
1423 | frame.size = 0;
|
---|
1424 |
|
---|
1425 | if (cport->port && cport->port->put_frame) {
|
---|
1426 | pjmedia_port_put_frame(cport->port, &frame);
|
---|
1427 | }
|
---|
1428 |
|
---|
1429 | cport->tx_level = 0;
|
---|
1430 | *frm_type = PJMEDIA_FRAME_TYPE_NONE;
|
---|
1431 | return PJ_SUCCESS;
|
---|
1432 |
|
---|
1433 | } else if (cport->src_level==0) {
|
---|
1434 |
|
---|
1435 | pjmedia_frame frame;
|
---|
1436 |
|
---|
1437 | /* If silence is transmitted to this port, transmit silence
|
---|
1438 | * PCM frame (otherwise if we transmit NULL frame, nothing will
|
---|
1439 | * be written to WAV port). This would work with stream too
|
---|
1440 | * since stream has it's own silence detector.
|
---|
1441 | */
|
---|
1442 | pjmedia_zero_samples((pj_int16_t*)cport->mix_buf,
|
---|
1443 | cport->samples_per_frame);
|
---|
1444 |
|
---|
1445 | /* Adjust the timestamp */
|
---|
1446 | frame.timestamp.u64 = timestamp->u64 * cport->clock_rate /
|
---|
1447 | conf->clock_rate;
|
---|
1448 | frame.type = PJMEDIA_FRAME_TYPE_NONE;
|
---|
1449 | frame.buf = (void*)cport->mix_buf;
|
---|
1450 | frame.size = (cport->samples_per_frame << 1);
|
---|
1451 |
|
---|
1452 | if (cport->port && cport->port->put_frame) {
|
---|
1453 | pjmedia_port_put_frame(cport->port, &frame);
|
---|
1454 | }
|
---|
1455 |
|
---|
1456 | cport->tx_level = 0;
|
---|
1457 | *frm_type = PJMEDIA_FRAME_TYPE_NONE;
|
---|
1458 | return PJ_SUCCESS;
|
---|
1459 |
|
---|
1460 |
|
---|
1461 | } else if (cport->tx_setting != PJMEDIA_PORT_ENABLE) {
|
---|
1462 | cport->tx_level = 0;
|
---|
1463 | *frm_type = PJMEDIA_FRAME_TYPE_NONE;
|
---|
1464 | return PJ_SUCCESS;
|
---|
1465 | }
|
---|
1466 |
|
---|
1467 | buf = (pj_int16_t*)cport->mix_buf;
|
---|
1468 |
|
---|
1469 | /* This is the convention set in get_frame(). For optimization purpose,
|
---|
1470 | * if we only have one transmitter transmitting to this port, then
|
---|
1471 | * the transmitter will directly copy the original 16bit frame to
|
---|
1472 | * mix_buf.
|
---|
1473 | */
|
---|
1474 | if (cport->transmitter_cnt==1 && cport->src_cnt == 1) {
|
---|
1475 |
|
---|
1476 | /* But still see if we need to adjust the level */
|
---|
1477 | if (cport->tx_adj_level != NORMAL_LEVEL) {
|
---|
1478 | pj_int16_t *input = buf;
|
---|
1479 | pj_int32_t adj = cport->tx_adj_level;
|
---|
1480 |
|
---|
1481 | for (j=0; j<conf->samples_per_frame; ++j) {
|
---|
1482 | pj_int32_t itemp;
|
---|
1483 |
|
---|
1484 | /* For the level adjustment, we need to store the sample to
|
---|
1485 | * a temporary 32bit integer value to avoid overflowing the
|
---|
1486 | * 16bit sample storage.
|
---|
1487 | */
|
---|
1488 | itemp = input[j];
|
---|
1489 | /*itemp = itemp * adj / NORMAL_LEVEL; */
|
---|
1490 | itemp = (itemp * adj) >> 7;
|
---|
1491 |
|
---|
1492 | /* Clip the signal if it's too loud */
|
---|
1493 | if (itemp > 32767) itemp = 32767;
|
---|
1494 | else if (itemp < -32768) itemp = -32768;
|
---|
1495 |
|
---|
1496 | input[j] = (pj_int16_t) itemp;
|
---|
1497 | }
|
---|
1498 | }
|
---|
1499 |
|
---|
1500 | }
|
---|
1501 | /* If there are sources in the mix buffer, convert the mixed samples
|
---|
1502 | * to the mixed samples itself. This is possible because mixed sample
|
---|
1503 | * is 32bit.
|
---|
1504 | *
|
---|
1505 | * In addition to this process, if we need to change the level of
|
---|
1506 | * TX signal, we adjust is here too.
|
---|
1507 | */
|
---|
1508 | else if (cport->tx_adj_level != NORMAL_LEVEL && cport->src_level) {
|
---|
1509 |
|
---|
1510 | pj_int32_t adj_level = cport->tx_adj_level;
|
---|
1511 |
|
---|
1512 | /* We need to adjust signal level. */
|
---|
1513 | for (j=0; j<conf->samples_per_frame; ++j) {
|
---|
1514 | pj_int32_t itemp;
|
---|
1515 |
|
---|
1516 | /* Calculate average level, and convert the sample to
|
---|
1517 | * 16bit signed integer.
|
---|
1518 | */
|
---|
1519 | itemp = unsigned2pcm(cport->mix_buf[j] / cport->src_level);
|
---|
1520 |
|
---|
1521 | /* Adjust the level */
|
---|
1522 | /*itemp = itemp * adj_level / NORMAL_LEVEL;*/
|
---|
1523 | itemp = (itemp * adj_level) >> 7;
|
---|
1524 |
|
---|
1525 | /* Clip the signal if it's too loud */
|
---|
1526 | if (itemp > 32767) itemp = 32767;
|
---|
1527 | else if (itemp < -32768) itemp = -32768;
|
---|
1528 |
|
---|
1529 | /* Put back in the buffer. */
|
---|
1530 | buf[j] = (pj_int16_t) itemp;
|
---|
1531 | }
|
---|
1532 |
|
---|
1533 | } else if (cport->src_level) {
|
---|
1534 | /* No need to adjust signal level. */
|
---|
1535 | for (j=0; j<conf->samples_per_frame; ++j) {
|
---|
1536 | buf[j] = unsigned2pcm(cport->mix_buf[j] / cport->src_level);
|
---|
1537 | }
|
---|
1538 | } else {
|
---|
1539 | // Not necessarry. Buffer has been zeroed before.
|
---|
1540 | // pjmedia_zero_samples(buf, conf->samples_per_frame);
|
---|
1541 | //pj_assert(buf[0] == 0);
|
---|
1542 |
|
---|
1543 | // This shouldn't happen. Function should've already bailed out when
|
---|
1544 | // cport->src_level == 0.
|
---|
1545 | pj_assert(0);
|
---|
1546 | }
|
---|
1547 |
|
---|
1548 | /* Calculate TX level if we need to do so.
|
---|
1549 | * This actually is not the most correct place to calculate TX signal
|
---|
1550 | * level of the port; it should calculate the level of the actual
|
---|
1551 | * frame just before put_frame() is called.
|
---|
1552 | * But doing so would make the code more complicated than it is
|
---|
1553 | * necessary, since the purpose of level calculation mostly is just
|
---|
1554 | * for VU meter display. By doing it here, it should give the acceptable
|
---|
1555 | * indication of the signal level of the port.
|
---|
1556 | */
|
---|
1557 | if (cport->src_cnt) {
|
---|
1558 | cport->tx_level = cport->src_level / cport->src_cnt;
|
---|
1559 | } else {
|
---|
1560 | cport->tx_level = 0;
|
---|
1561 | }
|
---|
1562 |
|
---|
1563 | /* If port has the same clock_rate and samples_per_frame settings as
|
---|
1564 | * the conference bridge, transmit the frame as is.
|
---|
1565 | */
|
---|
1566 | if (cport->clock_rate == conf->clock_rate &&
|
---|
1567 | cport->samples_per_frame == conf->samples_per_frame)
|
---|
1568 | {
|
---|
1569 | if (cport->port != NULL) {
|
---|
1570 | pjmedia_frame frame;
|
---|
1571 |
|
---|
1572 | frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
|
---|
1573 | frame.buf = (pj_int16_t*)cport->mix_buf;
|
---|
1574 | frame.size = conf->samples_per_frame * BYTES_PER_SAMPLE;
|
---|
1575 | /* No need to adjust timestamp, port has the same
|
---|
1576 | * clock rate as conference bridge
|
---|
1577 | */
|
---|
1578 | frame.timestamp = *timestamp;
|
---|
1579 |
|
---|
1580 | TRACE_((THIS_FILE, "put_frame %.*s, count=%d",
|
---|
1581 | (int)cport->name.slen, cport->name.ptr,
|
---|
1582 | frame.size / BYTES_PER_SAMPLE));
|
---|
1583 |
|
---|
1584 | return pjmedia_port_put_frame(cport->port, &frame);
|
---|
1585 | } else
|
---|
1586 | return PJ_SUCCESS;
|
---|
1587 | }
|
---|
1588 |
|
---|
1589 | /* If it has different clock_rate, must resample. */
|
---|
1590 | if (cport->clock_rate != conf->clock_rate) {
|
---|
1591 |
|
---|
1592 | unsigned dst_count;
|
---|
1593 |
|
---|
1594 | pjmedia_resample_run( cport->tx_resample, buf,
|
---|
1595 | cport->tx_buf + cport->tx_buf_count );
|
---|
1596 |
|
---|
1597 | dst_count = (unsigned)(conf->samples_per_frame * 1.0 *
|
---|
1598 | cport->clock_rate / conf->clock_rate);
|
---|
1599 | cport->tx_buf_count += dst_count;
|
---|
1600 |
|
---|
1601 | } else {
|
---|
1602 | /* Same clock rate.
|
---|
1603 | * Just copy the samples to tx_buffer.
|
---|
1604 | */
|
---|
1605 | pjmedia_copy_samples( cport->tx_buf + cport->tx_buf_count,
|
---|
1606 | buf, conf->samples_per_frame );
|
---|
1607 | cport->tx_buf_count += conf->samples_per_frame;
|
---|
1608 | }
|
---|
1609 |
|
---|
1610 | /* Transmit while we have enough frame in the tx_buf. */
|
---|
1611 | status = PJ_SUCCESS;
|
---|
1612 | ts = 0;
|
---|
1613 | while (cport->tx_buf_count >= cport->samples_per_frame &&
|
---|
1614 | status == PJ_SUCCESS)
|
---|
1615 | {
|
---|
1616 |
|
---|
1617 | TRACE_((THIS_FILE, "write_port %.*s: count=%d",
|
---|
1618 | (int)cport->name.slen, cport->name.ptr,
|
---|
1619 | cport->samples_per_frame));
|
---|
1620 |
|
---|
1621 | if (cport->port) {
|
---|
1622 | pjmedia_frame frame;
|
---|
1623 |
|
---|
1624 | frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
|
---|
1625 | frame.buf = cport->tx_buf;
|
---|
1626 | frame.size = cport->samples_per_frame * BYTES_PER_SAMPLE;
|
---|
1627 | /* Adjust timestamp as port may have different clock rate
|
---|
1628 | * than the bridge.
|
---|
1629 | */
|
---|
1630 | frame.timestamp.u64 = timestamp->u64 * cport->clock_rate /
|
---|
1631 | conf->clock_rate;
|
---|
1632 |
|
---|
1633 | /* Add timestamp for individual frame */
|
---|
1634 | frame.timestamp.u64 += ts;
|
---|
1635 | ts += cport->samples_per_frame;
|
---|
1636 |
|
---|
1637 | TRACE_((THIS_FILE, "put_frame %.*s, count=%d",
|
---|
1638 | (int)cport->name.slen, cport->name.ptr,
|
---|
1639 | frame.size / BYTES_PER_SAMPLE));
|
---|
1640 |
|
---|
1641 | status = pjmedia_port_put_frame(cport->port, &frame);
|
---|
1642 |
|
---|
1643 | } else
|
---|
1644 | status = PJ_SUCCESS;
|
---|
1645 |
|
---|
1646 | cport->tx_buf_count -= cport->samples_per_frame;
|
---|
1647 | if (cport->tx_buf_count) {
|
---|
1648 | pjmedia_copy_samples(cport->tx_buf,
|
---|
1649 | cport->tx_buf + cport->samples_per_frame,
|
---|
1650 | cport->tx_buf_count);
|
---|
1651 | }
|
---|
1652 |
|
---|
1653 | TRACE_((THIS_FILE, " tx_buf count now is %d",
|
---|
1654 | cport->tx_buf_count));
|
---|
1655 | }
|
---|
1656 |
|
---|
1657 | return status;
|
---|
1658 | }
|
---|
1659 |
|
---|
1660 |
|
---|
1661 | #ifndef USE_SND_THREAD
|
---|
1662 | static pj_status_t get_frame(pjmedia_port *this_port,
|
---|
1663 | pjmedia_frame *frame)
|
---|
1664 | {
|
---|
1665 | pjmedia_conf *conf = this_port->port_data.pdata;
|
---|
1666 | struct conf_port *port = conf->ports[this_port->port_data.ldata];
|
---|
1667 | pj_int16_t *output = frame->buf;
|
---|
1668 | const pj_int16_t *target_spk_buf;
|
---|
1669 |
|
---|
1670 | /* Check for correct size. */
|
---|
1671 | PJ_ASSERT_RETURN( frame->size == conf->samples_per_frame *
|
---|
1672 | conf->bits_per_sample / 8,
|
---|
1673 | PJMEDIA_ENCSAMPLESPFRAME);
|
---|
1674 |
|
---|
1675 | /* Skip if this port is muted/disabled. */
|
---|
1676 | if (port->tx_setting != PJMEDIA_PORT_ENABLE) {
|
---|
1677 | return PJ_SUCCESS;
|
---|
1678 | }
|
---|
1679 |
|
---|
1680 | if (port->transmitter_cnt == 0) {
|
---|
1681 | return PJ_SUCCESS;
|
---|
1682 | }
|
---|
1683 |
|
---|
1684 | if (port->spk_read_pos == port->spk_write_pos)
|
---|
1685 | port->spk_read_pos = (port->spk_read_pos + RX_BUF_COUNT / 2) % RX_BUF_COUNT;
|
---|
1686 |
|
---|
1687 | target_spk_buf = port->spk_buf[port->spk_read_pos];
|
---|
1688 | pjmedia_copy_samples( output, target_spk_buf, conf->samples_per_frame);
|
---|
1689 | // we have to do this, in case that the stream port did not disconnect from
|
---|
1690 | // the conference after the calling has been over
|
---|
1691 | pj_bzero(target_spk_buf, conf->samples_per_frame * sizeof(target_spk_buf[0]));
|
---|
1692 | port->spk_read_pos = (port->spk_read_pos+1)%RX_BUF_COUNT;
|
---|
1693 | frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
|
---|
1694 |
|
---|
1695 | return PJ_SUCCESS;
|
---|
1696 | }
|
---|
1697 |
|
---|
1698 |
|
---|
1699 | // Callback to be called for each clock ticks.
|
---|
1700 | static void clock_callback(const pj_timestamp *ts, void *user_data)
|
---|
1701 | {
|
---|
1702 | // to simulate the parameter in the previous version get_frame,
|
---|
1703 | // pjmedia_port *this_port = NULL; // not used any longer
|
---|
1704 | pjmedia_frame *frame;
|
---|
1705 |
|
---|
1706 | // and local auto variable
|
---|
1707 | pjmedia_conf *conf = user_data;
|
---|
1708 | pjmedia_frame_type speaker_frame_type = PJMEDIA_FRAME_TYPE_NONE;
|
---|
1709 | unsigned ci, cj, i, j;
|
---|
1710 |
|
---|
1711 |
|
---|
1712 | struct conf_port *conf_port;
|
---|
1713 | pjmedia_frame pjmframe;
|
---|
1714 |
|
---|
1715 | if (NULL == conf)
|
---|
1716 | return;
|
---|
1717 |
|
---|
1718 | if (conf->connect_cnt <= 0)
|
---|
1719 | return;
|
---|
1720 |
|
---|
1721 | conf_port = conf->ports[0];
|
---|
1722 | if (NULL == conf_port)
|
---|
1723 | return;
|
---|
1724 |
|
---|
1725 | pjmframe.buf = conf_port->spk_buf[conf_port->spk_write_pos];
|
---|
1726 | pjmframe.size = conf->samples_per_frame * conf->bits_per_sample / 8;
|
---|
1727 | pjmframe.timestamp.u64 = ts->u64;
|
---|
1728 | frame = &pjmframe;
|
---|
1729 |
|
---|
1730 | #else
|
---|
1731 |
|
---|
1732 | /*
|
---|
1733 | * Player callback.
|
---|
1734 | */
|
---|
1735 | static pj_status_t get_frame(pjmedia_port *this_port,
|
---|
1736 | pjmedia_frame *frame)
|
---|
1737 | {
|
---|
1738 | pjmedia_conf *conf = this_port->port_data.pdata;
|
---|
1739 | pjmedia_frame_type speaker_frame_type = PJMEDIA_FRAME_TYPE_NONE;
|
---|
1740 | unsigned ci, cj, i, j;
|
---|
1741 |
|
---|
1742 | #endif
|
---|
1743 |
|
---|
1744 | TRACE_((THIS_FILE, "- clock -"));
|
---|
1745 |
|
---|
1746 | /* Check that correct size is specified. */
|
---|
1747 | pj_assert(frame->size == conf->samples_per_frame *
|
---|
1748 | conf->bits_per_sample / 8);
|
---|
1749 |
|
---|
1750 | /* Must lock mutex */
|
---|
1751 | pj_mutex_lock(conf->mutex);
|
---|
1752 |
|
---|
1753 | /* Reset port source count. We will only reset port's mix
|
---|
1754 | * buffer when we have someone transmitting to it.
|
---|
1755 | */
|
---|
1756 | for (i=0, ci=0; i<conf->max_ports && ci < conf->port_cnt; ++i) {
|
---|
1757 | struct conf_port *conf_port = conf->ports[i];
|
---|
1758 |
|
---|
1759 | /* Skip empty slot. */
|
---|
1760 | if (!conf_port)
|
---|
1761 | continue;
|
---|
1762 |
|
---|
1763 | ++ci;
|
---|
1764 |
|
---|
1765 | /* Reset sources */
|
---|
1766 | conf_port->src_level = 0;
|
---|
1767 | conf_port->src_cnt = 0;
|
---|
1768 | }
|
---|
1769 |
|
---|
1770 | /* Get frames from all ports, and "mix" the signal
|
---|
1771 | * to mix_buf of all listeners of the port.
|
---|
1772 | */
|
---|
1773 | for (i=0, ci=0; i<conf->max_ports && ci<conf->port_cnt; ++i) {
|
---|
1774 | struct conf_port *conf_port = conf->ports[i];
|
---|
1775 | pj_int32_t level;
|
---|
1776 |
|
---|
1777 | /* Skip empty port. */
|
---|
1778 | if (!conf_port)
|
---|
1779 | continue;
|
---|
1780 |
|
---|
1781 | /* Var "ci" is to count how many ports have been visited so far. */
|
---|
1782 | ++ci;
|
---|
1783 |
|
---|
1784 | /* Skip if we're not allowed to receive from this port. */
|
---|
1785 | if (conf_port->rx_setting == PJMEDIA_PORT_DISABLE) {
|
---|
1786 | conf_port->rx_level = 0;
|
---|
1787 | continue;
|
---|
1788 | }
|
---|
1789 |
|
---|
1790 | /* Also skip if this port doesn't have listeners. */
|
---|
1791 | if (conf_port->listener_cnt == 0) {
|
---|
1792 | conf_port->rx_level = 0;
|
---|
1793 | continue;
|
---|
1794 | }
|
---|
1795 |
|
---|
1796 | /* Get frame from this port.
|
---|
1797 | * For port zero (sound port) and passive ports, get the frame from
|
---|
1798 | * the rx_buffer instead.
|
---|
1799 | */
|
---|
1800 | if (conf_port->port == NULL) {
|
---|
1801 | pj_int16_t *snd_buf;
|
---|
1802 |
|
---|
1803 | if (conf_port->snd_read_pos == conf_port->snd_write_pos) {
|
---|
1804 | conf_port->snd_read_pos =
|
---|
1805 | (conf_port->snd_write_pos+RX_BUF_COUNT-RX_BUF_COUNT/2) %
|
---|
1806 | RX_BUF_COUNT;
|
---|
1807 | }
|
---|
1808 |
|
---|
1809 | /* Skip if this port is muted/disabled. */
|
---|
1810 | if (conf_port->rx_setting != PJMEDIA_PORT_ENABLE) {
|
---|
1811 | conf_port->rx_level = 0;
|
---|
1812 | continue;
|
---|
1813 | }
|
---|
1814 |
|
---|
1815 | snd_buf = conf_port->snd_buf[conf_port->snd_read_pos];
|
---|
1816 | pjmedia_copy_samples(frame->buf, snd_buf, conf->samples_per_frame);
|
---|
1817 | conf_port->snd_read_pos = (conf_port->snd_read_pos+1) % RX_BUF_COUNT;
|
---|
1818 |
|
---|
1819 | } else {
|
---|
1820 |
|
---|
1821 | pj_status_t status;
|
---|
1822 | pjmedia_frame_type frame_type;
|
---|
1823 |
|
---|
1824 | status = read_port(conf, conf_port, frame->buf,
|
---|
1825 | conf->samples_per_frame, &frame_type);
|
---|
1826 |
|
---|
1827 | if (status != PJ_SUCCESS) {
|
---|
1828 | /* bennylp: why do we need this????
|
---|
1829 | * Also see comments on similar issue with write_port().
|
---|
1830 | PJ_LOG(4,(THIS_FILE, "Port %.*s get_frame() returned %d. "
|
---|
1831 | "Port is now disabled",
|
---|
1832 | (int)conf_port->name.slen,
|
---|
1833 | conf_port->name.ptr,
|
---|
1834 | status));
|
---|
1835 | conf_port->rx_setting = PJMEDIA_PORT_DISABLE;
|
---|
1836 | */
|
---|
1837 | continue;
|
---|
1838 | }
|
---|
1839 |
|
---|
1840 | /* Check that the port is not removed when we call get_frame() */
|
---|
1841 | if (conf->ports[i] == NULL)
|
---|
1842 | continue;
|
---|
1843 | }
|
---|
1844 |
|
---|
1845 | /* If we need to adjust the RX level from this port, adjust the level
|
---|
1846 | * and calculate the average level at the same time.
|
---|
1847 | * Otherwise just calculate the averate level.
|
---|
1848 | */
|
---|
1849 | if (conf_port->rx_adj_level != NORMAL_LEVEL) {
|
---|
1850 | pj_int16_t *input = frame->buf;
|
---|
1851 | pj_int32_t adj = conf_port->rx_adj_level;
|
---|
1852 |
|
---|
1853 | level = 0;
|
---|
1854 | for (j=0; j<conf->samples_per_frame; ++j) {
|
---|
1855 | pj_int32_t itemp;
|
---|
1856 |
|
---|
1857 | /* For the level adjustment, we need to store the sample to
|
---|
1858 | * a temporary 32bit integer value to avoid overflowing the
|
---|
1859 | * 16bit sample storage.
|
---|
1860 | */
|
---|
1861 | itemp = input[j];
|
---|
1862 | /*itemp = itemp * adj / NORMAL_LEVEL;*/
|
---|
1863 | itemp = (itemp * adj) >> 7;
|
---|
1864 |
|
---|
1865 | /* Clip the signal if it's too loud */
|
---|
1866 | if (itemp > 32767) itemp = 32767;
|
---|
1867 | else if (itemp < -32768) itemp = -32768;
|
---|
1868 |
|
---|
1869 | input[j] = (pj_int16_t) itemp;
|
---|
1870 |
|
---|
1871 | if (itemp >=0 ) level += itemp;
|
---|
1872 | else level -= itemp;
|
---|
1873 | }
|
---|
1874 |
|
---|
1875 | level /= conf->samples_per_frame;
|
---|
1876 |
|
---|
1877 | } else {
|
---|
1878 | level = pjmedia_calc_avg_signal(frame->buf,
|
---|
1879 | conf->samples_per_frame);
|
---|
1880 | }
|
---|
1881 |
|
---|
1882 | /* Apply simple AGC to the level, to avoid dramatic change in the
|
---|
1883 | * level thus causing noise because the signal now is not aligned
|
---|
1884 | * with the signal from the previous frame.
|
---|
1885 | */
|
---|
1886 | if (level >= conf_port->last_level) {
|
---|
1887 | level = (conf_port->last_level * ATTACK_A + level * ATTACK_B) /
|
---|
1888 | (ATTACK_A + ATTACK_B);
|
---|
1889 | } else {
|
---|
1890 | level = (conf_port->last_level * DECAY_A + level * DECAY_B) /
|
---|
1891 | (DECAY_A + DECAY_B);
|
---|
1892 | }
|
---|
1893 | conf_port->last_level = level;
|
---|
1894 |
|
---|
1895 |
|
---|
1896 | /* Convert level to 8bit complement ulaw */
|
---|
1897 | level = pjmedia_linear2ulaw(level) ^ 0xff;
|
---|
1898 |
|
---|
1899 | /* Put this level to port's last RX level. */
|
---|
1900 | conf_port->rx_level = level;
|
---|
1901 |
|
---|
1902 | /* Skip processing frame if level is zero */
|
---|
1903 | if (level == 0)
|
---|
1904 | continue;
|
---|
1905 |
|
---|
1906 | /* Convert the buffer to unsigned 16bit value */
|
---|
1907 | for (j=0; j<conf->samples_per_frame; ++j)
|
---|
1908 | conf->uns_buf[j] = pcm2unsigned(((pj_int16_t*)frame->buf)[j]);
|
---|
1909 |
|
---|
1910 | /* Add the signal to all listeners. */
|
---|
1911 | for (cj=0; cj < conf_port->listener_cnt; ++cj)
|
---|
1912 | {
|
---|
1913 | struct conf_port *listener;
|
---|
1914 | pj_uint32_t *mix_buf;
|
---|
1915 | unsigned k;
|
---|
1916 |
|
---|
1917 | listener = conf->ports[conf_port->listener_slots[cj]];
|
---|
1918 |
|
---|
1919 | /* Skip if this listener doesn't want to receive audio */
|
---|
1920 | if (listener->tx_setting != PJMEDIA_PORT_ENABLE)
|
---|
1921 | continue;
|
---|
1922 |
|
---|
1923 | /* Mix the buffer. If this is the first source for target port,
|
---|
1924 | * zero the mix buffer of target port first.
|
---|
1925 | */
|
---|
1926 | mix_buf = listener->mix_buf;
|
---|
1927 | if (listener->src_level == 0) {
|
---|
1928 | pj_bzero( mix_buf, conf->samples_per_frame*sizeof(mix_buf[0]));
|
---|
1929 | }
|
---|
1930 |
|
---|
1931 | /* A little bit of optimization:
|
---|
1932 | * When "conf_port" is the only transmitter to "listener",
|
---|
1933 | * just add copy the frame directly from the original
|
---|
1934 | * 16bit frame (avoiding unsigned2pcm() conversion).
|
---|
1935 | * But write_port() needs to be aware of this trick!
|
---|
1936 | */
|
---|
1937 | if (listener->transmitter_cnt == 1) {
|
---|
1938 | pjmedia_copy_samples((pj_int16_t*)mix_buf,
|
---|
1939 | frame->buf, conf->samples_per_frame);
|
---|
1940 | listener->src_level = level;
|
---|
1941 | } else {
|
---|
1942 | for (k=0; k<conf->samples_per_frame; ++k)
|
---|
1943 | mix_buf[k] += (conf->uns_buf[k] * level);
|
---|
1944 |
|
---|
1945 | listener->src_level += level;
|
---|
1946 | }
|
---|
1947 | listener->src_cnt++;
|
---|
1948 | }
|
---|
1949 | }
|
---|
1950 |
|
---|
1951 | /* Time for all ports to transmit whetever they have in their
|
---|
1952 | * buffer.
|
---|
1953 | */
|
---|
1954 | for (i=0, ci=0; i<conf->max_ports && ci<conf->port_cnt; ++i) {
|
---|
1955 | struct conf_port *conf_port = conf->ports[i];
|
---|
1956 | pjmedia_frame_type frm_type;
|
---|
1957 | pj_status_t status;
|
---|
1958 |
|
---|
1959 | if (!conf_port)
|
---|
1960 | continue;
|
---|
1961 |
|
---|
1962 | /* Var "ci" is to count how many ports have been visited. */
|
---|
1963 | ++ci;
|
---|
1964 |
|
---|
1965 | status = write_port( conf, conf_port, &frame->timestamp,
|
---|
1966 | &frm_type);
|
---|
1967 | if (status != PJ_SUCCESS) {
|
---|
1968 | /* bennylp: why do we need this????
|
---|
1969 | One thing for sure, put_frame()/write_port() may return
|
---|
1970 | non-successfull status on Win32 if there's temporary glitch
|
---|
1971 | on network interface, so disabling the port here does not
|
---|
1972 | sound like a good idea.
|
---|
1973 |
|
---|
1974 | PJ_LOG(4,(THIS_FILE, "Port %.*s put_frame() returned %d. "
|
---|
1975 | "Port is now disabled",
|
---|
1976 | (int)conf_port->name.slen,
|
---|
1977 | conf_port->name.ptr,
|
---|
1978 | status));
|
---|
1979 | conf_port->tx_setting = PJMEDIA_PORT_DISABLE;
|
---|
1980 | */
|
---|
1981 | continue;
|
---|
1982 | }
|
---|
1983 |
|
---|
1984 | /* Set the type of frame to be returned to sound playback
|
---|
1985 | * device.
|
---|
1986 | */
|
---|
1987 | if (i == 0)
|
---|
1988 | speaker_frame_type = frm_type;
|
---|
1989 | }
|
---|
1990 |
|
---|
1991 | /* Return sound playback frame. */
|
---|
1992 | if (conf->ports[0]->src_level) {
|
---|
1993 | TRACE_((THIS_FILE, "write to audio, count=%d",
|
---|
1994 | conf->samples_per_frame));
|
---|
1995 |
|
---|
1996 | pjmedia_copy_samples( frame->buf, (pj_int16_t*)conf->ports[0]->mix_buf,
|
---|
1997 | conf->samples_per_frame);
|
---|
1998 | } else {
|
---|
1999 | pjmedia_zero_samples( frame->buf, conf->samples_per_frame );
|
---|
2000 | }
|
---|
2001 |
|
---|
2002 | /* MUST set frame type */
|
---|
2003 | frame->type = speaker_frame_type;
|
---|
2004 |
|
---|
2005 | #ifndef USE_SND_THREAD
|
---|
2006 | conf_port->spk_write_pos = (conf_port->spk_write_pos + 1) % RX_BUF_COUNT;
|
---|
2007 | #endif
|
---|
2008 |
|
---|
2009 | pj_mutex_unlock(conf->mutex);
|
---|
2010 |
|
---|
2011 | #ifdef REC_FILE
|
---|
2012 | if (fhnd_rec == NULL)
|
---|
2013 | fhnd_rec = fopen(REC_FILE, "wb");
|
---|
2014 | if (fhnd_rec)
|
---|
2015 | fwrite(frame->buf, frame->size, 1, fhnd_rec);
|
---|
2016 | #endif
|
---|
2017 |
|
---|
2018 | #ifdef USE_SND_THREAD
|
---|
2019 | return PJ_SUCCESS;
|
---|
2020 | #endif
|
---|
2021 | }
|
---|
2022 |
|
---|
2023 |
|
---|
2024 | /*
|
---|
2025 | * get_frame() for passive port
|
---|
2026 | */
|
---|
2027 | static pj_status_t get_frame_pasv(pjmedia_port *this_port,
|
---|
2028 | pjmedia_frame *frame)
|
---|
2029 | {
|
---|
2030 | pj_assert(0);
|
---|
2031 | PJ_UNUSED_ARG(this_port);
|
---|
2032 | PJ_UNUSED_ARG(frame);
|
---|
2033 | return -1;
|
---|
2034 | }
|
---|
2035 |
|
---|
2036 |
|
---|
2037 | /*
|
---|
2038 | * Recorder callback.
|
---|
2039 | */
|
---|
2040 | static pj_status_t put_frame(pjmedia_port *this_port,
|
---|
2041 | const pjmedia_frame *frame)
|
---|
2042 | {
|
---|
2043 | pjmedia_conf *conf = this_port->port_data.pdata;
|
---|
2044 | struct conf_port *port = conf->ports[this_port->port_data.ldata];
|
---|
2045 | const pj_int16_t *input = frame->buf;
|
---|
2046 | pj_int16_t *target_snd_buf;
|
---|
2047 |
|
---|
2048 | /* Check for correct size. */
|
---|
2049 | PJ_ASSERT_RETURN( frame->size == conf->samples_per_frame *
|
---|
2050 | conf->bits_per_sample / 8,
|
---|
2051 | PJMEDIA_ENCSAMPLESPFRAME);
|
---|
2052 |
|
---|
2053 | /* Skip if this port is muted/disabled. */
|
---|
2054 | if (port->rx_setting != PJMEDIA_PORT_ENABLE) {
|
---|
2055 | return PJ_SUCCESS;
|
---|
2056 | }
|
---|
2057 |
|
---|
2058 | /* Skip if no port is listening to the microphone */
|
---|
2059 | if (port->listener_cnt == 0) {
|
---|
2060 | return PJ_SUCCESS;
|
---|
2061 | }
|
---|
2062 |
|
---|
2063 |
|
---|
2064 | /* Determine which rx_buffer to fill in */
|
---|
2065 | target_snd_buf = port->snd_buf[port->snd_write_pos];
|
---|
2066 |
|
---|
2067 | /* Copy samples from audio device to target rx_buffer */
|
---|
2068 | pjmedia_copy_samples(target_snd_buf, input, conf->samples_per_frame);
|
---|
2069 |
|
---|
2070 | /* Switch buffer */
|
---|
2071 | port->snd_write_pos = (port->snd_write_pos+1)%RX_BUF_COUNT;
|
---|
2072 |
|
---|
2073 |
|
---|
2074 | return PJ_SUCCESS;
|
---|
2075 | }
|
---|
2076 |
|
---|