From e62adb4d12a4cf5daa0be1866afe5cf10a3b5b7d Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Wed, 3 Apr 2024 10:28:28 -0400 Subject: [PATCH] unixfdsink: Take segment into account when converting timestamps Also rename `calculate_timestamp()` to `to_monotonic()` and `from_monotonic()` which better describe what it does. Part-of: --- .../gst/unixfd/gstunixfdsink.c | 33 +++++-- .../gst-plugins-bad/gst/unixfd/gstunixfdsrc.c | 6 +- .../tests/check/elements/unixfd.c | 88 +++++++++++++++++++ 3 files changed, 117 insertions(+), 10 deletions(-) diff --git a/subprojects/gst-plugins-bad/gst/unixfd/gstunixfdsink.c b/subprojects/gst-plugins-bad/gst/unixfd/gstunixfdsink.c index 0e7dd8cde8..fa4e39b11e 100644 --- a/subprojects/gst-plugins-bad/gst/unixfd/gstunixfdsink.c +++ b/subprojects/gst-plugins-bad/gst/unixfd/gstunixfdsink.c @@ -428,12 +428,22 @@ send_command_to_all (GstUnixFdSink * self, CommandType type, GUnixFDList * fds, } static GstClockTime -calculate_timestamp (GstClockTime timestamp, GstClockTime base_time, - GstClockTime latency, GstClockTimeDiff clock_diff) +to_monotonic (GstClockTime timestamp, const GstSegment * segment, + GstClockTime base_time, GstClockTime latency, GstClockTimeDiff clock_diff) { if (GST_CLOCK_TIME_IS_VALID (timestamp)) { /* Convert running time to pipeline clock time */ - timestamp += base_time; + gint res = + gst_segment_to_running_time_full (segment, GST_FORMAT_TIME, timestamp, + ×tamp); + if (res == 0) + return GST_CLOCK_TIME_NONE; + else if (res > 0) + timestamp += base_time; + else if (base_time > timestamp) + timestamp = base_time - timestamp; + else + timestamp = 0; if (GST_CLOCK_TIME_IS_VALID (latency)) timestamp += latency; /* Convert to system monotonic clock time */ @@ -485,11 +495,11 @@ gst_unix_fd_sink_render (GstBaseSink * bsink, GstBuffer * buffer) * id so we know which buffer to unref. */ new_buffer->id = (guint64) buffer; new_buffer->pts = - calculate_timestamp (GST_BUFFER_PTS (buffer), base_time, latency, - clock_diff); + to_monotonic (GST_BUFFER_PTS (buffer), + &GST_BASE_SINK_CAST (self)->segment, base_time, latency, clock_diff); new_buffer->dts = - calculate_timestamp (GST_BUFFER_DTS (buffer), base_time, latency, - clock_diff); + to_monotonic (GST_BUFFER_DTS (buffer), + &GST_BASE_SINK_CAST (self)->segment, base_time, latency, clock_diff); new_buffer->duration = GST_BUFFER_DURATION (buffer); new_buffer->offset = GST_BUFFER_OFFSET (buffer); new_buffer->offset_end = GST_BUFFER_OFFSET_END (buffer); @@ -498,6 +508,15 @@ gst_unix_fd_sink_render (GstBaseSink * bsink, GstBuffer * buffer) new_buffer->n_memory = n_memory; new_buffer->n_meta = n_meta; + if ((GST_BUFFER_PTS_IS_VALID (buffer) + && !GST_CLOCK_TIME_IS_VALID (new_buffer->pts)) + || (GST_BUFFER_DTS_IS_VALID (buffer) + && !GST_CLOCK_TIME_IS_VALID (new_buffer->dts))) { + GST_ERROR_OBJECT (self, + "Could not convert buffer timestamp to running time"); + return GST_FLOW_ERROR; + } + gboolean dmabuf_count = 0; GUnixFDList *fds = g_unix_fd_list_new (); for (int i = 0; i < n_memory; i++) { diff --git a/subprojects/gst-plugins-bad/gst/unixfd/gstunixfdsrc.c b/subprojects/gst-plugins-bad/gst/unixfd/gstunixfdsrc.c index 02f2ae7be7..62cc50bd29 100644 --- a/subprojects/gst-plugins-bad/gst/unixfd/gstunixfdsrc.c +++ b/subprojects/gst-plugins-bad/gst/unixfd/gstunixfdsrc.c @@ -282,7 +282,7 @@ gst_unix_fd_src_unlock_stop (GstBaseSrc * bsrc) } static GstClockTime -calculate_timestamp (GstClockTime timestamp, GstClockTime base_time, +from_monotonic (GstClockTime timestamp, GstClockTime base_time, GstClockTimeDiff clock_diff) { if (GST_CLOCK_TIME_IS_VALID (timestamp)) { @@ -369,9 +369,9 @@ again: } GST_BUFFER_PTS (*outbuf) = - calculate_timestamp (new_buffer->pts, base_time, clock_diff); + from_monotonic (new_buffer->pts, base_time, clock_diff); GST_BUFFER_DTS (*outbuf) = - calculate_timestamp (new_buffer->dts, base_time, clock_diff); + from_monotonic (new_buffer->dts, base_time, clock_diff); GST_BUFFER_DURATION (*outbuf) = new_buffer->duration; GST_BUFFER_OFFSET (*outbuf) = new_buffer->offset; GST_BUFFER_OFFSET_END (*outbuf) = new_buffer->offset_end; diff --git a/subprojects/gst-plugins-bad/tests/check/elements/unixfd.c b/subprojects/gst-plugins-bad/tests/check/elements/unixfd.c index ec98805dda..d3dd1d0ee1 100644 --- a/subprojects/gst-plugins-bad/tests/check/elements/unixfd.c +++ b/subprojects/gst-plugins-bad/tests/check/elements/unixfd.c @@ -25,6 +25,7 @@ #include #include +#include #include static void @@ -134,6 +135,92 @@ GST_START_TEST (test_unixfd_videotestsrc) GST_END_TEST; +GST_START_TEST (test_unixfd_segment) +{ + GError *error = NULL; + + /* Ensure we don't have socket from previous failed test */ + gchar *socket_path = + g_strdup_printf ("%s/unixfd-test-socket", g_get_user_runtime_dir ()); + if (g_file_test (socket_path, G_FILE_TEST_EXISTS)) { + g_unlink (socket_path); + } + + GstCaps *caps = gst_caps_new_empty_simple ("video/x-raw"); + + /* Setup service */ + gchar *pipeline_str = + g_strdup_printf + ("appsrc name=src format=time handle-segment-change=true ! unixfdsink socket-path=%s sync=false async=false", + socket_path); + GstElement *pipeline_service = gst_parse_launch (pipeline_str, &error); + g_assert_no_error (error); + fail_unless (gst_element_set_state (pipeline_service, + GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS); + GstElement *appsrc = gst_bin_get_by_name (GST_BIN (pipeline_service), "src"); + gst_object_unref (appsrc); + g_free (pipeline_str); + + /* Setup client */ + pipeline_str = + g_strdup_printf + ("unixfdsrc socket-path=%s ! appsink name=sink sync=false async=false", + socket_path); + GstElement *pipeline_client = gst_parse_launch (pipeline_str, &error); + g_assert_no_error (error); + fail_unless (gst_element_set_state (pipeline_client, + GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS); + GstElement *appsink = gst_bin_get_by_name (GST_BIN (pipeline_client), "sink"); + gst_object_unref (appsink); + g_free (pipeline_str); + + /* Send a buffer with PTS=30s */ + GstSegment segment; + gst_segment_init (&segment, GST_FORMAT_TIME); + GstBuffer *buf = gst_buffer_new (); + GST_BUFFER_PTS (buf) = 30 * GST_SECOND; + GstSample *sample = gst_sample_new (buf, caps, &segment, NULL); + gst_app_src_push_sample (GST_APP_SRC (appsrc), sample); + gst_sample_unref (sample); + gst_buffer_unref (buf); + + /* Wait for it */ + sample = gst_app_sink_pull_sample (GST_APP_SINK (appsink)); + buf = gst_sample_get_buffer (sample); + GstClockTime first_pts = GST_BUFFER_PTS (buf); + gst_sample_unref (sample); + + /* Send a buffer with PTS=1s but with 30s offset in the segment */ + segment.base = 30 * GST_SECOND; + buf = gst_buffer_new (); + GST_BUFFER_PTS (buf) = 1 * GST_SECOND; + sample = gst_sample_new (buf, caps, &segment, NULL); + gst_app_src_push_sample (GST_APP_SRC (appsrc), sample); + gst_sample_unref (sample); + gst_buffer_unref (buf); + + /* Wait for it */ + sample = gst_app_sink_pull_sample (GST_APP_SINK (appsink)); + buf = gst_sample_get_buffer (sample); + GstClockTime second_pts = GST_BUFFER_PTS (buf); + gst_sample_unref (sample); + + /* They should be 1s appart */ + fail_unless_equals_uint64 (second_pts - first_pts, GST_SECOND); + + /* Teardown */ + fail_unless (gst_element_set_state (pipeline_client, + GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS); + fail_unless (gst_element_set_state (pipeline_service, + GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS); + gst_object_unref (pipeline_service); + gst_object_unref (pipeline_client); + g_free (socket_path); + gst_caps_unref (caps); +} + +GST_END_TEST; + static Suite * unixfd_suite (void) { @@ -142,6 +229,7 @@ unixfd_suite (void) suite_add_tcase (s, tc); tcase_add_test (tc, test_unixfd_videotestsrc); + tcase_add_test (tc, test_unixfd_segment); return s; }