/* * simulate_lost() is called to simulate packet lost */ static pj_status_t transport_simulate_lost(pjmedia_transport *tp, pjmedia_dir dir, unsigned pct_lost) { struct tp_adapter *adapter = (struct tp_adapter*)tp; return pjmedia_transport_simulate_lost(adapter->slave_tp, dir, pct_lost); }
static pj_status_t transport_simulate_lost(pjmedia_transport *tp, pjmedia_dir dir, unsigned pct_lost) { transport_srtp *srtp = (transport_srtp *) tp; return pjmedia_transport_simulate_lost(srtp->member_tp, dir, pct_lost); }
static pj_status_t transport_simulate_lost(pjmedia_transport *tp, pjmedia_dir dir, unsigned pct_lost) { transport_srtp *srtp = (transport_srtp *) tp; PJ_ASSERT_RETURN(tp, PJ_EINVAL); return pjmedia_transport_simulate_lost(srtp->member_tp, dir, pct_lost); }
/* * simulate_lost() is called to simulate packet lost */ static pj_status_t transport_simulate_lost(pjmedia_transport *tp, pjmedia_dir dir, unsigned pct_lost) { struct tp_zrtp *zrtp = (struct tp_zrtp*)tp; PJ_ASSERT_RETURN(tp, PJ_EINVAL); return pjmedia_transport_simulate_lost(zrtp->slave_tp, dir, pct_lost); }
/* This is the transmission "tick". * This function is called periodically every "tick" milliseconds, and * it will determine whether to transmit packet(s) (or to drop it). */ static void tx_tick(const pj_time_val *t) { struct stream *strm = g_app.tx; static char log_msg[120]; pjmedia_port *port = g_app.tx->port; long pkt_interval; /* packet interval, without jitter */ pkt_interval = PJMEDIA_PIA_SPF(&port->info) * 1000 / PJMEDIA_PIA_SRATE(&port->info); while (PJ_TIME_VAL_GTE(*t, strm->state.tx.next_schedule)) { struct log_entry entry; pj_bool_t drop_this_pkt = PJ_FALSE; int jitter; /* Init log entry */ pj_bzero(&entry, sizeof(entry)); entry.wall_clock = *t; /* * Determine whether to drop this packet */ if (strm->state.tx.cur_lost_burst) { /* We are currently dropping packet */ /* Make it comply to minimum lost burst */ if (strm->state.tx.cur_lost_burst < g_app.cfg.tx_min_lost_burst) { drop_this_pkt = PJ_TRUE; } /* Correlate the next packet loss */ if (!drop_this_pkt && strm->state.tx.cur_lost_burst < g_app.cfg.tx_max_lost_burst && MAX(strm->state.tx.total_lost-LOSS_EXTRA,0) * 100 / MAX(strm->state.tx.total_tx,1) < g_app.cfg.tx_pct_avg_lost ) { strm->state.tx.drop_prob = ((g_app.cfg.tx_pct_loss_corr * strm->state.tx.drop_prob) + ((100-g_app.cfg.tx_pct_loss_corr) * (pj_rand()%100)) ) / 100; if (strm->state.tx.drop_prob >= 100) strm->state.tx.drop_prob = 99; if (strm->state.tx.drop_prob >= 100 - g_app.cfg.tx_pct_avg_lost) drop_this_pkt = PJ_TRUE; } } /* If we're not dropping packet then use randomly distributed loss */ if (!drop_this_pkt && MAX(strm->state.tx.total_lost-LOSS_EXTRA,0) * 100 / MAX(strm->state.tx.total_tx,1) < g_app.cfg.tx_pct_avg_lost) { strm->state.tx.drop_prob = pj_rand() % 100; if (strm->state.tx.drop_prob >= 100 - g_app.cfg.tx_pct_avg_lost) drop_this_pkt = PJ_TRUE; } if (drop_this_pkt) { /* Drop the frame */ pjmedia_transport_simulate_lost(g_app.loop, PJMEDIA_DIR_ENCODING, 100); run_one_frame(g_app.tx_wav, g_app.tx->port, NULL); pjmedia_transport_simulate_lost(g_app.loop, PJMEDIA_DIR_ENCODING, 0); entry.event = EVENT_TX_DROP; entry.log = "** This packet was lost **"; ++strm->state.tx.total_lost; ++strm->state.tx.cur_lost_burst; } else { pjmedia_rtcp_stat stat; pjmedia_jb_state jstate; unsigned last_discard; pjmedia_stream_get_stat_jbuf(g_app.rx->strm, &jstate); last_discard = jstate.discard; run_one_frame(g_app.tx_wav, g_app.tx->port, NULL); pjmedia_stream_get_stat(g_app.rx->strm, &stat); pjmedia_stream_get_stat_jbuf(g_app.rx->strm, &jstate); entry.event = EVENT_TX; entry.jb_state = &jstate; entry.stat = &stat; entry.log = log_msg; if (jstate.discard > last_discard) strcat(log_msg, "** Note: packet was discarded by jitter buffer **"); strm->state.tx.cur_lost_burst = 0; } write_log(&entry, PJ_TRUE); ++strm->state.tx.total_tx; /* Calculate next schedule */ strm->state.tx.next_schedule.sec = 0; strm->state.tx.next_schedule.msec = (strm->state.tx.total_tx + 1) * pkt_interval; /* Apply jitter */ if (g_app.cfg.tx_max_jitter || g_app.cfg.tx_min_jitter) { if (g_app.cfg.tx_max_jitter == g_app.cfg.tx_min_jitter) { /* Fixed jitter */ switch (pj_rand() % 3) { case 0: jitter = 0 - g_app.cfg.tx_min_jitter; break; case 2: jitter = g_app.cfg.tx_min_jitter; break; default: jitter = 0; break; } } else { int jitter_range; jitter_range = (g_app.cfg.tx_max_jitter-g_app.cfg.tx_min_jitter)*2; jitter = pj_rand() % jitter_range; if (jitter < jitter_range/2) { jitter = 0 - g_app.cfg.tx_min_jitter - (jitter/2); } else { jitter = g_app.cfg.tx_min_jitter + (jitter/2); } } } else { jitter = 0; } pj_time_val_normalize(&strm->state.tx.next_schedule); sprintf(log_msg, "** Packet #%u tick is at %d.%03d, %d ms jitter applied **", strm->state.tx.total_tx+1, (int)strm->state.tx.next_schedule.sec, (int)strm->state.tx.next_schedule.msec, jitter); strm->state.tx.next_schedule.msec += jitter; pj_time_val_normalize(&strm->state.tx.next_schedule); } /* while */ }