From 2b61f7eb860acc584d5d3b872b9089a3d222276b Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Thu, 10 Mar 2022 10:34:29 +0100 Subject: gst: Use playbin's convert-frame feature again Now that GStreamer support for processing GL memory has been merged. See https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1916 --- meson.build | 8 +- src/gst/totem-gst-pixbuf-helpers.c | 289 +------------------------------------ 2 files changed, 6 insertions(+), 291 deletions(-) diff --git a/meson.build b/meson.build index 9a579988a..f64cfc4cc 100644 --- a/meson.build +++ b/meson.build @@ -126,7 +126,7 @@ add_project_arguments(common_flags, language: 'c') glib_req_version = '>= 2.72.0' gtk_req_version = '>= 3.22.0' hdy_req_version = '>= 1.5.0' -gst_req_version = '>= 1.6.0' +gst_req_version = '>= 1.21.1' grilo_req_version = '>= 0.3.0' peas_req_version = '>= 1.1.0' totem_plparser_req_version = '>= 3.26.5' @@ -144,9 +144,9 @@ else endif hdy_dep = dependency('libhandy-1', version: hdy_req_version) gst_dep = dependency('gstreamer-1.0', version: gst_req_version) -gst_tag_dep = dependency('gstreamer-tag-1.0', version: '>= 0.11.93') -gst_video_dep = dependency('gstreamer-video-1.0') -gst_pbutils_dep = dependency('gstreamer-pbutils-1.0') +gst_tag_dep = dependency('gstreamer-tag-1.0', version: gst_req_version) +gst_video_dep = dependency('gstreamer-video-1.0', version: gst_req_version) +gst_pbutils_dep = dependency('gstreamer-pbutils-1.0', version: gst_req_version) peas_dep = dependency('libpeas-1.0', version: peas_req_version) peas_gtk_dep = dependency('libpeas-gtk-1.0', version: peas_req_version) totem_plparser_dep = dependency('totem-plparser', version: totem_plparser_req_version) diff --git a/src/gst/totem-gst-pixbuf-helpers.c b/src/gst/totem-gst-pixbuf-helpers.c index 8744a85dd..d5d2b8832 100644 --- a/src/gst/totem-gst-pixbuf-helpers.c +++ b/src/gst/totem-gst-pixbuf-helpers.c @@ -47,290 +47,12 @@ destroy_pixbuf (guchar *pix, gpointer data) gst_sample_unref (GST_SAMPLE (data)); } -static gboolean -caps_are_raw (const GstCaps * caps) -{ - guint i, len; - - len = gst_caps_get_size (caps); - - for (i = 0; i < len; i++) { - GstStructure *st = gst_caps_get_structure (caps, i); - if (gst_structure_has_name (st, "video/x-raw")) - return TRUE; - } - - return FALSE; -} - -static gboolean -create_element (const gchar * factory_name, GstElement ** element, - GError ** err) -{ - *element = gst_element_factory_make (factory_name, NULL); - if (*element) - return TRUE; - - if (err && *err == NULL) { - *err = g_error_new (GST_CORE_ERROR, GST_CORE_ERROR_MISSING_PLUGIN, - "cannot create element '%s' - please check your GStreamer installation", - factory_name); - } - - return FALSE; -} - -static GstElement * -build_convert_frame_pipeline (FrameCaptureType capture_type, - GstElement ** src_element, - GstElement ** sink_element, const GstCaps * from_caps, - const GstCaps * to_caps, GError ** err) -{ - GstElement *csp = NULL, *vscale = NULL; - GstElement *src = NULL, *sink = NULL, *dl = NULL, *pipeline; - GError *error = NULL; - gboolean ret; - - if (!caps_are_raw (to_caps)) - goto no_pipeline; - - /* videoscale is here to correct for the pixel-aspect-ratio for us */ - GST_DEBUG ("creating elements"); - if (!create_element ("appsrc", &src, &error) || - !create_element ("appsink", &sink, &error)) - goto no_elements; - if (capture_type == FRAME_CAPTURE_TYPE_RAW) { - if (!create_element ("videoconvert", &csp, &error) || - !create_element ("videoscale", &vscale, &error)) - goto no_elements; - } else { - if (!create_element ("glcolorconvert", &csp, &error) || - !create_element ("glcolorscale", &vscale, &error) || - !create_element ("gldownload", &dl, &error)) - goto no_elements; - } - - pipeline = gst_pipeline_new ("videoconvert-pipeline"); - if (pipeline == NULL) - goto no_pipeline; - - /* Add black borders if necessary to keep the DAR */ - if (g_object_class_find_property (G_OBJECT_GET_CLASS (vscale), "add-borders")) - g_object_set (vscale, "add-borders", TRUE, NULL); - - GST_DEBUG ("adding elements"); - gst_bin_add_many (GST_BIN (pipeline), src, csp, vscale, sink, dl, NULL); - - /* set caps */ - g_object_set (src, "caps", from_caps, NULL); - g_object_set (sink, "caps", to_caps, NULL); - - if (dl) - ret = gst_element_link_many (src, csp, vscale, dl, sink, NULL); - else - ret = gst_element_link_many (src, csp, vscale, sink, NULL); - if (!ret) - goto link_failed; - - g_object_set (src, "emit-signals", TRUE, NULL); - g_object_set (sink, "emit-signals", TRUE, NULL); - - *src_element = src; - *sink_element = sink; - - return pipeline; - /* ERRORS */ -no_elements: - { - if (src) - gst_object_unref (src); - if (csp) - gst_object_unref (csp); - if (vscale) - gst_object_unref (vscale); - if (dl) - gst_object_unref (dl); - if (sink) - gst_object_unref (sink); - GST_ERROR ("Could not convert video frame: %s", error->message); - if (err) - *err = error; - else - g_error_free (error); - return NULL; - } -no_pipeline: - { - gst_object_unref (src); - gst_object_unref (csp); - gst_object_unref (vscale); - g_clear_pointer (&dl, gst_object_unref); - gst_object_unref (sink); - - GST_ERROR ("Could not convert video frame: no pipeline (unknown error)"); - if (err) - *err = g_error_new (GST_CORE_ERROR, GST_CORE_ERROR_FAILED, - "Could not convert video frame: no pipeline (unknown error)"); - return NULL; - } -link_failed: - { - gst_object_unref (pipeline); - - GST_ERROR ("Could not convert video frame: failed to link elements"); - if (err) - *err = g_error_new (GST_CORE_ERROR, GST_CORE_ERROR_NEGOTIATION, - "Could not convert video frame: failed to link elements"); - return NULL; - } -} - -/** - * totem_gst_video_convert_sample: - * @capture_type: capture type - * @sample: a #GstSample - * @to_caps: the #GstCaps to convert to - * @timeout: the maximum amount of time allowed for the processing. - * @error: pointer to a #GError. Can be %NULL. - * - * Converts a raw video buffer into the specified output caps. - * - * The output caps can be any raw video formats or any image formats (jpeg, png, ...). - * - * The width, height and pixel-aspect-ratio can also be specified in the output caps. - * - * Returns: The converted #GstSample, or %NULL if an error happened (in which case @err - * will point to the #GError). - */ -static GstSample * -totem_gst_video_convert_sample (FrameCaptureType capture_type, - GstSample * sample, const GstCaps * to_caps, - GstClockTime timeout, GError ** error) -{ - GstMessage *msg; - GstBuffer *buf; - GstSample *result = NULL; - GError *err = NULL; - GstBus *bus; - GstCaps *from_caps, *to_caps_copy = NULL; - GstFlowReturn ret; - GstElement *pipeline, *src, *sink; - guint i, n; - - g_return_val_if_fail (sample != NULL, NULL); - g_return_val_if_fail (to_caps != NULL, NULL); - - buf = gst_sample_get_buffer (sample); - g_return_val_if_fail (buf != NULL, NULL); - - from_caps = gst_sample_get_caps (sample); - g_return_val_if_fail (from_caps != NULL, NULL); - - to_caps_copy = gst_caps_new_empty (); - n = gst_caps_get_size (to_caps); - for (i = 0; i < n; i++) { - GstStructure *s = gst_caps_get_structure (to_caps, i); - - s = gst_structure_copy (s); - gst_structure_remove_field (s, "framerate"); - gst_caps_append_structure (to_caps_copy, s); - } - - pipeline = - build_convert_frame_pipeline (capture_type, &src, &sink, from_caps, - to_caps_copy, &err); - if (!pipeline) - goto no_pipeline; - - /* now set the pipeline to the paused state, after we push the buffer into - * appsrc, this should preroll the converted buffer in appsink */ - GST_DEBUG ("running conversion pipeline to caps %" GST_PTR_FORMAT, - to_caps_copy); - if (gst_element_set_state (pipeline, - GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) - goto state_change_failed; - - /* feed buffer in appsrc */ - GST_DEBUG ("feeding buffer %p, size %" G_GSIZE_FORMAT ", caps %" - GST_PTR_FORMAT, buf, gst_buffer_get_size (buf), from_caps); - g_signal_emit_by_name (src, "push-buffer", buf, &ret); - - /* now see what happens. We either got an error somewhere or the pipeline - * prerolled */ - bus = gst_element_get_bus (pipeline); - msg = gst_bus_timed_pop_filtered (bus, - timeout, GST_MESSAGE_ERROR | GST_MESSAGE_ASYNC_DONE); - - if (msg) { - switch (GST_MESSAGE_TYPE (msg)) { - case GST_MESSAGE_ASYNC_DONE: - { - /* we're prerolled, get the frame from appsink */ - g_signal_emit_by_name (sink, "pull-preroll", &result); - - if (result) { - GST_DEBUG ("conversion successful: result = %p", result); - } else { - GST_ERROR ("prerolled but no result frame?!"); - } - break; - } - case GST_MESSAGE_ERROR:{ - gchar *dbg = NULL; - - gst_message_parse_error (msg, &err, &dbg); - if (err) { - GST_ERROR ("Could not convert video frame: %s", err->message); - GST_DEBUG ("%s [debug: %s]", err->message, GST_STR_NULL (dbg)); - if (error) - *error = err; - else - g_error_free (err); - } - g_free (dbg); - break; - } - default:{ - g_return_val_if_reached (NULL); - } - } - gst_message_unref (msg); - } else { - GST_ERROR ("Could not convert video frame: timeout during conversion"); - if (error) - *error = g_error_new (GST_CORE_ERROR, GST_CORE_ERROR_FAILED, - "Could not convert video frame: timeout during conversion"); - } - - gst_element_set_state (pipeline, GST_STATE_NULL); - gst_object_unref (bus); - gst_object_unref (pipeline); - gst_caps_unref (to_caps_copy); - - return result; - - /* ERRORS */ -no_pipeline: -state_change_failed: - { - gst_caps_unref (to_caps_copy); - - if (error) - *error = err; - else - g_error_free (err); - - return NULL; - } -} - GdkPixbuf * totem_gst_playbin_get_frame (GstElement *play, GError **error) { FrameCaptureType capture_type; GstStructure *s; GstSample *sample = NULL; - GstSample *last_sample = NULL; guint bpp; GdkPixbuf *pixbuf = NULL; GstCaps *to_caps, *sample_caps; @@ -359,19 +81,12 @@ totem_gst_playbin_get_frame (GstElement *play, GError **error) NULL); /* get frame */ - g_object_get (G_OBJECT (play), "sample", &last_sample, NULL); - if (!last_sample) { - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Failed to retrieve video frame"); - return NULL; - } - sample = totem_gst_video_convert_sample (capture_type, last_sample, to_caps, 25 * GST_SECOND, error); - gst_sample_unref (last_sample); + g_signal_emit_by_name (play, "convert-sample", to_caps, &sample); gst_caps_unref (to_caps); if (!sample) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Failed to convert video frame"); + "Failed to retrieve or convert video frame"); return NULL; } -- cgit v1.2.1