Ticket #438: splitcomb2.patch
File splitcomb2.patch, 17.4 KB (added by bennylp, 17 years ago) |
---|
-
pjmedia/src/pjmedia/splitcomb.c
17 17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 18 */ 19 19 #include <pjmedia/splitcomb.h> 20 #include <pjmedia/delaybuf.h> 20 21 #include <pjmedia/errno.h> 21 22 #include <pj/assert.h> 22 23 #include <pj/log.h> … … 28 29 #define THIS_FILE "splitcomb.c" 29 30 #define TMP_SAMP_TYPE pj_int16_t 30 31 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 37 34 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 */ 42 enum 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 38 57 #if 0 39 58 # define TRACE_UP_(x) PJ_LOG(5,x) 40 59 # define TRACE_DN_(x) PJ_LOG(5,x) … … 64 83 struct { 65 84 pjmedia_port *port; 66 85 pj_bool_t reversed; 67 } port_desc[ 64];86 } port_desc[MAX_CHANNELS]; 68 87 69 88 /* Temporary buffers needed to extract mono frame from 70 89 * multichannel frame. We could use stack for this, but this … … 84 103 struct splitcomb*parent; 85 104 unsigned ch_num; 86 105 106 /* Maximum burst before media flow is suspended */ 107 int max_burst; 108 87 109 /* A reverse port need a temporary buffer to store frame 88 110 * (because of the different phase, see splitcomb.h for details). 89 111 * Since we can not expect get_frame() and put_frame() to be 90 * called evenly one after another, we use circularbuffers to91 * accomodate the "jitter".112 * called evenly one after another, we use delay buffers to 113 * accomodate the burst. 92 114 */ 93 unsigned buf_cnt;115 struct { 94 116 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; 101 119 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. 104 138 */ 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; 108 140 }; 109 141 110 142 … … 228 260 unsigned options, 229 261 pjmedia_port **p_chport) 230 262 { 231 const pj_str_t name = pj_str("s plitcomb-ch");263 const pj_str_t name = pj_str("scomb-rev"); 232 264 struct splitcomb *sc = (struct splitcomb*) splitcomb; 233 265 struct reverse_port *rport; 234 unsigned i;266 unsigned buf_cnt; 235 267 pjmedia_port *port; 268 pj_status_t status; 236 269 237 270 /* Sanity check */ 238 271 PJ_ASSERT_RETURN(pool && splitcomb, PJ_EINVAL); … … 265 298 port->on_destroy = &rport_on_destroy; 266 299 267 300 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; 271 304 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; 277 315 } 278 rport->dn_write_pos = rport->buf_cnt/2;279 316 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; 286 326 } 287 rport->up_write_pos = rport->buf_cnt/2;288 327 328 /* And temporary upstream/get buffer */ 329 rport->tmp_up_buf = (pj_int16_t*) 330 pj_pool_alloc(pool, port->info.bytes_per_frame); 289 331 290 332 /* Save port in the splitcomb */ 291 333 sc->port_desc[ch_num].port = &rport->base; … … 294 336 295 337 /* Done */ 296 338 *p_chport = port; 297 return PJ_SUCCESS;339 return status; 298 340 } 299 341 300 342 … … 335 377 } 336 378 } 337 379 380 /* Update operation on the specified direction */ 381 static void op_update(struct reverse_port *rport, int dir, int op) 382 { 383 char *dir_name[2] = {"downstream", "upstream"}; 338 384 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 339 419 /* 340 420 * "Write" a multichannel frame. This would split the multichannel frame 341 421 * into individual mono channel, and write it to the appropriate port. … … 373 453 if (!port) 374 454 continue; 375 455 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 376 463 if (!sc->port_desc[ch].reversed) { 377 464 /* Write to normal port */ 378 465 pjmedia_frame mono_frame; 379 466 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 387 467 mono_frame.buf = sc->put_buf; 388 468 mono_frame.size = frame->size / this_port->info.channel_count; 389 469 mono_frame.type = frame->type; … … 395 475 } else { 396 476 /* Write to reversed phase port */ 397 477 struct reverse_port *rport = (struct reverse_port*)port; 398 399 if (rport->dn_write_pos == rport->dn_read_pos) {400 478 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); 411 481 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); 416 485 } 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;428 486 } 429 487 } 430 488 … … 444 502 unsigned ch; 445 503 pj_bool_t has_frame = PJ_FALSE; 446 504 447 /* Clear output frame */448 pjmedia_zero_samples((pj_int16_t*)frame->buf,449 this_port->info.samples_per_frame);450 451 505 /* Read frame from each port */ 452 506 for (ch=0; ch < this_port->info.channel_count; ++ch) { 453 507 pjmedia_port *port = sc->port_desc[ch].port; 454 508 pjmedia_frame mono_frame; 455 509 pj_status_t status; 456 510 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); 459 515 460 /* Read from the port */ 461 if (sc->port_desc[ch].reversed == PJ_FALSE) { 516 } else if (sc->port_desc[ch].reversed == PJ_FALSE) { 462 517 /* Read from normal port */ 463 518 mono_frame.buf = sc->get_buf; 464 519 mono_frame.size = port->info.bytes_per_frame; … … 468 523 if (status != PJ_SUCCESS || 469 524 mono_frame.type != PJMEDIA_FRAME_TYPE_AUDIO) 470 525 { 471 continue; 526 pjmedia_zero_samples(sc->get_buf, 527 port->info.samples_per_frame); 472 528 } 473 529 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 481 530 frame->timestamp.u64 = mono_frame.timestamp.u64; 482 531 483 532 } else { 484 533 /* Read from temporary buffer for reverse port */ 485 534 struct reverse_port *rport = (struct reverse_port*)port; 486 535 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); 489 538 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_UPSTREAM].paused) { 540 pjmedia_delay_buf_get(rport->buf[DIR_UPSTREAM].dbuf, 541 sc->get_buf); 500 542 501 /* Adjust read position */ 502 rport->up_read_pos = 503 (rport->up_write_pos - rport->buf_cnt/2) % 504 rport->buf_cnt; 543 } else { 544 pjmedia_zero_samples(sc->get_buf, 545 rport->base.info.samples_per_frame); 505 546 } 506 547 507 TRACE_UP_((THIS_FILE, "Upstream read at buffer pos %d",508 rport->up_read_pos));548 frame->timestamp.u64 = rport->buf[DIR_UPSTREAM].ts.u64; 549 } 509 550 510 511 store_mono_frame((const pj_int16_t*)rport->upstream_buf[rport->up_read_pos],512 513 514 551 /* Combine the mono frame into multichannel frame */ 552 store_mono_frame(sc->get_buf, 553 (pj_int16_t*)frame->buf, ch, 554 this_port->info.channel_count, 555 this_port->info.samples_per_frame); 515 556 516 rport->up_read_pos = (rport->up_read_pos + 1) %517 rport->buf_cnt;518 }519 557 520 558 521 559 has_frame = PJ_TRUE; … … 548 586 const pjmedia_frame *frame) 549 587 { 550 588 struct reverse_port *rport = (struct reverse_port*) this_port; 551 unsigned count;552 589 553 590 pj_assert(frame->size <= rport->base.info.bytes_per_frame); 554 591 555 /* Check for overflows */556 if (rport->up_write_pos == rport->up_read_pos) {557 558 /* Only report overflow if the frame is constantly read559 * at the other end of the buffer (the multichannel side).560 * It is possible that nobody reads the buffer, so causing561 * overflow to happen rapidly, and writing log message this562 * 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 574 592 /* Handle NULL frame */ 575 593 if (frame->type != PJMEDIA_FRAME_TYPE_AUDIO) { 576 594 TRACE_UP_((THIS_FILE, "Upstream write %d null samples at buf pos %d", 577 595 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; 596 /* Should write zero frame to delaybuf? */ 581 597 return PJ_SUCCESS; 582 598 } 583 599 … … 585 601 PJ_ASSERT_RETURN(frame->size == this_port->info.bytes_per_frame, 586 602 PJ_EINVAL); 587 603 588 /* Copy normal frame to curcular buffer*/589 count = frame->size * 8 / this_port->info.bits_per_sample;604 /* Update rport state. */ 605 op_update(rport, DIR_UPSTREAM, OP_PUT); 590 606 591 TRACE_UP_((THIS_FILE, "Upstream write %d samples at buf pos %d", 592 count, rport->up_write_pos)); 607 /* Discard frame if rport is paused on this direction */ 608 if (rport->buf[DIR_UPSTREAM].paused) 609 return PJ_SUCCESS; 593 610 611 /* Unfortunately must copy to temporary buffer since delay buf 612 * modifies the frame content. 613 */ 614 pjmedia_copy_samples(rport->tmp_up_buf, (const pj_int16_t*)frame->buf, 615 this_port->info.samples_per_frame); 594 616 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; 598 599 return PJ_SUCCESS; 617 /* Put frame to delay buffer */ 618 return pjmedia_delay_buf_put(rport->buf[DIR_UPSTREAM].dbuf, 619 rport->tmp_up_buf); 600 620 } 601 621 602 622 … … 607 627 pjmedia_frame *frame) 608 628 { 609 629 struct reverse_port *rport = (struct reverse_port*) this_port; 610 unsigned count;611 630 612 count = rport->base.info.samples_per_frame; 631 /* Update state */ 632 op_update(rport, DIR_DOWNSTREAM, OP_GET); 613 633 634 /* Return no frame if media flow on this direction is being 635 * paused. 636 */ 637 if (rport->buf[DIR_DOWNSTREAM].paused) { 638 frame->type = PJMEDIA_FRAME_TYPE_NONE; 639 return PJ_SUCCESS; 640 } 641 642 /* Get frame from delay buffer */ 614 643 frame->size = this_port->info.bytes_per_frame; 615 644 frame->type = PJMEDIA_FRAME_TYPE_AUDIO; 645 frame->timestamp.u64 = rport->buf[DIR_DOWNSTREAM].ts.u64; 616 646 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; 647 return pjmedia_delay_buf_get(rport->buf[DIR_DOWNSTREAM].dbuf, 648 (short*)frame->buf); 644 649 } 645 650 646 651 647 652 static pj_status_t rport_on_destroy(pjmedia_port *this_port) 648 653 { 649 /* Nothing to do */ 650 PJ_UNUSED_ARG(this_port); 654 struct reverse_port *rport = (struct reverse_port*) this_port; 651 655 656 pjmedia_delay_buf_destroy(rport->buf[DIR_DOWNSTREAM].dbuf); 657 pjmedia_delay_buf_destroy(rport->buf[DIR_UPSTREAM].dbuf); 658 652 659 return PJ_SUCCESS; 653 660 } 654 661 -
pjmedia/src/pjmedia/delaybuf.c
241 241 242 242 b->buf_cnt -= erase_cnt; 243 243 244 PJ_LOG(5,(b->obj_name," Successfully shrinking %d samples, "244 PJ_LOG(5,(b->obj_name,"Overflow, %d samples reduced, " 245 245 "buf_cnt=%d", erase_cnt, b->buf_cnt)); 246 246 } 247 247 … … 532 532 533 533 pj_lock_release(b->lock); 534 534 535 PJ_LOG(5,(b->obj_name,"Delay buffer resetted"));535 PJ_LOG(5,(b->obj_name,"Delay buffer is reset")); 536 536 537 537 return PJ_SUCCESS; 538 538 }