/** * @brief Store the new TS, calculate new values and update the state * * @param ts_sc The ts_sc_comp object * @param ts The timestamp to add * @param sn The sequence number of the RTP packet */ void c_add_ts(struct ts_sc_comp *const ts_sc, const uint32_t ts, const uint16_t sn) { uint16_t sn_delta; assert(ts_sc != NULL); ts_debug(ts_sc, "Timestamp = %u", ts); /* consider that TS bits are not deducible by default */ ts_sc->is_deducible = false; /* we save the old value */ ts_sc->old_ts = ts_sc->ts; ts_sc->old_sn = ts_sc->sn; /* we store the new value */ ts_sc->ts = ts; ts_sc->sn = sn; /* if we had no old values, TS_STRIDE cannot be computed yet */ if(!ts_sc->are_old_val_init) { assert(ts_sc->state == INIT_TS); ts_debug(ts_sc, "TS_STRIDE cannot be computed, stay in INIT_TS state"); ts_sc->are_old_val_init = true; return; } /* compute the absolute delta between new and old SN */ /* abs() on unsigned 16-bit values seems to be a problem sometimes */ if(ts_sc->sn >= ts_sc->old_sn) { sn_delta = ts_sc->sn - ts_sc->old_sn; } else { sn_delta = ts_sc->old_sn - ts_sc->sn; } ts_debug(ts_sc, "SN delta = %u", sn_delta); /* compute the absolute delta between new and old TS */ /* abs() on unsigned 32-bit values seems to be a problem sometimes */ if(ts_sc->ts >= ts_sc->old_ts) { ts_sc->ts_delta = ts_sc->ts - ts_sc->old_ts; } else { ts_sc->ts_delta = ts_sc->old_ts - ts_sc->ts; } ts_debug(ts_sc, "TS delta = %u", ts_sc->ts_delta); /* go back to INIT_TS state if TS is constant */ if(ts_sc->ts_delta == 0) { ts_debug(ts_sc, "TS is constant, go in INIT_TS state"); ts_sc->state = INIT_TS; return; } /* go back to INIT_TS state if TS_STRIDE cannot be SDVL-encoded */ if(!sdvl_can_value_be_encoded(ts_sc->ts_delta)) { /* TS_STRIDE is too large for SDVL encoding */ ts_debug(ts_sc, "TS_STRIDE is too large for SDVL encoding, " "go in INIT_TS state"); ts_sc->state = INIT_TS; return; } /* TS_STRIDE can be computed, so leave INIT_TS state */ if(ts_sc->state == INIT_TS) { ts_debug(ts_sc, "TS_STRIDE can be computed, go to INIT_STRIDE state"); ts_sc->state = INIT_STRIDE; ts_sc->nr_init_stride_packets = 0; } if(ts_sc->state == INIT_STRIDE) { /* TS is changing and TS_STRIDE can be computed but TS_STRIDE was * not transmitted enough times to the decompressor to be used */ ts_debug(ts_sc, "state INIT_STRIDE"); /* reset INIT_STRIDE counter if TS_STRIDE/TS_OFFSET changed */ if(ts_sc->ts_delta != ts_sc->ts_stride || (ts_sc->ts % ts_sc->ts_delta) != ts_sc->ts_offset) { ts_debug(ts_sc, "TS_STRIDE and/or TS_OFFSET changed"); ts_sc->nr_init_stride_packets = 0; } /* compute TS_STRIDE, TS_OFFSET and TS_SCALED */ ts_sc->ts_stride = ts_sc->ts_delta; ts_debug(ts_sc, "TS_STRIDE = %u", ts_sc->ts_stride); assert(ts_sc->ts_stride != 0); ts_sc->ts_offset = ts_sc->ts % ts_sc->ts_stride; ts_debug(ts_sc, "TS_OFFSET = %u modulo %u = %u", ts_sc->ts, ts_sc->ts_stride, ts_sc->ts_offset); assert(ts_sc->ts_stride != 0); ts_sc->ts_scaled = (ts_sc->ts - ts_sc->ts_offset) / ts_sc->ts_stride; ts_debug(ts_sc, "TS_SCALED = (%u - %u) / %u = %u", ts_sc->ts, ts_sc->ts_offset, ts_sc->ts_stride, ts_sc->ts_scaled); } else if(ts_sc->state == SEND_SCALED) { const uint32_t old_scaled = ts_sc->ts_scaled; const uint32_t old_offset = ts_sc->ts_offset; /* TS is changing, TS_STRIDE can be computed, and TS_STRIDE was * transmitted enough times to the decompressor to be used */ ts_debug(ts_sc, "state SEND_SCALED"); /* does TS_STRIDE changed? */ ts_debug(ts_sc, "TS_STRIDE calculated = %u", ts_sc->ts_delta); ts_debug(ts_sc, "previous TS_STRIDE = %u", ts_sc->ts_stride); if(ts_sc->ts_delta != ts_sc->ts_stride) { assert(ts_sc->ts_stride != 0); if((ts_sc->ts_delta % ts_sc->ts_stride) != 0) { /* TS delta changed and is not a multiple of previous TS_STRIDE: * record the new value as TS_STRIDE and transmit it several * times for robustness purposes */ ts_debug(ts_sc, "/!\\ TS_STRIDE changed and is not a multiple " "of previous TS_STRIDE, so change TS_STRIDE and " "transmit it several times along all TS bits " "(probably a clock resync at source)"); ts_sc->state = INIT_STRIDE; ts_sc->nr_init_stride_packets = 0; ts_debug(ts_sc, "state -> INIT_STRIDE"); ts_sc->ts_stride = ts_sc->ts_delta; } else if((ts_sc->ts_delta / ts_sc->ts_stride) != sn_delta) { /* TS delta changed but is a multiple of previous TS_STRIDE: * do not change TS_STRIDE, but transmit all TS bits several * times for robustness purposes */ ts_debug(ts_sc, "/!\\ TS delta changed but is a multiple of " "previous TS_STRIDE, so do not change TS_STRIDE, but " "retransmit it several times along all TS bits " "(probably a RTP TS jump at source)"); ts_sc->state = INIT_STRIDE; ts_sc->nr_init_stride_packets = 0; ts_debug(ts_sc, "state -> INIT_STRIDE"); } else { /* do not change TS_STRIDE, probably a packet loss */ ts_debug(ts_sc, "/!\\ TS delta changed, is a multiple of " "previous TS_STRIDE and follows SN changes, so do " "not change TS_STRIDE (probably a packet loss)"); } } ts_debug(ts_sc, "TS_STRIDE = %u", ts_sc->ts_stride); /* update TS_OFFSET is needed */ assert(ts_sc->ts_stride != 0); ts_sc->ts_offset = ts_sc->ts % ts_sc->ts_stride; ts_debug(ts_sc, "TS_OFFSET = %u modulo %u = %u", ts_sc->ts, ts_sc->ts_stride, ts_sc->ts_offset); /* compute TS_SCALED */ assert(ts_sc->ts_stride != 0); ts_sc->ts_scaled = (ts_sc->ts - ts_sc->ts_offset) / ts_sc->ts_stride; ts_debug(ts_sc, "TS_SCALED = (%u - %u) / %u = %u", ts_sc->ts, ts_sc->ts_offset, ts_sc->ts_stride, ts_sc->ts_scaled); /* could TS_SCALED be deduced from SN? */ if(ts_sc->state != SEND_SCALED) { ts_sc->is_deducible = false; } else { uint32_t ts_scaled_delta; /* be cautious with positive and negative deltas */ if(ts_sc->ts_scaled >= old_scaled) { ts_scaled_delta = ts_sc->ts_scaled - old_scaled; if(ts_sc->sn >= ts_sc->old_sn) { ts_sc->is_deducible = (ts_scaled_delta == sn_delta); } else { ts_sc->is_deducible = false; } } else { ts_scaled_delta = old_scaled - ts_sc->ts_scaled; if(ts_sc->old_sn >= ts_sc->sn) { ts_sc->is_deducible = (ts_scaled_delta == sn_delta); } else { ts_sc->is_deducible = false; } } } if(ts_sc->is_deducible) { ts_debug(ts_sc, "TS can be deducted from SN (old TS_SCALED = %u, " "new TS_SCALED = %u, old SN = %u, new SN = %u)", old_scaled, ts_sc->ts_scaled, ts_sc->old_sn, ts_sc->sn); } else { ts_debug(ts_sc, "TS can not be deducted from SN (old TS_SCALED = %u, " "new TS_SCALED = %u, old SN = %u, new SN = %u)", old_scaled, ts_sc->ts_scaled, ts_sc->old_sn, ts_sc->sn); } /* Wraparound (See RFC 4815 Section 4.4.3) */ if(ts_sc->ts < ts_sc->old_ts) { ts_debug(ts_sc, "TS wraparound detected"); if(old_offset != ts_sc->ts_offset) { ts_debug(ts_sc, "TS_OFFSET changed, re-initialize TS_STRIDE"); ts_sc->state = INIT_STRIDE; ts_sc->nr_init_stride_packets = 0; } else { ts_debug(ts_sc, "TS_OFFSET is unchanged"); } } } else { /* invalid state, should not happen */ ts_debug(ts_sc, "invalid state (%d), should not happen", ts_sc->state); assert(0); return; } }
/** * @brief Test the SDVL compression scheme * * @param argc The number of command line arguments * @param argv The command line arguments * @return 0 if test succeeds, non-zero if test fails */ int main(int argc, char *argv[]) { bool verbose; /* whether to run in verbose mode or not */ int is_failure = 1; /* test fails by default */ /* do we run in verbose mode ? */ if(argc == 1) { /* no argument, run in silent mode */ verbose = false; } else if(argc == 2 && strcmp(argv[1], "verbose") == 0) { /* run in verbose mode */ verbose = true; } else { /* invalid usage */ printf("test the SDVL compression scheme\n"); printf("usage: %s [verbose]\n", argv[0]); goto error; } /* sdvl_can_value_be_encoded() */ CHECK(sdvl_can_value_be_encoded(0U) == true); CHECK(sdvl_can_value_be_encoded(1U) == true); CHECK(sdvl_can_value_be_encoded(1U << 5) == true); CHECK(sdvl_can_value_be_encoded(1U << 6) == true); CHECK(sdvl_can_value_be_encoded(1U << 7) == true); CHECK(sdvl_can_value_be_encoded(1U << 12) == true); CHECK(sdvl_can_value_be_encoded(1U << 13) == true); CHECK(sdvl_can_value_be_encoded(1U << 14) == true); CHECK(sdvl_can_value_be_encoded(1U << 19) == true); CHECK(sdvl_can_value_be_encoded(1U << 20) == true); CHECK(sdvl_can_value_be_encoded(1U << 21) == true); CHECK(sdvl_can_value_be_encoded(1U << 27) == true); CHECK(sdvl_can_value_be_encoded(1U << 28) == true); CHECK(sdvl_can_value_be_encoded(1U << 29) == false); CHECK(sdvl_can_value_be_encoded(1U << 30) == false); CHECK(sdvl_can_value_be_encoded(1U << 31) == false); CHECK(sdvl_can_value_be_encoded(UINT32_MAX) == false); /* sdvl_can_length_be_encoded() */ CHECK(sdvl_can_length_be_encoded(0) == true); CHECK(sdvl_can_length_be_encoded(1) == true); CHECK(sdvl_can_length_be_encoded(6) == true); CHECK(sdvl_can_length_be_encoded(7) == true); CHECK(sdvl_can_length_be_encoded(8) == true); CHECK(sdvl_can_length_be_encoded(13) == true); CHECK(sdvl_can_length_be_encoded(14) == true); CHECK(sdvl_can_length_be_encoded(15) == true); CHECK(sdvl_can_length_be_encoded(20) == true); CHECK(sdvl_can_length_be_encoded(21) == true); CHECK(sdvl_can_length_be_encoded(22) == true); CHECK(sdvl_can_length_be_encoded(28) == true); CHECK(sdvl_can_length_be_encoded(29) == true); CHECK(sdvl_can_length_be_encoded(30) == false); CHECK(sdvl_can_length_be_encoded(31) == false); CHECK(sdvl_can_length_be_encoded(32) == false); /* sdvl_get_min_len() */ CHECK(sdvl_get_min_len(0, 0) == 0); CHECK(sdvl_get_min_len(1, 0) == 7); CHECK(sdvl_get_min_len(6, 0) == 7); CHECK(sdvl_get_min_len(7, 0) == 7); CHECK(sdvl_get_min_len(8, 0) == 14); CHECK(sdvl_get_min_len(13, 0) == 14); CHECK(sdvl_get_min_len(14, 0) == 14); CHECK(sdvl_get_min_len(15, 0) == 21); CHECK(sdvl_get_min_len(20, 0) == 21); CHECK(sdvl_get_min_len(21, 0) == 21); CHECK(sdvl_get_min_len(22, 0) == 29); CHECK(sdvl_get_min_len(28, 0) == 29); CHECK(sdvl_get_min_len(29, 0) == 29); CHECK(sdvl_get_min_len(0, 1) == 0); CHECK(sdvl_get_min_len(1, 1) == 0); CHECK(sdvl_get_min_len(6, 1) == 7); CHECK(sdvl_get_min_len(7, 1) == 7); CHECK(sdvl_get_min_len(8, 1) == 7); CHECK(sdvl_get_min_len(13, 1) == 14); CHECK(sdvl_get_min_len(14, 1) == 14); CHECK(sdvl_get_min_len(15, 1) == 14); CHECK(sdvl_get_min_len(20, 1) == 21); CHECK(sdvl_get_min_len(21, 1) == 21); CHECK(sdvl_get_min_len(22, 1) == 21); CHECK(sdvl_get_min_len(28, 1) == 29); CHECK(sdvl_get_min_len(29, 1) == 29); CHECK(sdvl_get_min_len(0, 21) == 0); CHECK(sdvl_get_min_len(1, 21) == 0); CHECK(sdvl_get_min_len(6, 21) == 0); CHECK(sdvl_get_min_len(7, 21) == 0); CHECK(sdvl_get_min_len(8, 21) == 0); CHECK(sdvl_get_min_len(13, 21) == 0); CHECK(sdvl_get_min_len(14, 21) == 0); CHECK(sdvl_get_min_len(15, 21) == 0); CHECK(sdvl_get_min_len(20, 21) == 0); CHECK(sdvl_get_min_len(21, 21) == 0); CHECK(sdvl_get_min_len(22, 21) == 7); CHECK(sdvl_get_min_len(28, 21) == 7); CHECK(sdvl_get_min_len(29, 21) == 14); CHECK(sdvl_get_min_len(0, 32) == 0); CHECK(sdvl_get_min_len(1, 32) == 0); CHECK(sdvl_get_min_len(6, 32) == 0); CHECK(sdvl_get_min_len(7, 32) == 0); CHECK(sdvl_get_min_len(8, 32) == 0); CHECK(sdvl_get_min_len(13, 32) == 0); CHECK(sdvl_get_min_len(14, 32) == 0); CHECK(sdvl_get_min_len(15, 32) == 0); CHECK(sdvl_get_min_len(20, 32) == 0); CHECK(sdvl_get_min_len(21, 32) == 0); CHECK(sdvl_get_min_len(22, 32) == 0); CHECK(sdvl_get_min_len(28, 32) == 0); CHECK(sdvl_get_min_len(29, 32) == 0); /* sdvl_get_encoded_len() */ CHECK(sdvl_get_encoded_len(0U) == 1); CHECK(sdvl_get_encoded_len(1U) == 1); CHECK(sdvl_get_encoded_len(1U << 5) == 1); CHECK(sdvl_get_encoded_len(1U << 6) == 1); CHECK(sdvl_get_encoded_len(1U << 7) == 2); CHECK(sdvl_get_encoded_len(1U << 12) == 2); CHECK(sdvl_get_encoded_len(1U << 13) == 2); CHECK(sdvl_get_encoded_len(1U << 14) == 3); CHECK(sdvl_get_encoded_len(1U << 19) == 3); CHECK(sdvl_get_encoded_len(1U << 20) == 3); CHECK(sdvl_get_encoded_len(1U << 21) == 4); CHECK(sdvl_get_encoded_len(1U << 27) == 4); CHECK(sdvl_get_encoded_len(1U << 28) == 4); CHECK(sdvl_get_encoded_len(1U << 29) == 5); CHECK(sdvl_get_encoded_len(1U << 30) == 5); CHECK(sdvl_get_encoded_len(1U << 31) == 5); /* sdvl_encode() / sdvl_encode_full() / sdvl_decode() */ { const uint32_t values[] = { 1, 6, 7, 8, 13, 14, 15, 20, 21, 22, 28, 29, 30, 31, 32 }; const size_t values_nr = sizeof(values) / sizeof(uint32_t); const size_t exp_bytes[] = { 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5 }; const size_t exp_bytes_nr = sizeof(exp_bytes) / sizeof(size_t); const size_t exp_bits[] = { 7, 7, 7, 14, 14, 14, 21, 21, 21, 29, 29, 29, 32, 32, 32 }; const size_t exp_bits_nr = sizeof(exp_bits) / sizeof(size_t); size_t sdvl_bytes_max_nr; #define sdvl_bytes_max_buf_size 4U CHECK(values_nr == exp_bytes_nr); CHECK(values_nr == exp_bits_nr); for(sdvl_bytes_max_nr = 0; sdvl_bytes_max_nr <= 4; sdvl_bytes_max_nr++) { size_t i; for(i = 0; i < values_nr; i++) { const uint32_t value = (values[i] == 32 ? UINT32_MAX : ((1U << values[i]) - 1U)); uint8_t sdvl_bytes[sdvl_bytes_max_buf_size]; size_t sdvl_bytes_nr; uint32_t decoded_value; size_t useful_bits_nr; /* sdvl_encode_full() */ const bool exp_status = (exp_bytes[i] <= 4 && exp_bytes[i] <= sdvl_bytes_max_nr); CHECK(sdvl_encode_full(sdvl_bytes, sdvl_bytes_max_nr, &sdvl_bytes_nr, value) == exp_status); if(exp_status) { printf("sdvl_encode_full(%u) = ", value); for(size_t j = 0; j < sdvl_bytes_nr; j++) { printf("0x%02x ", sdvl_bytes[j]); } printf("\n"); CHECK(sdvl_bytes_nr == exp_bytes[i]); /* sdvl_decode() */ for(size_t j = 0; j <= sdvl_bytes_nr; j++) { const size_t exp_result = (j < sdvl_bytes_nr ? 0 : exp_bytes[i]); CHECK(sdvl_decode(sdvl_bytes, j, &decoded_value, &useful_bits_nr) == exp_result); if(exp_result > 0) { CHECK(decoded_value == value); CHECK(useful_bits_nr == exp_bits[i]); } } } /* sdvl_encode() */ { uint8_t sdvl_bytes2[sdvl_bytes_max_buf_size]; size_t sdvl_bytes2_nr; CHECK(sdvl_encode(sdvl_bytes2, sdvl_bytes_max_nr, &sdvl_bytes2_nr, value, exp_bits[i]) == exp_status); if(exp_status) { printf("sdvl_encode(%u) = ", value); for(size_t j = 0; j < sdvl_bytes_nr; j++) { printf("0x%02x ", sdvl_bytes[j]); } printf("\n"); CHECK(sdvl_bytes2_nr == sdvl_bytes_nr); CHECK(memcmp(sdvl_bytes2, sdvl_bytes, sdvl_bytes2_nr) == 0); } } } } } /* test succeeds */ trace(verbose, "all tests are successful\n"); is_failure = 0; error: return is_failure; }