From f8b4235e3347a47bd15f6adb8dc1c5a9dfccdf41 Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Wed, 9 Oct 2013 09:35:29 -0300 Subject: [PATCH] scenario: Add an action that checks the "force-key-unit" event execution --- validate/data/Makefile.am | 2 + validate/data/force_key_unit.scenario | 2 + validate/gst/validate/gst-validate-report.c | 3 + validate/gst/validate/gst-validate-report.h | 3 +- validate/tools/gst-validate-transcoding.c | 290 +++++++++++++++++++- 5 files changed, 289 insertions(+), 11 deletions(-) create mode 100644 validate/data/force_key_unit.scenario diff --git a/validate/data/Makefile.am b/validate/data/Makefile.am index 337db2f560..6019523923 100644 --- a/validate/data/Makefile.am +++ b/validate/data/Makefile.am @@ -12,6 +12,7 @@ scenarios_DATA = simple_seeks.scenario \ adaptive_video_size.scenario \ adaptive_video_framerate.scenario \ adaptive_video_framerate_size.scenario\ + force_key_unit.scenario\ switch_audio_track.scenario EXTRA_DIST = simple_seeks.scenario \ @@ -27,4 +28,5 @@ EXTRA_DIST = simple_seeks.scenario \ adaptive_video_size.scenario \ adaptive_video_framerate.scenario \ adaptive_video_framerate_size.scenario\ + force_key_unit.scenario\ switch_audio_track.scenario diff --git a/validate/data/force_key_unit.scenario b/validate/data/force_key_unit.scenario new file mode 100644 index 0000000000..65b154f2fd --- /dev/null +++ b/validate/data/force_key_unit.scenario @@ -0,0 +1,2 @@ +video-request-key-unit, playback_time=1.0, direction=upstream, running_time=-1.0, all-header=true, count=1 +eos, playback_time=2.0 diff --git a/validate/gst/validate/gst-validate-report.c b/validate/gst/validate/gst-validate-report.c index 74b8c4e789..5b5fa5791f 100644 --- a/validate/gst/validate/gst-validate-report.c +++ b/validate/gst/validate/gst-validate-report.c @@ -210,6 +210,9 @@ gst_validate_report_load_issues (void) REGISTER_VALIDATE_ISSUE (CRITICAL, SCENARIO_NOT_ENDED, _("All the actions were not executed before the program stoped"), NULL); + REGISTER_VALIDATE_ISSUE (CRITICAL, SCENARIO_ACTION_EXECUTION_ERROR, + _("The execution of an action did not properly happen"), + NULL); } void diff --git a/validate/gst/validate/gst-validate-report.h b/validate/gst/validate/gst-validate-report.h index b4c4dba53c..756d3db719 100644 --- a/validate/gst/validate/gst-validate-report.h +++ b/validate/gst/validate/gst-validate-report.h @@ -110,7 +110,8 @@ typedef enum { #define GST_VALIDATE_ISSUE_ID_QUERY_POSITION_SUPERIOR_DURATION (((GstValidateIssueId) GST_VALIDATE_AREA_QUERY) << GST_VALIDATE_ISSUE_ID_SHIFT | 1) #define GST_VALIDATE_ISSUE_ID_QUERY_POSITION_OUT_OF_SEGMENT (((GstValidateIssueId) GST_VALIDATE_AREA_QUERY) << GST_VALIDATE_ISSUE_ID_SHIFT | 2) -#define GST_VALIDATE_ISSUE_ID_SCENARIO_NOT_ENDED (((GstValidateIssueId) GST_VALIDATE_AREA_SCENARIO) << GST_VALIDATE_ISSUE_ID_SHIFT | 1) +#define GST_VALIDATE_ISSUE_ID_SCENARIO_NOT_ENDED (((GstValidateIssueId) GST_VALIDATE_AREA_SCENARIO) << GST_VALIDATE_ISSUE_ID_SHIFT | 1) +#define GST_VALIDATE_ISSUE_ID_SCENARIO_ACTION_EXECUTION_ERROR (((GstValidateIssueId) GST_VALIDATE_AREA_SCENARIO) << GST_VALIDATE_ISSUE_ID_SHIFT | 2) #define GST_VALIDATE_ISSUE_ID_AREA(id) ((guintptr)(id >> GST_VALIDATE_ISSUE_ID_SHIFT)) diff --git a/validate/tools/gst-validate-transcoding.c b/validate/tools/gst-validate-transcoding.c index e5bbdf7ddb..45f2eef502 100644 --- a/validate/tools/gst-validate-transcoding.c +++ b/validate/tools/gst-validate-transcoding.c @@ -29,9 +29,11 @@ #include #include +#include #include #include + #ifdef G_OS_UNIX #include #endif @@ -40,10 +42,34 @@ static gint ret = 0; static GMainLoop *mainloop; -static GstElement *pipeline; +static GstElement *pipeline, *encodebin; static GstEncodingProfile *encoding_profile = NULL; static gboolean eos_on_shutdown = FALSE; +typedef struct +{ + volatile gint refcount; + + GstSegment segment; /* The currently configured segment */ + + /* FIXME Do we need a weak ref here? */ + GstValidateScenario *scenario; + guint count_bufs; + gboolean seen_event; + GstClockTime running_time; + + /* Make sure to remove all probes when we are done */ + gboolean done; + +} KeyUnitProbeInfo; + +/* This is used to + * 1) Make sure we receive the event + * 2) Count the number of frames that were not KF seen after the event + */ +#define FORCE_KF_DATA_NAME "force-key-unit" +#define NOT_KF_AFTER_FORCE_KF_EVT_TOLERANCE 1 + #ifdef G_OS_UNIX static gboolean intr_handler (gpointer user_data) @@ -63,6 +89,238 @@ intr_handler (gpointer user_data) } #endif /* G_OS_UNIX */ +static void +key_unit_data_unref (KeyUnitProbeInfo *info) +{ + if (G_UNLIKELY (g_atomic_int_dec_and_test (&info->refcount))) { + g_slice_free (KeyUnitProbeInfo, info); + } +} + +static KeyUnitProbeInfo * +key_unit_data_ref (KeyUnitProbeInfo *info) +{ + g_atomic_int_inc (&info->refcount); + + return info; +} + +static KeyUnitProbeInfo * +key_unit_data_new (GstValidateScenario *scenario, GstClockTime running_time) +{ + KeyUnitProbeInfo *info = g_slice_new0 (KeyUnitProbeInfo); + info->refcount = 1; + + info->scenario = scenario; + info->running_time = running_time; + + return info; +} + +static GstPadProbeReturn +_check_is_key_unit_cb (GstPad * pad, GstPadProbeInfo * info, KeyUnitProbeInfo *kuinfo) +{ + if (GST_IS_EVENT (GST_PAD_PROBE_INFO_DATA (info))) { + if (gst_video_event_is_force_key_unit (GST_PAD_PROBE_INFO_DATA (info))) + kuinfo->seen_event = TRUE; + else if (GST_EVENT_TYPE (info->data) == GST_EVENT_SEGMENT && + GST_PAD_DIRECTION (pad) == GST_PAD_SRC) { + const GstSegment *segment = NULL; + + gst_event_parse_segment (info->data, &segment); + kuinfo->segment = *segment; + } + } else if (GST_IS_BUFFER (GST_PAD_PROBE_INFO_DATA (info)) && kuinfo->seen_event) { + + if (GST_CLOCK_TIME_IS_VALID (kuinfo->running_time)) { + GstClockTime running_time = gst_segment_to_running_time (&kuinfo->segment, + GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (info->data)); + + if (running_time < kuinfo->running_time) + return GST_PAD_PROBE_OK; + } + + if (GST_BUFFER_FLAG_IS_SET (GST_PAD_PROBE_INFO_BUFFER (info), GST_BUFFER_FLAG_DELTA_UNIT)) { + if (kuinfo->count_bufs >= NOT_KF_AFTER_FORCE_KF_EVT_TOLERANCE) { + GST_VALIDATE_REPORT (kuinfo->scenario, + SCENARIO_ACTION_EXECUTION_ERROR, + "Did not receive a key frame after requested one, " + " at running_time %" GST_TIME_FORMAT " (with a %i " + "frame tolerance)", GST_TIME_ARGS (kuinfo->running_time), + NOT_KF_AFTER_FORCE_KF_EVT_TOLERANCE); + + return GST_PAD_PROBE_REMOVE; + } + + kuinfo->count_bufs++; + } else { + GST_DEBUG_OBJECT (kuinfo->scenario, + "Properly got keyframe after \"force-keyframe\" event " + "with running_time %" GST_TIME_FORMAT " (latency %d frame(s))", + GST_TIME_ARGS (kuinfo->running_time), kuinfo->count_bufs); + + return GST_PAD_PROBE_REMOVE; + } + } + + return GST_PAD_PROBE_OK; +} + +static int +_find_video_encoder (GValue * velement, gpointer udata) +{ + GstElement *element = g_value_get_object (velement); + + const gchar *klass = gst_element_class_get_metadata (GST_ELEMENT_GET_CLASS (element), + GST_ELEMENT_METADATA_KLASS); + + if (g_strstr_len (klass, -1, "Video") && + g_strstr_len (klass, -1, "Encoder")) { + + return 0; + } + + return !0; +} + + +static gboolean +_execute_request_key_unit (GstValidateScenario * scenario, + GstValidateAction * action) +{ + guint count; + GstIterator *iter; + gboolean all_headers; + + gboolean ret = TRUE; + GValue result = { 0, }; + GstEvent *event = NULL; + GstQuery *segment_query; + KeyUnitProbeInfo *info = NULL; + GstElement *video_encoder = NULL; + GstPad *pad = NULL, *encoder_srcpad = NULL; + GstClockTime running_time = GST_CLOCK_TIME_NONE; + const gchar *direction = gst_structure_get_string (action->structure, + "direction"); + + iter = gst_bin_iterate_recurse (GST_BIN (encodebin)); + if (!gst_iterator_find_custom (iter, + (GCompareFunc) _find_video_encoder, &result,NULL)) { + g_error ("Could not find any video encode"); + + goto fail; + } + + gst_iterator_free (iter); + video_encoder = g_value_get_object (&result); + encoder_srcpad = gst_element_get_static_pad (video_encoder, "src"); + + if (!encoder_srcpad) { + GST_FIXME ("Implement weird encoder management"); + g_error ("We do not handle encoder with not static srcpad"); + + goto fail; + } + + gst_validate_action_get_clocktime (scenario, action, + "running-time", &running_time); + + if (gst_structure_get_boolean (action->structure, "all-headers", + &all_headers)) { + g_error ("Missing field: all-headers"); + + goto fail; + } + + if (!gst_structure_get_uint (action->structure, "count", &count)) { + if (!gst_structure_get_int (action->structure, "count", (gint *) & count)) { + g_error ("Missing field: count"); + + goto fail; + } + } + + info = key_unit_data_new (scenario, running_time); + if (g_strcmp0 (direction, "upstream") == 0) { + event = gst_video_event_new_upstream_force_key_unit (running_time, + all_headers, count); + + pad = gst_element_get_static_pad (video_encoder, "src"); + gst_pad_add_probe (encoder_srcpad, + GST_PAD_PROBE_TYPE_EVENT_UPSTREAM, + (GstPadProbeCallback) _check_is_key_unit_cb, + key_unit_data_ref (info), + (GDestroyNotify) key_unit_data_unref); + } else if (g_strcmp0 (direction, "downstream") == 0) { + GstClockTime timestamp = GST_CLOCK_TIME_NONE, + stream_time = GST_CLOCK_TIME_NONE; + + pad = gst_element_get_static_pad (video_encoder, "sink"); + if (!pad) { + GST_FIXME ("Implement weird encoder management"); + g_error ("We do not handle encoder with not static sinkpad"); + + goto fail; + } + + gst_validate_action_get_clocktime (scenario, action, + "timestamp", ×tamp); + + gst_validate_action_get_clocktime (scenario, action, + "stream-time", &stream_time); + + event = gst_video_event_new_downstream_force_key_unit (timestamp, stream_time, + running_time, all_headers, count); + + gst_pad_add_probe (pad, + GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, + (GstPadProbeCallback) _check_is_key_unit_cb, + key_unit_data_ref (info), + (GDestroyNotify) key_unit_data_unref); + } else { + g_error ("request keyunit direction %s invalide (should be in" + " [downstrean, upstream]", direction); + + goto fail; + } + + g_print ("Sendings a \"force key unit\" event %s\n", direction); + + segment_query = gst_query_new_segment (GST_FORMAT_TIME); + gst_pad_query (encoder_srcpad, segment_query); + + gst_query_parse_segment (segment_query, &(info->segment.rate), + &(info->segment.format), + (gint64*) &(info->segment.start), + (gint64*) &(info->segment.stop)); + + gst_pad_add_probe (encoder_srcpad, + GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, + (GstPadProbeCallback) _check_is_key_unit_cb, info, + (GDestroyNotify) key_unit_data_unref); + + + if (!gst_pad_send_event (pad, event)) { + GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR, + "Could not send \"force key unit\" event %s", direction); + goto fail; + } + +done: + if (video_encoder) + gst_object_unref (video_encoder); + if (pad) + gst_object_unref (pad); + if (encoder_srcpad) + gst_object_unref (encoder_srcpad); + + return ret; + +fail: + ret = FALSE; + goto done; +} + static gboolean _execute_set_restriction (GstValidateScenario * scenario, GstValidateAction * action) @@ -275,24 +533,24 @@ pad_added_cb (GstElement * uridecodebin, GstPad * pad, GstElement * encodebin) static void create_transcoding_pipeline (gchar * uri, gchar * outuri) { - GstElement *src, *ebin, *sink; + GstElement *src, *sink; mainloop = g_main_loop_new (NULL, FALSE); pipeline = gst_pipeline_new ("encoding-pipeline"); src = gst_element_factory_make ("uridecodebin", NULL); - ebin = gst_element_factory_make ("encodebin", NULL); + encodebin = gst_element_factory_make ("encodebin", NULL); sink = gst_element_make_from_uri (GST_URI_SINK, outuri, "sink", NULL); g_assert (sink); g_object_set (src, "uri", uri, NULL); - g_object_set (ebin, "profile", encoding_profile, NULL); + g_object_set (encodebin, "profile", encoding_profile, NULL); - g_signal_connect (src, "pad-added", G_CALLBACK (pad_added_cb), ebin); + g_signal_connect (src, "pad-added", G_CALLBACK (pad_added_cb), encodebin); - gst_bin_add_many (GST_BIN (pipeline), src, ebin, sink, NULL); - gst_element_link (ebin, sink); + gst_bin_add_many (GST_BIN (pipeline), src, encodebin, sink, NULL); + gst_element_link (encodebin, sink); } static gboolean @@ -420,6 +678,20 @@ _parse_encoding_profile (const gchar * option_name, const gchar * value, return TRUE; } +static void +_register_actions (void) +{ + const gchar *resize_video_mandatory_fields[] = { "restriction-caps", NULL }; + const gchar *force_key_unit_mandatory_fields[] = { "direction", + "running-time", "all-headers", "count", NULL }; + + gst_validate_add_action_type ("set-restriction", _execute_set_restriction, + resize_video_mandatory_fields, "Change the restriction caps on the fly"); + gst_validate_add_action_type ("video-request-key-unit", + _execute_request_key_unit, force_key_unit_mandatory_fields, + "Request a video key unit"); +} + int main (int argc, gchar ** argv) { @@ -438,7 +710,6 @@ main (int argc, gchar ** argv) gboolean want_help = FALSE; gboolean list_scenarios = FALSE; - const gchar *resize_video_mandatory_fields[] = { "restriction-caps", NULL }; GOptionEntry options[] = { {"output-format", 'o', 0, G_OPTION_ARG_CALLBACK, &_parse_encoding_profile, "Set the properties to use for the encoding profile " @@ -500,9 +771,8 @@ main (int argc, gchar ** argv) gst_validate_init (); - gst_validate_add_action_type ("set-restriction", _execute_set_restriction, - resize_video_mandatory_fields, "Change the restriction caps on the fly"); + _register_actions (); if (argc != 3) { g_printerr ("%i arguments recived, 2 expected.\n" "You should run the test using:\n"