summaryrefslogtreecommitdiffstats
path: root/ext/metadata
diff options
context:
space:
mode:
Diffstat (limited to 'ext/metadata')
-rw-r--r--ext/metadata/TODO1
-rw-r--r--ext/metadata/gstbasemetadata.c1522
-rw-r--r--ext/metadata/gstbasemetadata.h30
-rw-r--r--ext/metadata/gstmetadatademux.c224
-rw-r--r--ext/metadata/gstmetadatademux.h17
-rw-r--r--ext/metadata/gstmetadatamux.c236
-rw-r--r--ext/metadata/gstmetadatamux.h13
-rw-r--r--ext/metadata/metadatatags.c60
8 files changed, 1285 insertions, 818 deletions
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:
+ * <itemizedlist>
+ * <listitem><para>parsers (only parse data)</para></listitem>
+ * <listitem><para>demuxers (parse data and remove metadata chunks)</para>
+ * </listitem>
+ * <listitem><para>muxers</para></listitem>
+ * <listitem><para>any other kind of element that wants to handle chunks in
+ * file formats based on chunks</para></listitem>
+ * </itemizedlist>
*
* <refsect2>
- * <title>Example launch line</title>
* <para>
- * <programlisting>
- * gst-launch -v -m filesrc location=./test.jpeg ! metadatabase ! fakesink silent=TRUE
- * </programlisting>
+ * 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.
+ * </para>
+ * <para>
+ * 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.
+ * </para>
+ * <para>
+ * 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.
+ * </para>
+ * <para>
+ * Currently there is implementation for JPEG and PNG (demux mode only)
+ * stream types and EXIF, IPTC and XMP metadatas.
* </para>
* </refsect2>
+ *
+ * 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 <string.h>
-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,99 +252,411 @@ 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;
+ 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_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);
+
+}
+
+const gchar *
+gst_base_metadata_get_type_name (int img_type)
+{
+ gchar *type_name = NULL;
+
+ 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;
+}
+
+static gboolean
+gst_base_metadata_pull_range_parse (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 (seg_offset < offset) {
- /* segment start somewhere before buffer */
+ if (format != GST_FORMAT_BYTES) {
+ /* this should never happen, but try chain anyway */
+ ret = TRUE;
+ goto done;
+ }
- /* all segment before buffer */
- if (seg_offset + seg_size <= offset) {
- ret = -1;
- 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;
+ }
}
- *seg_binter = offset - seg_offset;
- *boffset = 0;
+ flow =
+ gst_pad_pull_range (filter->sinkpad, offset, filter->next_size, &buf);
+ if (GST_FLOW_OK != flow) {
+ ret = FALSE;
+ goto done;
+ }
- /* 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;
+ res =
+ gst_base_metadata_parse (filter, GST_BUFFER_DATA (buf),
+ GST_BUFFER_SIZE (buf));
+ if (res == META_PARSING_ERROR) {
+ ret = FALSE;
+ goto done;
}
- ret = 0;
+ gst_buffer_unref (buf);
- } else {
- /* segment start somewhere into buffer */
+ } while (res == META_PARSING_NEED_MORE_DATA);
- *boffset = seg_offset - offset;
- *seg_binter = 0;
+done:
- if (seg_offset + seg_size <= offset + size) {
- /* all segment into buffer */
- *bsize = seg_size;
- } else {
- *bsize = size - *boffset;
- }
+ return ret;
- ret = 0;
+}
+
+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;
}
/*
- * extern functions declaration
+ * 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:
+ * <itemizedlist>
+ * <listitem><para>%TRUE no need futher processing, or the processing has
+ * been done</para></listitem>
+ * <listitem><para>%FALSE, if error. It is, need futher processing but
+ * #gst_base_metadata_calculate_offsets fails</para></listitem>
+ * </itemizedlist>
+ *
*/
+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:
+ * <itemizedlist>
+ * <listitem><para>%META_PARSING_ERROR if there was an error</para></listitem>
+ * <listitem><para>%META_PARSING_DONE if the parse has completly finished
+ * </para></listitem>
+ * <listitem><para>%META_PARSING_NEED_MORE_DATA if need another buffer</para>
+ * </listitem>
+ * </itemizedlist>
+ */
+
+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;
+
+}
+
/*
- * TRUE -> buffer striped or injeted
- * FALSE -> buffer unmodified
+ * 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:
+ * <itemizedlist>
+ * <listitem><para>%TRUE if the buffer has been striped or injected (@prepend
+ * doesn't change the return value).</para></listitem>
+ * <listitem><para>%FALSE if it hasn't changed.</para></listitem>
+ * </itemizedlist>
+ *
*/
-gboolean
+static gboolean
gst_base_metadata_strip_push_buffer (GstBaseMetadata * base,
gint64 offset_orig, GstBuffer ** prepend, GstBuffer ** buf)
{
@@ -268,13 +665,12 @@ gst_base_metadata_strip_push_buffer (GstBaseMetadata * base,
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;
+ gboolean buffer_is_ok_to_change = FALSE;
guint32 size_buf_in = GST_BUFFER_SIZE (*buf);
gint64 *boffset_strip = NULL;
guint32 *bsize_strip = NULL;
- guint32 *seg_binter_strip = NULL;
int i, j;
gboolean need_free_strip = FALSE;
@@ -284,6 +680,12 @@ gst_base_metadata_strip_push_buffer (GstBaseMetadata * base,
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) {
@@ -302,31 +704,38 @@ gst_base_metadata_strip_push_buffer (GstBaseMetadata * base,
}
/*
- * strip segments
+ * 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:
+ * <itemizedlist>
+ * <listitem><para>%-1 if the segment is completly before the buffer</para>
+ * </listitem>
+ * <listitem><para>%0 if the segment intersects</para></listitem>
+ * <listitem><para>%1 if the segment is after the buffer</para></listitem>
+ * </itemizedlist>
*/
-gboolean
+
+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:
+ * <itemizedlist>
+ * <listitem><para>%TRUE if @pos is in original buffer</para></listitem>
+ * <listitem><para>%FALSE if @pos is into a injected chunk</para></listitem>
+ * </itemizedlist>
+ *
+ */
+
+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:
+ * <itemizedlist>
+ * <listitem><para>%TRUE if succeded</para></listitem>
+ * <listitem><para>%FALSE if fails. Which happens if the stream hasn't been
+ * completely parsed yet</para></listitem>
+ * </itemizedlist>
*/
-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)
- return FALSE;
+ if (base->state != MT_STATE_PARSED) {
+ ret = FALSE;
+ goto done;
+ }
+
+ 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)
{
@@ -784,6 +1299,26 @@ gst_base_metadata_init (GstBaseMetadata * filter, GstBaseMetadataClass * gclass)
}
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_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
@@ -836,59 +1371,49 @@ 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);
+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ goto done;
- gst_object_unref (filter);
+ 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;
+ }
- return ret;
+done:
+ return ret;
}
-
static gboolean
gst_base_metadata_src_event (GstPad * pad, GstEvent * event)
{
@@ -945,7 +1470,13 @@ gst_base_metadata_src_event (GstPad * pad, GstEvent * event)
filter->prepend_buffer = NULL;
}
- /* FIXME: related to append */
+ /* 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
+ */
+
+ /* 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);
@@ -973,7 +1504,7 @@ gst_base_metadata_src_event (GstPad * pad, GstEvent * event)
}
ret = gst_pad_event_default (pad, event);
- event = NULL; /* event has another owner */
+ event = NULL;
done:
@@ -987,303 +1518,116 @@ done:
}
-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)
-{
-
-
- 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);
-
-}
-
static gboolean
-gst_base_metadata_configure_caps (GstBaseMetadata * filter)
+gst_base_metadata_sink_event (GstPad * pad, GstEvent * event)
{
- GstCaps *caps = NULL;
+ GstBaseMetadata *filter = NULL;
gboolean ret = FALSE;
- gchar *mime = NULL;
- GstPad *peer = NULL;
+ GstBaseMetadataClass *bclass;
- peer = gst_pad_get_peer (filter->sinkpad);
+ filter = GST_BASE_METADATA (gst_pad_get_parent (pad));
+ bclass = GST_BASE_METADATA_GET_CLASS (filter);
- switch (filter->img_type) {
- case IMG_JPEG:
- mime = "image/jpeg";
+ 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 IMG_PNG:
- mime = "image/png";
+ case GST_EVENT_TAG:
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;
- }
+ ret = bclass->sink_event (pad, 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_checkgetrange (GstPad * srcpad)
{
- gboolean ret = FALSE;
- GstFormat format;
- GstBaseMetadata *filter = GST_BASE_METADATA (gst_pad_get_parent (pad));
+ GstBaseMetadata *filter = NULL;
- switch (GST_QUERY_TYPE (query)) {
- case GST_QUERY_POSITION:
- gst_query_parse_position (query, &format, NULL);
+ filter = GST_BASE_METADATA (GST_PAD_PARENT (srcpad));
- if (format == GST_FORMAT_BYTES) {
- gst_query_set_position (query, GST_FORMAT_BYTES, filter->offset);
- ret = TRUE;
- }
- break;
- case GST_QUERY_DURATION:
+ return gst_pad_check_pull_range (filter->sinkpad);
+}
- if (!gst_base_metadata_processing (filter)) {
- ret = FALSE;
- goto done;
- }
+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;
- gst_query_parse_duration (query, &format, NULL);
+ filter = GST_BASE_METADATA (GST_PAD_PARENT (pad));
- 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;
+ if (!gst_base_metadata_processing (filter)) {
+ ret = GST_FLOW_ERROR;
+ goto done;
}
-done:
+ if (offset + size > filter->duration) {
+ size = filter->duration - offset;
+ }
- gst_object_unref (filter);
+ size_orig = size;
- return ret;
+ gst_base_metadata_translate_pos_to_orig (filter, offset,
+ &offset_orig, &prepend);
-}
+ if (size > 1) {
+ gint64 pos;
-/*
- * Do parsing step-by-step and reconfigure caps if need
- * return:
- * META_PARSING_ERROR
- * META_PARSING_DONE
- * META_PARSING_NEED_MORE_DATA
- */
+ pos = offset + size - 1;
+ gst_base_metadata_translate_pos_to_orig (filter, pos, &pos, NULL);
+ size_orig = pos + 1 - offset_orig;
+ }
-static int
-gst_base_metadata_parse (GstBaseMetadata * filter, const guint8 * buf,
- guint32 size)
-{
+ if (size_orig) {
- int ret = META_PARSING_ERROR;
+ ret = gst_pad_pull_range (filter->sinkpad, offset_orig, size_orig, buf);
- filter->next_offset = 0;
- filter->next_size = 0;
+ if (ret == GST_FLOW_OK && *buf) {
+ gst_base_metadata_strip_push_buffer (filter, offset_orig, &prepend, buf);
- ret = metadata_parse (filter->metadata, buf, size,
- &filter->next_offset, &filter->next_size);
+ if (GST_BUFFER_SIZE (*buf) < size) {
+ /* need append */
+ need_append = TRUE;
+ }
- 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;
- }
+ *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)
@@ -1422,74 +1766,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)
{
GstBaseMetadata *filter = NULL;
@@ -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:
@@ -1526,83 +1802,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)
{
GstBaseMetadata *filter = NULL;
@@ -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
*
* <refsect2>
+ * <para>
+ * 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.
+ * </para>
* <title>Example launch line</title>
* <para>
* <programlisting>
- * gst-launch -v -m filesrc location=./test.jpeg ! metadatademux ! fakesink silent=TRUE
+ * gst-launch -v -m filesrc location=./test.jpeg ! metadatademux ! fakesink
+ * silent=TRUE
+ * </programlisting>
+ * <programlisting>
+ * GST_DEBUG:*metadata:5 gst-launch filesrc location=./test.jpeg !
+ * metadatademux ! fakesink
+ * </programlisting>
+ * </para>
+ * <title>Application sample code using 'libexif' to have more control</title>
+ * <para>
+ * <programlisting>
+ * 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));
+ * }
+ * }
* </programlisting>
+ * 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.
* </para>
* </refsect2>
*/
+
+/*
+ * includes
+ */
+
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
@@ -70,16 +109,13 @@
#include <string.h>
-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 <edgard.lima@indt.org.br>"
};
+/* *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);
@@ -307,44 +449,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)
{
GstMetadataDemux *filter = NULL;
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
*
* <refsect2>
+ * <para>
+ * 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.
+ * </para>
* <title>Example launch line</title>
* <para>
* <programlisting>
- * 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
* </programlisting>
+ * <programlisting>
+ * gst-launch -v -m filesrc location=orig.png ! metadatademux ! pngdec !
+ * ffmpegcolorspace ! jpegenc ! metadatamux ! filesink location=dest.jpeg
+ * </programlisting>
+ * </para>
+ * <title>How it works</title>
+ * <para>
+ * 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.
* </para>
* </refsect2>
*/
+
+/*
+ * includes
+ */
+
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
@@ -70,26 +92,35 @@
#include <string.h>
-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 <edgard.lima@indt.org.br>"
};
+/* *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,48 +395,9 @@ 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)
{
GstMetadataMux *filter = NULL;
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);