From 90f78abd15e5b99524ee16384d3d134a7e29c877 Mon Sep 17 00:00:00 2001 From: Edgard Lima Date: Wed, 23 Jan 2008 16:35:34 +0000 Subject: Add some documentation. Original commit message from CVS: Add some documentation. --- ext/metadata/TODO | 1 + ext/metadata/gstbasemetadata.c | 1760 +++++++++++++++++++++++---------------- ext/metadata/gstbasemetadata.h | 30 +- ext/metadata/gstmetadatademux.c | 224 +++-- ext/metadata/gstmetadatademux.h | 17 +- ext/metadata/gstmetadatamux.c | 236 ++++-- ext/metadata/gstmetadatamux.h | 13 +- ext/metadata/metadatatags.c | 60 +- 8 files changed, 1404 insertions(+), 937 deletions(-) (limited to 'ext/metadata') diff --git a/ext/metadata/TODO b/ext/metadata/TODO index 35c7eb8a..c6f837f5 100644 --- a/ext/metadata/TODO +++ b/ext/metadata/TODO @@ -22,6 +22,7 @@ OPEN ISSUES: 4- Add GST_TYPE_FRACTION support for GStreamer TAGS 5- currently, in JPEG files, if there is a Photoshop segment, everything inside it but IPTC will be lost. From the point of view of implementation it is easy, but I still don't now how to solve from the point of view of "designing". Anyway I think it is not so important. 6- language is not considered in XMP (How to do it with GStreamer?) +7- Add a helper function that convert from value to string. For example, aperture-size is 2.7 and the app wants to show "f/2.7", 'contrast' is a range and the app wants to show as "Normal", "Soft", "Hard". For the time being it is up to the APP to make this conversion. KNOWN BUGS diff --git a/ext/metadata/gstbasemetadata.c b/ext/metadata/gstbasemetadata.c index 75b515fd..011cd5ba 100644 --- a/ext/metadata/gstbasemetadata.c +++ b/ext/metadata/gstbasemetadata.c @@ -42,16 +42,57 @@ */ /** - * SECTION:metadatabase-metadata + * SECTION: gstbasemetadata + * @short_description: Base class for metadata handling elements + * + * This is a generice base class for metadata handling elements. The following + * types of elements are supported: + * + * parsers (only parse data) + * demuxers (parse data and remove metadata chunks) + * + * muxers + * any other kind of element that wants to handle chunks in + * file formats based on chunks + * * * - * Example launch line * - * - * gst-launch -v -m filesrc location=./test.jpeg ! metadatabase ! fakesink silent=TRUE - * + * This a abstract element that parses a stream and find chunks and offset + * where new chunks could be injected into it. + * Basically, the only thing need by implementors (parsers, (de)muxers) is to + * set virtual methods that will be used to setup the caps and do some + * 'processing', which is called after the stream is completely parsed and + * before the first buffer is sent to the next element. Usualy the 'processing' + * function will send tags messages and event in case of parsers and demuxers, + * and add chunks to be injected (using the helper function + * #gst_base_metadata_update_inject_segment_with_new_data in case of muxers. + * + * + * This can work in 'pull' and 'push' scheduling modes. In case of push mode, + * the stream will be parsed during sink activation if #gst_pad_get_range is + * available on upstream element. If get_range function is not available + * upstream, then the data will be hold until the stream is completly parsed, + * and then after that the fisrt buffer will be pushed downstream. The same + * happens with pull mode, if the downstream element calls gst_pad_get_range + * it will only gets data after the stream has been completely parsed. + * + * + * Seek, and query duration will only be available after the stream has been + * parsed. Query position will always be 0 (zero) before the stream is + * completely parsed. + * + * + * Currently there is implementation for JPEG and PNG (demux mode only) + * stream types and EXIF, IPTC and XMP metadatas. * * + * + * Last reviewed on 2008-01-21 (0.10.15) + */ + +/* + * includes */ #ifdef HAVE_CONFIG_H @@ -62,24 +103,16 @@ #include "gstbasemetadata.h" -#include "metadataexif.h" - -#include "metadataiptc.h" - #include "metadataxmp.h" #include -GST_DEBUG_CATEGORY_STATIC (gst_base_metadata_debug); -#define GST_CAT_DEFAULT gst_base_metadata_debug - -#define GOTO_DONE_IF_NULL(ptr) do { if ( NULL == (ptr) ) goto done; } while(FALSE) -#define GOTO_DONE_IF_NULL_AND_FAIL(ptr, ret) do { if ( NULL == (ptr) ) { (ret) = FALSE; goto done; } } while(FALSE) +/* + * enum and types + */ -/* Filter signals and args */ enum { - /* FILL ME */ LAST_SIGNAL }; @@ -91,25 +124,88 @@ enum ARG_XMP }; +/* + * defines and static global vars + */ + +GST_DEBUG_CATEGORY_STATIC (gst_base_metadata_debug); +#define GST_CAT_DEFAULT gst_base_metadata_debug + static GstBaseMetadataClass *parent_class = NULL; +/* + * static helper functions declaration + */ + +static void gst_base_metadata_init_members (GstBaseMetadata * filter); + +static void gst_base_metadata_dispose_members (GstBaseMetadata * filter); + +const gchar *gst_base_metadata_get_type_name (int img_type); + +static gboolean gst_base_metadata_pull_range_parse (GstBaseMetadata * filter); + +static gboolean gst_base_metadata_configure_caps (GstBaseMetadata * filter); + +static gboolean gst_base_metadata_processing (GstBaseMetadata * filter); + +static void gst_base_metadata_reset_streaming (GstBaseMetadata * filter); + +static void gst_base_metadata_reset_parsing (GstBaseMetadata * filter); + +static int +gst_base_metadata_parse (GstBaseMetadata * filter, const guint8 * buf, + guint32 size); + +static gboolean +gst_base_metadata_strip_push_buffer (GstBaseMetadata * base, + gint64 offset_orig, GstBuffer ** prepend, GstBuffer ** buf); + +static int +gst_base_metadata_buf_get_intersection_seg (const gint64 offset, guint32 size, + const gint64 seg_offset, const guint32 seg_size, + gint64 * boffset, guint32 * bsize); + +static gboolean +gst_base_metadata_translate_pos_to_orig (GstBaseMetadata * base, + gint64 pos, gint64 * orig_pos, GstBuffer ** buf); + +static gboolean gst_base_metadata_calculate_offsets (GstBaseMetadata * base); + + +/* + * GObject callback functions declaration + */ + +static void gst_base_metadata_base_init (gpointer gclass); + +static void gst_base_metadata_class_init (GstBaseMetadataClass * klass); + +static void +gst_base_metadata_init (GstBaseMetadata * filter, + GstBaseMetadataClass * gclass); + static void gst_base_metadata_dispose (GObject * object); static void gst_base_metadata_finalize (GObject * object); static void gst_base_metadata_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); + static void gst_base_metadata_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); +/* + * GStreamer callback functions declaration + */ + static GstStateChangeReturn gst_base_metadata_change_state (GstElement * element, GstStateChange transition); static gboolean gst_base_metadata_src_event (GstPad * pad, GstEvent * event); -static gboolean gst_base_metadata_sink_event (GstPad * pad, GstEvent * event); -static GstFlowReturn gst_base_metadata_chain (GstPad * pad, GstBuffer * buf); +static gboolean gst_base_metadata_sink_event (GstPad * pad, GstEvent * event); static gboolean gst_base_metadata_checkgetrange (GstPad * srcpad); @@ -117,31 +213,20 @@ static GstFlowReturn gst_base_metadata_get_range (GstPad * pad, guint64 offset_orig, guint size, GstBuffer ** buf); +static GstFlowReturn gst_base_metadata_chain (GstPad * pad, GstBuffer * buf); + static gboolean gst_base_metadata_sink_activate (GstPad * pad); static gboolean gst_base_metadata_src_activate_pull (GstPad * pad, gboolean active); -static gboolean gst_base_metadata_pull_range_base (GstBaseMetadata * filter); - -static void gst_base_metadata_init_members (GstBaseMetadata * filter); -static void gst_base_metadata_dispose_members (GstBaseMetadata * filter); - -static gboolean gst_base_metadata_configure_caps (GstBaseMetadata * filter); - -static int -gst_base_metadata_parse (GstBaseMetadata * filter, const guint8 * buf, - guint32 size); - static const GstQueryType *gst_base_metadata_get_query_types (GstPad * pad); static gboolean gst_base_metadata_src_query (GstPad * pad, GstQuery * query); -static void gst_base_metadata_base_init (gpointer gclass); -static void gst_base_metadata_class_init (GstBaseMetadataClass * klass); -static void -gst_base_metadata_init (GstBaseMetadata * filter, - GstBaseMetadataClass * gclass); +/* + * GST BOILERPLATE + */ GType gst_base_metadata_get_type (void) @@ -167,166 +252,490 @@ gst_base_metadata_get_type (void) return base_metadata_type; } -/* static helper function */ - /* - * offset - offset of buffer in original stream - * size - size of buffer - * seg_offset - offset of segment in original stream - * seg_size - size of segment - * boffset - offset inside buffer where segment starts (-1 for no intersection) - * bsize - size of intersection - * seg_binter - if segment start inside buffer is zero. if segment start before - * buffer and intersect, it is the offset inside segment. - * - * ret: - * -1 - segment before buffer - * 0 - segment intersects - * 1 - segment after buffer + *static helper functions implementation */ -static int -gst_base_metadata_get_strip_seg (const gint64 offset, guint32 size, - const gint64 seg_offset, const guint32 seg_size, - gint64 * boffset, guint32 * bsize, guint32 * seg_binter) +static void +gst_base_metadata_init_members (GstBaseMetadata * filter) { - int ret = -1; - *boffset = -1; - *bsize = 0; - *seg_binter = -1; + filter->metadata = NULL; - /* all segment after buffer */ - if (seg_offset >= offset + size) { - ret = 1; - goto done; - } + filter->img_type = IMG_NONE; - if (seg_offset < offset) { - /* segment start somewhere before buffer */ + filter->duration_orig = 0; + filter->duration = 0; - /* all segment before buffer */ - if (seg_offset + seg_size <= offset) { - ret = -1; - goto done; - } + filter->state = MT_STATE_NULL; - *seg_binter = offset - seg_offset; - *boffset = 0; + filter->options = META_OPT_EXIF | META_OPT_IPTC | META_OPT_XMP; - /* FIXME : optimize to >= size -> = size */ - if (seg_offset + seg_size >= offset + size) { - /* segment cover all buffer */ - *bsize = size; - } else { - /* segment goes from start of buffer to somewhere before end */ - *bsize = seg_size - *seg_binter; - } + filter->need_processing = FALSE; - ret = 0; + filter->adapter_parsing = NULL; + filter->adapter_holding = NULL; + filter->next_offset = 0; + filter->next_size = 0; + filter->need_more_data = FALSE; + filter->offset_orig = 0; + filter->offset = 0; - } else { - /* segment start somewhere into buffer */ + filter->append_buffer = NULL; + filter->prepend_buffer = NULL; - *boffset = seg_offset - offset; - *seg_binter = 0; +} - if (seg_offset + seg_size <= offset + size) { - /* all segment into buffer */ - *bsize = seg_size; - } else { - *bsize = size - *boffset; - } +static void +gst_base_metadata_dispose_members (GstBaseMetadata * filter) +{ - ret = 0; + /* buffers used to build output buffer */ + if (filter->prepend_buffer) { + gst_buffer_unref (filter->prepend_buffer); + filter->prepend_buffer = NULL; } -done: + if (filter->append_buffer) { + gst_buffer_unref (filter->append_buffer); + filter->append_buffer = NULL; + } - return ret; + /* adapter used during parsing process */ -} + if (filter->adapter_parsing) { + gst_object_unref (filter->adapter_parsing); + filter->adapter_parsing = NULL; + } -/* - * extern functions declaration - */ + if (filter->adapter_holding) { + gst_object_unref (filter->adapter_holding); + filter->adapter_holding = NULL; + } -/* + metadata_dispose (&filter->metadata); -/* - * TRUE -> buffer striped or injeted - * FALSE -> buffer unmodified - */ +} -gboolean -gst_base_metadata_strip_push_buffer (GstBaseMetadata * base, - gint64 offset_orig, GstBuffer ** prepend, GstBuffer ** buf) +const gchar * +gst_base_metadata_get_type_name (int img_type) { - MetadataChunk *strip = META_DATA_STRIP_CHUNKS (base->metadata).chunk; - MetadataChunk *inject = META_DATA_INJECT_CHUNKS (base->metadata).chunk; - const gsize strip_len = META_DATA_STRIP_CHUNKS (base->metadata).len; - const gsize inject_len = META_DATA_INJECT_CHUNKS (base->metadata).len; - - gboolean buffer_reallocated = FALSE; + gchar *type_name = NULL; - guint32 size_buf_in = GST_BUFFER_SIZE (*buf); + switch (img_type) { + case IMG_JPEG: + type_name = "jpeg"; + break; + case IMG_PNG: + type_name = "png"; + break; + default: + type_name = "invalid type"; + break; + } + return type_name; +} - gint64 *boffset_strip = NULL; - guint32 *bsize_strip = NULL; - guint32 *seg_binter_strip = NULL; +static gboolean +gst_base_metadata_pull_range_parse (GstBaseMetadata * filter) +{ - int i, j; - gboolean need_free_strip = FALSE; + int res; + gboolean ret = TRUE; + guint32 offset = 0; + gint64 duration = 0; + GstFormat format = GST_FORMAT_BYTES; - guint32 striped_bytes = 0; - guint32 injected_bytes = 0; + if (!(ret = + gst_pad_query_peer_duration (filter->sinkpad, &format, &duration))) { + /* this should never happen, but try chain anyway */ + ret = TRUE; + goto done; + } + filter->duration_orig = duration; - guint32 prepend_size = prepend && *prepend ? GST_BUFFER_SIZE (*prepend) : 0; + if (format != GST_FORMAT_BYTES) { + /* this should never happen, but try chain anyway */ + ret = TRUE; + goto done; + } - if (inject_len) { + do { + GstFlowReturn flow; + GstBuffer *buf = NULL; - for (i = 0; i < inject_len; ++i) { - int res; + offset += filter->next_offset; - if (inject[i].offset_orig >= offset_orig) { - if (inject[i].offset_orig < offset_orig + size_buf_in) { - injected_bytes += inject[i].size; - } else { - /* segment is after size (segments are sorted) */ - break; - } + /* 'filter->next_size' only says the minimum required number of bytes. + We try provided more bytes (4096) just to avoid a lot of calls to + 'metadata_parse' + returning META_PARSING_NEED_MORE_DATA */ + if (filter->next_size < 4096) { + if (duration - offset < 4096) { + /* In case there is no 4096 bytes available upstream. + It should be done upstream but we do here for safety */ + filter->next_size = duration - offset; + } else { + filter->next_size = 4096; } } - } - - /* - * strip segments - */ - + flow = + gst_pad_pull_range (filter->sinkpad, offset, filter->next_size, &buf); + if (GST_FLOW_OK != flow) { + ret = FALSE; + goto done; + } + + res = + gst_base_metadata_parse (filter, GST_BUFFER_DATA (buf), + GST_BUFFER_SIZE (buf)); + if (res == META_PARSING_ERROR) { + ret = FALSE; + goto done; + } + + gst_buffer_unref (buf); + + } while (res == META_PARSING_NEED_MORE_DATA); + +done: + + return ret; + +} + +static gboolean +gst_base_metadata_configure_caps (GstBaseMetadata * filter) +{ + GstCaps *caps = NULL; + gboolean ret = FALSE; + gchar *mime = NULL; + GstPad *peer = NULL; + + peer = gst_pad_get_peer (filter->sinkpad); + + switch (filter->img_type) { + case IMG_JPEG: + mime = "image/jpeg"; + break; + case IMG_PNG: + mime = "image/png"; + break; + default: + goto done; + break; + } + + caps = gst_caps_new_simple (mime, NULL); + + if (!gst_pad_set_caps (peer, caps)) { + goto done; + } + + ret = gst_pad_set_caps (filter->sinkpad, caps); + +done: + + if (caps) { + gst_caps_unref (caps); + caps = NULL; + } + + if (peer) { + gst_object_unref (peer); + peer = NULL; + } + + return ret; + +} + +/* + * gst_base_metadata_processing: + * @filter: the base metadata instance + * + * Do some external 'process', if not done yet, just after parse the stream + * and before give the fisrt buffer downstream. + * Typically here the tag message and events will be sent, in case of demuxer + * and new chunks will be added in case of muxers. + * @see_also: #gst_base_metadata_calculate_offsets + * + * Returns: + * + * %TRUE no need futher processing, or the processing has + * been done + * %FALSE, if error. It is, need futher processing but + * #gst_base_metadata_calculate_offsets fails + * + * + */ + +static gboolean +gst_base_metadata_processing (GstBaseMetadata * filter) +{ + gboolean ret = TRUE; + GstBaseMetadataClass *bclass = GST_BASE_METADATA_GET_CLASS (filter); + + if (filter->need_processing) { + bclass->processing (filter); + if (gst_base_metadata_calculate_offsets (filter)) { + filter->need_processing = FALSE; + } else { + ret = FALSE; + } + } + + return ret; + +} + +/* + * gst_base_metadata_reset_streaming: + * @filter: the base metadata instance + * + * Clean the streaming process. The parser status is not changed. + * @see_also: #gst_base_metadata_reset_parsing + * + * Returns: nothing + * + */ + +static void +gst_base_metadata_reset_streaming (GstBaseMetadata * filter) +{ + filter->offset_orig = 0; + filter->offset = 0; + if (filter->adapter_holding) + gst_adapter_clear (filter->adapter_holding); +} + +/* + * gst_base_metadata_reset_parsing: + * @filter: the base metadata instance + * + * Reset the parsing process to start from the beginning again. + * @see_also: #gst_base_metadata_reset_streaming + * + * Returns: nothing + * + */ + +static void +gst_base_metadata_reset_parsing (GstBaseMetadata * filter) +{ + + + if (filter->prepend_buffer) { + gst_buffer_unref (filter->prepend_buffer); + filter->prepend_buffer = NULL; + } + + if (filter->append_buffer) { + gst_buffer_unref (filter->append_buffer); + filter->append_buffer = NULL; + } + + if (filter->adapter_parsing) + gst_adapter_clear (filter->adapter_parsing); + + if (filter->adapter_holding) + gst_adapter_clear (filter->adapter_holding); + + filter->img_type = IMG_NONE; + filter->duration_orig = 0; + filter->duration = 0; + filter->state = MT_STATE_NULL; + filter->need_processing = FALSE; + filter->next_offset = 0; + filter->next_size = 0; + filter->need_more_data = FALSE; + filter->offset_orig = 0; + filter->offset = 0; + + metadata_dispose (&filter->metadata); + +} + +/* + * gst_base_metadata_parse + * @filter: the base metadata instance + * @buf: buffer to be parsed + * @size: size of the buffer in bytes + * + * Parse the current buffer. + * This buffer must be provided by another function wich keeps track of the + * correct offset in the input stream.* + * If this function returns META_PARSING_NEED_MORE_DATA, the calling function + * must look at @filter->next_offset and @filter->next_size to jump (forward) + * in input stream to provide the correct data in @buf for the next call. + * @see_also: #gst_base_metadata_pull_range_parse #gst_base_metadata_chain + * + * Returns: + * + * %META_PARSING_ERROR if there was an error + * %META_PARSING_DONE if the parse has completly finished + * + * %META_PARSING_NEED_MORE_DATA if need another buffer + * + * + */ + +static int +gst_base_metadata_parse (GstBaseMetadata * filter, const guint8 * buf, + guint32 size) +{ + + int ret = META_PARSING_ERROR; + + filter->next_offset = 0; + filter->next_size = 0; + + ret = metadata_parse (filter->metadata, buf, size, + &filter->next_offset, &filter->next_size); + + if (ret == META_PARSING_ERROR) { + if (META_DATA_IMG_TYPE (filter->metadata) == IMG_NONE) { + /* image type not recognized */ + GST_ELEMENT_ERROR (filter, STREAM, TYPE_NOT_FOUND, (NULL), + ("Only jpeg and png are supported")); + goto done; + } + } else if (ret == META_PARSING_NEED_MORE_DATA) { + filter->need_more_data = TRUE; + } else { + filter->state = MT_STATE_PARSED; + filter->need_more_data = FALSE; + filter->need_processing = TRUE; + } + + /* reconfigure caps if it is different from type detected by 'base_metadata' + function */ + if (filter->img_type != META_DATA_IMG_TYPE (filter->metadata)) { + filter->img_type = META_DATA_IMG_TYPE (filter->metadata); + if (!gst_base_metadata_configure_caps (filter)) { + GST_ELEMENT_ERROR (filter, STREAM, FORMAT, (NULL), + ("Couldn't reconfigure caps for %s", + gst_base_metadata_get_type_name (filter->img_type))); + ret = META_PARSING_ERROR; + goto done; + } + } + +done: + + return ret; + +} + + +/* + * gst_base_metadata_strip_push_buffer: + * @base: the base metadata instance + * @offset_orig: the offset in original input stream of the buffer that will + * have data striped or injected + * @prepend: a buffer (can be NULL) that will be completely injected in the + * beginning og @buf + * @buf: a pointer to a buffer that will be modified (data striped/injected or + * prepended) + * + * Strip bytes from @buf that are part of some chunk that will be striped. Add + * a whole injected chunk if some inject chunk starts into the buffer. Prepend + * additonal data if @prepend is not NULL. After using the @prepend buffer it + * will be release and set to NULL. + * @see_also: #gst_base_metadata_get_range #gst_base_metadata_chain + * #gst_base_metadata_buf_get_intersection_seg + * + * Returns: + * + * %TRUE if the buffer has been striped or injected (@prepend + * doesn't change the return value). + * %FALSE if it hasn't changed. + * + * + */ + +static gboolean +gst_base_metadata_strip_push_buffer (GstBaseMetadata * base, + gint64 offset_orig, GstBuffer ** prepend, GstBuffer ** buf) +{ + MetadataChunk *strip = META_DATA_STRIP_CHUNKS (base->metadata).chunk; + MetadataChunk *inject = META_DATA_INJECT_CHUNKS (base->metadata).chunk; + const gsize strip_len = META_DATA_STRIP_CHUNKS (base->metadata).len; + const gsize inject_len = META_DATA_INJECT_CHUNKS (base->metadata).len; + + gboolean buffer_is_ok_to_change = FALSE; + + guint32 size_buf_in = GST_BUFFER_SIZE (*buf); + + gint64 *boffset_strip = NULL; + guint32 *bsize_strip = NULL; + + int i, j; + gboolean need_free_strip = FALSE; + + guint32 striped_bytes = 0; + guint32 injected_bytes = 0; + + guint32 prepend_size = prepend && *prepend ? GST_BUFFER_SIZE (*prepend) : 0; + + /* + * check all the 'inject chunks' having the 'original offset' starting into + * the buffer + * and calculates how many bytes will be injectd + */ + + if (inject_len) { + + for (i = 0; i < inject_len; ++i) { + int res; + + if (inject[i].offset_orig >= offset_orig) { + if (inject[i].offset_orig < offset_orig + size_buf_in) { + injected_bytes += inject[i].size; + } else { + /* segment is after size (segments are sorted) */ + break; + } + } + } + + } + + /* + * strip step + */ + if (strip_len == 0) goto inject; + /* just try to do fast allocation on stack */ if (G_UNLIKELY (strip_len > 16)) { boffset_strip = g_new (gint64, strip_len); bsize_strip = g_new (guint32, strip_len); - seg_binter_strip = g_new (guint32, strip_len); need_free_strip = TRUE; } else { boffset_strip = g_alloca (sizeof (boffset_strip[0]) * strip_len); bsize_strip = g_alloca (sizeof (bsize_strip[0]) * strip_len); - seg_binter_strip = g_alloca (sizeof (seg_binter_strip[0]) * strip_len); } memset (bsize_strip, 0x00, sizeof (bsize_strip[0]) * strip_len); + /* + * calculate the number of bytes from the buffer bytes that intersect some + * 'strip chunk'. + * also get the position and size of segments into current buffer + * (boffset_strip[i] and bsize_strip[i]) + * that will be striped + */ + for (i = 0; i < strip_len; ++i) { int res; - res = gst_base_metadata_get_strip_seg (offset_orig, size_buf_in, - strip[i].offset_orig, strip[i].size, &boffset_strip[i], &bsize_strip[i], - &seg_binter_strip[i]); + res = gst_base_metadata_buf_get_intersection_seg (offset_orig, size_buf_in, + strip[i].offset_orig, strip[i].size, + &boffset_strip[i], &bsize_strip[i]); /* segment is after size (segments are sorted) */ striped_bytes += bsize_strip[i]; @@ -340,8 +749,10 @@ gst_base_metadata_strip_push_buffer (GstBaseMetadata * base, guint8 *data; - if (!buffer_reallocated) { - buffer_reallocated = TRUE; + if (!buffer_is_ok_to_change) { + /* create a new buffer if there is no space on currect buffer or if is + read-only */ + buffer_is_ok_to_change = TRUE; if (injected_bytes + prepend_size > striped_bytes) { GstBuffer *new_buf = gst_buffer_new_and_alloc (GST_BUFFER_SIZE (*buf) + injected_bytes + @@ -365,6 +776,7 @@ gst_base_metadata_strip_push_buffer (GstBaseMetadata * base, data = GST_BUFFER_DATA (*buf); + /* strip out the bytes from buffer */ striped_bytes = 0; for (i = 0; i < strip_len; ++i) { /* intersect */ @@ -390,8 +802,10 @@ inject: guint8 *data; guint32 striped_so_far; - if (!buffer_reallocated) { - buffer_reallocated = TRUE; + if (!buffer_is_ok_to_change) { + /* create a new buffer fs there is no space on currect buffer or if is + read-only */ + buffer_is_ok_to_change = TRUE; if (injected_bytes + prepend_size > striped_bytes) { GstBuffer *new_buf = gst_buffer_new_and_alloc (GST_BUFFER_SIZE (*buf) + injected_bytes + @@ -428,6 +842,9 @@ inject: break; } + /* inject the whole inject chunk if the 'original offset' is inside the + original buffer */ + if (inject[i].offset_orig >= offset_orig) { if (inject[i].offset_orig < offset_orig + size_buf_in + striped_bytes - injected_bytes) { @@ -476,7 +893,6 @@ done: if (need_free_strip) { g_free (boffset_strip); g_free (bsize_strip); - g_free (seg_binter_strip); } return injected_bytes || striped_bytes; @@ -484,12 +900,111 @@ done: } /* - * pos - position in stream striped - * orig_pos - position in original stream - * return TRUE - position in original buffer - * FALSE - position in inserted chunk + * gst_base_metadata_buf_get_intersection_seg: + * @offset: offset of buffer in original stream + * @size: size of buffer + * @seg_offset: offset of segment in original stream + * @seg_size: size of segment + * @boffset: offset inside buffer where segment starts (-1 for no intersection) + * @bsize:size of intersection + * + * Calculate which bytes into the buffer given by @offset and @size intersetcs + * the segment given by @seg_offset and @seg_size + * @see_also: #gst_base_metadata_strip_push_buffer + * + * Returns: + * + * %-1 if the segment is completly before the buffer + * + * %0 if the segment intersects + * %1 if the segment is after the buffer + * + */ + +static int +gst_base_metadata_buf_get_intersection_seg (const gint64 offset, guint32 size, + const gint64 seg_offset, const guint32 seg_size, + gint64 * boffset, guint32 * bsize) +{ + int ret = -1; + + *boffset = -1; + *bsize = 0; + + /* all segment after buffer */ + if (seg_offset >= offset + size) { + ret = 1; + goto done; + } + + if (seg_offset < offset) { + /* segment start somewhere before buffer */ + + /* all segment before buffer */ + if (seg_offset + seg_size <= offset) { + ret = -1; + goto done; + } + + *boffset = 0; + + /* FIXME : optimize to >= size -> = size */ + if (seg_offset + seg_size >= offset + size) { + /* segment cover all buffer */ + *bsize = size; + } else { + /* segment goes from start of buffer to somewhere before end */ + *bsize = seg_size - (offset - seg_offset); + } + + ret = 0; + + } else { + /* segment start somewhere into buffer */ + + *boffset = seg_offset - offset; + + if (seg_offset + seg_size <= offset + size) { + /* all segment into buffer */ + *bsize = seg_size; + } else { + *bsize = size - *boffset; + } + + ret = 0; + + } + +done: + + return ret; + +} + +/* + * gst_base_metadata_translate_pos_to_orig: + * @base: the base metadata instance + * @pos: position in output stream + * @orig_pos: position in original stream + * @buf: if not NULL, will have data that starts at some point into a injected + * chunk + * + * Given a position in output stream (@pos), returns the position in original + * stream (@orig_pos) that contains the same data. If @pos is into a injected + * chunk, @orig_pos will contains the position just after the position in which + * the chunk has been insert and @buf (if non null) will be filled with + * injected bytes starting in @pos until the and of injected chunk. + * @see_also: #gst_base_metadata_src_event #gst_base_metadata_get_range + * + * Returns: + * + * %TRUE if @pos is in original buffer + * %FALSE if @pos is into a injected chunk + * + * */ -gboolean + +static gboolean gst_base_metadata_translate_pos_to_orig (GstBaseMetadata * base, gint64 pos, gint64 * orig_pos, GstBuffer ** buf) { @@ -585,13 +1100,24 @@ done: } /* - * return: - * -1 -> error - * 0 -> succeded - * 1 -> need more data + * gst_base_metadata_calculate_offsets: + * @base: the base metadata instance + * + * Recalculates the offset of injected chunks after take in account new sizes + * written by the 'muxers elements' and wrapped with bytes by the correct + * file type handler. + * @see_also: #gst_base_metadata_processing #metadata_lazy_update + * #gst_base_metadata_update_inject_segment_with_new_data + * + * Returns: + * + * %TRUE if succeded + * %FALSE if fails. Which happens if the stream hasn't been + * completely parsed yet + * */ -gboolean +static gboolean gst_base_metadata_calculate_offsets (GstBaseMetadata * base) { int i, j; @@ -601,9 +1127,15 @@ gst_base_metadata_calculate_offsets (GstBaseMetadata * base) MetadataChunk *inject = META_DATA_INJECT_CHUNKS (base->metadata).chunk; gsize strip_len; gsize inject_len; + gboolean ret = TRUE; + + if (base->state != MT_STATE_PARSED) { + ret = FALSE; + goto done; + } - if (base->state != MT_STATE_PARSED) - return FALSE; + metadata_chunk_array_remove_zero_size (&META_DATA_INJECT_CHUNKS (base-> + metadata)); metadata_lazy_update (base->metadata); @@ -626,7 +1158,11 @@ gst_base_metadata_calculate_offsets (GstBaseMetadata * base) bytes_inject += inject[i].size; } - /* calculate append (doesnt make much sense, but, anyway..) */ + /* FIXME: For the time being we don't have append buffers 'cause in the case + of files we are handling it is not possible to append after the last byte + on original stream (which is part of a kind of end-of-file chunk) */ +#if 0 + /* calculate append buffer */ append_size = 0; for (i = inject_len - 1; i >= 0; --i) { if (inject[i].offset_orig == base->duration_orig) @@ -649,6 +1185,7 @@ gst_base_metadata_calculate_offsets (GstBaseMetadata * base) } } } +#endif if (base->duration_orig) { base->duration = base->duration_orig; @@ -660,32 +1197,16 @@ gst_base_metadata_calculate_offsets (GstBaseMetadata * base) } } - return TRUE; - -} +done: -const gchar * -gst_base_metadata_get_type_name (int img_type) -{ - gchar *type_name = NULL; + return ret; - switch (img_type) { - case IMG_JPEG: - type_name = "jpeg"; - break; - case IMG_PNG: - type_name = "png"; - break; - default: - type_name = "invalid type"; - break; - } - return type_name; } -/* gstreamer functions */ - +/* + * GObject callback functions implementation + */ static void gst_base_metadata_base_init (gpointer gclass) @@ -694,7 +1215,6 @@ gst_base_metadata_base_init (gpointer gclass) "basemetadata element"); } -/* initialize the plugin's class */ static void gst_base_metadata_class_init (GstBaseMetadataClass * klass) { @@ -728,11 +1248,6 @@ gst_base_metadata_class_init (GstBaseMetadataClass * klass) } -/* initialize the new element - * instantiate pads and add them to element - * set functions - * initialize structure - */ static void gst_base_metadata_init (GstBaseMetadata * filter, GstBaseMetadataClass * gclass) { @@ -773,14 +1288,34 @@ gst_base_metadata_init (GstBaseMetadata * filter, GstBaseMetadataClass * gclass) GST_DEBUG_FUNCPTR (gst_base_metadata_src_activate_pull)); /* addind pads */ - gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad); - gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad); + gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad); + gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad); + + metadataparse_xmp_init (); + /* init members */ + + gst_base_metadata_init_members (filter); + +} + +static void +gst_base_metadata_dispose (GObject * object) +{ + GstBaseMetadata *filter = NULL; + + filter = GST_BASE_METADATA (object); + + gst_base_metadata_dispose_members (filter); - metadataparse_xmp_init (); - /* init members */ + metadataparse_xmp_dispose (); - gst_base_metadata_init_members (filter); + G_OBJECT_CLASS (parent_class)->dispose (object); +} +static void +gst_base_metadata_finalize (GObject * object) +{ + G_OBJECT_CLASS (parent_class)->finalize (object); } static void @@ -836,384 +1371,177 @@ gst_base_metadata_get_property (GObject * object, guint prop_id, } } -/* GstElement vmethod implementations */ - -static gboolean -gst_base_metadata_processing (GstBaseMetadata * filter) -{ - gboolean ret = TRUE; - GstBaseMetadataClass *bclass = GST_BASE_METADATA_GET_CLASS (filter); - - if (filter->need_processing) { - bclass->processing (filter); - if (gst_base_metadata_calculate_offsets (filter)) { - filter->need_processing = FALSE; - } else { - ret = FALSE; - } - } - - return ret; -} +/* + * GStreamer callback functions declaration + */ -static gboolean -gst_base_metadata_sink_event (GstPad * pad, GstEvent * event) +static GstStateChangeReturn +gst_base_metadata_change_state (GstElement * element, GstStateChange transition) { - GstBaseMetadata *filter = NULL; - gboolean ret = FALSE; - GstBaseMetadataClass *bclass; - - filter = GST_BASE_METADATA (gst_pad_get_parent (pad)); - bclass = GST_BASE_METADATA_GET_CLASS (filter); + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + GstBaseMetadata *filter = GST_BASE_METADATA (element); - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_EOS: - if (filter->need_more_data) { - GST_ELEMENT_WARNING (filter, STREAM, DECODE, (NULL), - ("Need more data. Unexpected EOS")); - } + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + gst_base_metadata_reset_parsing (filter); + metadata_init (&filter->metadata, filter->options); break; - case GST_EVENT_TAG: + case GST_STATE_CHANGE_READY_TO_PAUSED: + if (filter->metadata == NULL) + metadata_init (&filter->metadata, filter->options); break; default: break; } - ret = bclass->sink_event (pad, event); - - gst_object_unref (filter); - - return ret; - -} - - -static gboolean -gst_base_metadata_src_event (GstPad * pad, GstEvent * event) -{ - GstBaseMetadata *filter = NULL; - gboolean ret = FALSE; - - filter = GST_BASE_METADATA (gst_pad_get_parent (pad)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEEK: - { - gdouble rate; - GstFormat format; - GstSeekFlags flags; - GstSeekType start_type; - gint64 start; - GstSeekType stop_type; - gint64 stop; - - /* we don't know where are the chunks to be stripped before base */ - if (!gst_base_metadata_processing (filter)) { - ret = FALSE; - goto done; - } - - gst_event_parse_seek (event, &rate, &format, &flags, - &start_type, &start, &stop_type, &stop); - - switch (format) { - case GST_FORMAT_BYTES: - break; - case GST_FORMAT_PERCENT: - if (filter->duration < 0) - goto done; - start = start * filter->duration / 100; - stop = stop * filter->duration / 100; - break; - default: - goto done; - } - format = GST_FORMAT_BYTES; - - if (start_type == GST_SEEK_TYPE_CUR) - start = filter->offset + start; - else if (start_type == GST_SEEK_TYPE_END) { - if (filter->duration < 0) - goto done; - start = filter->duration + start; - } - start_type == GST_SEEK_TYPE_SET; - - if (filter->prepend_buffer) { - gst_buffer_unref (filter->prepend_buffer); - filter->prepend_buffer = NULL; - } - - /* FIXME: related to append */ - filter->offset = start; - gst_base_metadata_translate_pos_to_orig (filter, start, &start, - &filter->prepend_buffer); - filter->offset_orig = start; - - if (stop_type == GST_SEEK_TYPE_CUR) - stop = filter->offset + stop; - else if (stop_type == GST_SEEK_TYPE_END) { - if (filter->duration < 0) - goto done; - stop = filter->duration + stop; - } - stop_type == GST_SEEK_TYPE_SET; - - gst_base_metadata_translate_pos_to_orig (filter, stop, &stop, NULL); - - gst_event_unref (event); - event = gst_event_new_seek (rate, format, flags, - start_type, start, stop_type, stop); + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (ret == GST_STATE_CHANGE_FAILURE) + goto done; - } + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_base_metadata_reset_streaming (filter); + if (filter->state != MT_STATE_PARSED) + gst_base_metadata_reset_parsing (filter); break; default: break; } - ret = gst_pad_event_default (pad, event); - event = NULL; /* event has another owner */ - done: - if (event) { - gst_event_unref (event); - } - - gst_object_unref (filter); - return ret; - -} - -static void -gst_base_metadata_dispose (GObject * object) -{ - GstBaseMetadata *filter = NULL; - - filter = GST_BASE_METADATA (object); - - gst_base_metadata_dispose_members (filter); - - metadataparse_xmp_dispose (); - - G_OBJECT_CLASS (parent_class)->dispose (object); -} - -static void -gst_base_metadata_finalize (GObject * object) -{ - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void -gst_base_metadata_dispose_members (GstBaseMetadata * filter) -{ - - /* buffers used to build output buffer */ - - if (filter->prepend_buffer) { - gst_buffer_unref (filter->prepend_buffer); - filter->prepend_buffer = NULL; - } - - if (filter->append_buffer) { - gst_buffer_unref (filter->append_buffer); - filter->append_buffer = NULL; - } - - /* adapter used during parsing process */ - - if (filter->adapter_parsing) { - gst_object_unref (filter->adapter_parsing); - filter->adapter_parsing = NULL; - } - - if (filter->adapter_holding) { - gst_object_unref (filter->adapter_holding); - filter->adapter_holding = NULL; - } - - metadata_dispose (&filter->metadata); - -} - -static void -gst_base_metadata_init_members (GstBaseMetadata * filter) -{ - - filter->metadata = NULL; - - filter->img_type = IMG_NONE; - - filter->duration_orig = 0; - filter->duration = 0; - - filter->state = MT_STATE_NULL; - - filter->options = META_OPT_EXIF | META_OPT_IPTC | META_OPT_XMP; - - filter->need_processing = FALSE; - - filter->adapter_parsing = NULL; - filter->adapter_holding = NULL; - filter->next_offset = 0; - filter->next_size = 0; - filter->need_more_data = FALSE; - filter->offset_orig = 0; - filter->offset = 0; - - filter->append_buffer = NULL; - filter->prepend_buffer = NULL; - -} - -static void -gst_base_metadata_reset_streaming (GstBaseMetadata * filter) -{ - filter->offset_orig = 0; - filter->offset = 0; - if (filter->adapter_holding) - gst_adapter_clear (filter->adapter_holding); } -static void -gst_base_metadata_reset_parsing (GstBaseMetadata * filter) +static gboolean +gst_base_metadata_src_event (GstPad * pad, GstEvent * event) { + GstBaseMetadata *filter = NULL; + gboolean ret = FALSE; + filter = GST_BASE_METADATA (gst_pad_get_parent (pad)); - if (filter->prepend_buffer) { - gst_buffer_unref (filter->prepend_buffer); - filter->prepend_buffer = NULL; - } + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK: + { + gdouble rate; + GstFormat format; + GstSeekFlags flags; + GstSeekType start_type; + gint64 start; + GstSeekType stop_type; + gint64 stop; - if (filter->append_buffer) { - gst_buffer_unref (filter->append_buffer); - filter->append_buffer = NULL; - } + /* we don't know where are the chunks to be stripped before base */ + if (!gst_base_metadata_processing (filter)) { + ret = FALSE; + goto done; + } - if (filter->adapter_parsing) - gst_adapter_clear (filter->adapter_parsing); + gst_event_parse_seek (event, &rate, &format, &flags, + &start_type, &start, &stop_type, &stop); - if (filter->adapter_holding) - gst_adapter_clear (filter->adapter_holding); + switch (format) { + case GST_FORMAT_BYTES: + break; + case GST_FORMAT_PERCENT: + if (filter->duration < 0) + goto done; + start = start * filter->duration / 100; + stop = stop * filter->duration / 100; + break; + default: + goto done; + } + format = GST_FORMAT_BYTES; - filter->img_type = IMG_NONE; - filter->duration_orig = 0; - filter->duration = 0; - filter->state = MT_STATE_NULL; - filter->need_processing = FALSE; - filter->next_offset = 0; - filter->next_size = 0; - filter->need_more_data = FALSE; - filter->offset_orig = 0; - filter->offset = 0; + if (start_type == GST_SEEK_TYPE_CUR) + start = filter->offset + start; + else if (start_type == GST_SEEK_TYPE_END) { + if (filter->duration < 0) + goto done; + start = filter->duration + start; + } + start_type == GST_SEEK_TYPE_SET; - metadata_dispose (&filter->metadata); + if (filter->prepend_buffer) { + gst_buffer_unref (filter->prepend_buffer); + filter->prepend_buffer = NULL; + } -} + /* FIXME: if some chunk is injected after the end of original file + (which is not the case for any file type currently handled), appending + a buffer should be taken in account + */ -static gboolean -gst_base_metadata_configure_caps (GstBaseMetadata * filter) -{ - GstCaps *caps = NULL; - gboolean ret = FALSE; - gchar *mime = NULL; - GstPad *peer = NULL; + /* translate position and setup filter-prepend to be prepend to the + striped/injected buffer in next 'chain' calling */ + filter->offset = start; + gst_base_metadata_translate_pos_to_orig (filter, start, &start, + &filter->prepend_buffer); + filter->offset_orig = start; - peer = gst_pad_get_peer (filter->sinkpad); + if (stop_type == GST_SEEK_TYPE_CUR) + stop = filter->offset + stop; + else if (stop_type == GST_SEEK_TYPE_END) { + if (filter->duration < 0) + goto done; + stop = filter->duration + stop; + } + stop_type == GST_SEEK_TYPE_SET; - switch (filter->img_type) { - case IMG_JPEG: - mime = "image/jpeg"; - break; - case IMG_PNG: - mime = "image/png"; + gst_base_metadata_translate_pos_to_orig (filter, stop, &stop, NULL); + + gst_event_unref (event); + event = gst_event_new_seek (rate, format, flags, + start_type, start, stop_type, stop); + + } break; default: - goto done; break; } - caps = gst_caps_new_simple (mime, NULL); - - if (!gst_pad_set_caps (peer, caps)) { - goto done; - } - - ret = gst_pad_set_caps (filter->sinkpad, caps); + ret = gst_pad_event_default (pad, event); + event = NULL; done: - if (caps) { - gst_caps_unref (caps); - caps = NULL; + if (event) { + gst_event_unref (event); } - if (peer) { - gst_object_unref (peer); - peer = NULL; - } + gst_object_unref (filter); return ret; } -static const GstQueryType * -gst_base_metadata_get_query_types (GstPad * pad) -{ - static const GstQueryType gst_base_metadata_src_query_types[] = { - GST_QUERY_POSITION, - GST_QUERY_DURATION, - GST_QUERY_FORMATS, - 0 - }; - - return gst_base_metadata_src_query_types; -} - static gboolean -gst_base_metadata_src_query (GstPad * pad, GstQuery * query) +gst_base_metadata_sink_event (GstPad * pad, GstEvent * event) { + GstBaseMetadata *filter = NULL; gboolean ret = FALSE; - GstFormat format; - GstBaseMetadata *filter = GST_BASE_METADATA (gst_pad_get_parent (pad)); - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_POSITION: - gst_query_parse_position (query, &format, NULL); - - if (format == GST_FORMAT_BYTES) { - gst_query_set_position (query, GST_FORMAT_BYTES, filter->offset); - ret = TRUE; - } - break; - case GST_QUERY_DURATION: - - if (!gst_base_metadata_processing (filter)) { - ret = FALSE; - goto done; - } + GstBaseMetadataClass *bclass; - gst_query_parse_duration (query, &format, NULL); + filter = GST_BASE_METADATA (gst_pad_get_parent (pad)); + bclass = GST_BASE_METADATA_GET_CLASS (filter); - if (format == GST_FORMAT_BYTES) { - if (filter->duration >= 0) { - gst_query_set_duration (query, GST_FORMAT_BYTES, filter->duration); - ret = TRUE; - } + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + if (filter->need_more_data) { + GST_ELEMENT_WARNING (filter, STREAM, DECODE, (NULL), + ("Need more data. Unexpected EOS")); } break; - case GST_QUERY_FORMATS: - gst_query_set_formats (query, 1, GST_FORMAT_BYTES); - ret = TRUE; + case GST_EVENT_TAG: break; default: break; } -done: + ret = bclass->sink_event (pad, event); gst_object_unref (filter); @@ -1221,69 +1549,85 @@ done: } -/* - * Do parsing step-by-step and reconfigure caps if need - * return: - * META_PARSING_ERROR - * META_PARSING_DONE - * META_PARSING_NEED_MORE_DATA - */ +static gboolean +gst_base_metadata_checkgetrange (GstPad * srcpad) +{ + GstBaseMetadata *filter = NULL; -static int -gst_base_metadata_parse (GstBaseMetadata * filter, const guint8 * buf, - guint32 size) + filter = GST_BASE_METADATA (GST_PAD_PARENT (srcpad)); + + return gst_pad_check_pull_range (filter->sinkpad); +} + +static GstFlowReturn +gst_base_metadata_get_range (GstPad * pad, + guint64 offset, guint size, GstBuffer ** buf) { + GstBaseMetadata *filter = NULL; + GstFlowReturn ret = GST_FLOW_OK; + gint64 offset_orig = 0; + guint size_orig; + GstBuffer *prepend = NULL; + gboolean need_append = FALSE; - int ret = META_PARSING_ERROR; + filter = GST_BASE_METADATA (GST_PAD_PARENT (pad)); - filter->next_offset = 0; - filter->next_size = 0; + if (!gst_base_metadata_processing (filter)) { + ret = GST_FLOW_ERROR; + goto done; + } - ret = metadata_parse (filter->metadata, buf, size, - &filter->next_offset, &filter->next_size); + if (offset + size > filter->duration) { + size = filter->duration - offset; + } - if (ret == META_PARSING_ERROR) { - if (META_DATA_IMG_TYPE (filter->metadata) == IMG_NONE) { - /* image type not recognized */ - GST_ELEMENT_ERROR (filter, STREAM, TYPE_NOT_FOUND, (NULL), - ("Only jpeg and png are supported")); - goto done; - } - } else if (ret == META_PARSING_NEED_MORE_DATA) { - filter->need_more_data = TRUE; - } else { - filter->state = MT_STATE_PARSED; - filter->need_more_data = FALSE; - filter->need_processing = TRUE; + size_orig = size; + + gst_base_metadata_translate_pos_to_orig (filter, offset, + &offset_orig, &prepend); + + if (size > 1) { + gint64 pos; + + pos = offset + size - 1; + gst_base_metadata_translate_pos_to_orig (filter, pos, &pos, NULL); + size_orig = pos + 1 - offset_orig; } - /* reconfigure caps if it is different from type detected by 'base_metadata' function */ - if (filter->img_type != META_DATA_IMG_TYPE (filter->metadata)) { - filter->img_type = META_DATA_IMG_TYPE (filter->metadata); - if (!gst_base_metadata_configure_caps (filter)) { - GST_ELEMENT_ERROR (filter, STREAM, FORMAT, (NULL), - ("Couldn't reconfigure caps for %s", - gst_base_metadata_get_type_name (filter->img_type))); - ret = META_PARSING_ERROR; - goto done; + if (size_orig) { + + ret = gst_pad_pull_range (filter->sinkpad, offset_orig, size_orig, buf); + + if (ret == GST_FLOW_OK && *buf) { + gst_base_metadata_strip_push_buffer (filter, offset_orig, &prepend, buf); + + if (GST_BUFFER_SIZE (*buf) < size) { + /* need append */ + need_append = TRUE; + } + } + } else { + *buf = prepend; } done: + if (need_append) { + /* FIXME: together with SEEK and + * gst_base_metadata_translate_pos_to_orig + * this way if chunk is added in the end we are in trouble + * ...still not implemented 'cause it will not be the + * case for the time being (All the file types handled + * have a kind of end-of-file chunk (or mark, so nothing + * will be injected after it) + */ + } + return ret; } -/* chain function - * this function does the actual processing - */ - -/* FIXME */ -/* Current base is just done before is pull mode could be activated */ -/* may be it is possible to base in chain mode by doing some trick with gst-adapter */ -/* the pipeline below would be a test for that case */ -/* gst-launch-0.10 filesrc location=Exif.jpg ! queue ! metadatabase ! filesink location=gen3.jpg */ static GstFlowReturn gst_base_metadata_chain (GstPad * pad, GstBuffer * buf) @@ -1421,74 +1765,6 @@ done: } -static gboolean -gst_base_metadata_pull_range_base (GstBaseMetadata * filter) -{ - - int res; - gboolean ret = TRUE; - guint32 offset = 0; - gint64 duration = 0; - GstFormat format = GST_FORMAT_BYTES; - - if (!(ret = - gst_pad_query_peer_duration (filter->sinkpad, &format, &duration))) { - /* this should never happen, but try chain anyway */ - ret = TRUE; - goto done; - } - filter->duration_orig = duration; - - if (format != GST_FORMAT_BYTES) { - /* this should never happen, but try chain anyway */ - ret = TRUE; - goto done; - } - - do { - GstFlowReturn flow; - GstBuffer *buf = NULL; - - offset += filter->next_offset; - - /* 'filter->next_size' only says the minimum required number of bytes. - We try provided more bytes (4096) just to avoid a lot of calls to 'metadata_parse' - returning META_PARSING_NEED_MORE_DATA */ - if (filter->next_size < 4096) { - if (duration - offset < 4096) { - /* In case there is no 4096 bytes available upstream. - It should be done upstream but we do here for safety */ - filter->next_size = duration - offset; - } else { - filter->next_size = 4096; - } - } - - flow = - gst_pad_pull_range (filter->sinkpad, offset, filter->next_size, &buf); - if (GST_FLOW_OK != flow) { - ret = FALSE; - goto done; - } - - res = - gst_base_metadata_parse (filter, GST_BUFFER_DATA (buf), - GST_BUFFER_SIZE (buf)); - if (res == META_PARSING_ERROR) { - ret = FALSE; - goto done; - } - - gst_buffer_unref (buf); - - } while (res == META_PARSING_NEED_MORE_DATA); - -done: - - return ret; - -} - static gboolean gst_base_metadata_sink_activate (GstPad * pad) { @@ -1500,14 +1776,14 @@ gst_base_metadata_sink_activate (GstPad * pad) if (!gst_pad_check_pull_range (pad) || !gst_pad_activate_pull (filter->sinkpad, TRUE)) { - /* FIXME: currently it is not possible to base in chain. Fail here ? */ - /* nothing to be done by now, activate push mode */ + /* FIXME: currently it is not possible to base in chain. Fail here ? + nothing to be done by now, activate push mode */ return gst_pad_activate_push (pad, TRUE); } /* try to base */ if (filter->state == MT_STATE_NULL) { - ret = gst_base_metadata_pull_range_base (filter); + ret = gst_base_metadata_pull_range_parse (filter); } done: @@ -1525,83 +1801,6 @@ done: } -static gboolean -gst_base_metadata_checkgetrange (GstPad * srcpad) -{ - GstBaseMetadata *filter = NULL; - - filter = GST_BASE_METADATA (GST_PAD_PARENT (srcpad)); - - return gst_pad_check_pull_range (filter->sinkpad); -} - -static GstFlowReturn -gst_base_metadata_get_range (GstPad * pad, - guint64 offset, guint size, GstBuffer ** buf) -{ - GstBaseMetadata *filter = NULL; - GstFlowReturn ret = GST_FLOW_OK; - gint64 offset_orig = 0; - guint size_orig; - GstBuffer *prepend = NULL; - gboolean need_append = FALSE; - - filter = GST_BASE_METADATA (GST_PAD_PARENT (pad)); - - if (!gst_base_metadata_processing (filter)) { - ret = GST_FLOW_ERROR; - goto done; - } - - if (offset + size > filter->duration) { - size = filter->duration - offset; - } - - size_orig = size; - - gst_base_metadata_translate_pos_to_orig (filter, offset, - &offset_orig, &prepend); - - if (size > 1) { - gint64 pos; - - pos = offset + size - 1; - gst_base_metadata_translate_pos_to_orig (filter, pos, &pos, NULL); - size_orig = pos + 1 - offset_orig; - } - - if (size_orig) { - - ret = gst_pad_pull_range (filter->sinkpad, offset_orig, size_orig, buf); - - if (ret == GST_FLOW_OK && *buf) { - gst_base_metadata_strip_push_buffer (filter, offset_orig, &prepend, buf); - - if (GST_BUFFER_SIZE (*buf) < size) { - /* need append */ - need_append = TRUE; - } - - } - } else { - *buf = prepend; - } - -done: - - if (need_append) { - /* FIXME: together with SEEK and - * gst_base_metadata_translate_pos_to_orig - * this way if chunk is added in the end we are in trolble - * ...still not implemented 'cause it will not be the - * case for the time being - */ - } - - return ret; - -} - static gboolean gst_base_metadata_src_activate_pull (GstPad * pad, gboolean active) { @@ -1613,7 +1812,7 @@ gst_base_metadata_src_activate_pull (GstPad * pad, gboolean active) ret = gst_pad_activate_pull (filter->sinkpad, active); if (ret && filter->state == MT_STATE_NULL) { - ret = gst_base_metadata_pull_range_base (filter); + ret = gst_base_metadata_pull_range_parse (filter); } gst_object_unref (filter); @@ -1621,35 +1820,54 @@ gst_base_metadata_src_activate_pull (GstPad * pad, gboolean active) return ret; } +static const GstQueryType * +gst_base_metadata_get_query_types (GstPad * pad) +{ + static const GstQueryType gst_base_metadata_src_query_types[] = { + GST_QUERY_POSITION, + GST_QUERY_DURATION, + GST_QUERY_FORMATS, + 0 + }; + + return gst_base_metadata_src_query_types; +} -static GstStateChangeReturn -gst_base_metadata_change_state (GstElement * element, GstStateChange transition) +static gboolean +gst_base_metadata_src_query (GstPad * pad, GstQuery * query) { - GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; - GstBaseMetadata *filter = GST_BASE_METADATA (element); + gboolean ret = FALSE; + GstFormat format; + GstBaseMetadata *filter = GST_BASE_METADATA (gst_pad_get_parent (pad)); - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - gst_base_metadata_reset_parsing (filter); - metadata_init (&filter->metadata, filter->options); - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - if (filter->metadata == NULL) - metadata_init (&filter->metadata, filter->options); - break; - default: + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_POSITION: + gst_query_parse_position (query, &format, NULL); + + if (format == GST_FORMAT_BYTES) { + gst_query_set_position (query, GST_FORMAT_BYTES, filter->offset); + ret = TRUE; + } break; - } + case GST_QUERY_DURATION: - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - if (ret == GST_STATE_CHANGE_FAILURE) - goto done; + if (!gst_base_metadata_processing (filter)) { + ret = FALSE; + goto done; + } - switch (transition) { - case GST_STATE_CHANGE_PAUSED_TO_READY: - gst_base_metadata_reset_streaming (filter); - if (filter->state != MT_STATE_PARSED) - gst_base_metadata_reset_parsing (filter); + gst_query_parse_duration (query, &format, NULL); + + if (format == GST_FORMAT_BYTES) { + if (filter->duration >= 0) { + gst_query_set_duration (query, GST_FORMAT_BYTES, filter->duration); + ret = TRUE; + } + } + break; + case GST_QUERY_FORMATS: + gst_query_set_formats (query, 1, GST_FORMAT_BYTES); + ret = TRUE; break; default: break; @@ -1657,15 +1875,50 @@ gst_base_metadata_change_state (GstElement * element, GstStateChange transition) done: + gst_object_unref (filter); + return ret; + } +/* + * extern functions declaration + */ + +/** + * gst_base_metadata_set_option_flag: + * @base: the base metadata instance + * @options: mode of operation + * + * Set how the this base class will behaviour. As a demuxer or muxer. Hanlding + * EXIF, IPTC, XMP or not. + * @see_also: #gst_base_metadata_unset_option_flag + * #gst_base_metadata_get_option_flag + * + * Returns: nothing + * + */ + void gst_base_metadata_set_option_flag (GstBaseMetadata * base, MetaOptions options) { base->options |= options; } +/** + * gst_base_metadata_unset_option_flag: + * @base: the base metadata instance + * @options: mode of operation + * + * Set how the this base class will behaviour. As a demuxer or muxer. Hanlding + * EXIF, IPTC, XMP or not. + * @see_also: #gst_base_metadata_set_option_flag + * #gst_base_metadata_get_option_flag + * + * Returns: nothing + * + */ + void gst_base_metadata_unset_option_flag (GstBaseMetadata * base, MetaOptions options) @@ -1673,23 +1926,55 @@ gst_base_metadata_unset_option_flag (GstBaseMetadata * base, base->options &= ~options; } +/** + * gst_base_metadata_get_option_flag: + * @base: the base metadata instance + * + * Check how the this base class will behaviour. As a demuxer or muxer. + * Hanlding EXIF, IPTC, XMP or not. + * @see_also: #gst_base_metadata_set_option_flag + * #gst_base_metadata_unset_option_flag + * + * Returns: The current mode of operation + * + */ + MetaOptions gst_base_metadata_get_option_flag (const GstBaseMetadata * base) { return base->options; } -void -gst_base_metadata_update_segment_with_new_buffer (GstBaseMetadata * base, - guint8 ** buf, guint32 * size, MetadataChunkType type) + +/** + * gst_base_metadata_update_inject_segment_with_new_data: + * @base: the base metadata instance + * @data: new data to be injected + * @size: the size in bytes of @data + * @type: kind of metadata chunk it is (currently EXIF, IPTC or XMP) + * + * If the file type specification of the parsed stream allows a chunk of @type, + * set the @data and @size of the segment to be injected. This the @data has + * been injected (metadata type supported by the stream @type) then, the + * the @data ownership will be taken and @data and @size willl be set to 0; + * Hanlding EXIF, IPTC, XMP or not. + * @see_also: #gst_base_metadata_calculate_offsets + * + * Returns: Nothing + * + */ + +void gst_base_metadata_update_inject_segment_with_new_data + (GstBaseMetadata * base, + guint8 ** data, guint32 * size, MetadataChunkType type) { int i; MetadataChunk *inject = META_DATA_INJECT_CHUNKS (base->metadata).chunk; const gsize inject_len = META_DATA_INJECT_CHUNKS (base->metadata).len; - if (!(buf && size)) + if (!(data && size)) goto done; - if (*buf == 0) + if (*data == 0) goto done; if (*size == 0) goto done; @@ -1699,9 +1984,9 @@ gst_base_metadata_update_segment_with_new_buffer (GstBaseMetadata * base, inject[i].size = *size; if (inject[i].data) g_free (inject[i].data); - inject[i].data = *buf; + inject[i].data = *data; *size = 0; - *buf = 0; + *data = 0; break; } } @@ -1711,10 +1996,3 @@ done: return; } - -void -gst_base_metadata_chunk_array_remove_zero_size (GstBaseMetadata * base) -{ - metadata_chunk_array_remove_zero_size (&META_DATA_INJECT_CHUNKS (base-> - metadata)); -} diff --git a/ext/metadata/gstbasemetadata.h b/ext/metadata/gstbasemetadata.h index 1e49f8bc..9a3c08ce 100644 --- a/ext/metadata/gstbasemetadata.h +++ b/ext/metadata/gstbasemetadata.h @@ -49,14 +49,20 @@ G_BEGIN_DECLS -/* #defines don't like whitespacey bits */ -#define GST_TYPE_BASE_METADATA (gst_base_metadata_get_type()) -#define GST_BASE_METADATA(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_BASE_METADATA,GstBaseMetadata)) -#define GST_BASE_METADATA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_BASE_METADATA,GstBaseMetadataClass)) -#define GST_BASE_METADATA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_BASE_METADATA, GstBaseMetadataClass)) -#define GST_IS_BASE_METADATA(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_BASE_METADATA)) -#define GST_IS_BASE_METADATA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_BASE_METADATA)) -#define GST_BASE_METADATA_CAST(obj) ((GstBaseMetadata *)(obj)) +/* *INDENT-OFF* */ +#define GST_TYPE_BASE_METADATA (gst_base_metadata_get_type()) +#define GST_BASE_METADATA(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\ + GST_TYPE_BASE_METADATA,GstBaseMetadata)) +#define GST_BASE_METADATA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),\ + GST_TYPE_BASE_METADATA,GstBaseMetadataClass)) +#define GST_BASE_METADATA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ + GST_TYPE_BASE_METADATA, GstBaseMetadataClass)) +#define GST_IS_BASE_METADATA(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),\ + GST_TYPE_BASE_METADATA)) +#define GST_IS_BASE_METADATA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),\ + GST_TYPE_BASE_METADATA)) +#define GST_BASE_METADATA_CAST(obj) ((GstBaseMetadata *)(obj)) +/* *INDENT-ON* */ typedef struct _GstBaseMetadata GstBaseMetadata; typedef struct _GstBaseMetadataClass GstBaseMetadataClass; @@ -98,7 +104,6 @@ typedef enum _tag_MetadataState /** * GstBaseMetadata: - * @element: the parent element. * * The opaque #GstBaseMetadata data structure. */ @@ -165,11 +170,8 @@ extern MetaOptions gst_base_metadata_get_option_flag(const GstBaseMetadata *base); extern void -gst_base_metadata_update_segment_with_new_buffer (GstBaseMetadata *base, - guint8 ** buf, guint32 * size, MetadataChunkType type); - -extern void -gst_base_metadata_chunk_array_remove_zero_size (GstBaseMetadata *base); +gst_base_metadata_update_inject_segment_with_new_data (GstBaseMetadata *base, + guint8 ** data, guint32 * size, MetadataChunkType type); G_END_DECLS #endif /* __GST_BASE_METADATA_H__ */ diff --git a/ext/metadata/gstmetadatademux.c b/ext/metadata/gstmetadatademux.c index 6393319e..a183739f 100644 --- a/ext/metadata/gstmetadatademux.c +++ b/ext/metadata/gstmetadatademux.c @@ -42,18 +42,57 @@ */ /** - * SECTION:metadatademux-metadata + * SECTION: element-metadatademux + * @short_description: element that parse or demux metadata from image files + * @see_also: #metadatamux * * + * + * This element parses image files JPEG and PNG, to find metadata chunks (EXIF, + * IPTC, XMP) in it, and then send individual tags as a 'tag message' do the + * application and as 'tag event' to the next element in pipeline. It also + * strips out the metadata chunks from original stream (unless the 'parse-only' + * property is set to 'true'). In addition the whole metadata chunk (striped + * or not) it also sent as a message to the application bus, so the application + * can have more controls about the metadata. + * * Example launch line * * - * gst-launch -v -m filesrc location=./test.jpeg ! metadatademux ! fakesink silent=TRUE + * gst-launch -v -m filesrc location=./test.jpeg ! metadatademux ! fakesink + * silent=TRUE + * + * + * GST_DEBUG:*metadata:5 gst-launch filesrc location=./test.jpeg ! + * metadatademux ! fakesink + * + * + * Application sample code using 'libexif' to have more control + * + * + * val = gst_tag_list_get_value_index (taglist, GST_TAG_EXIF, 0); + * if (val) { + * exif_chunk = gst_value_get_buffer (val); + * if (exif_chunk) { + * ed = exif_data_new_from_data (GST_BUFFER_DATA (exif_chunk), + * GST_BUFFER_SIZE (exif_chunk)); + * } + * } * + * This same idea can be used to handle IPTC and XMP directly by using + * libdata and exempi (or any other libraries). Notice: the whole metadata + * chunk sent as a message to the application contains only metadata data, i.e. + * the wrapper specific to the file format (JPEG, PNG, ...) is already + * striped out. * * */ + +/* + * includes + */ + #ifdef HAVE_CONFIG_H # include #endif @@ -70,16 +109,13 @@ #include -GST_DEBUG_CATEGORY_STATIC (gst_metadata_demux_debug); -#define GST_CAT_DEFAULT gst_metadata_demux_debug -#define GOTO_DONE_IF_NULL(ptr) do { if ( NULL == (ptr) ) goto done; } while(FALSE) -#define GOTO_DONE_IF_NULL_AND_FAIL(ptr, ret) do { if ( NULL == (ptr) ) { (ret) = FALSE; goto done; } } while(FALSE) +/* + * enum and types + */ -/* Filter signals and args */ enum { - /* FILL ME */ LAST_SIGNAL }; @@ -89,6 +125,19 @@ enum ARG_PARSE_ONLY }; +/* + * defines and static global vars + */ + + +GST_DEBUG_CATEGORY_STATIC (gst_metadata_demux_debug); +#define GST_CAT_DEFAULT gst_metadata_demux_debug + +#define GOTO_DONE_IF_NULL(ptr) \ + do { if ( NULL == (ptr) ) goto done; } while(FALSE) +#define GOTO_DONE_IF_NULL_AND_FAIL(ptr, ret) \ + do { if ( NULL == (ptr) ) { (ret) = FALSE; goto done; } } while(FALSE) + static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, @@ -105,35 +154,113 @@ static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", "image/png, " "tags-extracted = (bool) true") ); -GST_BOILERPLATE (GstMetadataDemux, gst_metadata_demux, GstBaseMetadata, - GST_TYPE_BASE_METADATA); - static GstMetadataDemuxClass *metadata_parent_class = NULL; +/* + * static helper functions declaration + */ + +static gboolean +gst_metadata_demux_configure_srccaps (GstMetadataDemux * filter); + +/* + * GObject callback functions declaration + */ + +static void gst_metadata_demux_base_init (gpointer gclass); + +static void gst_metadata_demux_class_init (GstMetadataDemuxClass * klass); + +static void +gst_metadata_demux_init (GstMetadataDemux * filter, + GstMetadataDemuxClass * gclass); + static void gst_metadata_demux_dispose (GObject * object); static void gst_metadata_demux_finalize (GObject * object); static void gst_metadata_demux_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); + static void gst_metadata_demux_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); -static GstCaps *gst_metadata_demux_get_caps (GstPad * pad); +/* + * GstBaseMetadata virtual functions declaration + */ + +static void gst_metadata_demux_send_tags (GstBaseMetadata * base); + static gboolean gst_metadata_demux_set_caps (GstPad * pad, GstCaps * caps); + +static GstCaps *gst_metadata_demux_get_caps (GstPad * pad); + static gboolean gst_metadata_demux_sink_event (GstPad * pad, GstEvent * event); -static void gst_metadata_demux_send_tags (GstBaseMetadata * base); + +/* + * GST BOILERPLATE + */ + +GST_BOILERPLATE (GstMetadataDemux, gst_metadata_demux, GstBaseMetadata, + GST_TYPE_BASE_METADATA); + +/* + * static helper functions implementation + */ + +static gboolean +gst_metadata_demux_configure_srccaps (GstMetadataDemux * filter) +{ + GstCaps *caps = NULL; + gboolean ret = FALSE; + gchar *mime = NULL; + + switch (GST_BASE_METADATA_IMG_TYPE (filter)) { + case IMG_JPEG: + mime = "image/jpeg"; + break; + case IMG_PNG: + mime = "image/png"; + break; + default: + ret = FALSE; + goto done; + break; + } + + caps = + gst_caps_new_simple (mime, "tags-extracted", G_TYPE_BOOLEAN, TRUE, NULL); + + ret = gst_pad_set_caps (GST_BASE_METADATA_SRC_PAD (filter), caps); + +done: + + if (caps) { + gst_caps_unref (caps); + caps = NULL; + } + + return ret; + +} + +/* + * GObject callback functions implementation + */ static void gst_metadata_demux_base_init (gpointer gclass) { +/* *INDENT-OFF* */ static GstElementDetails element_details = { - "Metadata demuxr", - "Demuxr/Extracter/Metadata", - "Send metadata tags (EXIF, IPTC and XMP) and remove metadata chunks from stream", + "Metadata demuxer", + "Demuxer/Extracter/Metadata", + "Send metadata tags (EXIF, IPTC and XMP) and " + "remove metadata chunks from stream", "Edgard Lima " }; +/* *INDENT-ON* */ GstElementClass *element_class = GST_ELEMENT_CLASS (gclass); gst_element_class_add_pad_template (element_class, @@ -143,7 +270,6 @@ gst_metadata_demux_base_init (gpointer gclass) gst_element_class_set_details (element_class, &element_details); } -/* initialize the plugin's class */ static void gst_metadata_demux_class_init (GstMetadataDemuxClass * klass) { @@ -180,11 +306,6 @@ gst_metadata_demux_class_init (GstMetadataDemuxClass * klass) } -/* initialize the new element - * instantiate pads and add them to element - * set functions - * initialize structure - */ static void gst_metadata_demux_init (GstMetadataDemux * filter, GstMetadataDemuxClass * gclass) @@ -250,6 +371,23 @@ gst_metadata_demux_finalize (GObject * object) G_OBJECT_CLASS (metadata_parent_class)->finalize (object); } + +/* + * GstBaseMetadata virtual functions implementation + */ + +/* + * gst_metadata_demux_send_tags: + * @base: the base metadata instance + * + * Send individual tags as message to the bus and as event to the next + * element, and send the whole metadata chunk (with file specific wrapper + * striped) to the next element as a event. + * + * Returns: nothing + * + */ + static void gst_metadata_demux_send_tags (GstBaseMetadata * base) { @@ -260,6 +398,8 @@ gst_metadata_demux_send_tags (GstBaseMetadata * base) GstEvent *event; GstPad *srcpad = GST_BASE_METADATA_SRC_PAD (filter); + /* get whole chunk */ + if (gst_base_metadata_get_option_flag (base) & META_OPT_EXIF) metadataparse_exif_tag_list_add (taglist, GST_TAG_MERGE_KEEP, GST_BASE_METADATA_EXIF_ADAPTER (base), METADATA_TAG_MAP_WHOLECHUNK); @@ -284,6 +424,8 @@ gst_metadata_demux_send_tags (GstBaseMetadata * base) if (!taglist) taglist = gst_tag_list_new (); + /*get individual tags */ + if (gst_base_metadata_get_option_flag (base) & META_OPT_EXIF) metadataparse_exif_tag_list_add (taglist, GST_TAG_MERGE_KEEP, GST_BASE_METADATA_EXIF_ADAPTER (base), METADATA_TAG_MAP_INDIVIDUALS); @@ -306,44 +448,6 @@ gst_metadata_demux_send_tags (GstBaseMetadata * base) } -static gboolean -gst_metadata_demux_configure_srccaps (GstMetadataDemux * filter) -{ - GstCaps *caps = NULL; - gboolean ret = FALSE; - gchar *mime = NULL; - - switch (GST_BASE_METADATA_IMG_TYPE (filter)) { - case IMG_JPEG: - mime = "image/jpeg"; - break; - case IMG_PNG: - mime = "image/png"; - break; - default: - ret = FALSE; - goto done; - break; - } - - caps = - gst_caps_new_simple (mime, "tags-extracted", G_TYPE_BOOLEAN, TRUE, NULL); - - ret = gst_pad_set_caps (GST_BASE_METADATA_SRC_PAD (filter), caps); - -done: - - if (caps) { - gst_caps_unref (caps); - caps = NULL; - } - - return ret; - -} - - -/* this function handles the link with other elements */ static gboolean gst_metadata_demux_set_caps (GstPad * pad, GstCaps * caps) { diff --git a/ext/metadata/gstmetadatademux.h b/ext/metadata/gstmetadatademux.h index df1c42df..de47f259 100644 --- a/ext/metadata/gstmetadatademux.h +++ b/ext/metadata/gstmetadatademux.h @@ -49,23 +49,34 @@ #include "gstbasemetadata.h" G_BEGIN_DECLS -/* #defines don't like whitespacey bits */ + +/* *INDENT-OFF* */ #define GST_TYPE_METADATA_DEMUX \ (gst_metadata_demux_get_type()) #define GST_METADATA_DEMUX(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_METADATA_DEMUX,GstMetadataDemux)) #define GST_METADATA_DEMUX_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_METADATA_DEMUX,GstMetadataDemuxClass)) + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_METADATA_DEMUX,\ + GstMetadataDemuxClass)) #define GST_IS_METADATA_DEMUX(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_METADATA_DEMUX)) #define GST_IS_METADATA_DEMUX_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_METADATA_DEMUX)) +/* *INDENT-ON* */ + typedef struct _GstMetadataDemux GstMetadataDemux; typedef struct _GstMetadataDemuxClass GstMetadataDemuxClass; + +/** + * GstMetadataDemux: + * + * The opaque #GstMetadataDemux data structure. + */ + struct _GstMetadataDemux { - GstBaseMetadata element; + GstBaseMetadata metadata; }; struct _GstMetadataDemuxClass diff --git a/ext/metadata/gstmetadatamux.c b/ext/metadata/gstmetadatamux.c index 8175e7c6..85a8769d 100644 --- a/ext/metadata/gstmetadatamux.c +++ b/ext/metadata/gstmetadatamux.c @@ -42,18 +42,40 @@ */ /** - * SECTION:metadatamux-metadata + * SECTION: element-metadatamux * * + * + * This element writes tags into metadata (EXIF, IPTC and XMP) chunks, and + * writes the chunks into image files (JPEG, PNG). Tags the are received as + * GST_EVENT_TAG event or set by the application using #GstTagSetter interface. + * * Example launch line * * - * gst-launch -v -m filesrc location=./test.jpeg ! metadatamux ! fakesink silent=TRUE + * gst-launch -v -m filesrc location=orig.jpeg ! metadatamux ! filesink + * location=dest.jpeg * + * + * gst-launch -v -m filesrc location=orig.png ! metadatademux ! pngdec ! + * ffmpegcolorspace ! jpegenc ! metadatamux ! filesink location=dest.jpeg + * + * + * How it works + * + * If this element receives a GST_TAG_EXIF, GST_TAG_IPTC or GST_TAG_XMP which + * are whole chunk metadata tags, then this whole chunk will be modified by + * individual tags received and written to the file. Otherwise, a new chunk + * will be created from the scratch and then modified in same way. * * */ + +/* + * includes + */ + #ifdef HAVE_CONFIG_H # include #endif @@ -70,26 +92,35 @@ #include -GST_DEBUG_CATEGORY_STATIC (gst_metadata_mux_debug); -#define GST_CAT_DEFAULT gst_metadata_mux_debug - -#define GOTO_DONE_IF_NULL(ptr) do { if ( NULL == (ptr) ) goto done; } while(FALSE) -#define GOTO_DONE_IF_NULL_AND_FAIL(ptr, ret) do { if ( NULL == (ptr) ) { (ret) = FALSE; goto done; } } while(FALSE) +/* + * enum and types + */ -/* Filter signals and args */ enum { - /* FILL ME */ LAST_SIGNAL }; enum { ARG_0, - ARG_PARSE_ONLY }; +/* + * defines and static global vars + */ + + +GST_DEBUG_CATEGORY_STATIC (gst_metadata_mux_debug); +#define GST_CAT_DEFAULT gst_metadata_mux_debug + +#define GOTO_DONE_IF_NULL(ptr) \ + do { if ( NULL == (ptr) ) goto done; } while(FALSE) + +#define GOTO_DONE_IF_NULL_AND_FAIL(ptr, ret) \ + do { if ( NULL == (ptr) ) { (ret) = FALSE; goto done; } } while(FALSE) + static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, @@ -103,19 +134,24 @@ static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", GST_STATIC_CAPS ("image/jpeg; " "image/png") ); -static void -gst_metadata_mux_add_interfaces (GType type) -{ - static const GInterfaceInfo tag_setter_info = { NULL, NULL, NULL }; +static GstMetadataMuxClass *metadata_parent_class = NULL; - g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, &tag_setter_info); -} +/* + * static helper functions declaration + */ +static gboolean gst_metadata_mux_configure_srccaps (GstMetadataMux * filter); -GST_BOILERPLATE_FULL (GstMetadataMux, gst_metadata_mux, GstBaseMetadata, - GST_TYPE_BASE_METADATA, gst_metadata_mux_add_interfaces); +/* + * GObject callback functions declaration + */ -static GstMetadataMuxClass *metadata_parent_class = NULL; +static void gst_metadata_mux_base_init (gpointer gclass); + +static void gst_metadata_mux_class_init (GstMetadataMuxClass * klass); + +static void +gst_metadata_mux_init (GstMetadataMux * filter, GstMetadataMuxClass * gclass); static void gst_metadata_mux_dispose (GObject * object); @@ -123,24 +159,93 @@ static void gst_metadata_mux_finalize (GObject * object); static void gst_metadata_mux_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); + static void gst_metadata_mux_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); -static GstCaps *gst_metadata_mux_get_caps (GstPad * pad); +/* + * GstBaseMetadata virtual functions declaration + */ + +static void gst_metadata_mux_create_chunks_from_tags (GstBaseMetadata * base); + static gboolean gst_metadata_mux_set_caps (GstPad * pad, GstCaps * caps); + +static GstCaps *gst_metadata_mux_get_caps (GstPad * pad); + static gboolean gst_metadata_mux_sink_event (GstPad * pad, GstEvent * event); -static void gst_metadata_mux_create_chunks_from_tags (GstBaseMetadata * base); +/* + * GST BOILERPLATE + */ + +static void +gst_metadata_mux_add_interfaces (GType type) +{ + static const GInterfaceInfo tag_setter_info = { NULL, NULL, NULL }; + + g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, &tag_setter_info); +} + + +GST_BOILERPLATE_FULL (GstMetadataMux, gst_metadata_mux, GstBaseMetadata, + GST_TYPE_BASE_METADATA, gst_metadata_mux_add_interfaces); + + +/* + * static helper functions implementation + */ + +static gboolean +gst_metadata_mux_configure_srccaps (GstMetadataMux * filter) +{ + GstCaps *caps = NULL; + gboolean ret = FALSE; + gchar *mime = NULL; + + switch (GST_BASE_METADATA_IMG_TYPE (filter)) { + case IMG_JPEG: + mime = "image/jpeg"; + break; + case IMG_PNG: + mime = "image/png"; + break; + default: + ret = FALSE; + goto done; + break; + } + + caps = gst_caps_new_simple (mime, NULL); + + ret = gst_pad_set_caps (GST_BASE_METADATA_SRC_PAD (filter), caps); + +done: + + if (caps) { + gst_caps_unref (caps); + caps = NULL; + } + + return ret; + +} + +/* + * GObject callback functions declaration + */ static void gst_metadata_mux_base_init (gpointer gclass) { +/* *INDENT-OFF* */ static GstElementDetails element_details = { - "Metadata muxr", - "Muxr/Extracter/Metadata", - "Send metadata tags (EXIF, IPTC and XMP) and remove metadata chunks from stream", + "Metadata muxer", + "Muxer/Extracter/Metadata", + "Write metadata (EXIF, IPTC and XMP) into a image stream", "Edgard Lima " }; +/* *INDENT-ON* */ GstElementClass *element_class = GST_ELEMENT_CLASS (gclass); gst_element_class_add_pad_template (element_class, @@ -150,7 +255,6 @@ gst_metadata_mux_base_init (gpointer gclass) gst_element_class_set_details (element_class, &element_details); } -/* initialize the plugin's class */ static void gst_metadata_mux_class_init (GstMetadataMuxClass * klass) { @@ -170,10 +274,6 @@ gst_metadata_mux_class_init (GstMetadataMuxClass * klass) gobject_class->set_property = gst_metadata_mux_set_property; gobject_class->get_property = gst_metadata_mux_get_property; - g_object_class_install_property (gobject_class, ARG_PARSE_ONLY, - g_param_spec_boolean ("parse-only", "parse-only", - "If TRUE, don't strip out any chunk", FALSE, G_PARAM_READWRITE)); - gstbasemetadata_class->processing = GST_DEBUG_FUNCPTR (gst_metadata_mux_create_chunks_from_tags); gstbasemetadata_class->set_caps = @@ -187,11 +287,6 @@ gst_metadata_mux_class_init (GstMetadataMuxClass * klass) } -/* initialize the new element - * instantiate pads and add them to element - * set functions - * initialize structure - */ static void gst_metadata_mux_init (GstMetadataMux * filter, GstMetadataMuxClass * gclass) { @@ -209,14 +304,6 @@ gst_metadata_mux_set_property (GObject * object, guint prop_id, GstMetadataMux *filter = GST_METADATA_MUX (object); switch (prop_id) { - case ARG_PARSE_ONLY: - if (g_value_get_boolean (value)) - gst_base_metadata_set_option_flag (GST_BASE_METADATA (object), - META_OPT_PARSE_ONLY); - else - gst_base_metadata_unset_option_flag (GST_BASE_METADATA (object), - META_OPT_PARSE_ONLY); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -231,9 +318,6 @@ gst_metadata_mux_get_property (GObject * object, guint prop_id, gst_base_metadata_get_option_flag (GST_BASE_METADATA (object)); switch (prop_id) { - case ARG_PARSE_ONLY: - g_value_set_boolean (value, option & META_OPT_PARSE_ONLY); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -256,6 +340,23 @@ gst_metadata_mux_finalize (GObject * object) G_OBJECT_CLASS (metadata_parent_class)->finalize (object); } + +/* + * GstBaseMetadata virtual functions implementation + */ + +/* + * gst_metadata_mux_create_chunks_from_tags: + * @base: the base metadata instance + * + * This function creates new metadata (EXIF, IPTC, XMP) chunks with the tags + * received and add it to the list of segments that will be injected to the + * resulting file by #GstBaseMetadata. + * + * Returns: nothing + * + */ + static void gst_metadata_mux_create_chunks_from_tags (GstBaseMetadata * base) { @@ -272,19 +373,19 @@ gst_metadata_mux_create_chunks_from_tags (GstBaseMetadata * base) if (gst_base_metadata_get_option_flag (base) & META_OPT_EXIF) { metadatamux_exif_create_chunk_from_tag_list (&buf, &size, taglist); - gst_base_metadata_update_segment_with_new_buffer (base, &buf, &size, + gst_base_metadata_update_inject_segment_with_new_data (base, &buf, &size, MD_CHUNK_EXIF); } if (gst_base_metadata_get_option_flag (base) & META_OPT_IPTC) { metadatamux_iptc_create_chunk_from_tag_list (&buf, &size, taglist); - gst_base_metadata_update_segment_with_new_buffer (base, &buf, &size, + gst_base_metadata_update_inject_segment_with_new_data (base, &buf, &size, MD_CHUNK_IPTC); } if (gst_base_metadata_get_option_flag (base) & META_OPT_XMP) { metadatamux_xmp_create_chunk_from_tag_list (&buf, &size, taglist); - gst_base_metadata_update_segment_with_new_buffer (base, &buf, &size, + gst_base_metadata_update_inject_segment_with_new_data (base, &buf, &size, MD_CHUNK_XMP); } @@ -294,47 +395,8 @@ gst_metadata_mux_create_chunks_from_tags (GstBaseMetadata * base) g_free (buf); } - gst_base_metadata_chunk_array_remove_zero_size (base); - } -static gboolean -gst_metadata_mux_configure_srccaps (GstMetadataMux * filter) -{ - GstCaps *caps = NULL; - gboolean ret = FALSE; - gchar *mime = NULL; - - switch (GST_BASE_METADATA_IMG_TYPE (filter)) { - case IMG_JPEG: - mime = "image/jpeg"; - break; - case IMG_PNG: - mime = "image/png"; - break; - default: - ret = FALSE; - goto done; - break; - } - - caps = gst_caps_new_simple (mime, NULL); - - ret = gst_pad_set_caps (GST_BASE_METADATA_SRC_PAD (filter), caps); - -done: - - if (caps) { - gst_caps_unref (caps); - caps = NULL; - } - - return ret; - -} - - -/* this function handles the link with other elements */ static gboolean gst_metadata_mux_set_caps (GstPad * pad, GstCaps * caps) { diff --git a/ext/metadata/gstmetadatamux.h b/ext/metadata/gstmetadatamux.h index cafd6e08..bcdeaf1b 100644 --- a/ext/metadata/gstmetadatamux.h +++ b/ext/metadata/gstmetadatamux.h @@ -49,7 +49,8 @@ #include "gstbasemetadata.h" G_BEGIN_DECLS -/* #defines don't like whitespacey bits */ + +/* *INDENT-OFF* */ #define GST_TYPE_METADATA_MUX \ (gst_metadata_mux_get_type()) #define GST_METADATA_MUX(obj) \ @@ -60,12 +61,20 @@ G_BEGIN_DECLS (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_METADATA_MUX)) #define GST_IS_METADATA_MUX_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_METADATA_MUX)) +/* *INDENT-ON* */ + typedef struct _GstMetadataMux GstMetadataMux; typedef struct _GstMetadataMuxClass GstMetadataMuxClass; +/** + * GstMetadataMux: + * + * The opaque #GstMetadataMux data structure. + */ + struct _GstMetadataMux { - GstBaseMetadata element; + GstBaseMetadata metadata; }; struct _GstMetadataMuxClass diff --git a/ext/metadata/metadatatags.c b/ext/metadata/metadatatags.c index 6250c256..fbbdc328 100644 --- a/ext/metadata/metadatatags.c +++ b/ext/metadata/metadatatags.c @@ -83,34 +83,34 @@ metadata_tags_exif_register (void) gst_tag_register (GST_TAG_CAPTURE_FNUMBER, GST_TAG_FLAG_META, GST_TYPE_FRACTION, GST_TAG_CAPTURE_FNUMBER, "F number (focal ratio)", NULL); - /** - 0 - not defined - 1- Manual - 2- Normal program - 3- Aperture priority - 4- Shutter priority - 5- Creative program (biased toward death of field) - 6- Action program (biased toward fast shutter speed) - 7- Portrait mode (for closeup photos with the background out of focus) - 8- Landscape mode (for landscape photos with the background in focus) - *** exif is until here *** - 9- Night - 10- Back-light - 11- Spotlight - 12- Snow - 13- Beach - */ + /* + 0 - not defined + 1- Manual + 2- Normal program + 3- Aperture priority + 4- Shutter priority + 5- Creative program (biased toward death of field) + 6- Action program (biased toward fast shutter speed) + 7- Portrait mode (for closeup photos with the background out of focus) + 8- Landscape mode (for landscape photos with the background in focus) + *** exif is until here *** + 9- Night + 10- Back-light + 11- Spotlight + 12- Snow + 13- Beach + */ gst_tag_register (GST_TAG_CAPTURE_EXPOSURE_PROGRAM, GST_TAG_FLAG_META, G_TYPE_UINT, GST_TAG_CAPTURE_EXPOSURE_PROGRAM, "Class of program used for exposure", NULL); - /** The unit is the APEX value. - Ordinarily it is given in the range of -99.99 to 99.99. - if numerator is 0xFFFFFFFF means unknown - */ + /* The unit is the APEX value. + Ordinarily it is given in the range of -99.99 to 99.99. + if numerator is 0xFFFFFFFF means unknown + */ gst_tag_register (GST_TAG_CAPTURE_BRIGHTNESS, GST_TAG_FLAG_META, GST_TYPE_FRACTION, GST_TAG_CAPTURE_BRIGHTNESS, "Brightness (APEX from -99.99 to 99.99)", NULL); - /** + /* 0- Auto 1- Off *** exif is until here *** @@ -122,39 +122,39 @@ metadata_tags_exif_register (void) 7- Incandescent 8- Flash 9- Horizon (sun on the horizon) - */ + */ gst_tag_register (GST_TAG_CAPTURE_WHITE_BALANCE, GST_TAG_FLAG_META, G_TYPE_UINT, GST_TAG_CAPTURE_WHITE_BALANCE, "White balance mode", NULL); - /** if Zero ZOOM not used + /* if Zero ZOOM not used */ gst_tag_register (GST_TAG_CAPTURE_DIGITAL_ZOOM, GST_TAG_FLAG_META, GST_TYPE_FRACTION, GST_TAG_CAPTURE_DIGITAL_ZOOM, "Digital zoom ratio", NULL); - /** + /* 0- None 1- Low gain up 2- High gain up 3- Low gain down 4- High gain down - */ + */ gst_tag_register (GST_TAG_CAPTURE_GAIN, GST_TAG_FLAG_META, G_TYPE_UINT, GST_TAG_CAPTURE_GAIN, "", NULL); - /** + /* from -100 to 100 [-100, -34] - soft [-33, 33] - normal [34, 100] - hard *** exif is just 0, 1, 2 (normal, soft and hard) - */ + */ gst_tag_register (GST_TAG_CAPTURE_CONTRAST, GST_TAG_FLAG_META, G_TYPE_INT, GST_TAG_CAPTURE_CONTRAST, "", NULL); - /** + /* from -100 to 100 [-100, -34] - low [-33, 33] - normal [34, 100] - high *** exif is just 0, 1, 2 (normal, low and high) - */ + */ gst_tag_register (GST_TAG_CAPTURE_SATURATION, GST_TAG_FLAG_META, G_TYPE_INT, GST_TAG_CAPTURE_SATURATION, "", NULL); -- cgit v1.2.1