Ticket #438: splitcomb.patch

File splitcomb.patch, 17.6 KB (added by bennylp, 16 years ago)

Patch to add delay buffer in splitcomb

  • pjmedia/src/pjmedia/splitcomb.c

     
    1717 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  
    1818 */ 
    1919#include <pjmedia/splitcomb.h> 
     20#include <pjmedia/delaybuf.h> 
    2021#include <pjmedia/errno.h> 
    2122#include <pj/assert.h> 
    2223#include <pj/log.h> 
     
    2829#define THIS_FILE           "splitcomb.c" 
    2930#define TMP_SAMP_TYPE       pj_int16_t 
    3031 
    31 /* When delay buffer is used, we only need 1 frame buffering */ 
    32 #if defined(PJMEDIA_SOUND_USE_DELAYBUF) && PJMEDIA_SOUND_USE_DELAYBUF!=0 
    33 #   define MAX_BUF_CNT      1 
    34 #else 
    35 #   define MAX_BUF_CNT      PJMEDIA_SOUND_BUFFER_COUNT 
    36 #endif 
     32/* Maximum number of channels. */ 
     33#define MAX_CHANNELS        16 
    3734 
     35#define MAX_BUF_CNT         PJMEDIA_SOUND_BUFFER_COUNT 
     36 
     37/* Operations */ 
     38#define OP_PUT              (1) 
     39#define OP_GET              (-1) 
     40 
     41/* Media flow directions */ 
     42enum sc_dir 
     43{ 
     44    /* This is the direction from the splitcomb to the downstream 
     45     * port(s), or when put_frame() is called to the splitcomb. 
     46     */ 
     47    DIR_DOWNSTREAM, 
     48 
     49    /* This is the direction from the downstream port to the splitcomb, 
     50     * or when get_frame() is called to the splitcomb. 
     51     */ 
     52    DIR_UPSTREAM 
     53}; 
     54 
     55 
     56 
    3857#if 0 
    3958#   define TRACE_UP_(x) PJ_LOG(5,x) 
    4059#   define TRACE_DN_(x) PJ_LOG(5,x) 
     
    6483    struct { 
    6584        pjmedia_port *port; 
    6685        pj_bool_t     reversed; 
    67     } port_desc[64]; 
     86    } port_desc[MAX_CHANNELS]; 
    6887 
    6988    /* Temporary buffers needed to extract mono frame from 
    7089     * multichannel frame. We could use stack for this, but this 
     
    84103    struct splitcomb*parent; 
    85104    unsigned         ch_num; 
    86105 
     106    /* Maximum burst before media flow is suspended */ 
     107    int              max_burst; 
     108 
    87109    /* A reverse port need a temporary buffer to store frame 
    88110     * (because of the different phase, see splitcomb.h for details).  
    89111     * Since we can not expect get_frame() and put_frame() to be 
    90      * called evenly one after another, we use circular buffers to 
    91      * accomodate the "jitter". 
     112     * called evenly one after another, we use delay buffers to 
     113     * accomodate the burst. 
    92114     */ 
    93     unsigned         buf_cnt; 
     115    struct { 
    94116 
    95     /* Downstream is the direction when get_frame() is called to the 
    96      * splitter/combiner port. 
    97      */ 
    98     unsigned         dn_read_pos, dn_write_pos,  
    99                      dn_overflow_pos, dn_underflow_pos; 
    100     pj_int16_t      *dnstream_buf[MAX_BUF_CNT]; 
     117        /* The delay buffer where frames will be stored */ 
     118        pjmedia_delay_buf   *dbuf; 
    101119 
    102     /* Upstream is the direction when put_frame() is called to the 
    103      * splitter/combiner port. 
     120        /* Flag to indicate that audio flow on this direction 
     121         * is currently being suspended (perhaps because nothing 
     122         * is processing the frame on the other end. 
     123         */ 
     124        pj_bool_t       paused; 
     125 
     126        /* Operation level. When the level exceeds a maximum value, 
     127         * the media flow on this direction will be paused. 
     128         */ 
     129        int             level; 
     130 
     131        /* Timestamp. */ 
     132        pj_timestamp    ts; 
     133 
     134    } buf[2]; 
     135 
     136    /* Must have temporary put buffer for the delay buf, 
     137     * unfortunately. 
    104138     */ 
    105     unsigned         up_read_pos, up_write_pos,  
    106                      up_overflow_pos, up_underflow_pos; 
    107     pj_int16_t      *upstream_buf[MAX_BUF_CNT]; 
     139    pj_int16_t        *tmp_up_buf; 
    108140}; 
    109141 
    110142 
     
    228260                                      unsigned options, 
    229261                                      pjmedia_port **p_chport) 
    230262{ 
    231     const pj_str_t name = pj_str("splitcomb-ch"); 
     263    const pj_str_t name = pj_str("scomb-rev"); 
    232264    struct splitcomb *sc = (struct splitcomb*) splitcomb; 
    233265    struct reverse_port *rport; 
    234     unsigned i; 
     266    unsigned buf_cnt; 
    235267    pjmedia_port *port; 
     268    pj_status_t status; 
    236269 
    237270    /* Sanity check */ 
    238271    PJ_ASSERT_RETURN(pool && splitcomb, PJ_EINVAL); 
     
    265298    port->on_destroy = &rport_on_destroy; 
    266299 
    267300 
    268     rport->buf_cnt = options & 0xFF; 
    269     if (rport->buf_cnt == 0) 
    270         rport->buf_cnt = MAX_BUF_CNT; 
     301    buf_cnt = options & 0xFF; 
     302    if (buf_cnt == 0) 
     303        buf_cnt = MAX_BUF_CNT; 
    271304 
    272     /* Create put buffers */ 
    273     for (i=0; i<rport->buf_cnt; ++i) { 
    274         rport->dnstream_buf[i]=(pj_int16_t*) 
    275                                pj_pool_zalloc(pool, port->info.bytes_per_frame); 
    276         PJ_ASSERT_RETURN(rport->dnstream_buf[i], PJ_ENOMEM); 
     305    rport->max_burst = buf_cnt + 4; 
     306 
     307    /* Create downstream/put buffers */ 
     308    status = pjmedia_delay_buf_create(pool, "scomb-down", 
     309                                      port->info.clock_rate, 
     310                                      port->info.samples_per_frame, 
     311                                      buf_cnt, -1, 0, 
     312                                      &rport->buf[DIR_DOWNSTREAM].dbuf); 
     313    if (status != PJ_SUCCESS) { 
     314        return status; 
    277315    } 
    278     rport->dn_write_pos = rport->buf_cnt/2; 
    279316 
    280     /* Create get buffers */ 
    281     for (i=0; i<rport->buf_cnt; ++i) { 
    282         rport->upstream_buf[i] = (pj_int16_t*) 
    283                                  pj_pool_zalloc(pool,  
    284                                                 port->info.bytes_per_frame); 
    285         PJ_ASSERT_RETURN(rport->upstream_buf[i], PJ_ENOMEM); 
     317    /* Create upstream/get buffers */ 
     318    status = pjmedia_delay_buf_create(pool, "scomb-up", 
     319                                      port->info.clock_rate, 
     320                                      port->info.samples_per_frame, 
     321                                      buf_cnt, -1, 0, 
     322                                      &rport->buf[DIR_UPSTREAM].dbuf); 
     323    if (status != PJ_SUCCESS) { 
     324        pjmedia_delay_buf_destroy(rport->buf[DIR_DOWNSTREAM].dbuf); 
     325        return status; 
    286326    } 
    287     rport->up_write_pos = rport->buf_cnt/2; 
    288327 
     328    /* And temporary upstream/get buffer */ 
     329    rport->tmp_up_buf = (pj_int16_t*) 
     330                        pj_pool_alloc(pool, port->info.bytes_per_frame); 
    289331 
    290332    /* Save port in the splitcomb */ 
    291333    sc->port_desc[ch_num].port = &rport->base; 
     
    294336 
    295337    /* Done */ 
    296338    *p_chport = port; 
    297     return PJ_SUCCESS; 
     339    return status; 
    298340} 
    299341 
    300342 
     
    335377    } 
    336378} 
    337379 
     380/* Update operation on the specified direction  */ 
     381static void op_update(struct reverse_port *rport, int dir, int op) 
     382{ 
     383    char *dir_name[2] = {"downstream", "upstream"}; 
    338384 
     385    rport->buf[dir].level += op; 
     386    rport->buf[dir].ts.u64 += rport->base.info.samples_per_frame; 
     387 
     388    if (rport->buf[dir].paused) { 
     389        if (rport->buf[dir].level < -rport->max_burst) { 
     390            rport->buf[dir].level = -rport->max_burst; 
     391        } else if (rport->buf[dir].level > rport->max_burst) { 
     392            rport->buf[dir].level = rport->max_burst; 
     393        } else { 
     394            /* Level has fallen below max level, we can resume 
     395             * media flow. 
     396             */ 
     397            PJ_LOG(5,(rport->base.info.name.ptr,  
     398                      "Resuming media flow on %s direction", dir_name[dir])); 
     399            rport->buf[dir].level = 0; 
     400            pjmedia_delay_buf_learn(rport->buf[dir].dbuf); 
     401            rport->buf[dir].paused = PJ_FALSE; 
     402        } 
     403    } else { 
     404        if (rport->buf[dir].level >= rport->max_burst || 
     405            rport->buf[dir].level <= -rport->max_burst)  
     406        { 
     407            /* Level has reached maximum level, the other side of 
     408             * rport is not sending/retrieving frames. Pause the 
     409             * rport on this direction. 
     410             */ 
     411            PJ_LOG(5,(rport->base.info.name.ptr,  
     412                      "Pausing media flow on %s direction", dir_name[dir])); 
     413            rport->buf[dir].paused = PJ_TRUE; 
     414        } 
     415    } 
     416} 
     417 
     418 
    339419/* 
    340420 * "Write" a multichannel frame. This would split the multichannel frame 
    341421 * into individual mono channel, and write it to the appropriate port. 
     
    373453        if (!port) 
    374454            continue; 
    375455 
     456        /* Extract the mono frame to temporary buffer */ 
     457        extract_mono_frame((const pj_int16_t*)frame->buf, sc->put_buf, ch,  
     458                           this_port->info.channel_count,  
     459                           frame->size * 8 /  
     460                             this_port->info.bits_per_sample / 
     461                             this_port->info.channel_count); 
     462 
    376463        if (!sc->port_desc[ch].reversed) { 
    377464            /* Write to normal port */ 
    378465            pjmedia_frame mono_frame; 
    379466 
    380             /* Extract the mono frame */ 
    381             extract_mono_frame((const pj_int16_t*)frame->buf, sc->put_buf, ch,  
    382                                this_port->info.channel_count,  
    383                                frame->size * 8 /  
    384                                  this_port->info.bits_per_sample / 
    385                                  this_port->info.channel_count); 
    386  
    387467            mono_frame.buf = sc->put_buf; 
    388468            mono_frame.size = frame->size / this_port->info.channel_count; 
    389469            mono_frame.type = frame->type; 
     
    395475        } else { 
    396476            /* Write to reversed phase port */ 
    397477            struct reverse_port *rport = (struct reverse_port*)port; 
    398              
    399             if (rport->dn_write_pos == rport->dn_read_pos) { 
    400478 
    401                 /* Only report overflow if the frame is constantly read 
    402                  * by the 'consumer' of the reverse port. 
    403                  * It is possible that nobody reads the buffer, so causing 
    404                  * overflow to happen rapidly, and writing log message this 
    405                  * way does not seem to be wise. 
    406                  */ 
    407                 if (rport->dn_read_pos != rport->dn_overflow_pos) { 
    408                     rport->dn_overflow_pos = rport->dn_read_pos; 
    409                     LOG_DN_((THIS_FILE, "Overflow in downstream direction")); 
    410                 } 
     479            /* Update rport state. */ 
     480            op_update(rport, DIR_DOWNSTREAM, OP_PUT); 
    411481 
    412                 /* Adjust write position */ 
    413                 rport->dn_write_pos =  
    414                     (rport->dn_write_pos + rport->buf_cnt/2) %  
    415                     rport->buf_cnt; 
     482            if (!rport->buf[DIR_DOWNSTREAM].paused) { 
     483                pjmedia_delay_buf_put(rport->buf[DIR_DOWNSTREAM].dbuf,  
     484                                      sc->put_buf); 
    416485            } 
    417  
    418             /* Extract mono-frame and put it in downstream buffer */ 
    419             extract_mono_frame((const pj_int16_t*)frame->buf,  
    420                                rport->dnstream_buf[rport->dn_write_pos], 
    421                                ch, this_port->info.channel_count,  
    422                                frame->size * 8 /  
    423                                  this_port->info.bits_per_sample / 
    424                                  this_port->info.channel_count); 
    425  
    426             rport->dn_write_pos = (rport->dn_write_pos + 1) % 
    427                                    rport->buf_cnt; 
    428486        } 
    429487    } 
    430488 
     
    444502    unsigned ch; 
    445503    pj_bool_t has_frame = PJ_FALSE; 
    446504 
    447     /* Clear output frame */ 
    448     pjmedia_zero_samples((pj_int16_t*)frame->buf,  
    449                          this_port->info.samples_per_frame); 
    450  
    451505    /* Read frame from each port */ 
    452506    for (ch=0; ch < this_port->info.channel_count; ++ch) { 
    453507        pjmedia_port *port = sc->port_desc[ch].port; 
    454508        pjmedia_frame mono_frame; 
    455509        pj_status_t status; 
    456510 
    457         if (!port) 
    458             continue; 
     511        if (!port) { 
     512            pjmedia_zero_samples(sc->get_buf,  
     513                                 this_port->info.samples_per_frame / 
     514                                    this_port->info.channel_count); 
    459515 
    460         /* Read from the port */ 
    461         if (sc->port_desc[ch].reversed == PJ_FALSE) { 
     516        } else if (sc->port_desc[ch].reversed == PJ_FALSE) { 
    462517            /* Read from normal port */ 
    463518            mono_frame.buf = sc->get_buf; 
    464519            mono_frame.size = port->info.bytes_per_frame; 
     
    468523            if (status != PJ_SUCCESS ||  
    469524                mono_frame.type != PJMEDIA_FRAME_TYPE_AUDIO) 
    470525            { 
    471                 continue; 
     526                pjmedia_zero_samples(sc->get_buf,  
     527                                     port->info.samples_per_frame); 
    472528            } 
    473529 
    474             /* Combine the mono frame into multichannel frame */ 
    475             store_mono_frame((const pj_int16_t*)mono_frame.buf,  
    476                              (pj_int16_t*)frame->buf, ch, 
    477                              this_port->info.channel_count, 
    478                              mono_frame.size * 8 / 
    479                                 this_port->info.bits_per_sample); 
    480  
    481530            frame->timestamp.u64 = mono_frame.timestamp.u64; 
    482531 
    483532        } else { 
    484533            /* Read from temporary buffer for reverse port */ 
    485534            struct reverse_port *rport = (struct reverse_port*)port; 
    486535 
    487             /* Check for underflows */ 
    488             if (rport->up_read_pos == rport->up_write_pos) { 
     536            /* Update rport state. */ 
     537            op_update(rport, DIR_UPSTREAM, OP_GET); 
    489538 
    490                 /* Only report underflow if the buffer is constantly filled 
    491                  * up at the other side. 
    492                  * It is possible that nobody writes the buffer, so causing 
    493                  * underflow to happen rapidly, and writing log message this 
    494                  * way does not seem to be wise. 
    495                  */ 
    496                 if (rport->up_write_pos != rport->up_underflow_pos) { 
    497                     rport->up_underflow_pos = rport->up_write_pos; 
    498                     LOG_UP_((THIS_FILE, "Underflow in upstream direction")); 
    499                 } 
     539            if (!rport->buf[DIR_DOWNSTREAM].paused) { 
     540                PJ_LOG(5,(rport->base.info.name.ptr, "get_frame()")); 
     541                pjmedia_delay_buf_get(rport->buf[DIR_UPSTREAM].dbuf,  
     542                                      sc->get_buf); 
    500543 
    501                 /* Adjust read position */ 
    502                 rport->up_read_pos =  
    503                     (rport->up_write_pos - rport->buf_cnt/2) % 
    504                     rport->buf_cnt; 
     544            } else { 
     545                pjmedia_zero_samples(sc->get_buf,  
     546                                      rport->base.info.samples_per_frame); 
    505547            } 
    506548 
    507             TRACE_UP_((THIS_FILE, "Upstream read at buffer pos %d",  
    508                        rport->up_read_pos)); 
     549            frame->timestamp.u64 = rport->buf[DIR_UPSTREAM].ts.u64; 
     550        } 
    509551 
    510             /* Combine the mono frame into multichannel frame */ 
    511             store_mono_frame((const pj_int16_t*)rport->upstream_buf[rport->up_read_pos],  
    512                              (pj_int16_t*)frame->buf, ch, 
    513                              this_port->info.channel_count, 
    514                              port->info.samples_per_frame); 
     552        /* Combine the mono frame into multichannel frame */ 
     553        store_mono_frame(sc->get_buf,  
     554                         (pj_int16_t*)frame->buf, ch, 
     555                         this_port->info.channel_count, 
     556                         this_port->info.samples_per_frame); 
    515557 
    516             rport->up_read_pos = (rport->up_read_pos + 1) % 
    517                                    rport->buf_cnt; 
    518         } 
    519558 
    520559 
    521560        has_frame = PJ_TRUE; 
     
    548587                                   const pjmedia_frame *frame) 
    549588{ 
    550589    struct reverse_port *rport = (struct reverse_port*) this_port; 
    551     unsigned count; 
    552590 
    553591    pj_assert(frame->size <= rport->base.info.bytes_per_frame); 
    554592 
    555     /* Check for overflows */ 
    556     if (rport->up_write_pos == rport->up_read_pos) { 
    557  
    558         /* Only report overflow if the frame is constantly read 
    559          * at the other end of the buffer (the multichannel side). 
    560          * It is possible that nobody reads the buffer, so causing 
    561          * overflow to happen rapidly, and writing log message this 
    562          * way does not seem to be wise. 
    563          */ 
    564         if (rport->up_read_pos != rport->up_overflow_pos) { 
    565             rport->up_overflow_pos = rport->up_read_pos; 
    566             LOG_UP_((THIS_FILE, "Overflow in upstream direction")); 
    567         } 
    568  
    569         /* Adjust the write position */ 
    570         rport->up_write_pos = (rport->up_read_pos + rport->buf_cnt/2) % 
    571                                rport->buf_cnt; 
    572     } 
    573  
    574593    /* Handle NULL frame */ 
    575594    if (frame->type != PJMEDIA_FRAME_TYPE_AUDIO) { 
    576595        TRACE_UP_((THIS_FILE, "Upstream write %d null samples at buf pos %d", 
    577596                   this_port->info.samples_per_frame, rport->up_write_pos)); 
    578         pjmedia_zero_samples(rport->upstream_buf[rport->up_write_pos], 
    579                              this_port->info.samples_per_frame); 
    580         rport->up_write_pos = (rport->up_write_pos+1) % rport->buf_cnt; 
     597        /* Should write zero frame to delaybuf? */ 
    581598        return PJ_SUCCESS; 
    582599    } 
    583600 
     
    585602    PJ_ASSERT_RETURN(frame->size == this_port->info.bytes_per_frame, 
    586603                     PJ_EINVAL); 
    587604 
    588     /* Copy normal frame to curcular buffer */ 
    589     count = frame->size * 8 / this_port->info.bits_per_sample; 
     605    /* Update rport state. */ 
     606    op_update(rport, DIR_UPSTREAM, OP_PUT); 
    590607 
    591     TRACE_UP_((THIS_FILE, "Upstream write %d samples at buf pos %d", 
    592                count, rport->up_write_pos)); 
     608    /* Discard frame if rport is paused on this direction */ 
     609    if (rport->buf[DIR_UPSTREAM].paused) 
     610        return PJ_SUCCESS; 
    593611 
     612    /* Unfortunately must copy to temporary buffer since delay buf 
     613     * modifies the frame content. 
     614     */ 
     615    pjmedia_copy_samples(rport->tmp_up_buf, (const pj_int16_t*)frame->buf, 
     616                         this_port->info.samples_per_frame); 
    594617 
    595     pjmedia_copy_samples(rport->upstream_buf[rport->up_write_pos], 
    596                          (const pj_int16_t*) frame->buf, count); 
    597     rport->up_write_pos = (rport->up_write_pos+1) % rport->buf_cnt; 
     618    PJ_LOG(5,(rport->base.info.name.ptr, "put_frame()")); 
    598619 
    599     return PJ_SUCCESS; 
     620    /* Put frame to delay buffer */ 
     621    return pjmedia_delay_buf_put(rport->buf[DIR_UPSTREAM].dbuf,  
     622                                 rport->tmp_up_buf); 
    600623} 
    601624 
    602625 
     
    607630                                   pjmedia_frame *frame) 
    608631{ 
    609632    struct reverse_port *rport = (struct reverse_port*) this_port; 
    610     unsigned count; 
    611633 
    612     count = rport->base.info.samples_per_frame; 
     634    /* Update state */ 
     635    op_update(rport, DIR_DOWNSTREAM, OP_GET); 
    613636 
     637    /* Return no frame if media flow on this direction is being 
     638     * paused. 
     639     */ 
     640    if (rport->buf[DIR_DOWNSTREAM].paused) { 
     641        frame->type = PJMEDIA_FRAME_TYPE_NONE; 
     642        return PJ_SUCCESS; 
     643    } 
     644 
     645    /* Get frame from delay buffer */ 
    614646    frame->size = this_port->info.bytes_per_frame; 
    615647    frame->type = PJMEDIA_FRAME_TYPE_AUDIO; 
     648    frame->timestamp.u64 = rport->buf[DIR_DOWNSTREAM].ts.u64; 
    616649 
    617     /* Check for underflows */ 
    618     if (rport->dn_read_pos == rport->dn_write_pos) { 
    619  
    620         /* Only report underflow if the buffer is constantly filled 
    621          * up at the other side. 
    622          * It is possible that nobody writes the buffer, so causing 
    623          * underflow to happen rapidly, and writing log message this 
    624          * way does not seem to be wise. 
    625          */ 
    626         if (rport->dn_write_pos != rport->dn_underflow_pos) { 
    627             rport->dn_underflow_pos = rport->dn_write_pos; 
    628             LOG_DN_((THIS_FILE, "Underflow in downstream direction")); 
    629         } 
    630  
    631         /* Adjust read position */ 
    632         rport->dn_read_pos =  
    633             (rport->dn_write_pos - rport->buf_cnt/2) % rport->buf_cnt; 
    634          
    635     } 
    636  
    637     /* Get the samples from the circular buffer */ 
    638     pjmedia_copy_samples((pj_int16_t*)frame->buf,  
    639                          rport->dnstream_buf[rport->dn_read_pos], 
    640                          count); 
    641     rport->dn_read_pos = (rport->dn_read_pos+1) % rport->buf_cnt; 
    642  
    643     return PJ_SUCCESS; 
     650    return pjmedia_delay_buf_get(rport->buf[DIR_DOWNSTREAM].dbuf,  
     651                                 (short*)frame->buf); 
    644652} 
    645653 
    646654 
    647655static pj_status_t rport_on_destroy(pjmedia_port *this_port) 
    648656{ 
    649     /* Nothing to do */ 
    650     PJ_UNUSED_ARG(this_port); 
     657    struct reverse_port *rport = (struct reverse_port*) this_port; 
    651658 
     659    pjmedia_delay_buf_destroy(rport->buf[DIR_DOWNSTREAM].dbuf); 
     660    pjmedia_delay_buf_destroy(rport->buf[DIR_UPSTREAM].dbuf); 
     661 
    652662    return PJ_SUCCESS; 
    653663} 
    654664 
  • pjmedia/src/pjmedia/delaybuf.c

     
    241241 
    242242        b->buf_cnt -= erase_cnt; 
    243243 
    244         PJ_LOG(5,(b->obj_name,"Successfully shrinking %d samples, " 
     244        PJ_LOG(5,(b->obj_name,"Overflow, %d samples reduced, " 
    245245                  "buf_cnt=%d", erase_cnt, b->buf_cnt)); 
    246246    } 
    247247 
     
    532532 
    533533    pj_lock_release(b->lock); 
    534534 
    535     PJ_LOG(5,(b->obj_name,"Delay buffer resetted")); 
     535    PJ_LOG(5,(b->obj_name,"Delay buffer is reset")); 
    536536 
    537537    return PJ_SUCCESS; 
    538538}