Example #1
0
/*
 * Generates a printable string from an abs_time_t structure, with
 * granularity of whole seconds.
 *
 * @param[in]   t        - time for which a string is to be generated
 * @param[out]  buff     - buffer into which string will be written
 * @param[in]   buff_len - length of caller-supplied buffer
 */
const char * abs_time_to_str_secs(abs_time_t t, char * buff, uint32_t buff_len)
{
    struct timeval tv = TIME_GET_A(timeval, t);
    struct tm bd_time;
    struct tm * bd_result;
    size_t strftime_result;

    if (! buff || buff_len == 0) {
        return NULL;
    }

    if (buff_len < DATE_AND_TIME_LENGTH_INCLUDING_NULL) {
        buff[0] = 0;
        return buff;
    }

    bd_result = localtime_r(&tv.tv_sec, &bd_time);
    ASSERT(bd_result == &bd_time, "Bad return from localtime_r\n");

    strftime_result = strftime(buff, DATE_AND_TIME_LENGTH_INCLUDING_NULL,
                               "%FT%T", &bd_time);
    ASSERT(strftime_result == DATE_AND_TIME_LENGTH_INCLUDING_NULL-1,
           "Bad result, %"PRIdPTR", from strftime\n", strftime_result);
    return buff;
}
Example #2
0
/* 
 * This callback is executed when the time to send the next
 * receiver report is reached.
 */
UT_STATIC
void rtcp_receive_report_timeout_cb (const vqec_event_t * const evptr,
                                     int32_t fd, int16_t event, void *arg)
{
    rtp_session_t *p_sess = NULL;
    rtp_member_t *local_member = NULL;
    uint64_t time;
    struct timeval tv;

    if (!arg) {
        rtcp_tmevt_log_err("%s %s", __FUNCTION__, s_arg_is_null);

        /* ignore return {nothing to be done if stop fails} */
        (void)vqec_event_stop(evptr);    
        return;
    }

    p_sess = (rtp_session_t *)arg;
    local_member = p_sess->rtp_local_source;
    if (!local_member) {
        rtcp_tmevt_log_err("%s %s", __FUNCTION__, s_src_is_null);

        /* ignore return nothing to be done if stop fails */
        (void)vqec_event_stop(evptr);    
        return;
    }

    /* Update the timestamps */
    time = TIME_GET_A(msec, get_sys_time());
    local_member->rtp_timestamp = (uint32_t)time;
    local_member->ntp_timestamp = abs_time_to_ntp(msec_to_abs_time(time));

    MCALL(p_sess, rtp_update_stats, TRUE);
    MCALL(p_sess, rtp_session_timeout_transmit_report);
    timerclear(&tv);
    rel_time_t diff = TIME_SUB_A_A(p_sess->next_send_ts, 
                                   msec_to_abs_time(time));
    /* If the rel-time delta is -ive [lagging behind] use 0. */
    if (TIME_CMP_R(lt, diff, REL_TIME_0)) {
        diff = REL_TIME_0;
    }
    tv = TIME_GET_R(timeval, diff);

    /* ignore return no recourse if start fails*/
    (void)vqec_event_start(evptr, &tv);
}
Example #3
0
const char * abs_time_to_str(abs_time_t t, char * buff, uint32_t buff_len)
{

    struct timeval tv = TIME_GET_A(timeval, t);
    struct tm bd_time;
    struct tm * bd_result;
    size_t strftime_result;
    size_t sprintf_result;


#define DATE_AND_TIME_LENGTH_INCLUDING_NULL 20
#define LONG_DATE_AND_TIME_LENGTH_INCLUDING_NULL 40
#define FRAC_SEC_LENGTH_INCLUDING_NULL 8
#define TOTAL_LENGTH_INCLUDING_NULL \
(DATE_AND_TIME_LENGTH_INCLUDING_NULL + FRAC_SEC_LENGTH_INCLUDING_NULL - 1)

    if (! buff || buff_len == 0)
        return NULL;

    if (buff_len < TOTAL_LENGTH_INCLUDING_NULL) {
        buff[0] = 0;
        return buff;
    }

    bd_result = localtime_r(&tv.tv_sec, &bd_time);
    ASSERT(bd_result == &bd_time, "Bad return from localtime_r\n");


    strftime_result = strftime(buff, DATE_AND_TIME_LENGTH_INCLUDING_NULL, "%F %T", &bd_time);
    ASSERT(strftime_result == DATE_AND_TIME_LENGTH_INCLUDING_NULL-1,
           "Bad result, %"PRIdPTR", from strftime\n", strftime_result);

    sprintf_result = snprintf(&buff[DATE_AND_TIME_LENGTH_INCLUDING_NULL-1],
                              buff_len - DATE_AND_TIME_LENGTH_INCLUDING_NULL + 1,
                              ".%06ld", tv.tv_usec);

    ASSERT(sprintf_result == FRAC_SEC_LENGTH_INCLUDING_NULL-1,
           "Bad result from sprintf\n");

    return buff;
}
/******************************************************************************
 * Adjust nll's state / predict a receive time for the given sample.
 ******************************************************************************/
