/* This is the RX "tick". * This function is called periodically every "tick" milliseconds, and * it will determine whether to call get_frame() from the RX stream. */ static void rx_tick(const pj_time_val *t) { struct stream *strm = g_app.rx; pjmedia_port *port = g_app.rx->port; long pkt_interval; pkt_interval = PJMEDIA_PIA_SPF(&port->info) * 1000 / PJMEDIA_PIA_SRATE(&port->info) * g_app.cfg.rx_snd_burst; if (PJ_TIME_VAL_GTE(*t, strm->state.rx.next_schedule)) { unsigned i; for (i=0; i<g_app.cfg.rx_snd_burst; ++i) { struct log_entry entry; pjmedia_rtcp_stat stat; pjmedia_jb_state jstate; pj_bool_t has_frame; char msg[120]; unsigned last_empty; pjmedia_stream_get_stat(g_app.rx->strm, &stat); pjmedia_stream_get_stat_jbuf(g_app.rx->strm, &jstate); last_empty = jstate.empty; /* Pre GET event */ pj_bzero(&entry, sizeof(entry)); entry.event = EVENT_GET_PRE; entry.wall_clock = *t; entry.stat = &stat; entry.jb_state = &jstate; write_log(&entry, PJ_TRUE); /* GET */ run_one_frame(g_app.rx->port, g_app.rx_wav, &has_frame); /* Post GET event */ pjmedia_stream_get_stat(g_app.rx->strm, &stat); pjmedia_stream_get_stat_jbuf(g_app.rx->strm, &jstate); pj_bzero(&entry, sizeof(entry)); entry.event = EVENT_GET_POST; entry.wall_clock = *t; entry.stat = &stat; entry.jb_state = &jstate; msg[0] = '\0'; entry.log = msg; if (jstate.empty > last_empty) strcat(msg, "** JBUF was empty **"); if (!has_frame) strcat(msg, "** NULL frame was returned **"); write_log(&entry, PJ_TRUE); } strm->state.rx.next_schedule.msec += pkt_interval; pj_time_val_normalize(&strm->state.rx.next_schedule); } }
// Draws one frame then returns void run_one_frame() { frame_drawn = 0; while (!frame_drawn) { if (halted || stopped) { long current_cycles = cgb_speed ? 2 : 4; update_timers(current_cycles); sound_add_cycles(current_cycles); inc_serial_cycles(current_cycles); // If Key pressed in "stop" mode, then gameboy is "unstopped" if (stopped) { if(key_pressed()) { stopped = 0; } } if (halted) { update_graphics(current_cycles); } } else if (!(halted || stopped)) { current_cycles = 0; current_cycles += exec_opcode(skip_bug); } cycles += current_cycles; #ifdef EFIAPI if (cycles > 3000) { #else if (cycles > 15000) { #endif quit |= update_keys(); cycles = 0; } skip_bug = handle_interrupts(); if (debug && step_count > 0 && --step_count == 0) { int flags = get_command(); step_count = (flags & STEPS_SET) ? get_steps() : STEPS_OFF; } } } void setup_debug() { if (debug) { int flags = get_command(); step_count = (flags & STEPS_SET) ? get_steps() : STEPS_OFF; breakpoint = (flags & BREAKPOINT_SET) ? get_breakpoint() : BREAKPOINT_OFF; } } void run() { log_message(LOG_INFO, "About to setup debug\n"); setup_debug(); log_message(LOG_INFO, "About to run\n"); while(!quit) { run_one_frame(); } }
/* 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 */ }