diff --git a/docs/gst_plugins_cache.json b/docs/gst_plugins_cache.json index 8efa45201b..95d56a2685 100644 --- a/docs/gst_plugins_cache.json +++ b/docs/gst_plugins_cache.json @@ -12630,6 +12630,105 @@ "multifile": { "description": "Reads/Writes buffers from/to sequentially named files", "elements": { + "imagesequencesrc": { + "author": "Cesar Fabian Orccon Chipana \nThibault Saunier ", + "description": "Create a video stream from a sequence of image files", + "hierarchy": [ + "GstImageSequenceSrc", + "GstPushSrc", + "GstBaseSrc", + "GstElement", + "GstObject", + "GInitiallyUnowned", + "GObject" + ], + "klass": "Source/File/Video", + "long-name": "Image Sequence Source", + "pad-templates": { + "src": { + "caps": "ANY", + "direction": "src", + "presence": "always" + } + }, + "properties": { + "blocksize": { + "blurb": "Size in bytes to read per buffer (-1 = default)", + "construct": false, + "construct-only": false, + "default": "4096", + "max": "-1", + "min": "0", + "type-name": "guint", + "writable": true + }, + "do-timestamp": { + "blurb": "Apply current stream time to buffers", + "construct": false, + "construct-only": false, + "default": "false", + "type-name": "gboolean", + "writable": true + }, + "framerate": { + "blurb": "The output framerate.", + "construct": false, + "construct-only": false, + "default": "30/1", + "max": "2147483647/1", + "min": "1/1", + "type-name": "GstFraction", + "writable": true + }, + "location": { + "blurb": "Pattern to create file names of input files. File names are created by calling sprintf() with the pattern and the current index.", + "construct": false, + "construct-only": false, + "default": "NULL", + "type-name": "gchararray", + "writable": true + }, + "num-buffers": { + "blurb": "Number of buffers to output before sending EOS (-1 = unlimited)", + "construct": false, + "construct-only": false, + "default": "-1", + "max": "2147483647", + "min": "-1", + "type-name": "gint", + "writable": true + }, + "start-index": { + "blurb": "Start value of index. The initial value of index can be set either by setting index or start-index. When the end of the loop is reached, the index will be set to the value start-index.", + "construct": false, + "construct-only": false, + "default": "0", + "max": "2147483647", + "min": "0", + "type-name": "gint", + "writable": true + }, + "stop-index": { + "blurb": "Stop value of index. The special value -1 means no stop.", + "construct": false, + "construct-only": false, + "default": "-1", + "max": "2147483647", + "min": "-1", + "type-name": "gint", + "writable": true + }, + "typefind": { + "blurb": "Run typefind before negotiating (deprecated, non-functional)", + "construct": false, + "construct-only": false, + "default": "false", + "type-name": "gboolean", + "writable": true + } + }, + "rank": "none" + }, "multifilesink": { "author": "David Schleef ", "description": "Write buffers to a sequentially named set of files", diff --git a/gst/multifile/gstimagesequencesrc.c b/gst/multifile/gstimagesequencesrc.c index e33b574cbd..9d4d06c2eb 100644 --- a/gst/multifile/gstimagesequencesrc.c +++ b/gst/multifile/gstimagesequencesrc.c @@ -1,5 +1,7 @@ /* GStreamer + * Copyright (C) 2006 David A. Schleef ds@schleef.org * Copyright (C) 2019 Cesar Fabian Orccon Chipana + * Copyright (C) 2020 Thibault Saunier * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -18,27 +20,41 @@ */ /** - * SECTION:element-gstimagesequencesrc + * SECTION:element-imagesequencesrc * * Stream image sequences from image files. - * * - * |[ - * gst-launch-1.0 imagesequencesrc location=%d.jpg start-index=1 stop-index=50 framerate=24/1 ! decodebin ! videoconvert ! autovideosink - * ]| + * ``` + * gst-launch-1.0 imagesequencesrc location=image-%05d.jpg start-index=1 stop-index=50 framerate=24/1 ! decodebin ! videoconvert ! autovideosink + * ``` * - * + * This elements implements the #GstURIHandler interface meaning that you can use it with playbin, + * (make sure to quote the URI for the filename pattern, like: `%2505d` instead of the `%05d` you would use + * when dealing with the location). + * + * Note that you can pass the #imagesequencesrc:framerate, #imagesequencesrc:start-index and #imagesequencesrc:stop-index + * properties directly in the URI using its 'query' component, for example: + * + * ``` + * gst-launch-1.0 playbin uri="imagesequence://path/to/image-%2505d.jpeg?start-index=0&framerate=30/1" + * ``` */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + #include #include #include "gstimagesequencesrc.h" +#define LOCK(self) (g_rec_mutex_lock (&self->fields_lock)) +#define UNLOCK(self) (g_rec_mutex_unlock (&self->fields_lock)) + static GstFlowReturn gst_image_sequence_src_create (GstPushSrc * src, GstBuffer ** buffer); -static void gst_image_sequence_src_dispose (GObject * object); static void gst_image_sequence_src_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); @@ -48,10 +64,11 @@ static GstCaps *gst_image_sequence_src_getcaps (GstBaseSrc * src, GstCaps * filter); static gboolean gst_image_sequence_src_query (GstBaseSrc * src, GstQuery * query); -static void gst_image_sequence_src_set_caps (GstImageSequenceSrc * src, +static void gst_image_sequence_src_set_caps (GstImageSequenceSrc * self, GstCaps * caps); -static void gst_image_sequence_src_set_duration (GstImageSequenceSrc * src); -static gint gst_image_sequence_src_count_frames (GstImageSequenceSrc * self); +static void gst_image_sequence_src_set_duration (GstImageSequenceSrc * self); +static gint gst_image_sequence_src_count_frames (GstImageSequenceSrc * self, + gboolean can_read); static GstStaticPadTemplate gst_image_sequence_src_pad_template = @@ -67,17 +84,147 @@ enum { PROP_0, PROP_LOCATION, - PROP_INDEX, PROP_START_INDEX, PROP_STOP_INDEX, PROP_FRAMERATE }; #define DEFAULT_LOCATION "%05d" -#define DEFAULT_INDEX 0 +#define DEFAULT_START_INDEX 0 +#define DEFAULT_STOP_INDEX -1 +#define DEFAULT_FRAMERATE 30 + +/* Call with LOCK taken */ +static gboolean +gst_image_sequence_src_set_location (GstImageSequenceSrc * self, + const gchar * location) +{ + g_free (self->path); + if (location != NULL) + self->path = g_strdup (location); + else + self->path = NULL; + + return TRUE; +} + +/*** GSTURIHANDLER INTERFACE *************************************************/ + +static GstURIType +gst_image_sequence_src_uri_get_type (GType type) +{ + return GST_URI_SRC; +} + +static const gchar *const * +gst_image_sequence_src_uri_get_protocols (GType type) +{ + static const gchar *protocols[] = { "imagesequence", NULL }; + + return protocols; +} + +static gchar * +gst_image_sequence_src_uri_get_uri (GstURIHandler * handler) +{ + GstImageSequenceSrc *self = GST_IMAGE_SEQUENCE_SRC (handler); + gchar *uri = NULL; + + LOCK (self); + if (self->uri) + uri = gst_uri_to_string (self->uri); + else if (self->path) + uri = gst_uri_construct ("imagesequence", self->path); + UNLOCK (self); + + return uri; +} + +static gboolean +gst_image_sequence_src_uri_set_uri (GstURIHandler * handler, const gchar * uri, + GError ** err) +{ + gchar *hostname = NULL, *location = NULL, *tmp; + gboolean ret = FALSE; + GstImageSequenceSrc *self = GST_IMAGE_SEQUENCE_SRC (handler); + GstUri *ruri = gst_uri_from_string (uri); + GHashTable *query = NULL; + + if (!ruri) { + g_set_error (err, GST_URI_ERROR, GST_URI_ERROR_BAD_URI, + "imagesequencesrc URI is invalid: '%s'", uri); + goto beach; + } + + + LOCK (self); + g_clear_pointer (&self->uri, gst_uri_unref); + self->uri = ruri; + tmp = gst_filename_to_uri (gst_uri_get_path (ruri), err); + location = g_filename_from_uri (tmp, &hostname, err); + g_free (tmp); + query = gst_uri_get_query_table (ruri); + if (!location || (err != NULL && *err != NULL)) { + GST_WARNING_OBJECT (self, "Invalid URI '%s' for imagesequencesrc: %s", uri, + (err != NULL && *err != NULL) ? (*err)->message : "unknown error"); + goto beach; + } + + if (hostname && strcmp (hostname, "localhost")) { + /* Only 'localhost' is permitted */ + GST_WARNING_OBJECT (self, "Invalid hostname '%s' for filesrc", hostname); + g_set_error (err, GST_URI_ERROR, GST_URI_ERROR_BAD_URI, + "File URI with invalid hostname '%s'", hostname); + goto beach; + } +#ifdef G_OS_WIN32 + /* Unfortunately, g_filename_from_uri() doesn't handle some UNC paths + * correctly on windows, it leaves them with an extra backslash + * at the start if they're of the mozilla-style file://///host/path/file + * form. Correct this. + */ + if (location[0] == '\\' && location[1] == '\\' && location[2] == '\\') + memmove (location, location + 1, strlen (location + 1) + 1); +#endif + + ret = gst_image_sequence_src_set_location (self, location); + + if (query) { + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init (&iter, query); + while (g_hash_table_iter_next (&iter, &key, &value)) { + GST_INFO_OBJECT (self, "Setting property from URI: %s=%s", (gchar *) key, + (gchar *) value); + gst_util_set_object_arg (G_OBJECT (self), key, value); + } + } + +beach: + UNLOCK (self); + + g_free (location); + g_free (hostname); + g_clear_pointer (&query, g_hash_table_unref); + + return ret; +} + +static void +gst_image_sequence_src_uri_handler_init (gpointer g_iface, gpointer iface_data) +{ + GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface; + + iface->get_type = gst_image_sequence_src_uri_get_type; + iface->get_protocols = gst_image_sequence_src_uri_get_protocols; + iface->get_uri = gst_image_sequence_src_uri_get_uri; + iface->set_uri = gst_image_sequence_src_uri_set_uri; +} #define gst_image_sequence_src_parent_class parent_class #define _do_init \ + G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER, gst_image_sequence_src_uri_handler_init); \ GST_DEBUG_CATEGORY_INIT (gst_image_sequence_src_debug, "imagesequencesrc", \ 0, "imagesequencesrc element"); G_DEFINE_TYPE_WITH_CODE (GstImageSequenceSrc, gst_image_sequence_src, @@ -88,7 +235,7 @@ is_seekable (GstBaseSrc * src) { GstImageSequenceSrc *self = GST_IMAGE_SEQUENCE_SRC (src); - if ((self->count_frames != 0) && (self->fps_n) && (self->fps_d)) + if ((self->n_frames != 0) && (self->fps_n) && (self->fps_d)) return TRUE; return FALSE; } @@ -97,27 +244,44 @@ is_seekable (GstBaseSrc * src) static gboolean do_seek (GstBaseSrc * bsrc, GstSegment * segment) { - GstImageSequenceSrc *src; - gboolean reverse; + GstImageSequenceSrc *self; - src = GST_IMAGE_SEQUENCE_SRC (bsrc); + self = GST_IMAGE_SEQUENCE_SRC (bsrc); - reverse = segment->rate < 0; - - if (reverse) { - GST_FIXME_OBJECT (src, "Handle reverse playback"); - return FALSE; + self->reverse = segment->rate < 0; + if (self->reverse) { + segment->time = segment->start; } - segment->time = segment->start; - src->index = src->start_index + - segment->position * src->fps_n / (src->fps_d * GST_SECOND); - - GST_DEBUG_OBJECT (src, "Seek to frame at index %d.\n", src->index); + self->index = + self->start_index + + segment->position * self->fps_n / (self->fps_d * GST_SECOND); return TRUE; } +static void +gst_image_sequence_src_finalize (GObject * object) +{ + GstImageSequenceSrc *self = GST_IMAGE_SEQUENCE_SRC (object); + + g_clear_pointer (&self->path, g_free); + g_rec_mutex_clear (&self->fields_lock); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_image_sequence_src_dispose (GObject * object) +{ + GstImageSequenceSrc *self = GST_IMAGE_SEQUENCE_SRC (object); + + gst_clear_caps (&self->caps); + g_clear_pointer (&self->uri, gst_uri_unref); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + static void gst_image_sequence_src_class_init (GstImageSequenceSrcClass * klass) { @@ -129,35 +293,32 @@ gst_image_sequence_src_class_init (GstImageSequenceSrcClass * klass) gobject_class->set_property = gst_image_sequence_src_set_property; gobject_class->get_property = gst_image_sequence_src_get_property; + g_object_class_install_property (gobject_class, PROP_LOCATION, g_param_spec_string ("location", "File Location", "Pattern to create file names of input files. File names are " "created by calling sprintf() with the pattern and the current " "index.", DEFAULT_LOCATION, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_INDEX, - g_param_spec_int ("index", "File Index", - "Index to use with location property to create file names. The " - "index is incremented by one for each buffer read.", - 0, INT_MAX, DEFAULT_INDEX, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_START_INDEX, g_param_spec_int ("start-index", "Start Index", "Start value of index. The initial value of index can be set " "either by setting index or start-index. When the end of the loop " "is reached, the index will be set to the value start-index.", - 0, INT_MAX, DEFAULT_INDEX, + 0, INT_MAX, DEFAULT_START_INDEX, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_STOP_INDEX, g_param_spec_int ("stop-index", "Stop Index", "Stop value of index. The special value -1 means no stop.", - -1, INT_MAX, DEFAULT_INDEX, + -1, INT_MAX, DEFAULT_STOP_INDEX, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_FRAMERATE, gst_param_spec_fraction ("framerate", "Framerate", - "Set the framerate to internal caps.", - 1, 1, G_MAXINT, 1, 1, 1, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)); + "The output framerate.", + 1, 1, G_MAXINT, 1, DEFAULT_FRAMERATE, 1, + G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)); + gobject_class->finalize = gst_image_sequence_src_finalize; gobject_class->dispose = gst_image_sequence_src_dispose; gstbasesrc_class->get_caps = gst_image_sequence_src_getcaps; @@ -170,9 +331,10 @@ gst_image_sequence_src_class_init (GstImageSequenceSrcClass * klass) gst_element_class_add_static_pad_template (gstelement_class, &gst_image_sequence_src_pad_template); gst_element_class_set_static_metadata (gstelement_class, - "ImageSequenceSrc plugin", "Src/File", - "Creates an image-sequence video stream", - "Cesar Fabian Orccon Chipana "); + "Image Sequence Source", "Source/File/Video", + "Create a video stream from a sequence of image files", + "Cesar Fabian Orccon Chipana \n" + "Thibault Saunier "); } static void @@ -185,25 +347,16 @@ gst_image_sequence_src_init (GstImageSequenceSrc * self) bsrc = GST_BASE_SRC (self); gst_base_src_set_format (bsrc, GST_FORMAT_TIME); - self->start_index = DEFAULT_INDEX; - self->index = DEFAULT_INDEX; - self->stop_index = -1; - self->filename = g_strdup (DEFAULT_LOCATION); + + g_rec_mutex_init (&self->fields_lock); + self->start_index = DEFAULT_START_INDEX; + self->index = 0; + self->stop_index = DEFAULT_STOP_INDEX; + self->path = NULL; self->caps = NULL; - self->count_frames = 0; - self->fps_n = self->fps_d = 1; -} - -static void -gst_image_sequence_src_dispose (GObject * object) -{ - GstImageSequenceSrc *src = GST_IMAGE_SEQUENCE_SRC (object); - - g_clear_pointer (&src->filename, g_free); - if (src->caps) - gst_clear_caps (&src->caps); - - G_OBJECT_CLASS (parent_class)->dispose (object); + self->n_frames = 0; + self->fps_n = 30; + self->fps_d = 1; } static GstCaps * @@ -211,7 +364,7 @@ gst_image_sequence_src_getcaps (GstBaseSrc * src, GstCaps * filter) { GstImageSequenceSrc *self = GST_IMAGE_SEQUENCE_SRC (src); - GST_DEBUG_OBJECT (src, "returning %" GST_PTR_FORMAT, self->caps); + GST_DEBUG_OBJECT (self, "returning %" GST_PTR_FORMAT, self->caps); if (filter) { if (self->caps) @@ -228,9 +381,9 @@ static gboolean gst_image_sequence_src_query (GstBaseSrc * bsrc, GstQuery * query) { gboolean ret; - GstImageSequenceSrc *src; + GstImageSequenceSrc *self; - src = GST_IMAGE_SEQUENCE_SRC (bsrc); + self = GST_IMAGE_SEQUENCE_SRC (bsrc); switch (GST_QUERY_TYPE (query)) { case GST_QUERY_DURATION: @@ -241,8 +394,16 @@ gst_image_sequence_src_query (GstBaseSrc * bsrc, GstQuery * query) switch (format) { case GST_FORMAT_TIME: - if (src->count_frames > 0) - gst_query_set_duration (query, format, src->duration); + LOCK (self); + if (self->n_frames <= 0) { + gst_image_sequence_src_count_frames (self, FALSE); + gst_image_sequence_src_set_duration (self); + } + + if (self->n_frames > 0) + gst_query_set_duration (query, format, self->duration); + UNLOCK (self); + ret = TRUE; break; default: @@ -258,46 +419,24 @@ gst_image_sequence_src_query (GstBaseSrc * bsrc, GstQuery * query) return ret; } -static gboolean -gst_image_sequence_src_set_location (GstImageSequenceSrc * src, - const gchar * location) -{ - g_free (src->filename); - if (location != NULL) - src->filename = g_strdup (location); - else - src->filename = NULL; - - return TRUE; -} - static void gst_image_sequence_src_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstImageSequenceSrc *self = GST_IMAGE_SEQUENCE_SRC (object); + LOCK (self); switch (prop_id) { case PROP_LOCATION: gst_image_sequence_src_set_location (self, g_value_get_string (value)); break; - case PROP_INDEX: - GST_OBJECT_LOCK (self); - /* index was really meant to be read-only, but for backwards-compatibility - * we set start_index to make it work as it used to */ - if (!GST_OBJECT_FLAG_IS_SET (self, GST_BASE_SRC_FLAG_STARTED)) - self->start_index = g_value_get_int (value); - else - self->index = g_value_get_int (value); - GST_OBJECT_UNLOCK (self); - break; case PROP_START_INDEX: self->start_index = g_value_get_int (value); - gst_image_sequence_src_count_frames (self); + gst_image_sequence_src_count_frames (self, FALSE); break; case PROP_STOP_INDEX: self->stop_index = g_value_get_int (value); - gst_image_sequence_src_count_frames (self); + gst_image_sequence_src_count_frames (self, FALSE); break; case PROP_FRAMERATE: self->fps_n = gst_value_get_fraction_numerator (value); @@ -307,44 +446,65 @@ gst_image_sequence_src_set_property (GObject * object, guint prop_id, G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } + UNLOCK (self); } static void gst_image_sequence_src_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { - GstImageSequenceSrc *src = GST_IMAGE_SEQUENCE_SRC (object); + GstImageSequenceSrc *self = GST_IMAGE_SEQUENCE_SRC (object); + LOCK (self); switch (prop_id) { case PROP_LOCATION: - g_value_set_string (value, src->filename); - break; - case PROP_INDEX: - g_value_set_int (value, src->index); + g_value_set_string (value, self->path); break; case PROP_START_INDEX: - g_value_set_int (value, src->start_index); + g_value_set_int (value, self->start_index); break; case PROP_STOP_INDEX: - g_value_set_int (value, src->stop_index); + g_value_set_int (value, self->stop_index); break; case PROP_FRAMERATE: - src->fps_n = gst_value_get_fraction_numerator (value); - src->fps_d = gst_value_get_fraction_denominator (value); - GST_DEBUG_OBJECT (src, "Set (framerate) property to (%d/%d)", src->fps_n, - src->fps_d); + self->fps_n = gst_value_get_fraction_numerator (value); + self->fps_d = gst_value_get_fraction_denominator (value); + GST_DEBUG_OBJECT (self, "Set (framerate) property to (%d/%d)", + self->fps_n, self->fps_d); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } + UNLOCK (self); } +/* Call with LOCK */ static gint -gst_image_sequence_src_count_frames (GstImageSequenceSrc * self) +gst_image_sequence_src_count_frames (GstImageSequenceSrc * self, + gboolean can_read) { - self->count_frames = self->stop_index - self->start_index + 1; - return self->count_frames; + if (can_read && self->stop_index < 0 && self->path) { + gint i; + + for (i = self->start_index;; i++) { + gchar *filename = g_strdup_printf (self->path, i); + + if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR)) { + i--; + g_free (filename); + break; + } + + g_free (filename); + } + if (i > self->start_index) + self->stop_index = i; + } + + if (self->stop_index >= self->start_index) + self->n_frames = self->stop_index - self->start_index + 1; + return self->n_frames; } static void @@ -355,36 +515,50 @@ gst_image_sequence_src_set_caps (GstImageSequenceSrc * self, GstCaps * caps) g_assert (caps != NULL); new_caps = gst_caps_copy (caps); - if (self->count_frames > 0) { + if (self->n_frames > 0) { GValue fps = G_VALUE_INIT; g_value_init (&fps, GST_TYPE_FRACTION); gst_value_set_fraction (&fps, self->fps_n, self->fps_d); gst_caps_set_value (new_caps, "framerate", &fps); + g_value_unset (&fps); } gst_caps_replace (&self->caps, new_caps); gst_pad_set_caps (GST_BASE_SRC_PAD (self), new_caps); - GST_DEBUG_OBJECT (self, "Setting new caps: %s", - gst_caps_to_string (new_caps)); + GST_DEBUG_OBJECT (self, "Setting new caps: %" GST_PTR_FORMAT, new_caps); } +/* Call with LOCK */ static void gst_image_sequence_src_set_duration (GstImageSequenceSrc * self) { + GstClockTime old_duration = self->duration; + + if (self->n_frames <= 0) + return; + /* Calculate duration */ self->duration = - gst_util_uint64_scale (GST_SECOND * self->count_frames, self->fps_d, + gst_util_uint64_scale (GST_SECOND * self->n_frames, self->fps_d, self->fps_n); + + if (self->duration != old_duration) { + UNLOCK (self); + gst_element_post_message (GST_ELEMENT (self), + gst_message_new_duration_changed (GST_OBJECT (self))); + LOCK (self); + } } +/* Call with LOCK */ static gchar * gst_image_sequence_src_get_filename (GstImageSequenceSrc * self) { gchar *filename; GST_DEBUG ("Reading filename at index %d.", self->index); - filename = g_strdup_printf (self->filename, self->index); + filename = g_strdup_printf (self->path, self->index); return filename; } @@ -399,58 +573,70 @@ gst_image_sequence_src_create (GstPushSrc * src, GstBuffer ** buffer) GstBuffer *buf; gboolean ret; GError *error = NULL; + gint fps_n, fps_d, start_index, stop_index; self = GST_IMAGE_SEQUENCE_SRC (src); - if (self->index > self->stop_index) + LOCK (self); + start_index = self->start_index; + stop_index = self->stop_index; + if (self->index > stop_index && stop_index > 0) { + UNLOCK (self); + return GST_FLOW_EOS; + } if (self->index < self->start_index) self->index = self->start_index; - g_assert (self->start_index <= self->index && - self->index <= self->stop_index); + g_assert (start_index <= self->index && + (self->index <= stop_index || stop_index <= 0)); filename = gst_image_sequence_src_get_filename (self); + fps_n = self->fps_n; + fps_d = self->fps_d; + UNLOCK (self); - GST_DEBUG_OBJECT (self, "reading from file \"%s\".", filename); + if (!filename) + goto handle_error; ret = g_file_get_contents (filename, &data, &size, &error); if (!ret) goto handle_error; - buf = gst_buffer_new_wrapped (data, size); + buf = gst_buffer_new_wrapped_full (0, data, size, 0, size, NULL, g_free); if (!self->caps) { GstCaps *caps; caps = gst_type_find_helper_for_buffer (NULL, buf, NULL); - gst_image_sequence_src_set_caps (self, caps); + if (!caps) { + GST_ELEMENT_ERROR (self, STREAM, TYPE_NOT_FOUND, (NULL), + ("Could not determine image type.")); + + return GST_FLOW_NOT_SUPPORTED; + } + + LOCK (self); + gst_image_sequence_src_count_frames (self, TRUE); gst_image_sequence_src_set_duration (self); + UNLOCK (self); + + gst_image_sequence_src_set_caps (self, caps); gst_caps_unref (caps); } - self->buffer_duration = GST_SECOND * self->fps_d / self->fps_n; - GST_BUFFER_PTS (buf) = - (self->index - self->start_index) * self->buffer_duration; - GST_BUFFER_DURATION (buf) = self->buffer_duration; - - GST_DEBUG_OBJECT (self, "index: %d", self->index); - - GST_DEBUG_OBJECT (self, "Timestamp: %" GST_TIME_FORMAT, - GST_TIME_ARGS (GST_BUFFER_PTS (buf))); - GST_DEBUG_OBJECT (self, "Buffer duration: %" GST_TIME_FORMAT, - GST_TIME_ARGS (GST_BUFFER_DURATION (buf))); - - GST_BUFFER_OFFSET (buf) = self->index - self->start_index; - GST_BUFFER_OFFSET_END (buf) = self->index - self->start_index + 1; - - GST_DEBUG_OBJECT (self, "read file \"%s\".", filename); + gst_util_uint64_scale_ceil ((self->index - start_index) * GST_SECOND, + fps_d, fps_n); + GST_BUFFER_DURATION (buf) = gst_util_uint64_scale (GST_SECOND, fps_d, fps_n); + GST_BUFFER_OFFSET (buf) = self->index - start_index; + GST_LOG_OBJECT (self, "index: %d, %s - %" GST_PTR_FORMAT, self->index, + filename, buf); g_free (filename); *buffer = buf; - self->index++; + self->index += self->reverse ? -1 : 1; return GST_FLOW_OK; handle_error: diff --git a/gst/multifile/gstimagesequencesrc.h b/gst/multifile/gstimagesequencesrc.h index d84ec27634..f2d9db228b 100644 --- a/gst/multifile/gstimagesequencesrc.h +++ b/gst/multifile/gstimagesequencesrc.h @@ -1,5 +1,6 @@ /* GStreamer * Copyright (C) 2019 Cesar Fabian Orccon Chipana + * Copyright (C) 2020 Thibault Saunier * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -25,44 +26,29 @@ G_BEGIN_DECLS -#define GST_TYPE_IMAGE_SEQUENCE_SRC \ - (gst_image_sequence_src_get_type()) -#define GST_IMAGE_SEQUENCE_SRC(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_IMAGE_SEQUENCE_SRC,GstImageSequenceSrc)) -#define GST_IMAGE_SEQUENCE_SRC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_IMAGE_SEQUENCE_SRC,GstImageSequenceSrcClass)) -#define GST_IS_IMAGE_SEQUENCE_SRC(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_IMAGE_SEQUENCE_SRC)) -#define GST_IS_IMAGE_SEQUENCE_SRC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_IMAGE_SEQUENCE_SRC)) - -typedef struct _GstImageSequenceSrc GstImageSequenceSrc; -typedef struct _GstImageSequenceSrcClass GstImageSequenceSrcClass; +#define GST_TYPE_IMAGE_SEQUENCE_SRC (gst_image_sequence_src_get_type()) +G_DECLARE_FINAL_TYPE(GstImageSequenceSrc, gst_image_sequence_src, GST, IMAGE_SEQUENCE_SRC, GstPushSrc) struct _GstImageSequenceSrc { GstPushSrc parent; - gchar *filename; - int start_index; - int stop_index; - int index; - int count_frames; + GRecMutex fields_lock; + gchar* path; + GstUri *uri; + gint start_index; + gint stop_index; + gint index; + gint n_frames; guint64 duration; - guint64 buffer_duration; + gboolean reverse; GstCaps *caps; gint fps_n, fps_d; }; -struct _GstImageSequenceSrcClass -{ - GstPushSrcClass parent_class; -}; - -GType gst_image_sequence_src_get_type (void); G_END_DECLS diff --git a/gst/multifile/gstmultifile.c b/gst/multifile/gstmultifile.c index 6cbb655f5d..1d22f69907 100644 --- a/gst/multifile/gstmultifile.c +++ b/gst/multifile/gstmultifile.c @@ -33,6 +33,7 @@ #include "gstsplitfilesrc.h" #include "gstsplitmuxsink.h" #include "gstsplitmuxsrc.h" +#include "gstimagesequencesrc.h" static gboolean plugin_init (GstPlugin * plugin) @@ -43,6 +44,8 @@ plugin_init (GstPlugin * plugin) gst_multi_file_sink_get_type ()); gst_element_register (plugin, "splitfilesrc", GST_RANK_NONE, gst_split_file_src_get_type ()); + gst_element_register (plugin, "imagesequencesrc", GST_RANK_NONE, + gst_image_sequence_src_get_type ()); if (!register_splitmuxsink (plugin)) return FALSE; diff --git a/gst/multifile/meson.build b/gst/multifile/meson.build index 2ced699568..b7215f301b 100644 --- a/gst/multifile/meson.build +++ b/gst/multifile/meson.build @@ -8,6 +8,7 @@ multifile_sources = [ 'gstsplitmuxsrc.c', 'gstsplitutils.c', 'patternspec.c', + 'gstimagesequencesrc.c', ] gstmultifile = library('gstmultifile',