void vqec_nll_adjust (vqec_nll_t * nll, 
                      abs_time_t actual_time,
                      uint32_t pcr32,
                      rel_time_t est_rtp_delta,
                      boolean * disc, 
                      abs_time_t * predicted_time)
{
    rel_time_t time_delta = REL_TIME_0, correction = REL_TIME_0, arrival_error;
    abs_time_t pred_arrival, prev_pred_base;
    boolean reset_base;

    if (!nll || !disc || !predicted_time) {        
        VQEC_DP_DEBUG(VQEC_DP_DEBUG_NLL, "adjust() invalid inputs %p/%p/%p\n",
                   nll, disc, predicted_time);
        if (predicted_time) {
            *predicted_time = get_sys_time();
        }
        return;
    }

    prev_pred_base = nll->pred_base;
    if (*disc) {
        nll->num_exp_disc++;
        VQEC_DP_DEBUG(VQEC_DP_DEBUG_NLL, 
                   "nll(%p) explicit discontinuity signaled: "
                   "actual_time %llu, pcr32 %u, est_rtp %llu\n", nll,
                   TIME_GET_A(msec, actual_time), pcr32, 
                   TIME_GET_R(usec, est_rtp_delta));
    }
    
    /* 
     * The NLL operates in two modes: non-tracking and tracking. In 
     * non-tracking mode, the original receive times of the samples are 
     * unknown, and therefore, it just uses the sender RTP timestamps to
     *  determine the send time for packets. In tracking mode, the receive
     *  times of samples are known, except, e.g., when  packets are received
     *  of order.  
     */
    switch (nll->mode) {

    case VQEC_NLL_MODE_NONTRACKING:     /* non-tracking mode */

        if (!nll->got_first) {
            /*
             * if this is the 1st sample, set pred_base = actual_time if 
             * actual_time is non-0, otherwise set it to current time.
             */
            nll->got_first = TRUE;
            *disc = TRUE;
            if (!IS_ABS_TIME_ZERO(actual_time)) {
                nll->pred_base = actual_time;
            } else {
                nll->pred_base = get_sys_time();
            }

        } else {                        /* else-of !nll->got_first */

            /*
             * the code below considers the condition delta(rtp) >100 msecs
             * an "Implicit discontinuity". Whenever there the code detects 
             * that the timing may be discontinuous, or the caller sets the 
             * discontinuity flag, "est_rtp_delta" is added to the previous 
             * value of pred_base. No limits are imposed on "est_rtp_delta".
             */

            if (!*disc) {
                time_delta = vqec_nll_rtp_delta(nll->pcr32_base, pcr32); 
            }

            if (*disc || 
                TIME_CMP_R(gt, time_delta, 
                           MAX_NLL_DISCONTINUITY_THRESHOLD) ||
                TIME_CMP_R(gt, TIME_NEG_R(time_delta), 
                           MAX_NLL_DISCONTINUITY_THRESHOLD)) {
            
                nll->pred_base = 
                    TIME_ADD_A_R(nll->pred_base, est_rtp_delta);
                if (!*disc) {
                    nll->num_imp_disc++;  /* implicitly discontinuous oper */
                    *disc = TRUE;

                    VQEC_DP_DEBUG(VQEC_DP_DEBUG_NLL, "Implicit discontinuity : "
                               "nll(%p) pcr %u old_pcr %u pred_base %llu\n", 
                               nll, pcr32, nll->pcr32_base, 
                               TIME_GET_A(msec, nll->pred_base));
                }
            
            } else {                    /* else-of (*disc || TIME_CMP_R(... */

                nll->pred_base = TIME_ADD_A_R(nll->pred_base, time_delta);
            }
        }                               /* !nll->got_first */


        /* 
         * Protect backward fold in predicted time; the previous predicted
         * time is selected if this condition is true.
         */
        if (TIME_CMP_A(lt, nll->pred_base, prev_pred_base)) {
            VQEC_DP_DEBUG(VQEC_DP_DEBUG_NLL, "Prediction in past: nll(%p)"
                       "new_base = %llu  old_base = %llu pcr32 %u\n", nll,
                       TIME_GET_A(msec, nll->pred_base), 
                       TIME_GET_A(msec, prev_pred_base), 
                       nll->pcr32_base);

            nll->pred_base = prev_pred_base;
            nll->predict_in_past++;
        }

        nll->pcr32_base = pcr32;
        *predicted_time = nll->pred_base;
           
        if (nll->switch_to_tracking) {  /* switch to tracking mode */
            /* 
             * The rcc-repair burst and primary streams are received
             * somewhat "asynchronously" of each other, i.e., the primary
             * packets may be received at any epoch within the join latency
             * window. Because of the bandwidth / join skew, in tracking
             * mode the arrival error should be computed from the first
             * primary packet's actual receive time.  However, the time
             * skew between the predicted receive time of the 1st primary, 
             * and it's actual receive time must be preserved, and added to
             * all subsequent predictions in tracking mode. This value is
             * termed "primary_offset". To switch to tracking mode it is
             * *necessary* that a packet with non-0 actual time is provided. 
             * The error case is explicitly handled by using current predicted
             * base as actual time causing primary_offset to be 0. This concept
             * of time-shift is now an integral part of the nll, as is 
             * computed / cached implicitly when switching to tracking mode.
             */ 
            abs_time_t act;
            if (IS_ABS_TIME_ZERO(actual_time)) {
                act = nll->pred_base;
            } else {
                act = actual_time;
            }

            nll->primary_offset = 
                TIME_SUB_A_A(nll->pred_base, act);
            nll->pred_base = 
                nll->last_actual_time = act;

            nll->switch_to_tracking = FALSE;
            nll->mode = VQEC_NLL_MODE_TRACKING;

            VQEC_DP_DEBUG(VQEC_DP_DEBUG_NLL, 
                       "Switch to tracking : nll(%p) pred_base %llu "
                       "primary offset %lld actual time %llu \n", nll, 
                       TIME_GET_A(msec, nll->pred_base),
                       TIME_GET_R(usec, nll->primary_offset),
                       TIME_GET_A(msec, actual_time));
        }
        break;                          /* done with non-tracking processing */



    case VQEC_NLL_MODE_TRACKING:        /* tracking mode */

        if (!nll->got_first) {
            /*
             * if this is the 1st sample, set pred_base to actual_time + 
             * non-tracking-offset if actual time is non-0; otherwise, set it to 
             * current time + non-tracking-offset.
             */
            nll->got_first = TRUE;
            *disc = TRUE;
            if (!IS_ABS_TIME_ZERO(actual_time)) {
                nll->pred_base = actual_time;
            } else {
                nll->pred_base = get_sys_time();
            }

        } else {                        /* else-of !nll->got_first */

            reset_base = FALSE;

            if (!*disc) {
                time_delta = vqec_nll_rtp_delta(nll->pcr32_base, pcr32);

                /*
                 * We leave open the possibility of bounding this delta
                 * prior  computing arrival error, and instead using the 
                 * receive timestamp delta estimate (as for explicit disc)
                 * in an attempt to better predict implicit discontinuities.
                 * (this can also be done if the arrival error exceeded 
                 * threshold, but there was no explicit discontinuity).
                 */

            } else {

                /* 
                 * In case of explicit discontinuity, approximate rtp delta from 
                 * the receive timestamps of the current & previous sample, if
                 * they are both non-0. This approx is used only If the delta is
                 * < DISCONTINUITY_THRESHOLD msecs. Otherwise we'll reset
                 *  the nll's pred_base / error average.
                 */
                if (!IS_ABS_TIME_ZERO(actual_time) &&
                    (!IS_ABS_TIME_ZERO(nll->last_actual_time))) {
                    time_delta = TIME_SUB_A_A(actual_time, nll->last_actual_time); 
                    if (TIME_CMP_R(gt, time_delta, 
                                   MAX_NLL_DISCONTINUITY_THRESHOLD) ||
                        TIME_CMP_R(gt, TIME_NEG_R(time_delta), 
                                   MAX_NLL_DISCONTINUITY_THRESHOLD)) {
                        reset_base = TRUE;
                    }                       /* (TIME_CMP_R(gt...) */
                }                           /* (!IS_ABS_TIME_ZERO()... */
            }                               /* (!*disc) */
            
            
            /* 
             * If we have a bounded estimate of the sender's time delta,
             * compute arrival error, and ensure that it is bounded by
             * +/- MAX_ARRIVAL_ERROR msecs. If not, we'll reset the nll's
             *  pred_base / error average. If no actual time is provided,
             * we use, actual_time = pred_base + rtp_delta = pred_arrival.
             */
            if (!reset_base) {
                abs_time_t act;
                if (IS_ABS_TIME_ZERO(actual_time)) {
                    act =  TIME_ADD_A_R(nll->pred_base, time_delta);
                } else {
                    act = actual_time;
                }

                pred_arrival  = TIME_ADD_A_R(nll->pred_base, time_delta);
                arrival_error = TIME_SUB_A_A(pred_arrival, act);
                
                if (TIME_CMP_R(lt, arrival_error, MAX_ARRIVAL_ERROR) 
                    && TIME_CMP_R(lt, TIME_NEG_R(arrival_error), 
                                  MAX_ARRIVAL_ERROR)) {
                    correction = vqec_nll_update_error(nll, arrival_error);
                    nll->pred_base = TIME_ADD_A_R(pred_arrival, correction);
                } else {

                    reset_base = TRUE;  /* arrival error is out-of-bound */
                } 
            }
        
            if (reset_base) {           /* explicit disc or excessive error  */
                if (!*disc) {  
                    nll->num_imp_disc++;  /* implicit discontinuous oper */
                    *disc = TRUE;
                }

                /* 
                 * We must have an actual time provided for this particular
                 * case. If one is not provided, the last pred_base is used; 
                 * a counter is incremented to keep track of such rather 
                 * unlikely incidents.
                 */
                if (!IS_ABS_TIME_ZERO(actual_time)) {
                    nll->pred_base = actual_time;
                } else {
                    nll->reset_base_no_act_time++;
                    VQEC_DP_DEBUG(VQEC_DP_DEBUG_NLL, "Reset base w/o act time "
                               "nll(%p)", nll);
                }
                nll->error_avg = REL_TIME_0; /* reset error average */

                VQEC_DP_DEBUG(VQEC_DP_DEBUG_NLL, "Tracking mode discontinuity : "
                           "nll(%p) imp_disc %u exp_disc %u pcr %u old_pcr %u "
                           "pred_base %llu\n",
                           nll, nll->num_imp_disc, nll->num_exp_disc, pcr32, 
                           nll->pcr32_base, TIME_GET_A(msec, nll->pred_base));
            }                           /* end-of (reset_base) */
        }                               /* end-of (!nll->got_first) */

        /* 
         * Protect backward fold in predicted time; the previous predicted
         * time is selected if this condition is true.
         */
        if (TIME_CMP_A(lt, nll->pred_base, prev_pred_base)) {
            VQEC_DP_DEBUG(VQEC_DP_DEBUG_NLL, "Prediction in past: nll(%p)"
                       "new_base = %llu  old_base = %llu pcr32 %u\n", nll,
                       TIME_GET_A(msec, nll->pred_base), 
                       TIME_GET_A(msec, prev_pred_base), 
                       nll->pcr32_base);
            
            nll->pred_base = prev_pred_base;
            nll->predict_in_past++;
        }

        nll->pcr32_base = pcr32;
        nll->last_actual_time = actual_time;
        *predicted_time = TIME_ADD_A_R(nll->pred_base, nll->primary_offset);
        break;                          /* done with tracking processing */


    default:                            /* not reached */
        VQEC_DP_ASSERT_FATAL(0, "nll fatal error");
        break;
    }                                   /* end switch(mode) */

    if (VQEC_DP_GET_DEBUG_FLAG(VQEC_DP_DEBUG_COLLECT_STATS)) {
        nll->num_obs++;
    }

    VQEC_DP_DEBUG(VQEC_DP_DEBUG_NLL_ADJUST, 
               "Adjustment: nll(%p), base %llu, "
               "pcr32 %u, correction %lld, cum err %lld\n", nll, 
               TIME_GET_A(msec, nll->pred_base), nll->pcr32_base, 
               TIME_GET_R(usec, correction), TIME_GET_R(usec, nll->error_avg));
}