Ticket #323: conference.c

File conference.c, 58.1 KB (added by bennylp, 17 years ago)
Line 
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
59static 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 */
108struct 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 */
218struct 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 */
238static pj_status_t put_frame(pjmedia_port *this_port,
239                             const pjmedia_frame *frame);
240static pj_status_t get_frame(pjmedia_port *this_port,
241                             pjmedia_frame *frame);
242static pj_status_t get_frame_pasv(pjmedia_port *this_port,
243                                  pjmedia_frame *frame);
244static pj_status_t destroy_port(pjmedia_port *this_port);
245
246#ifndef USE_SND_THREAD
247static void clock_callback(const pj_timestamp *ts, void *user_data);
248#endif
249
250/*
251 * Create port.
252 */
253static 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 */
385static 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 */
440static 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 */
508PJ_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 */
596static 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 */
616static 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 */
637PJ_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 */
662static 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 */
672PJ_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 */
689PJ_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 */
715PJ_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 */
780PJ_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 */
878PJ_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 */
906PJ_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 */
970PJ_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 */
1032PJ_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 */
1041PJ_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 */
1110PJ_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 */
1132PJ_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
1163PJ_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 */
1187PJ_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 */
1216PJ_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 */
1246PJ_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 */
1274static 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 */
1280static 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 */
1289static 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 */
1401static 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
1662static 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.
1700static 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 */
1735static 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 */
2027static 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 */
2040static 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