* @exttimestamp: a previous extended timestamp
* @timestamp: a new timestamp
*
- * Update the @exttimestamp field with @timestamp. For the first call of the
- * method, @exttimestamp should point to a location with a value of -1.
+ * Update the @exttimestamp field with the extended timestamp of @timestamp
+ * For the first call of the method, @exttimestamp should point to a location
+ * with a value of -1.
*
- * This function makes sure that the returned value is a constantly increasing
- * value even in the case where there is a timestamp wraparound.
+ * This function is able to handle both forward and backward timestamps taking
+ * into account:
+ * - timestamp wraparound making sure that the returned value is properly increased.
+ * - timestamp unwraparound making sure that the returned value is properly decreased.
*
- * Returns: The extended timestamp of @timestamp.
+ * Returns: The extended timestamp of @timestamp or 0 if the result can't go anywhere backwards.
*/
guint64
gst_rtp_buffer_ext_timestamp (guint64 * exttimestamp, guint32 timestamp)
{
- guint64 result, diff, ext;
+ guint64 result, ext;
g_return_val_if_fail (exttimestamp != NULL, -1);
result = timestamp + (ext & ~(G_GUINT64_CONSTANT (0xffffffff)));
/* check for timestamp wraparound */
- if (result < ext)
- diff = ext - result;
- else
- diff = result - ext;
-
- if (diff > G_MAXINT32) {
- /* timestamp went backwards more than allowed, we wrap around and get
- * updated extended timestamp. */
- result += (G_GUINT64_CONSTANT (1) << 32);
+ if (result < ext) {
+ guint64 diff = ext - result;
+
+ if (diff > G_MAXINT32) {
+ /* timestamp went backwards more than allowed, we wrap around and get
+ * updated extended timestamp. */
+ result += (G_GUINT64_CONSTANT (1) << 32);
+ }
+ } else {
+ guint64 diff = result - ext;
+
+ if (diff > G_MAXINT32) {
+ if (result < (G_GUINT64_CONSTANT (1) << 32)) {
+ GST_WARNING
+ ("Cannot unwrap, any wrapping took place yet. Returning 0 without updating extended timestamp.");
+ return 0;
+ } else {
+ /* timestamp went forwards more than allowed, we unwrap around and get
+ * updated extended timestamp. */
+ result -= (G_GUINT64_CONSTANT (1) << 32);
+ /* We don't want the extended timestamp storage to go back, ever */
+ return result;
+ }
+ }
}
}
+
*exttimestamp = result;
return result;
GST_END_TEST;
+
+GST_START_TEST (test_ext_timestamp_basic)
+{
+ guint64 exttimestamp = -1;
+ guint64 result = 0;
+
+ /* no wraparound when timestamps are increasing */
+ result = gst_rtp_buffer_ext_timestamp (&exttimestamp, 0);
+ fail_unless_equals_uint64 (result, 0);
+ result = gst_rtp_buffer_ext_timestamp (&exttimestamp, 10);
+ fail_unless_equals_uint64 (result, 10);
+ result = gst_rtp_buffer_ext_timestamp (&exttimestamp, 10);
+ fail_unless_equals_uint64 (result, 10);
+ result = gst_rtp_buffer_ext_timestamp (&exttimestamp,
+ G_GUINT64_CONSTANT (1) + G_MAXINT32);
+ fail_unless_equals_uint64 (result, G_GUINT64_CONSTANT (1) + G_MAXINT32);
+
+ /* Even big bumps under G_MAXINT32 don't result in wrap-around */
+ exttimestamp = -1;
+ result = gst_rtp_buffer_ext_timestamp (&exttimestamp, 1087500);
+ fail_unless_equals_uint64 (result, 1087500);
+ result = gst_rtp_buffer_ext_timestamp (&exttimestamp, 24);
+ fail_unless_equals_uint64 (result, 24);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_ext_timestamp_wraparound)
+{
+ guint64 ext_ts = -1;
+
+ fail_unless_equals_uint64 (gst_rtp_buffer_ext_timestamp (&ext_ts,
+ G_MAXUINT32 - 90000 + G_GUINT64_CONSTANT (1)),
+ (G_MAXUINT32 - 90000 + G_GUINT64_CONSTANT (1)));
+
+ fail_unless_equals_uint64 (gst_rtp_buffer_ext_timestamp (&ext_ts, 0),
+ G_MAXUINT32 + G_GUINT64_CONSTANT (1));
+
+ fail_unless_equals_uint64 (gst_rtp_buffer_ext_timestamp (&ext_ts, 90000),
+ (G_MAXUINT32 + G_GUINT64_CONSTANT (1) + 90000));
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_ext_timestamp_wraparound_disordered)
+{
+ guint64 ext_ts = -1;
+
+ fail_unless (gst_rtp_buffer_ext_timestamp (&ext_ts,
+ G_MAXUINT32 - 90000 + G_GUINT64_CONSTANT (1))
+ == (G_MAXUINT32 - 90000 + G_GUINT64_CONSTANT (1)));
+
+ fail_unless (gst_rtp_buffer_ext_timestamp (&ext_ts, 0)
+ == G_MAXUINT32 + G_GUINT64_CONSTANT (1));
+
+ /* Unwrapping around */
+ fail_unless (gst_rtp_buffer_ext_timestamp (&ext_ts,
+ G_MAXUINT32 - 90000 + G_GUINT64_CONSTANT (1))
+ == (G_MAXUINT32 - 90000 + G_GUINT64_CONSTANT (1)));
+
+ fail_unless (gst_rtp_buffer_ext_timestamp (&ext_ts, 90000)
+ == (G_MAXUINT32 + G_GUINT64_CONSTANT (1) + 90000));
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_ext_timestamp_wraparound_disordered_cannot_unwrap)
+{
+ guint64 ext_ts = -1;
+
+ fail_unless (gst_rtp_buffer_ext_timestamp (&ext_ts, 90000)
+ == 90000);
+
+ /* Cannot unwrapping around */
+ fail_unless (gst_rtp_buffer_ext_timestamp (&ext_ts,
+ G_MAXUINT32 - 90000 + G_GUINT64_CONSTANT (1)) == 0);
+
+ fail_unless (gst_rtp_buffer_ext_timestamp (&ext_ts, 90000)
+ == 90000);
+}
+
+GST_END_TEST;
+
static Suite *
rtp_suite (void)
{
//tcase_add_test (tc_chain, test_rtp_buffer_list);
+ tcase_add_test (tc_chain, test_ext_timestamp_basic);
+ tcase_add_test (tc_chain, test_ext_timestamp_wraparound);
+ tcase_add_test (tc_chain, test_ext_timestamp_wraparound_disordered);
+ tcase_add_test (tc_chain,
+ test_ext_timestamp_wraparound_disordered_cannot_unwrap);
+
return s;
}