summaryrefslogtreecommitdiffstats
path: root/ext/metadata
diff options
context:
space:
mode:
authorEdgard Lima <edgard.lima@indt.org.br>2008-01-25 17:45:28 +0000
committerEdgard Lima <edgard.lima@indt.org.br>2008-01-25 17:45:28 +0000
commit9e31b57dc37cb94761ab3aa98018c6f9bc496e0c (patch)
tree3f9470e0fef203014f017eb35a06b1da3426ed53 /ext/metadata
parentef421fee72539e6202c8535687f15da2c3d4d1a7 (diff)
downloadgst-plugins-bad-9e31b57dc37cb94761ab3aa98018c6f9bc496e0c.tar.gz
gst-plugins-bad-9e31b57dc37cb94761ab3aa98018c6f9bc496e0c.tar.bz2
gst-plugins-bad-9e31b57dc37cb94761ab3aa98018c6f9bc496e0c.zip
Add lot of documentation.
Original commit message from CVS: Add lot of documentation.
Diffstat (limited to 'ext/metadata')
-rw-r--r--ext/metadata/TODO9
-rw-r--r--ext/metadata/gstbasemetadata.c4
-rw-r--r--ext/metadata/metadata.c180
-rw-r--r--ext/metadata/metadata.h23
-rw-r--r--ext/metadata/metadataexif.c423
-rw-r--r--ext/metadata/metadataexif.h4
-rw-r--r--ext/metadata/metadataiptc.c332
-rw-r--r--ext/metadata/metadataiptc.h4
-rw-r--r--ext/metadata/metadataparsejpeg.c307
-rw-r--r--ext/metadata/metadataparsejpeg.h18
-rw-r--r--ext/metadata/metadataparsepng.c276
-rw-r--r--ext/metadata/metadataparsepng.h16
-rw-r--r--ext/metadata/metadataparseutil.c72
-rw-r--r--ext/metadata/metadataparseutil.h8
-rw-r--r--ext/metadata/metadatatags.c86
-rw-r--r--ext/metadata/metadatatags.h16
-rw-r--r--ext/metadata/metadatatypes.c90
-rw-r--r--ext/metadata/metadatatypes.h22
-rw-r--r--ext/metadata/metadataxmp.c621
-rw-r--r--ext/metadata/metadataxmp.h12
-rw-r--r--ext/metadata/test/Makefile16
-rw-r--r--ext/metadata/test/MetadataEditorMain.glade154
-rw-r--r--ext/metadata/test/metadata_editor.c1015
23 files changed, 1964 insertions, 1744 deletions
diff --git a/ext/metadata/TODO b/ext/metadata/TODO
index c6f837f5..5aa15169 100644
--- a/ext/metadata/TODO
+++ b/ext/metadata/TODO
@@ -26,3 +26,12 @@ OPEN ISSUES:
KNOWN BUGS
+1- gst-launch-0.10 filesrc location=BlueSquare.png ! metadatademux ! metadatamux ! pngdec ! ffmpegcolorspace ! freeze ! xvimagesink
+
+the following pipelines work fine:
+
+gst-launch-0.10 filesrc location=BlueSquare.png ! metadatamux ! metadatademux ! pngdec ! ffmpegcolorspace ! freeze ! xvimagesink
+gst-launch-0.10 filesrc location=BlueSquare.png ! metadatademux ! metadatamux ! queue ! pngdec ! ffmpegcolorspace ! freeze ! xvimagesink
+gst-launch-0.10 filesrc location=BlueSquare.png ! ! metadatamux ! pngdec ! ffmpegcolorspace ! freeze ! xvimagesink
+gst-launch-0.10 filesrc location=BlueSquare.png ! metadatademux ! ! pngdec ! ffmpegcolorspace ! freeze ! xvimagesink
+
diff --git a/ext/metadata/gstbasemetadata.c b/ext/metadata/gstbasemetadata.c
index 011cd5ba..6d826607 100644
--- a/ext/metadata/gstbasemetadata.c
+++ b/ext/metadata/gstbasemetadata.c
@@ -1291,7 +1291,7 @@ gst_base_metadata_init (GstBaseMetadata * filter, GstBaseMetadataClass * gclass)
gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
- metadataparse_xmp_init ();
+ metadata_xmp_init ();
/* init members */
gst_base_metadata_init_members (filter);
@@ -1307,7 +1307,7 @@ gst_base_metadata_dispose (GObject * object)
gst_base_metadata_dispose_members (filter);
- metadataparse_xmp_dispose ();
+ metadata_xmp_dispose ();
G_OBJECT_CLASS (parent_class)->dispose (object);
}
diff --git a/ext/metadata/metadata.c b/ext/metadata/metadata.c
index fa5409ca..3ce891d9 100644
--- a/ext/metadata/metadata.c
+++ b/ext/metadata/metadata.c
@@ -41,32 +41,67 @@
* Boston, MA 02111-1307, USA.
*/
+/*
+ * SECTION: metadata
+ * @short_description: This module provides high-level functions to parse files
+ *
+ * This module find out the stream type (JPEG or PNG), and provide functions to
+ * the caller to know where are the metadata chunks and where should it be
+ * written, as well, it gives the caller the metedata chunk to be written and
+ * also gets a metadata chunk and wraps it according the strem type
+ * specification.
+ *
+ * <refsect2>
+ * <para>
+ * #metadata_init must be called before any other function in this module and
+ * must be paired with a call to #metadata_dispose. #metadata_parse is used to
+ * parse the stream (find the metadata chunks and the place it should be
+ * written to. And #metadata_lazy_update is used by muxers to wrap the metadata
+ * chunk according the stream type specification. Actually after indentify the
+ * stream type, the real jog of parsing is delivered to speciallized module.
+ * See, #metadata[mux/parse][jpeg/png].[c/h] files.
+ * </para>
+ * </refsect2>
+ *
+ * Last reviewed on 2008-01-24 (0.10.15)
+ */
+
+/*
+ * includes
+ */
+
#include <string.h>
#include "metadata.h"
/*
- *static declarations
+ * static helper functions declaration
*/
static MetadataParsingReturn
-metadata_parse_none (MetaData * meta_data, const guint8 * buf,
- guint32 * bufsize, guint8 ** next_start, guint32 * next_size);
+metadata_parse_none (MetaData * meta_data, const guint8 * data,
+ guint32 * data_size, guint8 ** next_start, guint32 * next_size);
/*
* extern functions implementations
*/
/*
- * Init metadata handle vars.
- * This function must becalled before any other function from this module.
- * This functoin must not be called twice without call 'metadata_dispose'
+ * metadata_init:
+ * @meta_data: [in] metadata handler to be inited
+ * @options: [in] which types of metadata will be processed (EXIF, IPTC and/or
+ * XMP) and how it will be handled (DEMUXING or MUXING). Look at #MetaOptions
+ * to see the available options.
+ *
+ * Init metadata handle.
+ * This function must be called before any other function from this module.
+ * This function must not be called twice without call to #metadata_dispose
* beteween them.
- * meta_data [in]: metadata handler to be inited
- * parse [in]: pass TRUE for demuxing and FALSE for muxing
- * options [in]: which types of metadata will be processed (EXIF, IPTC and/or XMP).
- * Look at 'MetaOptions' to see the available options.
+ * @see_also: #metadata_dispose #metadata_parse
+ *
+ * Returns: nothing
*/
+
void
metadata_init (MetaData ** meta_data, const MetaOptions options)
{
@@ -89,7 +124,8 @@ metadata_init (MetaData ** meta_data, const MetaOptions options)
if ((*meta_data)->options & META_OPT_DEMUX) {
/* when parsing we will probably strip only 3 chunk (exif, iptc and xmp)
so we use 4 just in case there is more than one chunk of them.
- But this is just for convinience, 'cause the chunk_array incriases dinamically */
+ But this is just for convinience, 'cause the chunk_array increases
+ dinamically */
metadata_chunk_array_init (&(*meta_data)->strip_chunks, 4);
/* at most 1 chunk will be injected (JPEG JFIF) */
metadata_chunk_array_init (&(*meta_data)->inject_chunks, 1);
@@ -103,9 +139,15 @@ metadata_init (MetaData ** meta_data, const MetaOptions options)
}
/*
- * Dispose medadata handler data.
- * Call this function to free any resource allocated by 'metadata_init'
+ * metadata_dispose:
+ * @meta_data: [in] metadata handler to be freed
+ *
+ * Call this function to free any resource allocated by #metadata_init
+ * @see_also: #metadata_init
+ *
+ * Returns: nothing
*/
+
void
metadata_dispose (MetaData ** meta_data)
{
@@ -151,27 +193,41 @@ metadata_dispose (MetaData ** meta_data)
}
+
/*
- * meta_data [in]: metata handle
- * buf [in]: data to be parsed
- * bufsize [in]: size of data in bytes
- * next_offset [out]: number of bytes to jump from the begining of 'buf' in the next call.
- * i.e, 0 (zero) mean that in the next call to function "buf" must have the same
- * data (probably resized, see 'size')
- * size [out]: number of minimal bytes in buf for the next call to this function
- * return:
- * META_PARSING_ERROR
- * META_PARSING_DONE
- * META_PARSING_NEED_MORE_DATA (look 'next_offset' and 'size')
- * when this function returns 0 you have strip and inject chunks ready to use
- * If you change the contents of strip and inject chunks, you have to call
- * 'metadata_lazy_update' (this is the case when muxing)
- * see MetaData->strip_chunks and MetaData->inject_chunks
+ * metadata_parse:
+ * @meta_data: [in] metadata handle
+ * @buf: [in] data to be parsed
+ * @buf_size: [in] size of @buf in bytes
+ * @next_offset: [out] number of bytes to jump from the begining of @buf in
+ * the next call. i.e, 0 (zero) mean that in the next call this function @buf
+ * must have the same data (probably resized, see @next_size)
+ * @next_size: [out] number of minimal bytes in @buf for the next call to this
+ * function
+ *
+ * This function is used to parse the stream step-by-step incrementaly, which
+ * means, discover the stream type and find the metadata chunks
+ * (#META_OPT_DEMUX), or point out where metadata chunks should be written
+ * (#META_OPT_MUX). It is important to notice that there could be both strip
+ * and inject chunks in both demuxing and muxing modes.
+ * @see_also: #metadata_init #META_DATA_STRIP_CHUNKS #META_DATA_INJECT_CHUNKS
+ *
+ * Returns:
+ * <itemizedlist>
+ * <listitem><para>%META_PARSING_ERROR
+ * </para></listitem>
+ * <listitem><para>%META_PARSING_DONE if parse has finished. Now strip and
+ * inject chunks has been found
+ * </para></listitem>
+ * <listitem><para>%META_PARSING_NEED_MORE_DATA if this function should be
+ * called again (look @next_offset and @next_size)
+ * </para></listitem>
+ * </itemizedlist>
*/
MetadataParsingReturn
metadata_parse (MetaData * meta_data, const guint8 * buf,
- guint32 bufsize, guint32 * next_offset, guint32 * next_size)
+ guint32 buf_size, guint32 * next_offset, guint32 * next_size)
{
int ret = META_PARSING_DONE;
@@ -180,7 +236,7 @@ metadata_parse (MetaData * meta_data, const guint8 * buf,
if (meta_data->state == STATE_NULL) {
ret =
- metadata_parse_none (meta_data, buf, &bufsize, &next_start, next_size);
+ metadata_parse_none (meta_data, buf, &buf_size, &next_start, next_size);
if (ret == META_PARSING_DONE)
meta_data->state = STATE_READING;
else
@@ -192,24 +248,24 @@ metadata_parse (MetaData * meta_data, const guint8 * buf,
if (G_LIKELY (meta_data->options & META_OPT_DEMUX))
ret =
metadataparse_jpeg_parse (&meta_data->format_data.jpeg_parse,
- (guint8 *) buf, &bufsize, meta_data->offset_orig, &next_start,
+ (guint8 *) buf, &buf_size, meta_data->offset_orig, &next_start,
next_size);
else
ret =
metadatamux_jpeg_parse (&meta_data->format_data.jpeg_mux,
- (guint8 *) buf, &bufsize, meta_data->offset_orig, &next_start,
+ (guint8 *) buf, &buf_size, meta_data->offset_orig, &next_start,
next_size);
break;
case IMG_PNG:
if (G_LIKELY (meta_data->options & META_OPT_DEMUX))
ret =
metadataparse_png_parse (&meta_data->format_data.png_parse,
- (guint8 *) buf, &bufsize, meta_data->offset_orig, &next_start,
+ (guint8 *) buf, &buf_size, meta_data->offset_orig, &next_start,
next_size);
else
ret =
metadatamux_png_parse (&meta_data->format_data.png_mux,
- (guint8 *) buf, &bufsize, meta_data->offset_orig, &next_start,
+ (guint8 *) buf, &buf_size, meta_data->offset_orig, &next_start,
next_size);
break;
default:
@@ -232,13 +288,24 @@ done:
}
/*
- * This function must be called after 'metadata_parse' and after the element has modified the 'segments'.
+ * metadata_lazy_update:
+ * @meta_data: [in] metata handle
+ *
+ * This function must be called after #metadata_parse and after the element
+ * has modified the segments (chunks)
+ * Data written to #META_DATA_INJECT_CHUNKS will be properly wrapped
* This function is really importante in case o muxing 'cause:
- * 1- 'cause gives the oportunity to muxers to wrapper new segments with apropriate bytes
- * ex: in case of JPEG it can wrap the EXIF chunk (created using tags) with chunk id and chunk size
- * 2- 'cause gives the oportunity to muxer to decide if some chunks should still be striped/injected
- * ex: if there is no EXIF chunk to be inserted, the muxer decides to not strip JFIF anymore
- * see MetaData->strip_chunks and MetaData->inject_chunks
+ * 1- 'cause gives the oportunity to muxers to wrapper new segments with
+ * apropriate bytes
+ * ex: in case of JPEG it can wrap the EXIF chunk (created using tags) with
+ * chunk id and chunk size
+ * 2- 'cause gives the oportunity to muxer to decide if some chunks should
+ * still be striped/injected
+ * ex: if there is no EXIF chunk to be inserted, the muxer decides to not
+ * strip JFIF anymore
+ * @see_also: #metadata_parse #META_DATA_INJECT_CHUNKS
+ *
+ * Returns: nothing
*/
void
@@ -266,15 +333,38 @@ metadata_lazy_update (MetaData * meta_data)
/*
- * static functions implementation
+ * static helper functions implementation
*/
/*
- * Find out the type of the stream
+ * metadata_parse_none:
+ * @meta_data: [in] metata handle
+ * @buf: [in] data to be parsed
+ * @buf_size: [in] size of @buf in bytes
+ * @next_offset: [out] number of bytes to jump from the begining of @buf in
+ * the next call. i.e, 0 (zero) mean that in the next call this function @buf
+ * must have the same data (probably resized, see @next_size)
+ * @next_size: [out] number of minimal bytes in @buf for the next call to this
+ * function
+ *
+ * Parse the fisrt bytes of the stream to identify the stream type
+ * @see_also: metadata_parse
+ *
+ * Returns:
+ * <itemizedlist>
+ * <listitem><para>%META_PARSING_ERROR none of the alloed strem types (JPEG,
+ * PNG) has been identified
+ * </para></listitem>
+ * <listitem><para>%META_PARSING_DONE if the stream type has been identified
+ * </para></listitem>
+ * <listitem><para>%META_PARSING_NEED_MORE_DATA if this function should be
+ * called again (look @next_offset and @next_size)
+ * </para></listitem>
+ * </itemizedlist>
*/
static MetadataParsingReturn
metadata_parse_none (MetaData * meta_data, const guint8 * buf,
- guint32 * bufsize, guint8 ** next_start, guint32 * next_size)
+ guint32 * buf_size, guint8 ** next_start, guint32 * next_size)
{
int ret = META_PARSING_ERROR;
@@ -292,7 +382,7 @@ metadata_parse_none (MetaData * meta_data, const guint8 * buf,
*/
/* we need at least 3 bytes to see if it is JPEG */
- if (*bufsize < 3) {
+ if (*buf_size < 3) {
*next_size = 3;
ret = META_PARSING_NEED_MORE_DATA;
goto done;
@@ -319,7 +409,7 @@ metadata_parse_none (MetaData * meta_data, const guint8 * buf,
}
/* we need at least 8 bytes to see if it is PNG */
- if (*bufsize < 8) {
+ if (*buf_size < 8) {
*next_size = 8;
ret = META_PARSING_NEED_MORE_DATA;
goto done;
diff --git a/ext/metadata/metadata.h b/ext/metadata/metadata.h
index 2f14471a..6e08ae7f 100644
--- a/ext/metadata/metadata.h
+++ b/ext/metadata/metadata.h
@@ -44,6 +44,10 @@
#ifndef __METADATA_H__
#define __METADATA_H__
+/*
+ * includes
+ */
+
#include <gst/base/gstadapter.h>
#include "metadatatypes.h"
@@ -54,16 +58,19 @@
G_BEGIN_DECLS
-/* *INDENT-OFF* */
+/*
+ * enum and types
+ */
+/* *INDENT-OFF* */
typedef enum _tag_MetaOptions
{
META_OPT_EXIF = (1 << 0),
META_OPT_IPTC = (1 << 1),
META_OPT_XMP = (1 << 2),
META_OPT_PARSE_ONLY = (1 << 3), /* only makes sense with META_OPT_DEMUX */
- META_OPT_DEMUX = (1 << 4),
- META_OPT_MUX = (1 << 5),
+ META_OPT_DEMUX = (1 << 4), /* to operates in demuxing mode */
+ META_OPT_MUX = (1 << 5), /* to operates in muxing mode */
META_OPT_ALL = (1 << 6) - 1
} MetaOptions;
/* *INDENT-ON* */
@@ -104,18 +111,26 @@ typedef struct _tag_MetaData
} MetaData;
+/*
+ * defines and macros
+ */
+
#define META_DATA_IMG_TYPE(p) (p)->img_type
#define META_DATA_STRIP_CHUNKS(p) (p)->strip_chunks
#define META_DATA_INJECT_CHUNKS(p) (p)->inject_chunks
+/*
+ * external function prototypes
+ */
+
extern void metadata_init (MetaData ** meta_data, const MetaOptions options);
extern void metadata_dispose (MetaData ** meta_data);
extern MetadataParsingReturn
metadata_parse (MetaData * meta_data, const guint8 * buf,
- guint32 bufsize, guint32 * next_offset, guint32 * next_size);
+ guint32 buf_size, guint32 * next_offset, guint32 * next_size);
extern void metadata_lazy_update (MetaData * meta_data);
diff --git a/ext/metadata/metadataexif.c b/ext/metadata/metadataexif.c
index 76dee2a7..39948a6f 100644
--- a/ext/metadata/metadataexif.c
+++ b/ext/metadata/metadataexif.c
@@ -41,23 +41,52 @@
* Boston, MA 02111-1307, USA.
*/
+/*
+ * SECTION: metadataexif
+ * @short_description: This module provides functions to extract tags from
+ * EXIF metadata chunks and create EXIF chunks from metadata tags.
+ * @see_also: #metadatatags.[c/h]
+ *
+ * If libexif isn't available at compilation time, only the whole chunk
+ * (#METADATA_TAG_MAP_WHOLECHUNK) tags is created. It means that individual
+ * tags aren't mapped.
+ *
+ * Last reviewed on 2008-01-24 (0.10.15)
+ */
+
+/*
+ * includes
+ */
+
#include "metadataexif.h"
#include "metadataparseutil.h"
#include "metadatatags.h"
+/*
+ * defines
+ */
+
GST_DEBUG_CATEGORY (gst_metadata_exif_debug);
#define GST_CAT_DEFAULT gst_metadata_exif_debug
+/*
+ * Implementation when libexif isn't available at compilation time
+ */
+
#ifndef HAVE_EXIF
+/*
+ * extern functions implementations
+ */
+
+
void
metadataparse_exif_tag_list_add (GstTagList * taglist, GstTagMergeMode mode,
GstAdapter * adapter, MetadataTagMapping mapping)
{
if (mapping & METADATA_TAG_MAP_WHOLECHUNK) {
- GST_LOG
- ("EXIF not defined, here I should send just one tag as whole chunk");
+ GST_LOG ("EXIF not defined, sending just one tag as whole chunk");
metadataparse_util_tag_list_add_chunk (taglist, mode, GST_TAG_EXIF,
adapter);
}
@@ -73,11 +102,23 @@ metadatamux_exif_create_chunk_from_tag_list (guint8 ** buf, guint32 * size,
#else /* ifndef HAVE_EXIF */
+/*
+ * Implementation when libexif is available at compilation time
+ */
+
+/*
+ * includes
+ */
+
#include <libexif/exif-data.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
+/*
+ * enum and types
+ */
+
typedef struct _tag_MEUserData
{
GstTagList *taglist;
@@ -92,10 +133,9 @@ typedef struct _tag_MapIntStr
const gchar *str;
} MapIntStr;
-static void
-exif_data_foreach_content_func (ExifContent * content, void *callback_data);
-
-static void exif_content_foreach_entry_func (ExifEntry * entry, void *);
+/*
+ * defines and static global vars
+ */
/* *INDENT-OFF* */
static MapIntStr mappedTags[] = {
@@ -131,41 +171,47 @@ static MapIntStr mappedTags[] = {
};
/* *INDENT-ON* */
-static const gchar *
-metadataparse_exif_get_tag_from_exif (ExifTag exif, GType * type)
-{
- int i = 0;
+/*
+ * static helper functions declaration
+ */
- while (mappedTags[i].exif) {
- if (exif == mappedTags[i].exif) {
- *type = gst_tag_get_type (mappedTags[i].str);
- break;
- }
- ++i;
- }
+static const gchar *metadataparse_exif_get_tag_from_exif (ExifTag exif,
+ GType * type);
- return mappedTags[i].str;
+static ExifTag
+metadatamux_exif_get_exif_from_tag (const gchar * tag, GType * type,
+ ExifIfd * ifd);
-}
+static void
+metadataparse_exif_data_foreach_content_func (ExifContent * content,
+ void *callback_data);
-static ExifTag
-metadataparse_exif_get_exif_from_tag (const gchar * tag, GType * type,
- ExifIfd * ifd)
-{
- int i = 0;
+static void
+metadataparse_exif_content_foreach_entry_func (ExifEntry * entry,
+ void *user_data);
- while (mappedTags[i].exif) {
- if (0 == strcmp (mappedTags[i].str, tag)) {
- *type = gst_tag_get_type (tag);
- *ifd = mappedTags[i].ifd;
- break;
- }
- ++i;
- }
+static void
+metadatamux_exif_for_each_tag_in_list (const GstTagList * list,
+ const gchar * tag, gpointer user_data);
- return mappedTags[i].exif;
+/*
+ * extern functions implementations
+ */
-}
+/*
+ * metadataparse_exif_tag_list_add:
+ * @taglist: tag list in which extracted tags will be added
+ * @mode: tag list merge mode
+ * @adapter: contains the EXIF metadata chunk
+ * @mapping: if is to extract individual tags and/or the whole chunk.
+ *
+ * This function gets a EXIF chunk (@adapter) and extract tags from it
+ * and the add to @taglist.
+ * Note: The EXIF chunk (@adapetr) must NOT be wrapped by any bytes specific
+ * to any file format
+ *
+ * Returns: nothing
+ */
void
metadataparse_exif_tag_list_add (GstTagList * taglist, GstTagMergeMode mode,
@@ -195,8 +241,8 @@ metadataparse_exif_tag_list_add (GstTagList * taglist, GstTagMergeMode mode,
goto done;
}
- exif_data_foreach_content (exif, exif_data_foreach_content_func,
- (void *) &user_data);
+ exif_data_foreach_content (exif,
+ metadataparse_exif_data_foreach_content_func, (void *) &user_data);
done:
@@ -207,44 +253,178 @@ done:
}
-static void
-exif_data_foreach_content_func (ExifContent * content, void *user_data)
+/*
+ * metadatamux_exif_create_chunk_from_tag_list:
+ * @buf: buffer that will have the created EXIF chunk
+ * @size: size of the buffer that will be created
+ * @taglist: list of tags to be added to EXIF chunk
+ *
+ * Get tags from @taglist, create a EXIF chunk based on it and save to @buf.
+ * Note: The EXIF chunk is NOT wrapped by any bytes specific to any file format
+ *
+ * Returns: nothing
+ */
+
+void
+metadatamux_exif_create_chunk_from_tag_list (guint8 ** buf, guint32 * size,
+ const GstTagList * taglist)
{
- ExifIfd ifd = exif_content_get_ifd (content);
+ ExifData *ed = NULL;
+ GstBuffer *exif_chunk = NULL;
+ const GValue *val = NULL;
- GST_LOG ("\n Content %p: %s (ifd=%d)", content, exif_ifd_get_name (ifd),
- ifd);
- exif_content_foreach_entry (content, exif_content_foreach_entry_func,
- user_data);
+ if (!(buf && size))
+ goto done;
+ if (*buf) {
+ g_free (*buf);
+ *buf = NULL;
+ }
+ *size = 0;
+
+ 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));
+ }
+ }
+
+ if (!ed) {
+ ed = exif_data_new ();
+ exif_data_set_data_type (ed, EXIF_DATA_TYPE_COMPRESSED);
+ exif_data_fix (ed);
+ }
+
+ gst_tag_list_foreach (taglist, metadatamux_exif_for_each_tag_in_list, ed);
+
+ exif_data_save_data (ed, buf, size);
+
+
+done:
+
+ if (ed)
+ exif_data_unref (ed);
+
+ return;
}
-#if 0
-static gboolean
-exif_fast_mdc (glong n, glong d, gulong * m)
-{
- gboolean ret = FALSE;
- static const int a[] =
- { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 39, 41, 43, 47, 49, 53, 0 };
+/*
+ * static helper functions implementation
+ */
+
+/*
+ * metadataparse_exif_get_tag_from_exif:
+ * @exif: EXIF tag to look for
+ * @type: the type of the GStreamer tag mapped to @exif
+ *
+ * This returns the GStreamer tag mapped to an EXIF tag.
+ *
+ * Returns:
+ * <itemizedlist>
+ * <listitem><para>The GStreamer tag mapped to the @exif
+ * </para></listitem>
+ * <listitem><para>%NULL if there is no mapped GST tag for @exif
+ * </para></listitem>
+ * </itemizedlist>
+ */
+
+static const gchar *
+metadataparse_exif_get_tag_from_exif (ExifTag exif, GType * type)
+{
int i = 0;
- *m = 1;
+ while (mappedTags[i].exif) {
+ if (exif == mappedTags[i].exif) {
+ *type = gst_tag_get_type (mappedTags[i].str);
+ break;
+ }
+ ++i;
+ }
+
+ return mappedTags[i].str;
- while (a[i] <= n && a[i] <= d) {
- while ((n % a[i] == 0) && (d % a[i]) == 0) {
- *m *= a[i];
- ret = TRUE;
+}
+
+/*
+ * metadatamux_exif_get_exif_from_tag:
+ * @tag: GST tag to look for
+ * @type: the type of the GStreamer @tag
+ * @ifd: the place into EXIF chunk @exif belongs to.
+ *
+ * This returns thet EXIF tag mapped to an GStreamer @tag.
+ *
+ * Returns:
+ * <itemizedlist>
+ * <listitem><para>The EXIF tag mapped to the GST @tag
+ * </para></listitem>
+ * <listitem><para>0 if there is no mapped EXIF tag for GST @tag
+ * </para></listitem>
+ * </itemizedlist>
+ */
+
+static ExifTag
+metadatamux_exif_get_exif_from_tag (const gchar * tag, GType * type,
+ ExifIfd * ifd)
+{
+ int i = 0;
+
+ while (mappedTags[i].exif) {
+ if (0 == strcmp (mappedTags[i].str, tag)) {
+ *type = gst_tag_get_type (tag);
+ *ifd = mappedTags[i].ifd;
+ break;
}
++i;
}
- return ret;
+ return mappedTags[i].exif;
}
-#endif
+
+/*
+ * metadataparse_exif_data_foreach_content_func:
+ * @content: EXIF structure from libexif containg a IFD
+ * @user_data: pointer to #MEUserData
+ *
+ * This function designed to be called for each EXIF IFD in a EXIF chunk. This
+ * function gets calls another function for each tag into @content. Then all
+ * tags into a EXIF chunk can be extracted to a tag list in @user_data.
+ * @see_also: #metadataparse_exif_tag_list_add
+ * #metadataparse_exif_content_foreach_entry_func
+ *
+ * Returns: nothing
+ */
static void
-exif_content_foreach_entry_func (ExifEntry * entry, void *user_data)
+metadataparse_exif_data_foreach_content_func (ExifContent * content,
+ void *user_data)
+{
+ ExifIfd ifd = exif_content_get_ifd (content);
+
+ GST_LOG ("\n Content %p: %s (ifd=%d)", content, exif_ifd_get_name (ifd),
+ ifd);
+ exif_content_foreach_entry (content,
+ metadataparse_exif_content_foreach_entry_func, user_data);
+}
+
+/*
+ * metadataparse_exif_content_foreach_entry_func:
+ * @entry: EXIF structure from libexif having a EXIF tag
+ * @user_data: pointer to #MEUserData
+ *
+ * This function designed to be called for each EXIF tag in a EXIF IFD. This
+ * function gets the EXIF tag from @entry and then add to the tag list
+ * in @user_data by using a merge mode also specified in @user_data
+ * @see_also: #metadataparse_exif_data_foreach_content_func
+ *
+ * Returns: nothing
+ */
+
+static void
+metadataparse_exif_content_foreach_entry_func (ExifEntry * entry,
+ void *user_data)
{
char buf[2048];
MEUserData *meudata = (MEUserData *) user_data;
@@ -390,76 +570,22 @@ done:
}
/*
+ * metadatamux_exif_for_each_tag_in_list:
+ * @list: GStreamer tag list from which @tag belongs to
+ * @tag: GStreamer tag to be added to the EXIF chunk
+ * @user_data: pointer to #ExifData in which the tag will be added
*
+ * This function designed to be called for each tag in GST tag list. This
+ * function adds get the tag value from tag @list and then add it to the EXIF
+ * chunk by using #ExifData and related functions from libexif
+ * @see_also: #metadatamux_exif_create_chunk_from_tag_list
+ *
+ * Returns: nothing
*/
-static ExifRational
-float_to_rational (gfloat f)
-{
- ExifRational r;
- int i = 6; /* precision */
-
- r.denominator = 1;
-
- while (i--) {
- if (f == floorf (f)) {
- break;
- }
- f *= 10.0f;
- r.denominator *= 10;
- }
-
- r.numerator = f;
-
- if (!(r.numerator & 0x1 || r.denominator & 0x1)) {
- /* divide both by 2 */
- r.numerator >>= 1;
- r.denominator >>= 1;
- }
- if (r.numerator % 5 == 0 && r.denominator % 5 == 0) {
- r.numerator /= 5;
- r.denominator /= 5;
- }
-
- return r;
-
-}
-
-static ExifSRational
-float_to_srational (gfloat f)
-{
- ExifSRational sr;
- int i = 6; /* precision */
-
- sr.denominator = 1;
-
- while (i--) {
- if (f == floorf (f)) {
- break;
- }
- f *= 10.0f;
- sr.denominator *= 10;
- }
-
- sr.numerator = f;
-
- if (!(sr.numerator & 0x1 || sr.denominator & 0x1)) {
- /* divide both by 2 */
- sr.numerator >>= 1;
- sr.denominator >>= 1;
- }
- if (sr.numerator % 5 == 0 && sr.denominator % 5 == 0) {
- sr.numerator /= 5;
- sr.denominator /= 5;
- }
-
- return sr;
-
-}
-
static void
-metadataexif_for_each_tag_in_list (const GstTagList * list, const gchar * tag,
- gpointer user_data)
+metadatamux_exif_for_each_tag_in_list (const GstTagList * list,
+ const gchar * tag, gpointer user_data)
{
ExifData *ed = (ExifData *) user_data;
ExifTag exif_tag;
@@ -468,7 +594,7 @@ metadataexif_for_each_tag_in_list (const GstTagList * list, const gchar * tag,
ExifIfd ifd;
const ExifByteOrder byte_order = exif_data_get_byte_order (ed);
- exif_tag = metadataparse_exif_get_exif_from_tag (tag, &type, &ifd);
+ exif_tag = metadatamux_exif_get_exif_from_tag (tag, &type, &ifd);
if (!exif_tag)
goto done;
@@ -505,7 +631,9 @@ metadataexif_for_each_tag_in_list (const GstTagList * list, const gchar * tag,
entry->tag == EXIF_TAG_Y_RESOLUTION) {
ExifEntry *unit_entry = NULL;
- if ((unit_entry = exif_data_get_entry (ed, EXIF_TAG_RESOLUTION_UNIT))) {
+ unit_entry = exif_data_get_entry (ed, EXIF_TAG_RESOLUTION_UNIT);
+
+ if (unit_entry) {
ExifShort vsh = exif_get_short (unit_entry->data, byte_order);
if (vsh != 2) /* inches */
@@ -568,48 +696,5 @@ done:
}
-void
-metadatamux_exif_create_chunk_from_tag_list (guint8 ** buf, guint32 * size,
- const GstTagList * taglist)
-{
- ExifData *ed = NULL;
- GstBuffer *exif_chunk = NULL;
- const GValue *val = NULL;
-
- if (!(buf && size))
- goto done;
- if (*buf) {
- g_free (*buf);
- *buf = NULL;
- }
- *size = 0;
-
- 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));
- }
- }
-
- if (!ed) {
- ed = exif_data_new ();
- exif_data_set_data_type (ed, EXIF_DATA_TYPE_COMPRESSED);
- exif_data_fix (ed);
- }
-
- gst_tag_list_foreach (taglist, metadataexif_for_each_tag_in_list, ed);
-
- exif_data_save_data (ed, buf, size);
-
-
-done:
-
- if (ed)
- exif_data_unref (ed);
-
- return;
-}
#endif /* else (ifndef HAVE_EXIF) */
diff --git a/ext/metadata/metadataexif.h b/ext/metadata/metadataexif.h
index 71cd6511..8f1d8a21 100644
--- a/ext/metadata/metadataexif.h
+++ b/ext/metadata/metadataexif.h
@@ -50,6 +50,10 @@
G_BEGIN_DECLS
+/*
+ * external function prototypes
+ */
+
extern void
metadataparse_exif_tag_list_add (GstTagList * taglist, GstTagMergeMode mode,
GstAdapter * adapter, MetadataTagMapping mapping);
diff --git a/ext/metadata/metadataiptc.c b/ext/metadata/metadataiptc.c
index 0b040ede..31388372 100644
--- a/ext/metadata/metadataiptc.c
+++ b/ext/metadata/metadataiptc.c
@@ -41,23 +41,51 @@
* Boston, MA 02111-1307, USA.
*/
+/*
+ * SECTION: metadataiptc
+ * @short_description: This module provides functions to extract tags from
+ * IPTC metadata chunks and create IPTC chunks from metadata tags.
+ * @see_also: #metadatatags.[c/h]
+ *
+ * If libiptcdata isn't available at compilation time, only the whole chunk
+ * (#METADATA_TAG_MAP_WHOLECHUNK) tags is created. It means that individual
+ * tags aren't mapped.
+ *
+ * Last reviewed on 2008-01-24 (0.10.15)
+ */
+
+/*
+ * includes
+ */
+
#include "metadataiptc.h"
#include "metadataparseutil.h"
#include "metadatatags.h"
+/*
+ * defines
+ */
+
GST_DEBUG_CATEGORY (gst_metadata_iptc_debug);
#define GST_CAT_DEFAULT gst_metadata_iptc_debug
+/*
+ * Implementation when libiptcdata isn't available at compilation time
+ */
+
#ifndef HAVE_IPTC
+/*
+ * extern functions implementations
+ */
+
void
metadataparse_iptc_tag_list_add (GstTagList * taglist, GstTagMergeMode mode,
GstAdapter * adapter, MetadataTagMapping mapping)
{
if (mapping & METADATA_TAG_MAP_WHOLECHUNK) {
- GST_LOG
- ("IPTC not defined, here I should send just one tag as whole chunk");
+ GST_LOG ("IPTC not defined, sending just one tag as whole chunk");
metadataparse_util_tag_list_add_chunk (taglist, mode, GST_TAG_IPTC,
adapter);
}
@@ -74,11 +102,23 @@ metadatamux_iptc_create_chunk_from_tag_list (guint8 ** buf, guint32 * size,
#else /* ifndef HAVE_IPTC */
+/*
+ * Implementation when libiptcdata is available at compilation time
+ */
+
+/*
+ * includes
+ */
+
#include <iptc-data.h>
#include <iptc-tag.h>
#include <string.h>
#include <gst/gsttaglist.h>
+/*
+ * enum and types
+ */
+
typedef struct _tag_MEUserData
{
GstTagList *taglist;
@@ -92,56 +132,62 @@ typedef struct _tag_MapIntStr
const gchar *str;
} MapIntStr;
-static void
-iptc_data_foreach_dataset_func (IptcDataSet * dataset, void *user_data);
+/*
+ * defines and static global vars
+ */
/* *INDENT-OFF* */
static MapIntStr mappedTags[] = {
- {IPTC_RECORD_APP_2, IPTC_TAG_OBJECT_NAME, /*ASCII*/ GST_TAG_TITLE /*STRING*/},
- {IPTC_RECORD_APP_2, IPTC_TAG_BYLINE, /*ASCII*/ GST_TAG_COMPOSER /*STRING*/},
- {IPTC_RECORD_APP_2, IPTC_TAG_CAPTION, /*ASCII*/ GST_TAG_DESCRIPTION /*STRING*/},
- {IPTC_RECORD_APP_2, IPTC_TAG_COPYRIGHT_NOTICE, /*ASCII*/ GST_TAG_COPYRIGHT /*STRING*/},
+ {IPTC_RECORD_APP_2, IPTC_TAG_OBJECT_NAME, /*ASCII*/
+ GST_TAG_TITLE /*STRING*/},
+ {IPTC_RECORD_APP_2, IPTC_TAG_BYLINE, /*ASCII*/
+ GST_TAG_COMPOSER /*STRING*/},
+ {IPTC_RECORD_APP_2, IPTC_TAG_CAPTION, /*ASCII*/
+ GST_TAG_DESCRIPTION /*STRING*/},
+ {IPTC_RECORD_APP_2, IPTC_TAG_COPYRIGHT_NOTICE, /*ASCII*/
+ GST_TAG_COPYRIGHT /*STRING*/},
{0, 0, NULL}
};
/* *INDENT-ON* */
-static const gchar *
-metadataparse_iptc_get_tag_from_iptc (IptcTag iptc, GType * type,
- IptcRecord * record)
-{
- int i = 0;
-
- while (mappedTags[i].iptc) {
- if (iptc == mappedTags[i].iptc) {
- *type = gst_tag_get_type (mappedTags[i].str);
- *record = mappedTags[i].record;
- break;
- }
- ++i;
- }
+/*
+ * static helper functions declaration
+ */
- return mappedTags[i].str;
+static const gchar *metadataparse_iptc_get_tag_from_iptc (IptcTag iptc,
+ GType * type, IptcRecord * record);
-}
static IptcTag
-metadataparse_iptc_get_iptc_from_tag (const gchar * tag, GType * type,
- IptcRecord * record)
-{
- int i = 0;
+metadatamux_iptc_get_iptc_from_tag (const gchar * tag, GType * type,
+ IptcRecord * record);
- while (mappedTags[i].iptc) {
- if (0 == strcmp (mappedTags[i].str, tag)) {
- *type = gst_tag_get_type (tag);
- *record = mappedTags[i].record;
- break;
- }
- ++i;
- }
+static void
+metadataparse_iptc_data_foreach_dataset_func (IptcDataSet * dataset,
+ void *user_data);
- return mappedTags[i].iptc;
+static void
+metadatamux_iptc_for_each_tag_in_list (const GstTagList * list,
+ const gchar * tag, gpointer user_data);
-}
+/*
+ * extern functions implementations
+ */
+
+/*
+ * metadataparse_iptc_tag_list_add:
+ * @taglist: tag list in which extracted tags will be added
+ * @mode: tag list merge mode
+ * @adapter: contains the IPTC metadata chunk
+ * @mapping: if is to extract individual tags and/or the whole chunk.
+ *
+ * This function gets a IPTC chunk (@adapter) and extract tags form it
+ * and then add to @taglist.
+ * Note: The IPTC chunk (@adapetr) must NOT be wrapped by any bytes specific
+ * to any file format
+ *
+ * Returns: nothing
+ */
void
metadataparse_iptc_tag_list_add (GstTagList * taglist, GstTagMergeMode mode,
@@ -171,8 +217,63 @@ metadataparse_iptc_tag_list_add (GstTagList * taglist, GstTagMergeMode mode,
goto done;
}
- iptc_data_foreach_dataset (iptc, iptc_data_foreach_dataset_func,
- (void *) &user_data);
+ iptc_data_foreach_dataset (iptc,
+ metadataparse_iptc_data_foreach_dataset_func, (void *) &user_data);
+
+done:
+
+ if (iptc)
+ iptc_data_unref (iptc);
+
+ return;
+
+}
+
+/*
+ * metadatamux_iptc_create_chunk_from_tag_list:
+ * @buf: buffer that will have the created IPTC chunk
+ * @size: size of the buffer that will be created
+ * @taglist: list of tags to be added to IPTC chunk
+ *
+ * Get tags from @taglist, create a IPTC chunk based on it and save to @buf.
+ * Note: The IPTC chunk is NOT wrapped by any bytes specific to any file format
+ *
+ * Returns: nothing
+ */
+
+void
+metadatamux_iptc_create_chunk_from_tag_list (guint8 ** buf, guint32 * size,
+ const GstTagList * taglist)
+{
+ IptcData *iptc = NULL;
+ GstBuffer *iptc_chunk = NULL;
+ const GValue *val = NULL;
+
+ if (!(buf && size))
+ goto done;
+ if (*buf) {
+ g_free (*buf);
+ *buf = NULL;
+ }
+ *size = 0;
+
+ val = gst_tag_list_get_value_index (taglist, GST_TAG_IPTC, 0);
+ if (val) {
+ iptc_chunk = gst_value_get_buffer (val);
+ if (iptc_chunk) {
+ iptc = iptc_data_new_from_data (GST_BUFFER_DATA (iptc_chunk),
+ GST_BUFFER_SIZE (iptc_chunk));
+ }
+ }
+
+ if (!iptc) {
+ iptc = iptc_data_new ();
+ }
+
+ gst_tag_list_foreach (taglist, metadatamux_iptc_for_each_tag_in_list, iptc);
+
+ iptc_data_save (iptc, buf, size);
+
done:
@@ -180,11 +281,100 @@ done:
iptc_data_unref (iptc);
return;
+}
+
+/*
+ * static helper functions implementation
+ */
+
+/*
+ * metadataparse_iptc_get_tag_from_iptc:
+ * @iptc: IPTC tag to look for
+ * @type: the type of the GStreamer tag mapped to @iptc
+ * @record: the place into IPTC chunk @iptc belongs to.
+ *
+ * This returns the GStreamer tag mapped to an IPTC tag.
+ *
+ * Returns:
+ * <itemizedlist>
+ * <listitem><para>The GStreamer tag mapped to the @iptc
+ * </para></listitem>
+ * <listitem><para>%NULL if there is no mapped GST tag for @iptc
+ * </para></listitem>
+ * </itemizedlist>
+ */
+
+static const gchar *
+metadataparse_iptc_get_tag_from_iptc (IptcTag iptc, GType * type,
+ IptcRecord * record)
+{
+ int i = 0;
+
+ while (mappedTags[i].iptc) {
+ if (iptc == mappedTags[i].iptc) {
+ *type = gst_tag_get_type (mappedTags[i].str);
+ *record = mappedTags[i].record;
+ break;
+ }
+ ++i;
+ }
+
+ return mappedTags[i].str;
}
+/*
+ * metadatamux_iptc_get_iptc_from_tag:
+ * @tag: GST tag to look for
+ * @type: the type of the GStreamer @tag
+ * @record: the place into IPTC chunk @iptc belongs to.
+ *
+ * This returns thet IPTC tag mapped to an GStreamer @tag.
+ *
+ * Returns:
+ * <itemizedlist>
+ * <listitem><para>The IPTC tag mapped to the GST @tag
+ * </para></listitem>
+ * <listitem><para>0 if there is no mapped IPTC tag for GST @tag
+ * </para></listitem>
+ * </itemizedlist>
+ */
+
+static IptcTag
+metadatamux_iptc_get_iptc_from_tag (const gchar * tag, GType * type,
+ IptcRecord * record)
+{
+ int i = 0;
+
+ while (mappedTags[i].iptc) {
+ if (0 == strcmp (mappedTags[i].str, tag)) {
+ *type = gst_tag_get_type (tag);
+ *record = mappedTags[i].record;
+ break;
+ }
+ ++i;
+ }
+
+ return mappedTags[i].iptc;
+
+}
+
+/*
+ * metadataparse_iptc_data_foreach_dataset_func:
+ * @dataset: IPTC structure from libiptcdata having IPTC tag
+ * @user_data: pointer to #MEUserData
+ *
+ * This function designed to be called for each IPTC tag in a IPTC chunk. This
+ * function gets the IPTC tag from @dataset and then add to the tag list
+ * in @user_data by using a merge mode also specified in @user_data
+ * @see_also: #metadataparse_iptc_tag_list_add
+ *
+ * Returns: nothing
+ */
+
static void
-iptc_data_foreach_dataset_func (IptcDataSet * dataset, void *user_data)
+metadataparse_iptc_data_foreach_dataset_func (IptcDataSet * dataset,
+ void *user_data)
{
char buf[1024];
@@ -213,10 +403,23 @@ done:
}
+/*
+ * metadatamux_iptc_for_each_tag_in_list:
+ * @list: GStreamer tag list from which @tag belongs to
+ * @tag: GStreamer tag to be added to the IPTC chunk
+ * @user_data: pointer to #IptcData in which the tag will be added
+ *
+ * This function designed to be called for each tag in GST tag list. This
+ * function adds get the tag value from tag @list and then add it to the IPTC
+ * chunk by using #IptcData and related functions from libiptcdata
+ * @see_also: #metadatamux_iptc_create_chunk_from_tag_list
+ *
+ * Returns: nothing
+ */
static void
-metadataiptc_for_each_tag_in_list (const GstTagList * list, const gchar * tag,
- gpointer user_data)
+metadatamux_iptc_for_each_tag_in_list (const GstTagList * list,
+ const gchar * tag, gpointer user_data)
{
IptcData *iptc = (IptcData *) user_data;
IptcTag iptc_tag;
@@ -226,7 +429,7 @@ metadataiptc_for_each_tag_in_list (const GstTagList * list, const gchar * tag,
gboolean new_dataset = FALSE;
gchar *tag_value = NULL;
- iptc_tag = metadataparse_iptc_get_iptc_from_tag (tag, &type, &record);
+ iptc_tag = metadatamux_iptc_get_iptc_from_tag (tag, &type, &record);
if (!iptc_tag)
goto done;
@@ -257,46 +460,5 @@ done:
iptc_dataset_unref (dataset);
}
-void
-metadatamux_iptc_create_chunk_from_tag_list (guint8 ** buf, guint32 * size,
- const GstTagList * taglist)
-{
- IptcData *iptc = NULL;
- GstBuffer *iptc_chunk = NULL;
- const GValue *val = NULL;
-
- if (!(buf && size))
- goto done;
- if (*buf) {
- g_free (*buf);
- *buf = NULL;
- }
- *size = 0;
-
- val = gst_tag_list_get_value_index (taglist, GST_TAG_IPTC, 0);
- if (val) {
- iptc_chunk = gst_value_get_buffer (val);
- if (iptc_chunk) {
- iptc = iptc_data_new_from_data (GST_BUFFER_DATA (iptc_chunk),
- GST_BUFFER_SIZE (iptc_chunk));
- }
- }
-
- if (!iptc) {
- iptc = iptc_data_new ();
- }
-
- gst_tag_list_foreach (taglist, metadataiptc_for_each_tag_in_list, iptc);
-
- iptc_data_save (iptc, buf, size);
-
-
-done:
-
- if (iptc)
- iptc_data_unref (iptc);
-
- return;
-}
#endif /* else (ifndef HAVE_IPTC) */
diff --git a/ext/metadata/metadataiptc.h b/ext/metadata/metadataiptc.h
index 0fa0a4d7..e13bb0ca 100644
--- a/ext/metadata/metadataiptc.h
+++ b/ext/metadata/metadataiptc.h
@@ -50,6 +50,10 @@
G_BEGIN_DECLS
+/*
+ * external function prototypes
+ */
+
extern void
metadataparse_iptc_tag_list_add (GstTagList * taglist, GstTagMergeMode mode,
GstAdapter * adapter, MetadataTagMapping mapping);
diff --git a/ext/metadata/metadataparsejpeg.c b/ext/metadata/metadataparsejpeg.c
index 3c2719da..ce6a161c 100644
--- a/ext/metadata/metadataparsejpeg.c
+++ b/ext/metadata/metadataparsejpeg.c
@@ -41,14 +41,62 @@
* Boston, MA 02111-1307, USA.
*/
-#include "metadataparsejpeg.h"
+/*
+ * SECTION: metadataparsejpeg
+ * @short_description: This module provides functions to parse JPEG files
+ *
+ * This module parses a JPEG stream finding metadata chunks, and marking them
+ * to be removed from the stream and saving them in a adapter.
+ *
+ * <refsect2>
+ * <para>
+ * #metadataparse_jpeg_init must be called before any other function in this
+ * module and must be paired with a call to #metadataparse_jpeg_dispose.
+ * #metadataparse_jpeg_parse is used to parse the stream (find the metadata
+ * chunks and the place it should be written to.
+ * #metadataparse_jpeg_lazy_update do nothing.
+ * </para>
+ * <para>
+ * This module tries to find metadata chunk until it reaches the "start of scan
+ * image". So if the metadata chunk, which could be EXIF, XMP or IPTC (inside
+ * Photoshop), is after the "start of scan image" it will not be found. This is
+ * 'cause of performance reason and 'cause we believe that files with metadata
+ * chunk after the "scan of image" chunk are very bad practice, so we don't
+ * worry about them.
+ * </para>
+ * <para>
+ * If it is working in non-parse_only mode, and the first chunk is a EXIF
+ * instead of a JFIF chunk, the EXIF chunk will be marked for removal and a new
+ * JFIF chunk will be create and marked to be injected as the first chunk.
+ * </para>
+ * </refsect2>
+ *
+ * Last reviewed on 2008-01-24 (0.10.15)
+ */
+
+/*
+ * includes
+ */
#include <string.h>
+#include "metadataparsejpeg.h"
+
#ifdef HAVE_IPTC
#include <libiptcdata/iptc-jpeg.h>
#endif
+/*
+ * defines and macros
+ */
+
+/* returns the current byte, advance to the next one and decrease the size */
+#define READ(buf, size) ( (size)--, *((buf)++) )
+
+/*
+ * static helper functions declaration
+ */
+
static MetadataParsingReturn
metadataparse_jpeg_reading (JpegParseData * jpeg_data, guint8 ** buf,
guint32 * bufsize, const guint32 offset, const guint8 * step_buf,
@@ -72,13 +120,30 @@ static MetadataParsingReturn
metadataparse_jpeg_jump (JpegParseData * jpeg_data, guint8 ** buf,
guint32 * bufsize, guint8 ** next_start, guint32 * next_size);
-#define READ(buf, size) ( (size)--, *((buf)++) )
+/*
+ * extern functions implementations
+ */
-void
-metadataparse_jpeg_lazy_update (JpegParseData * jpeg_data)
-{
- /* nothing to do */
-}
+/*
+ * metadataparse_jpeg_init:
+ * @jpeg_data: [in] jpeg data handler to be inited
+ * @exif_adpt: where to create/write an adapter to hold the EXIF chunk found
+ * @iptc_adpt: where to create/write an adapter to hold the IPTC chunk found
+ * @xmp_adpt: where to create/write an adapter to hold the XMP chunk found
+ * @strip_chunks: Array of chunks (offset and size) marked for removal
+ * @inject_chunks: Array of chunks (offset, data, size) marked for injection
+ * @parse_only: TRUE if it should only find the chunks and write then to the
+ * adapter (@exif_adpt, @iptc_adpt, @xmp_adpt). Or FALSE if should also put
+ * them on @strip_chunks.
+ *
+ * Init jpeg data handle.
+ * This function must be called before any other function from this module.
+ * This function must not be called twice without call to
+ * #metadataparse_jpeg_dispose beteween them.
+ * @see_also: #metadataparse_jpeg_dispose #metadataparse_jpeg_parse
+ *
+ * Returns: nothing
+ */
void
metadataparse_jpeg_init (JpegParseData * jpeg_data, GstAdapter ** exif_adpt,
@@ -100,6 +165,17 @@ metadataparse_jpeg_init (JpegParseData * jpeg_data, GstAdapter ** exif_adpt,
}
+/*
+ * metadataparse_jpeg_dispose:
+ * @jpeg_data: [in] jpeg data handler to be freed
+ *
+ * Call this function to free any resource allocated by
+ * #metadataparse_jpeg_init
+ * @see_also: #metadataparse_jpeg_init
+ *
+ * Returns: nothing
+ */
+
void
metadataparse_jpeg_dispose (JpegParseData * jpeg_data)
{
@@ -108,6 +184,42 @@ metadataparse_jpeg_dispose (JpegParseData * jpeg_data)
jpeg_data->xmp_adapter = NULL;
}
+/*
+ * metadata_parse:
+ * @jpeg_data: [in] jpeg data handle
+ * @buf: [in] data to be parsed
+ * @bufsize: [in] size of @buf in bytes
+ * @offset: is the offset where @buf starts from the beginnig of the whole
+ * stream
+ * @next_start: is a pointer after @buf which indicates where @buf should start
+ * on the next call to this function. It means, that after returning, this
+ * function has consumed *@next_start - @buf bytes. Which also means
+ * that @offset should also be incremanted by (*@next_start - @buf) for the
+ * next time.
+ * @next_size: [out] number of minimal bytes in @buf for the next call to this
+ * function
+ *
+ * This function is used to parse a JPEG stream step-by-step incrementally.
+ * Basically this function works like a state machine, that will run in a loop
+ * while there is still bytes in @buf to be read or it has finished parsing.
+ * If the it hasn't parsed yet and there is no more data in @buf, then the
+ * current state is saved and a indication will be make about the buffer to
+ * be passed by the caller function.
+ * @see_also: #metadataparse_jpeg_init
+ *
+ * Returns:
+ * <itemizedlist>
+ * <listitem><para>%META_PARSING_ERROR
+ * </para></listitem>
+ * <listitem><para>%META_PARSING_DONE if parse has finished. Now strip and
+ * inject chunks has been found
+ * </para></listitem>
+ * <listitem><para>%META_PARSING_NEED_MORE_DATA if this function should be
+ * called again (look @next_start and @next_size)
+ * </para></listitem>
+ * </itemizedlist>
+ */
+
MetadataParsingReturn
metadataparse_jpeg_parse (JpegParseData * jpeg_data, guint8 * buf,
guint32 * bufsize, const guint32 offset, guint8 ** next_start,
@@ -118,10 +230,19 @@ metadataparse_jpeg_parse (JpegParseData * jpeg_data, guint8 * buf,
guint8 mark[2] = { 0x00, 0x00 };
const guint8 *step_buf = buf;
+ /* step_buf holds where buf starts. this const value will be passed to
+ the nested parsing function, so those function knows how far they from
+ the initial buffer. This is not related to the beginning of the whole
+ stream, it is just related to the buf passed in this step to this
+ function */
+
*next_start = buf;
if (jpeg_data->state == JPEG_PARSE_NULL) {
+ /* only the first time this function is called it will verify the stream
+ type to be sure it is a JPEG */
+
if (*bufsize < 2) {
*next_size = (buf - *next_start) + 2;
ret = META_PARSING_NEED_MORE_DATA;
@@ -184,6 +305,68 @@ done:
}
+/*
+ * metadataparse_jpeg_lazy_update:
+ * @jpeg_data: [in] jpeg data handle
+ *
+ * This function do nothing
+ * @see_also: metadata_lazy_update
+ *
+ * Returns: nothing
+ */
+
+void
+metadataparse_jpeg_lazy_update (JpegParseData * jpeg_data)
+{
+ /* nothing to do */
+}
+
+/*
+ * static helper functions implementation
+ */
+
+/*
+ * metadataparse_jpeg_reading:
+ * @jpeg_data: [in] jpeg data handle
+ * @buf: [in] data to be parsed. @buf will increment during the parsing step.
+ * So it will hold the next byte to be read inside a parsing function or on
+ * the next nested parsing function. And so, @bufsize will decrement.
+ * @bufsize: [in] size of @buf in bytes. This value will decrement during the
+ * parsing for the same reason that @buf will advance.
+ * @offset: is the offset where @step_buf starts from the beginnig of the
+ * stream
+ * @step_buf: holds the pointer to the buffer passed to
+ * #metadataparse_jpeg_parse. It means that any point inside this function
+ * the offset (related to the beginning of the whole stream) after the last
+ * byte read so far is "(*buf - step_buf) + offset"
+ * @next_start: is a pointer after @step_buf which indicates where the next
+ * call to #metadataparse_jpeg_parse should start on the next call to this
+ * function. It means, that after return, this function has
+ * consumed *@next_start - @buf bytes. Which also means that @offset should
+ * also be incremanted by (*@next_start - @buf) for the next time.
+ * @next_size: [out] number of minimal bytes in @buf for the next call to this
+ * function
+ *
+ * This function is used to parse a JPEG stream step-by-step incrementally.
+ * If this function finds a EXIF, IPTC or XMP chunk (or a chunk that should be
+ * jumped), then it changes the state of the parsing process so that the
+ * remaing parsing can be done by another more specialized function.
+ * @see_also: #metadataparse_jpeg_init #metadataparse_jpeg_exif
+ * #metadataparse_jpeg_iptc #metadataparse_jpeg_xmp #metadataparse_jpeg_jump
+ *
+ * Returns:
+ * <itemizedlist>
+ * <listitem><para>%META_PARSING_ERROR
+ * </para></listitem>
+ * <listitem><para>%META_PARSING_DONE if parse has finished. Now strip and
+ * inject chunks has been found. Or some chunk has been found and should be
+ * held or jumped.
+ * </para></listitem>
+ * <listitem><para>%META_PARSING_NEED_MORE_DATA if this function should be
+ * called again (look @next_start and @next_size)
+ * </para></listitem>
+ * </itemizedlist>
+ */
/* look for markers */
static MetadataParsingReturn
@@ -218,7 +401,8 @@ metadataparse_jpeg_reading (JpegParseData * jpeg_data, guint8 ** buf,
ret = META_PARSING_DONE;
jpeg_data->state = JPEG_PARSE_DONE;
goto done;
- } else if (mark[1] == 0xDA) { /* start of scan, lets not look behinf of this */
+ } else if (mark[1] == 0xDA) {
+ /* start of scan image, lets not look behind of this */
ret = META_PARSING_DONE;
jpeg_data->state = JPEG_PARSE_DONE;
goto done;
@@ -264,7 +448,9 @@ metadataparse_jpeg_reading (JpegParseData * jpeg_data, guint8 ** buf,
if (!jpeg_data->parse_only) {
memset (&chunk, 0x00, sizeof (MetadataChunk));
- chunk.offset_orig = (*buf - step_buf) + offset - 4; /* maker + size */
+
+ chunk.offset_orig = (*buf - step_buf) + offset - 4; /* 4 == maker + size */
+
chunk.size = chunk_size + 2; /* chunk size plus app marker */
chunk.type = MD_CHUNK_EXIF;
@@ -276,15 +462,15 @@ metadataparse_jpeg_reading (JpegParseData * jpeg_data, guint8 ** buf,
if (!jpeg_data->jfif_found) {
/* only inject if no JFIF has been found */
- static const guint8 segment[] = { 0xff, 0xe0, 0x00, 0x10,
- 0x4a, 0x46, 0x49, 0x46, 0x00,
- 0x01, 0x02,
- 0x00, 0x00, 0x01, 0x00, 0x01,
- 0x00, 0x00
- };
-
if (!jpeg_data->parse_only) {
+ static const guint8 segment[] = { 0xff, 0xe0, 0x00, 0x10,
+ 0x4a, 0x46, 0x49, 0x46, 0x00,
+ 0x01, 0x02,
+ 0x00, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x00
+ };
+
memset (&chunk, 0x00, sizeof (MetadataChunk));
chunk.offset_orig = 2;
chunk.size = 18;
@@ -321,7 +507,7 @@ metadataparse_jpeg_reading (JpegParseData * jpeg_data, guint8 ** buf,
MetadataChunk chunk;
memset (&chunk, 0x00, sizeof (MetadataChunk));
- chunk.offset_orig = (*buf - step_buf) + offset - 4; /* maker + size */
+ chunk.offset_orig = (*buf - step_buf) + offset - 4; /* 4 == maker + size */
chunk.size = chunk_size + 2; /* chunk size plus app marker */
chunk.type = MD_CHUNK_XMP;
@@ -344,7 +530,9 @@ metadataparse_jpeg_reading (JpegParseData * jpeg_data, guint8 ** buf,
}
}
#ifdef HAVE_IPTC
- else if (mark[1] == 0xED) { /* may be it is photoshop and may be there is iptc */
+ else if (mark[1] == 0xED) {
+ /* may be it is photoshop and may be there is iptc */
+
if (chunk_size >= 16) { /* size2 "Photoshop 3.0" */
if (*bufsize < 14) {
@@ -361,7 +549,7 @@ metadataparse_jpeg_reading (JpegParseData * jpeg_data, guint8 ** buf,
MetadataChunk chunk;
memset (&chunk, 0x00, sizeof (MetadataChunk));
- chunk.offset_orig = (*buf - step_buf) + offset - 4; /* maker + size */
+ chunk.offset_orig = (*buf - step_buf) + offset - 4; /* 4 == maker + size */
chunk.size = chunk_size + 2; /* chunk size plus app marker */
chunk.type = MD_CHUNK_IPTC;
@@ -400,6 +588,43 @@ done:
}
+/*
+ * metadataparse_jpeg_exif:
+ * @jpeg_data: [in] jpeg data handle
+ * @buf: [in] data to be parsed
+ * @bufsize: [in] size of @buf in bytes
+ * @next_start: look at #metadataparse_jpeg_reading
+ * @next_size: look at #metadataparse_jpeg_reading
+ * NOTE: To have a explanation of each parameters of this function look at
+ * the documentation of #metadataparse_jpeg_reading
+ *
+ * This function saves the EXIF chunk to @jpeg_data->exif_adapter and makes the
+ * parsing process point to the next buffer after the EXIF chunk.
+ * This function will be called by the parsing process 'cause at some point
+ * #metadataparse_jpeg_reading found out the EXIF chunk, skipped the JPEG
+ * wrapper bytes and changed the state of parsing process to JPEG_PARSE_EXIF.
+ * Which just happens if @jpeg_data->parse_only is FALSE and there is a EXIF
+ * chunk into the stream and @jpeg_data->exif_adapter is not NULL.
+ * This function will just be called once even if there is more than one EXIF
+ * chunk in the stream. This function do it by setting @jpeg_data->exif_adapter
+ * to NULL.
+ * After this function has completely parsed (hold) the chunk, it changes the
+ * parsing state back to JPEG_PARSE_READING which makes
+ * #metadataparse_jpeg_reading to be called again
+ * @see_also: #metadataparse_util_hold_chunk #metadataparse_jpeg_reading
+ *
+ * Returns:
+ * <itemizedlist>
+ * <listitem><para>%META_PARSING_ERROR
+ * </para></listitem>
+ * <listitem><para>%META_PARSING_DONE if the chunk bas been completely hold
+ * </para></listitem>
+ * <listitem><para>%META_PARSING_NEED_MORE_DATA if this function should be
+ * called again (look @next_start and @next_size)
+ * </para></listitem>
+ * </itemizedlist>
+ */
+
static MetadataParsingReturn
metadataparse_jpeg_exif (JpegParseData * jpeg_data, guint8 ** buf,
guint32 * bufsize, guint8 ** next_start, guint32 * next_size)
@@ -419,6 +644,15 @@ metadataparse_jpeg_exif (JpegParseData * jpeg_data, guint8 ** buf,
}
+/*
+ * metadataparse_jpeg_iptc:
+ *
+ * Look at #metadataparse_jpeg_exif. This function has the same behavior as
+ * that. The only difference is that this function also cut out others
+ * PhotoShop data and only holds IPTC data in it.
+ *
+ */
+
#ifdef HAVE_IPTC
static MetadataParsingReturn
metadataparse_jpeg_iptc (JpegParseData * jpeg_data, guint8 ** buf,
@@ -443,6 +677,7 @@ metadataparse_jpeg_iptc (JpegParseData * jpeg_data, guint8 ** buf,
size = gst_adapter_available (*jpeg_data->iptc_adapter);
buf = gst_adapter_peek (*jpeg_data->iptc_adapter, size);
+ /* FIXME: currently we are trhowing away others PhotoShop data */
res = iptc_jpeg_ps3_find_iptc (buf, size, &iptc_len);
if (res < 0) {
@@ -473,6 +708,14 @@ metadataparse_jpeg_iptc (JpegParseData * jpeg_data, guint8 ** buf,
}
#endif
+/*
+ * metadataparse_jpeg_xmp:
+ *
+ * Look at #metadataparse_jpeg_exif. This function has the same behavior as
+ * that.
+ *
+ */
+
static MetadataParsingReturn
metadataparse_jpeg_xmp (JpegParseData * jpeg_data, guint8 ** buf,
guint32 * bufsize, guint8 ** next_start, guint32 * next_size)
@@ -490,6 +733,32 @@ metadataparse_jpeg_xmp (JpegParseData * jpeg_data, guint8 ** buf,
return ret;
}
+/*
+ * metadataparse_jpeg_jump:
+ * @jpeg_data: [in] jpeg data handle
+ * @buf: [in] data to be parsed
+ * @bufsize: [in] size of @buf in bytes
+ * @next_start: look at #metadataparse_jpeg_reading
+ * @next_size: look at #metadataparse_jpeg_reading
+ * NOTE: To have a explanation of each parameters of this function look at
+ * the documentation of #metadataparse_jpeg_reading
+ *
+ * This function just makes a chunk we are not interested in to be jumped.
+ * This is done basically by incrementing @next_start and @buf,
+ * and decreasing @bufsize and setting the next parsing state properly.
+ * @see_also: #metadataparse_jpeg_reading #metadataparse_util_jump_chunk
+ *
+ * Returns:
+ * <itemizedlist>
+ * <listitem><para>%META_PARSING_DONE if bytes has been skiped and there is
+ * still data in @buf
+ * </para></listitem>
+ * <listitem><para>%META_PARSING_NEED_MORE_DATA if the skiped bytes end at
+ * some point after @buf + @bufsize
+ * </para></listitem>
+ * </itemizedlist>
+ */
+
static MetadataParsingReturn
metadataparse_jpeg_jump (JpegParseData * jpeg_data, guint8 ** buf,
guint32 * bufsize, guint8 ** next_start, guint32 * next_size)
diff --git a/ext/metadata/metadataparsejpeg.h b/ext/metadata/metadataparsejpeg.h
index e00095d9..5f85d8f8 100644
--- a/ext/metadata/metadataparsejpeg.h
+++ b/ext/metadata/metadataparsejpeg.h
@@ -44,12 +44,20 @@
#ifndef __METADATAPARSE_JPEG_H__
#define __METADATAPARSE_JPEG_H__
+/*
+ * includes
+ */
+
#include <gst/base/gstadapter.h>
#include "metadatatypes.h"
G_BEGIN_DECLS
+/*
+ * enum and types
+ */
+
typedef enum _tag_JpegParseState
{
JPEG_PARSE_NULL,
@@ -79,6 +87,9 @@ typedef struct _tag_JpegParseData
gboolean jfif_found;
} JpegParseData;
+/*
+ * external function prototypes
+ */
extern void
metadataparse_jpeg_init (JpegParseData * jpeg_data, GstAdapter ** exif_adpt,
@@ -88,11 +99,12 @@ metadataparse_jpeg_init (JpegParseData * jpeg_data, GstAdapter ** exif_adpt,
extern void metadataparse_jpeg_dispose (JpegParseData * jpeg_data);
-extern void metadataparse_jpeg_lazy_update (JpegParseData * jpeg_data);
-
extern MetadataParsingReturn
metadataparse_jpeg_parse (JpegParseData * jpeg_data, guint8 * buf,
- guint32 * bufsize, const guint32 offset, guint8 ** next_start, guint32 * next_size);
+ guint32 * bufsize, const guint32 offset, guint8 ** next_start,
+ guint32 * next_size);
+
+extern void metadataparse_jpeg_lazy_update (JpegParseData * jpeg_data);
G_END_DECLS
#endif /* __METADATAPARSE_JPEG_H__ */
diff --git a/ext/metadata/metadataparsepng.c b/ext/metadata/metadataparsepng.c
index a5de4dbe..b5a8e775 100644
--- a/ext/metadata/metadataparsepng.c
+++ b/ext/metadata/metadataparsepng.c
@@ -41,10 +41,45 @@
* Boston, MA 02111-1307, USA.
*/
+/*
+ * SECTION: metadataparsepng
+ * @short_description: This module provides functions to parse PNG files
+ *
+ * This module parses a PNG stream finding XMP metadata chunks, and marking
+ * them to be removed from the stream and saving the XMP chunk in a adapter.
+ *
+ * <refsect2>
+ * <para>
+ * #metadataparse_png_init must be called before any other function in this
+ * module and must be paired with a call to #metadataparse_png_dispose.
+ * #metadataparse_png_parse is used to parse the stream (find the metadata
+ * chunks and the place it should be written to.
+ * #metadataparse_png_lazy_update do nothing.
+ * </para>
+ * </refsect2>
+ *
+ * Last reviewed on 2008-01-24 (0.10.15)
+ */
+
+/*
+ * includes
+ */
+
#include "metadataparsepng.h"
#include <string.h>
+/*
+ * defines and macros
+ */
+
+/* returns the current byte, advance to the next one and decrease the size */
+#define READ(buf, size) ( (size)--, *((buf)++) )
+
+/*
+ * static helper functions declaration
+ */
+
static MetadataParsingReturn
metadataparse_png_reading (PngParseData * png_data, guint8 ** buf,
guint32 * bufsize, const guint32 offset, const guint8 * step_buf,
@@ -58,13 +93,30 @@ static MetadataParsingReturn
metadataparse_png_jump (PngParseData * png_data, guint8 ** buf,
guint32 * bufsize, guint8 ** next_start, guint32 * next_size);
-#define READ(buf, size) ( (size)--, *((buf)++) )
+/*
+ * extern functions implementations
+ */
-void
-metadataparse_png_lazy_update (PngParseData * jpeg_data)
-{
- /* nothing to do */
-}
+/*
+ * metadataparse_png_init:
+ * @png_data: [in] png data handler to be inited
+ * @exif_adpt: ignored
+ * @iptc_adpt: ignored
+ * @xmp_adpt: where to create/write an adapter to hold the XMP chunk found
+ * @strip_chunks: Array of chunks (offset and size) marked for removal
+ * @inject_chunks: Array of chunks (offset, data, size) marked for injection
+ * @parse_only: TRUE if it should only find the chunks and write then to the
+ * adapter (@xmp_adpt). Or FALSE if should also put
+ * them on @strip_chunks.
+ *
+ * Init png data handle.
+ * This function must be called before any other function from this module.
+ * This function must not be called twice without call to
+ * #metadataparse_png_dispose beteween them.
+ * @see_also: #metadataparse_png_dispose #metadataparse_png_parse
+ *
+ * Returns: nothing
+ */
void
metadataparse_png_init (PngParseData * png_data, GstAdapter ** exif_adpt,
@@ -82,12 +134,59 @@ metadataparse_png_init (PngParseData * png_data, GstAdapter ** exif_adpt,
}
+/*
+ * metadataparse_png_dispose:
+ * @png_data: [in] png data handler to be freed
+ *
+ * Call this function to free any resource allocated by
+ * #metadataparse_png_init
+ * @see_also: #metadataparse_png_init
+ *
+ * Returns: nothing
+ */
+
void
metadataparse_png_dispose (PngParseData * png_data)
{
png_data->xmp_adapter = NULL;
}
+/*
+ * metadata_parse:
+ * @png_data: [in] png data handle
+ * @buf: [in] data to be parsed
+ * @bufsize: [in] size of @buf in bytes
+ * @offset: is the offset where @buf starts from the beginnig of the whole
+ * stream.
+ * @next_start: is a pointer after @buf which indicates where @buf should start
+ * on the next call to this function. It means, that after returning, this
+ * function has consumed *@next_start - @buf bytes. Which also means
+ * that @offset should also be incremanted by (*@next_start - @buf) for the
+ * next time.
+ * @next_size: [out] number of minimal bytes in @buf for the next call to this
+ * function
+ *
+ * This function is used to parse a PNG stream step-by-step incrementally.
+ * Basically this function works like a state machine, that will run in a loop
+ * while there is still bytes in @buf to be read or it has finished parsing.
+ * If the it hasn't parsed yet and there is no more data in @buf, then the
+ * current state is saved and a indication will be make about the buffer to
+ * be passed by the caller function.
+ * @see_also: #metadataparse_png_init
+ *
+ * Returns:
+ * <itemizedlist>
+ * <listitem><para>%META_PARSING_ERROR
+ * </para></listitem>
+ * <listitem><para>%META_PARSING_DONE if parse has finished. Now strip and
+ * inject chunks has been found
+ * </para></listitem>
+ * <listitem><para>%META_PARSING_NEED_MORE_DATA if this function should be
+ * called again (look @next_start and @next_size)
+ * </para></listitem>
+ * </itemizedlist>
+ */
+
MetadataParsingReturn
metadataparse_png_parse (PngParseData * png_data, guint8 * buf,
guint32 * bufsize, const guint32 offset, guint8 ** next_start,
@@ -98,10 +197,19 @@ metadataparse_png_parse (PngParseData * png_data, guint8 * buf,
guint8 mark[8];
const guint8 *step_buf = buf;
+ /* step_buf holds where buf starts. this const value will be passed to
+ the nested parsing function, so those function knows how far they from
+ the initial buffer. This is not related to the beginning of the whole
+ stream, it is just related to the buf passed in this step to this
+ function */
+
*next_start = buf;
if (png_data->state == PNG_PARSE_NULL) {
+ /* only the first time this function is called it will verify the stream
+ type to be sure it is a PNG */
+
if (*bufsize < 8) {
*next_size = (buf - *next_start) + 8;
ret = META_PARSING_NEED_MORE_DATA;
@@ -117,9 +225,9 @@ metadataparse_png_parse (PngParseData * png_data, guint8 * buf,
mark[6] = READ (buf, *bufsize);
mark[7] = READ (buf, *bufsize);
- if (mark[0] != 0x89 || mark[1] != 0x50 || mark[2] != 0x4E || mark[3] != 0x47
- || mark[4] != 0x0D || mark[5] != 0x0A || mark[6] != 0x1A
- || mark[7] != 0x0A) {
+ if (mark[0] != 0x89 || mark[1] != 0x50 || mark[2] != 0x4E ||
+ mark[3] != 0x47 || mark[4] != 0x0D || mark[5] != 0x0A ||
+ mark[6] != 0x1A || mark[7] != 0x0A) {
ret = META_PARSING_ERROR;
goto done;
}
@@ -160,6 +268,68 @@ done:
}
+/*
+ * metadataparse_png_lazy_update:
+ * @png_data: [in] png data handle
+ *
+ * This function do nothing
+ * @see_also: metadata_lazy_update
+ *
+ * Returns: nothing
+ */
+
+void
+metadataparse_png_lazy_update (PngParseData * png_data)
+{
+ /* nothing to do */
+}
+
+/*
+ * static helper functions implementation
+ */
+
+/*
+ * metadataparse_png_reading:
+ * @png_data: [in] png data handle
+ * @buf: [in] data to be parsed. @buf will increment during the parsing step.
+ * So it will hold the next byte to be read inside a parsing function or on
+ * the next nested parsing function. And so, @bufsize will decrement.
+ * @bufsize: [in] size of @buf in bytes. This value will decrement during the
+ * parsing for the same reason that @buf will advance.
+ * @offset: is the offset where @step_buf starts from the beginnig of the
+ * stream
+ * @step_buf: holds the pointer to the buffer passed to
+ * #metadataparse_png_parse. It means that any point inside this function
+ * the offset (related to the beginning of the whole stream) after the last
+ * byte read so far is "(*buf - step_buf) + offset"
+ * @next_start: is a pointer after @step_buf which indicates where the next
+ * call to #metadataparse_png_parse should start on the next call to this
+ * function. It means, that after return, this function has
+ * consumed *@next_start - @buf bytes. Which also means that @offset should
+ * also be incremanted by (*@next_start - @buf) for the next time.
+ * @next_size: [out] number of minimal bytes in @buf for the next call to this
+ * function
+ *
+ * This function is used to parse a PNG stream step-by-step incrementally.
+ * If this function finds a XMP chunk (or a chunk that should be
+ * jumped), then it changes the state of the parsing process so that the
+ * remaing parsing can be done by another more specialized function.
+ * @see_also: #metadataparse_png_init #metadataparse_png_xmp
+ * #metadataparse_png_jump
+ *
+ * Returns:
+ * <itemizedlist>
+ * <listitem><para>%META_PARSING_ERROR
+ * </para></listitem>
+ * <listitem><para>%META_PARSING_DONE if parse has finished. Now strip and
+ * inject chunks has been found. Or some chunk has been found and should be
+ * held or jumped.
+ * </para></listitem>
+ * <listitem><para>%META_PARSING_NEED_MORE_DATA if this function should be
+ * called again (look @next_start and @next_size)
+ * </para></listitem>
+ * </itemizedlist>
+ */
/* look for markers */
static MetadataParsingReturn
@@ -213,7 +383,7 @@ metadataparse_png_reading (PngParseData * png_data, guint8 ** buf,
memset (&chunk, 0x00, sizeof (MetadataChunk));
chunk.offset_orig = (*buf - step_buf) + offset - 8; /* maker + size */
- chunk.size = chunk_size + 12; /* chunk size plus app marker plus crc */
+ chunk.size = chunk_size + 12; /* chunk size + app marker + crc */
chunk.type = MD_CHUNK_XMP;
metadata_chunk_array_append_sorted (png_data->strip_chunks, &chunk);
@@ -221,9 +391,10 @@ metadataparse_png_reading (PngParseData * png_data, guint8 ** buf,
/* if adapter has been provided, prepare to hold chunk */
if (png_data->xmp_adapter) {
- *buf += 22; /* jump "XML:com.adobe.xmp" plus some flags */
+ *buf += 22; /* jump "XML:com.adobe.xmp" + some flags */
*bufsize -= 22;
- png_data->read = chunk_size - 22; /* four CRC bytes at the end will be jumped after */
+ /* four CRC bytes at the end will be jumped after */
+ png_data->read = chunk_size - 22;
png_data->state = PNG_PARSE_XMP;
ret = META_PARSING_DONE;
goto done;
@@ -244,14 +415,44 @@ done:
}
-static MetadataParsingReturn
-metadataparse_png_jump (PngParseData * png_data, guint8 ** buf,
- guint32 * bufsize, guint8 ** next_start, guint32 * next_size)
-{
- png_data->state = PNG_PARSE_READING;
- return metadataparse_util_jump_chunk (&png_data->read, buf,
- bufsize, next_start, next_size);
-}
+/*
+ * metadataparse_png_xmp:
+ * @png_data: [in] png data handle
+ * @buf: [in] data to be parsed
+ * @bufsize: [in] size of @buf in bytes
+ * @next_start: look at #metadataparse_png_reading
+ * @next_size: look at #metadataparse_png_reading
+ * NOTE: To have a explanation of each parameters of this function look at
+ * the documentation of #metadataparse_png_reading
+ *
+ * This function saves the XMP chunk to @png_data->xmp_adapter and makes the
+ * parsing process point to the next buffer after the XMP chunk.
+ * This function will be called by the parsing process 'cause at some point
+ * #metadataparse_png_reading found out the XMP chunk, skipped the PNG
+ * wrapper bytes and changed the state of parsing process to PNG_PARSE_XMP.
+ * Which just happens if @png_data->parse_only is FALSE and there is a XMP
+ * chunk into the stream and @png_data->xmp_adapter is not NULL.
+ * This function will just be called once even if there is more than one XMP
+ * chunk in the stream. This function do it by setting @png_data->xmp_adapter
+ * to NULL.
+ * After this function has completely parsed (hold) the chunk, it changes the
+ * parsing state to PNG_PARSE_JUMP which makes
+ * #metadataparse_png_jump to be called in order to jumo the remaing 4 CRC
+ * bytes
+ * @see_also: #metadataparse_util_hold_chunk #metadataparse_png_reading
+ * #metadataparse_png_jump
+ *
+ * Returns:
+ * <itemizedlist>
+ * <listitem><para>%META_PARSING_ERROR
+ * </para></listitem>
+ * <listitem><para>%META_PARSING_DONE if the chunk bas been completely hold
+ * </para></listitem>
+ * <listitem><para>%META_PARSING_NEED_MORE_DATA if this function should be
+ * called again (look @next_start and @next_size)
+ * </para></listitem>
+ * </itemizedlist>
+ */
static MetadataParsingReturn
metadataparse_png_xmp (PngParseData * png_data, guint8 ** buf,
@@ -271,3 +472,38 @@ metadataparse_png_xmp (PngParseData * png_data, guint8 ** buf,
return ret;
}
+
+/*
+ * metadataparse_png_jump:
+ * @png_data: [in] png data handle
+ * @buf: [in] data to be parsed
+ * @bufsize: [in] size of @buf in bytes
+ * @next_start: look at #metadataparse_png_reading
+ * @next_size: look at #metadataparse_png_reading
+ * NOTE: To have a explanation of each parameters of this function look at
+ * the documentation of #metadataparse_png_reading
+ *
+ * This function just makes a chunk we are not interested in to be jumped.
+ * This is done basically by incrementing @next_start and @buf,
+ * and decreasing @bufsize and setting the next parsing state properly.
+ * @see_also: #metadataparse_png_reading #metadataparse_util_jump_chunk
+ *
+ * Returns:
+ * <itemizedlist>
+ * <listitem><para>%META_PARSING_DONE if bytes has been skiped and there is
+ * still data in @buf
+ * </para></listitem>
+ * <listitem><para>%META_PARSING_NEED_MORE_DATA if the skiped bytes end at
+ * some point after @buf + @bufsize
+ * </para></listitem>
+ * </itemizedlist>
+ */
+
+static MetadataParsingReturn
+metadataparse_png_jump (PngParseData * png_data, guint8 ** buf,
+ guint32 * bufsize, guint8 ** next_start, guint32 * next_size)
+{
+ png_data->state = PNG_PARSE_READING;
+ return metadataparse_util_jump_chunk (&png_data->read, buf,
+ bufsize, next_start, next_size);
+}
diff --git a/ext/metadata/metadataparsepng.h b/ext/metadata/metadataparsepng.h
index 26f87294..8e2b1d62 100644
--- a/ext/metadata/metadataparsepng.h
+++ b/ext/metadata/metadataparsepng.h
@@ -44,12 +44,20 @@
#ifndef __METADATAPARSE_PNG_H__
#define __METADATAPARSE_PNG_H__
+/*
+ * includes
+ */
+
#include <gst/base/gstadapter.h>
#include "metadatatypes.h"
G_BEGIN_DECLS
+/*
+ * enum and types
+ */
+
typedef enum _tag_PngParseState
{
PNG_PARSE_NULL,
@@ -73,6 +81,9 @@ typedef struct _tag_PngParseData
guint32 read;
} PngParseData;
+/*
+ * external function prototypes
+ */
extern void
metadataparse_png_init (PngParseData * png_data, GstAdapter ** exif_adpt,
@@ -82,11 +93,12 @@ metadataparse_png_init (PngParseData * png_data, GstAdapter ** exif_adpt,
extern void metadataparse_png_dispose (PngParseData * png_data);
-extern void metadataparse_png_lazy_update (PngParseData * jpeg_data);
+extern void metadataparse_png_lazy_update (PngParseData * png_data);
extern MetadataParsingReturn
metadataparse_png_parse (PngParseData * png_data, guint8 * buf,
- guint32 * bufsize, const guint32 offset, guint8 ** next_start, guint32 * next_size);
+ guint32 * bufsize, const guint32 offset, guint8 ** next_start,
+ guint32 * next_size);
G_END_DECLS
#endif /* __METADATAPARSE_PNG_H__ */
diff --git a/ext/metadata/metadataparseutil.c b/ext/metadata/metadataparseutil.c
index dbb1618d..794d534a 100644
--- a/ext/metadata/metadataparseutil.c
+++ b/ext/metadata/metadataparseutil.c
@@ -41,9 +41,38 @@
* Boston, MA 02111-1307, USA.
*/
+/*
+ * SECTION: metadataparseutil
+ * @short_description: This module has some util function for parsing.
+ *
+ * Last reviewed on 2008-01-24 (0.10.15)
+ */
+
+/*
+ * includes
+ */
+
#include "metadataparseutil.h"
#include <string.h>
+/*
+ * extern functions implementations
+ */
+
+/*
+ * metadataparse_util_tag_list_add_chunk:
+ * @taglist: tag list in which the whole chunk tag will be added
+ * @mode: GStreamer merge mode
+ * @name: name of the tag (ex: GST_TAG_EXIF, GST_TAG_IPTC, GST_TAG_XMP)
+ * @adapter: contains the whole chunk to be added as tag to @taglist
+ *
+ * This function get all the bytes from @adapter, create a GST_BUFFER, copy
+ * the bytes to it and then add it to @taglist as a tage @name using a
+ * merge @mode.
+ *
+ * Returns: nothing.
+ */
+
void
metadataparse_util_tag_list_add_chunk (GstTagList * taglist,
GstTagMergeMode mode, const gchar * name, GstAdapter * adapter)
@@ -64,6 +93,39 @@ metadataparse_util_tag_list_add_chunk (GstTagList * taglist,
}
+/*
+ * metadataparse_util_hold_chunk:
+ * @read: number of bytes that still need to be hold
+ * @buf: [in] data to be parsed
+ * @bufsize: [in] size of @buf in bytes
+ * @next_start: indicates a pointer after the @buf where the next parsing step
+ * should start from
+ * @next_size: indicates the minimal size of the the buffer to be given on
+ * the next call to the parser
+ * @adapter: adapter to hold the chunk
+ * NOTE: To have a explanation of each parameters of this function look at the
+ * documentation of #metadataparse_jpeg_reading or #metadataparse_png_reading
+ *
+ * This function holds a chunk into the adapter. If there is enough bytes
+ * (*@read > *@bufsize), then it just hold and make the parser continue after
+ * the chunk by setting @next_start properly. Otherwise, if there is not
+ * enough bytes in @buf, it just set @next_start and @next_size, to make the
+ * parse return META_PARSING_NEED_MORE_DATA and request the caller the proper
+ * offset and size, so in the sencond time this function is called it should
+ * (or must) have enough data hold the whole chunk.
+ *
+ * Returns:
+ * <itemizedlist>
+ * <listitem><para>%META_PARSING_ERROR
+ * </para></listitem>
+ * <listitem><para>%META_PARSING_DONE if the chunk bas been completely hold
+ * </para></listitem>
+ * <listitem><para>%META_PARSING_NEED_MORE_DATA if this function should be
+ * called again (look @next_start and @next_size)
+ * </para></listitem>
+ * </itemizedlist>
+ */
+
MetadataParsingReturn
metadataparse_util_hold_chunk (guint32 * read, guint8 ** buf,
guint32 * bufsize, guint8 ** next_start,
@@ -95,6 +157,16 @@ metadataparse_util_hold_chunk (guint32 * read, guint8 ** buf,
return ret;
}
+/*
+ * metadataparse_util_jump_chunk:
+ * NOTE: To have a explanation of each parameters of this function look at
+ * the documentation of #metadataparse_util_hold_chunk
+ *
+ * This function works in the same way as #metadataparse_util_hold_chunk, but
+ * just skip the bytes instead of also hold it
+ *
+ */
+
MetadataParsingReturn
metadataparse_util_jump_chunk (guint32 * read, guint8 ** buf,
guint32 * bufsize, guint8 ** next_start, guint32 * next_size)
diff --git a/ext/metadata/metadataparseutil.h b/ext/metadata/metadataparseutil.h
index 1dac942c..e24341bd 100644
--- a/ext/metadata/metadataparseutil.h
+++ b/ext/metadata/metadataparseutil.h
@@ -44,6 +44,10 @@
#ifndef __GST_METADATAPARSE_UTIL_H__
#define __GST_METADATAPARSE_UTIL_H__
+/*
+ * includes
+ */
+
#include <gst/gst.h>
#include <gst/base/gstadapter.h>
@@ -51,6 +55,10 @@
G_BEGIN_DECLS
+/*
+ * external function prototypes
+ */
+
extern void
metadataparse_util_tag_list_add_chunk (GstTagList * taglist,
GstTagMergeMode mode, const gchar * name, GstAdapter * adapter);
diff --git a/ext/metadata/metadatatags.c b/ext/metadata/metadatatags.c
index fbbdc328..b7807033 100644
--- a/ext/metadata/metadatatags.c
+++ b/ext/metadata/metadatatags.c
@@ -41,9 +41,68 @@
* Boston, MA 02111-1307, USA.
*/
+/*
+ * SECTION: metadatatags
+ * @short_description: This module contains has tag definitions to be mapped
+ * to EXIF, IPTC and XMP tags.
+ *
+ * This module register tags need for image metadata but aren't already define
+ * in GStreamer base. So, the EXIF, IPTC and XMP tags can be mapped to tags
+ * not registered in this file (tags already in GST base)
+ *
+ * Last reviewed on 2008-01-24 (0.10.15)
+ */
+
+/*
+ * includes
+ */
+
#include "metadatatags.h"
/*
+ * static helper functions declaration
+ */
+
+static void metadata_tags_exif_register (void);
+
+static void metadata_tags_iptc_register (void);
+
+static void metadata_tags_xmp_register (void);
+
+/*
+ * extern functions implementations
+ */
+
+void
+metadata_tags_register (void)
+{
+
+ /* whole chunk tags */
+
+ gst_tag_register (GST_TAG_EXIF, GST_TAG_FLAG_META,
+ GST_TYPE_BUFFER, GST_TAG_EXIF, "exif metadata chunk", NULL);
+
+ gst_tag_register (GST_TAG_IPTC, GST_TAG_FLAG_META,
+ GST_TYPE_BUFFER, GST_TAG_IPTC, "iptc metadata chunk", NULL);
+
+ gst_tag_register (GST_TAG_XMP, GST_TAG_FLAG_META,
+ GST_TYPE_BUFFER, GST_TAG_XMP, "xmp metadata chunk", NULL);
+
+ /* tags related to some metadata */
+
+ metadata_tags_exif_register ();
+ metadata_tags_iptc_register ();
+ metadata_tags_xmp_register ();
+
+}
+
+
+/*
+ * static helper functions implementation
+ */
+
+
+/*
* EXIF tags
*/
@@ -179,30 +238,3 @@ metadata_tags_xmp_register (void)
{
}
-
-/*
- *
- */
-
-void
-metadata_tags_register (void)
-{
-
- /* whole chunk tags */
-
- gst_tag_register (GST_TAG_EXIF, GST_TAG_FLAG_META,
- GST_TYPE_BUFFER, GST_TAG_EXIF, "exif metadata chunk", NULL);
-
- gst_tag_register (GST_TAG_IPTC, GST_TAG_FLAG_META,
- GST_TYPE_BUFFER, GST_TAG_IPTC, "iptc metadata chunk", NULL);
-
- gst_tag_register (GST_TAG_XMP, GST_TAG_FLAG_META,
- GST_TYPE_BUFFER, GST_TAG_XMP, "xmp metadata chunk", NULL);
-
- /* tags related to some metadata */
-
- metadata_tags_exif_register ();
- metadata_tags_iptc_register ();
- metadata_tags_xmp_register ();
-
-}
diff --git a/ext/metadata/metadatatags.h b/ext/metadata/metadatatags.h
index a5ee914e..c458324f 100644
--- a/ext/metadata/metadatatags.h
+++ b/ext/metadata/metadatatags.h
@@ -44,17 +44,29 @@
#ifndef __GST_METADATA_TAGS_H__
#define __GST_METADATA_TAGS_H__
+/*
+ * includes
+ */
+
#include <gst/gst.h>
#include <gst/base/gstadapter.h>
G_BEGIN_DECLS
+/*
+ * enum and types
+ */
+
/* set bit to desired mapping */
typedef enum {
METADATA_TAG_MAP_INDIVIDUALS = 1 << 0,
METADATA_TAG_MAP_WHOLECHUNK = 1 << 1
} MetadataTagMapping;
+/*
+ * defines
+ */
+
#define GST_TAG_EXIF "exif"
#define GST_TAG_IPTC "iptc"
@@ -79,6 +91,10 @@ typedef enum {
#define GST_TAG_CAPTURE_CONTRAST "capture-contrast"
#define GST_TAG_CAPTURE_SATURATION "capture-saturation"
+/*
+ * external function prototypes
+ */
+
extern void
metadata_tags_register (void);
diff --git a/ext/metadata/metadatatypes.c b/ext/metadata/metadatatypes.c
index 8a5b4968..e142569c 100644
--- a/ext/metadata/metadatatypes.c
+++ b/ext/metadata/metadatatypes.c
@@ -41,10 +41,41 @@
* Boston, MA 02111-1307, USA.
*/
+/*
+ * SECTION: metadatatypes
+ * @short_description: This module contains function to operates a list of
+ * chunks
+ *
+ * Last reviewed on 2008-01-24 (0.10.15)
+ */
+
+/*
+ * includes
+ */
+
#include "metadatatypes.h"
#include <string.h>
+/*
+ * extern functions implementations
+ */
+
+/*
+ * metadata_chunk_array_init:
+ * @array: an array of chunks
+ * @alloc_size: number of chunks that can be added to the array without futher
+ * allocation
+ *
+ * Call this function before any other function in this module.
+ * Nerver call this function a second time without call
+ * #metadata_chunk_array_free beteween them
+ * An example of use is:
+ * int test() { MetadataChunkArray a; metadata_chunk_array_init(&a, 1); ... }
+ *
+ * Returns: nothing
+ */
+
void
metadata_chunk_array_init (MetadataChunkArray * array, gsize alloc_size)
{
@@ -53,6 +84,16 @@ metadata_chunk_array_init (MetadataChunkArray * array, gsize alloc_size)
array->allocated_len = alloc_size;
}
+/*
+ * metadata_chunk_array_free:
+ * @array: an array of chunks
+ *
+ * Call this function after have finished using the @array to free any internal
+ * memory alocated by it.
+ *
+ * Returns: nothing
+ */
+
void
metadata_chunk_array_free (MetadataChunkArray * array)
{
@@ -64,6 +105,17 @@ metadata_chunk_array_free (MetadataChunkArray * array)
}
}
+/*
+ * metadata_chunk_array_clear:
+ * @array: an array of chunks
+ *
+ * Free memory allocated internally by each chunk and set the @array->len to 0
+ * (zero). So, the number of chunks into the array will be zero,
+ * but the number of slots into the array to strore chunks will be kept
+ *
+ * Returns: nothing
+ */
+
void
metadata_chunk_array_clear (MetadataChunkArray * array)
{
@@ -75,6 +127,19 @@ metadata_chunk_array_clear (MetadataChunkArray * array)
}
}
+/*
+ * metadata_chunk_array_append:
+ * @array: an array of chunks
+ * @chunk: chunk to be append
+ *
+ * Just append a @chunk to the end of the @array. The @array now will be the
+ * owner of @chunk->data. Just call this function if you a sure the @array
+ * chunks will be sorted by @chunk->offset_orig anyway.
+ * @see_also: #metadata_chunk_array_append_sorted
+ *
+ * Returns: nothing
+ */
+
void
metadata_chunk_array_append (MetadataChunkArray * array, MetadataChunk * chunk)
{
@@ -87,6 +152,19 @@ metadata_chunk_array_append (MetadataChunkArray * array, MetadataChunk * chunk)
++array->len;
}
+/*
+ * metadata_chunk_array_append_sorted:
+ * @array: an array of chunks
+ * @chunk: chunk to be append
+ *
+ * Append a @chunk sorted by @chunk->offset_orig the @array. The @array now
+ * will be the owner of @chunk->data. This function supposes that @array
+ * is already sorted by @chunk->offset_orig.
+ * @see_also: #metadata_chunk_array_append
+ *
+ * Returns: nothing
+ */
+
void
metadata_chunk_array_append_sorted (MetadataChunkArray * array,
MetadataChunk * chunk)
@@ -116,6 +194,18 @@ metadata_chunk_array_append_sorted (MetadataChunkArray * array,
}
+/*
+ * metadata_chunk_array_remove_zero_size:
+ * @array: an array of chunks
+ *
+ * This function removes all the chunks in @array that has 'chunk.size == 0'.
+ * It is possible to have the 'chunk.data==NULL' and 'chunk.size != 0', those
+ * chunks are used by muxer for lazy 'filling' and are not removed by this
+ * function.
+ *
+ * Returns: nothing
+ */
+
void
metadata_chunk_array_remove_zero_size (MetadataChunkArray * array)
{
diff --git a/ext/metadata/metadatatypes.h b/ext/metadata/metadatatypes.h
index d58d23b4..fd7df9dd 100644
--- a/ext/metadata/metadatatypes.h
+++ b/ext/metadata/metadatatypes.h
@@ -44,10 +44,18 @@
#ifndef __METADATATYPES_H__
#define __METADATATYPES_H__
+/*
+ * includes
+ */
+
#include <glib.h>
G_BEGIN_DECLS
+/*
+ * enum and types
+ */
+
/* *INDENT-OFF* */
typedef enum _tag_MetadataParsingReturn {
@@ -78,10 +86,14 @@ typedef struct _tag_MetadataChunk
typedef struct _tag_MetadataChunkArray
{
MetadataChunk * chunk;
- gsize len;
- gsize allocated_len;
+ gsize len; /* number of chunks into aray */
+ gsize allocated_len; /* number of slots into the array to store chunks */
} MetadataChunkArray;
+/*
+ * external function prototypes
+ */
+
extern void
metadata_chunk_array_init(MetadataChunkArray * array, gsize alloc_size);
@@ -94,11 +106,9 @@ metadata_chunk_array_clear(MetadataChunkArray * array);
extern void
metadata_chunk_array_append(MetadataChunkArray * array, MetadataChunk * chunk);
-/* sorted by offset (chunk supposed to be already sorted
- * returns false if chunks are inserted in same offset
- */
extern void
-metadata_chunk_array_append_sorted(MetadataChunkArray * array, MetadataChunk * chunk);
+metadata_chunk_array_append_sorted(MetadataChunkArray * array,
+ MetadataChunk * chunk);
extern void
metadata_chunk_array_remove_zero_size (MetadataChunkArray * array);
diff --git a/ext/metadata/metadataxmp.c b/ext/metadata/metadataxmp.c
index cc8172ce..6712fbdb 100644
--- a/ext/metadata/metadataxmp.c
+++ b/ext/metadata/metadataxmp.c
@@ -41,40 +41,76 @@
* Boston, MA 02111-1307, USA.
*/
+/*
+ * SECTION: metadataxmp
+ * @short_description: This module provides functions to extract tags from
+ * XMP metadata chunks and create XMP chunks from metadata tags.
+ * @see_also: #metadatatags.[c/h]
+ *
+ * If lib exempi isn't available at compilation time, only the whole chunk
+ * (#METADATA_TAG_MAP_WHOLECHUNK) tags is created. It means that individual
+ * tags aren't mapped.
+ *
+ * <refsect2>
+ * <para>
+ * #metadata_xmp_init must be called before any other function in this
+ * module and must be paired with a call to #metadata_xmp_dispose
+ * </para>
+ * </refsect2>
+ *
+ * Last reviewed on 2008-01-24 (0.10.15)
+ */
+
+/*
+ * includes
+ */
+
#include "metadataxmp.h"
#include "metadataparseutil.h"
#include "metadatatags.h"
+/*
+ * defines
+ */
+
GST_DEBUG_CATEGORY (gst_metadata_xmp_debug);
#define GST_CAT_DEFAULT gst_metadata_xmp_debug
-#ifndef HAVE_XMP
-
-void
-metadataparse_xmp_tag_list_add (GstTagList * taglist, GstTagMergeMode mode,
- GstAdapter * adapter, MetadataTagMapping mapping)
-{
+/*
+ * Implementation when lib exempi isn't available at compilation time
+ */
- if (mapping & METADATA_TAG_MAP_WHOLECHUNK) {
- GST_LOG ("XMP not defined, here I should send just one tag as whole chunk");
- metadataparse_util_tag_list_add_chunk (taglist, mode, GST_TAG_XMP, adapter);
- }
+#ifndef HAVE_XMP
-}
+/*
+ * extern functions implementations
+ */
gboolean
-metadataparse_xmp_init (void)
+metadata_xmp_init (void)
{
return TRUE;
}
void
-metadataparse_xmp_dispose (void)
+metadata_xmp_dispose (void)
{
return;
}
void
+metadataparse_xmp_tag_list_add (GstTagList * taglist, GstTagMergeMode mode,
+ GstAdapter * adapter, MetadataTagMapping mapping)
+{
+
+ if (mapping & METADATA_TAG_MAP_WHOLECHUNK) {
+ GST_LOG ("XMP not defined, sending just one tag as whole chunk");
+ metadataparse_util_tag_list_add_chunk (taglist, mode, GST_TAG_XMP, adapter);
+ }
+
+}
+
+void
metadatamux_xmp_create_chunk_from_tag_list (guint8 ** buf, guint32 * size,
const GstTagList * taglist)
{
@@ -83,10 +119,20 @@ metadatamux_xmp_create_chunk_from_tag_list (guint8 ** buf, guint32 * size,
#else /* ifndef HAVE_XMP */
+/*
+ * Implementation when lib exempi isn't available at compilation time
+ */
+
+/*
+ * includes
+ */
+
#include <xmp.h>
#include <string.h>
-#define XMP_SCHEMA_NODE 0x80000000UL
+/*
+ * enum and types
+ */
typedef struct _tag_SchemaTagMap
{
@@ -102,6 +148,12 @@ typedef struct _tag_SchemaMap
const SchemaTagMap *tags_map;
} SchemaMap;
+/*
+ * defines and static global vars
+ */
+
+#define XMP_SCHEMA_NODE 0x80000000UL
+
static const SchemaTagMap schema_map_dublin_tags_map[] = {
{"description", GST_TAG_DESCRIPTION},
{"title", GST_TAG_TITLE},
@@ -109,7 +161,8 @@ static const SchemaTagMap schema_map_dublin_tags_map[] = {
{NULL, NULL}
};
-static const SchemaMap schema_map_dublin = { "http://purl.org/dc/elements/1.1/",
+static const SchemaMap schema_map_dublin = {
+ "http://purl.org/dc/elements/1.1/",
"dc:",
3,
schema_map_dublin_tags_map
@@ -120,10 +173,22 @@ static const SchemaMap *schemas_map[] = {
NULL
};
+/*
+ * static helper functions declaration
+ */
+
+static const SchemaTagMap *metadataparse_xmp_get_tagsmap_from_path (const
+ SchemaMap * schema_map, const gchar * path, uint32_t opt);
+
+static const SchemaTagMap *metadatamux_xmp_get_tagsmap_from_gsttag (const
+ SchemaMap * schema_map, const gchar * tag);
+
static void
-metadataparse_xmp_iter_add_to_tag_list (GstTagList * taglist,
- GstTagMergeMode mode, const char *path, const char *value,
- const SchemaMap * schema_map, const uint32_t opt);
+metadataparse_xmp_iter (GstTagList * taglist, GstTagMergeMode mode, XmpPtr xmp);
+
+static void
+metadataparse_xmp_iter_node_schema (GstTagList * taglist, GstTagMergeMode mode,
+ XmpPtr xmp, const char *schema, const char *path);
static void
metadataparse_xmp_iter_array (GstTagList * taglist, GstTagMergeMode mode,
@@ -131,34 +196,75 @@ metadataparse_xmp_iter_array (GstTagList * taglist, GstTagMergeMode mode,
const SchemaMap * schema_map);
static void
-metadataparse_xmp_iter_node_schema (GstTagList * taglist, GstTagMergeMode mode,
- XmpPtr xmp, const char *schema, const char *path);
+metadataparse_xmp_iter_simple_qual (GstTagList * taglist, GstTagMergeMode mode,
+ const char *path, const char *value, const SchemaMap * schema_map);
static void
metadataparse_xmp_iter_simple (GstTagList * taglist, GstTagMergeMode mode,
- const char *schema, const char *path, const char *value,
- const SchemaMap * schema_map);
+ const char *path, const char *value, const SchemaMap * schema_map);
static void
-metadataparse_xmp_iter_simple_qual (GstTagList * taglist, GstTagMergeMode mode,
- const char *schema, const char *path, const char *value,
- const SchemaMap * schema_map);
+metadataparse_xmp_iter_add_to_tag_list (GstTagList * taglist,
+ GstTagMergeMode mode, const char *path, const char *value,
+ const SchemaMap * schema_map, const uint32_t opt);
static void
-metadataparse_xmp_iter (GstTagList * taglist, GstTagMergeMode mode, XmpPtr xmp);
+metadatamux_xmp_for_each_tag_in_list (const GstTagList * list,
+ const gchar * tag, gpointer user_data);
+
+/*
+ * extern functions implementations
+ */
+
+/*
+ * metadata_xmp_init:
+ *
+ * Init lib exempi (if present in compilation time)
+ * This function must be called before any other function from this module.
+ * This function must not be called twice without call
+ * to #metadata_xmp_dispose beteween them.
+ * @see_also: #metadata_xmp_dispose
+ *
+ * Returns: nothing
+ */
gboolean
-metadataparse_xmp_init (void)
+metadata_xmp_init (void)
{
return xmp_init ();
}
+/*
+ * metadata_xmp_dispose:
+ *
+ * Call this function to free any resource allocated by #metadata_xmp_init
+ * @see_also: #metadata_xmp_init
+ *
+ * Returns: nothing
+ */
+
void
-metadataparse_xmp_dispose (void)
+metadata_xmp_dispose (void)
{
xmp_terminate ();
}
+/*
+ * metadataparse_xmp_tag_list_add:
+ * @taglist: tag list in which extracted tags will be added
+ * @mode: tag list merge mode
+ * @adapter: contains the XMP metadata chunk
+ * @mapping: if is to extract individual tags and/or the whole chunk.
+ *
+ * This function gets a XMP chunk (@adapter) and extract tags from it
+ * and then to add to @taglist.
+ * Note: The XMP chunk (@adapetr) must NOT be wrapped by any bytes specific
+ * to any file format
+ * @see_also: #metadataparse_xmp_iter
+ *
+ * Returns: nothing
+ */
+
void
metadataparse_xmp_tag_list_add (GstTagList * taglist, GstTagMergeMode mode,
GstAdapter * adapter, MetadataTagMapping mapping)
@@ -172,8 +278,9 @@ metadataparse_xmp_tag_list_add (GstTagList * taglist, GstTagMergeMode mode,
}
/* add chunk tag */
- if (mapping & METADATA_TAG_MAP_WHOLECHUNK)
+ if (mapping & METADATA_TAG_MAP_WHOLECHUNK) {
metadataparse_util_tag_list_add_chunk (taglist, mode, GST_TAG_XMP, adapter);
+ }
if (!(mapping & METADATA_TAG_MAP_INDIVIDUALS))
goto done;
@@ -196,32 +303,96 @@ done:
}
-static const SchemaTagMap *
-metadataparse_get_tagsmap_from_gsttag (const SchemaMap * schema_map,
- const gchar * tag)
+/*
+ * metadatamux_xmp_create_chunk_from_tag_list:
+ * @buf: buffer that will have the created XMP chunk
+ * @size: size of the buffer that will be created
+ * @taglist: list of tags to be added to XMP chunk
+ *
+ * Get tags from @taglist, create a XMP chunk based on it and save to @buf.
+ * Note: The XMP chunk is NOT wrapped by any bytes specific to any file format
+ *
+ * Returns: nothing
+ */
+
+void
+metadatamux_xmp_create_chunk_from_tag_list (guint8 ** buf, guint32 * size,
+ const GstTagList * taglist)
{
- SchemaTagMap *tags_map = NULL;
- int i;
+ GstBuffer *xmp_chunk = NULL;
+ const GValue *val = NULL;
+ XmpPtr xmp = NULL;
+ XmpStringPtr xmp_str_buf = xmp_string_new ();
- if (NULL == schema_map)
+ if (!(buf && size))
goto done;
+ if (*buf) {
+ g_free (*buf);
+ *buf = NULL;
+ }
+ *size = 0;
+ val = gst_tag_list_get_value_index (taglist, GST_TAG_XMP, 0);
+ if (val) {
+ xmp_chunk = gst_value_get_buffer (val);
+ if (xmp_chunk)
+ xmp = xmp_new (GST_BUFFER_DATA (xmp_chunk), GST_BUFFER_SIZE (xmp_chunk));
+ }
- for (i = 0; schema_map->tags_map[i].gst_tag; i++) {
- if (0 == strcmp (schema_map->tags_map[i].gst_tag, tag)) {
- tags_map = (SchemaTagMap *) & schema_map->tags_map[i];
- break;
- }
+ if (NULL == xmp)
+ xmp = xmp_new_empty ();
+
+ gst_tag_list_foreach (taglist, metadatamux_xmp_for_each_tag_in_list, xmp);
+
+ if (!xmp_serialize (xmp, xmp_str_buf, 0, 2)) {
+ GST_ERROR ("failed to serialize xmp into chunk\n");
+ } else if (xmp_str_buf) {
+ unsigned int len = strlen (xmp_string_cstr (xmp_str_buf));
+
+ *size = len + 1;
+ *buf = malloc (*size);
+ memcpy (*buf, xmp_string_cstr (xmp_str_buf), *size);
+ } else {
+ GST_ERROR ("failed to serialize xmp into chunk\n");
}
done:
- return tags_map;
+ if (xmp_str_buf)
+ xmp_string_free (xmp_str_buf);
+
+ if (xmp)
+ xmp_free (xmp);
+ return;
}
+/*
+ * static helper functions implementation
+ */
+
+/*
+ * metadataparse_xmp_get_tagsmap_from_path:
+ * @schema_map: Structure containg a map beteween GST tags and tags into a XMP
+ * schema
+ * @path: string describing a XMP tag
+ * @opt: indicates if the string (@path) has extras caracters like '[' and ']'
+ *
+ * This returns a structure that contains the GStreamer tag mapped to an XMP
+ * tag.
+ *
+ * Returns:
+ * <itemizedlist>
+ * <listitem><para>Structure containing the GST tag mapped
+ * to the XMP tag (@path)
+ * </para></listitem>
+ * <listitem><para>%NULL if there is no mapped GST tag for XMP tag (@path)
+ * </para></listitem>
+ * </itemizedlist>
+ */
+
static const SchemaTagMap *
-metadataparse_get_tagsmap_from_path (const SchemaMap * schema_map,
+metadataparse_xmp_get_tagsmap_from_path (const SchemaMap * schema_map,
const gchar * path, uint32_t opt)
{
@@ -238,7 +409,7 @@ metadataparse_get_tagsmap_from_path (const SchemaMap * schema_map,
string = g_string_new (path);
- /* remove the language qualifier */
+ /* remove the language qualifier "[xxx]" */
ch = string->str + string->len - 3;
while (ch != string->str + schema_map->prefix_len) {
if (*ch == '[') {
@@ -266,75 +437,154 @@ done:
}
-static void
-metadataparse_xmp_iter_add_to_tag_list (GstTagList * taglist,
- GstTagMergeMode mode, const char *path, const char *value,
- const SchemaMap * schema_map, const uint32_t opt)
-{
-
- const SchemaTagMap *smaptag =
- metadataparse_get_tagsmap_from_path (schema_map, path, opt);
+/*
+ * metadatamux_xmp_get_tagsmap_from_gsttag:
+ * @schema_map: Structure containg a map beteween GST tags and tags into a XMP
+ * schema
+ * @tag: GStreaner tag to look for
+ *
+ * This returns a structure that contains the XMP tag mapped to a GStreamer
+ * tag.
+ *
+ * Returns:
+ * <itemizedlist>
+ * <listitem><para>Structure containing the XMP tag mapped
+ * to the GST tag (@path)
+ * </para></listitem>
+ * <listitem><para>%NULL if there is no mapped XMP tag for GST @tag
+ * </para></listitem>
+ * </itemizedlist>
+ */
- if (NULL == smaptag)
- goto done;
+static const SchemaTagMap *
+metadatamux_xmp_get_tagsmap_from_gsttag (const SchemaMap * schema_map,
+ const gchar * tag)
+{
+ SchemaTagMap *tags_map = NULL;
+ int i;
- if (NULL == smaptag->gst_tag)
+ if (NULL == schema_map)
goto done;
- GType type = gst_tag_get_type (smaptag->gst_tag);
- switch (type) {
- case G_TYPE_STRING:
- gst_tag_list_add (taglist, mode, smaptag->gst_tag, value, NULL);
- break;
- default:
+ for (i = 0; schema_map->tags_map[i].gst_tag; i++) {
+ if (0 == strcmp (schema_map->tags_map[i].gst_tag, tag)) {
+ tags_map = (SchemaTagMap *) & schema_map->tags_map[i];
break;
+ }
}
done:
- return;
+ return tags_map;
}
+/*
+ * metadataparse_xmp_iter:
+ * @taglist: tag list in which extracted tags will be added
+ * @mode: tag list merge mode
+ * @xmp: handle to XMP data from lib exempi
+ *
+ * This function looks all the shemas in a XMP data (@xmp) and then calls
+ * #metadataparse_xmp_iter_node_schema for each schema. In the end, the idea is
+ * to add all XMP mapped tags to @taglist by unsing a specified merge @mode
+ * @see_also: #metadataparse_xmp_tag_list_add
+ * #metadataparse_xmp_iter_node_schema
+ *
+ * Returns: nothing
+ */
+
void
-metadataparse_xmp_iter_simple_qual (GstTagList * taglist, GstTagMergeMode mode,
- const char *schema, const char *path, const char *value,
- const SchemaMap * schema_map)
+metadataparse_xmp_iter (GstTagList * taglist, GstTagMergeMode mode, XmpPtr xmp)
{
- GString *string = g_string_new (path);
- gchar *ch;
+ XmpStringPtr xstr_schema = xmp_string_new ();
+ XmpStringPtr xstr_path = xmp_string_new ();
+ XmpStringPtr xstr_prop = xmp_string_new ();
+ uint32_t opt = 0;
+ XmpIteratorPtr xmp_iter = NULL;
- /* remove the language qualifier */
- ch = string->str + string->len - 3;
- while (ch != string->str + schema_map->prefix_len) {
- if (*ch == '[') {
- *ch = '\0';
+ xmp_iter = xmp_iterator_new (xmp, NULL, NULL, XMP_ITER_JUSTCHILDREN);
+
+ if (NULL == xmp_iter)
+ goto done;
+
+ while (xmp_iterator_next (xmp_iter, xstr_schema, xstr_path, xstr_prop, &opt)) {
+ const char *schema = xmp_string_cstr (xstr_schema);
+ const char *path = xmp_string_cstr (xstr_path);
+
+ if (XMP_IS_NODE_SCHEMA (opt)) {
+ GST_LOG ("%s", schema);
+ metadataparse_xmp_iter_node_schema (taglist, mode, xmp, schema, path);
+ } else {
+ GST_LOG ("Unexpected iteraction");
}
- --ch;
}
- GST_LOG (" %s = %s", string->str, value);
+done:
- metadataparse_xmp_iter_add_to_tag_list (taglist, mode, path, value,
- schema_map, XMP_PROP_HAS_QUALIFIERS);
+ if (xmp_iter)
+ xmp_iterator_free (xmp_iter);
- g_string_free (string, TRUE);
+ if (xstr_prop)
+ xmp_string_free (xstr_prop);
+
+ if (xstr_path)
+ xmp_string_free (xstr_path);
+
+ if (xstr_schema)
+ xmp_string_free (xstr_schema);
}
+/*
+ * metadataparse_xmp_iter_node_schema:
+ * @taglist: tag list in which extracted tags will be added
+ * @mode: tag list merge mode
+ * @xmp: handle to XMP data from lib exempi
+ * @schema: schema name string
+ * @path: schema path
+ *
+ * This function gets a @schema, finds the #SchemaMap (structure
+ * containing @schema description and map with GST tags) to it. And then call
+ * #metadataparse_xmp_iter_array. In the end, the idea is
+ * to add all XMP Schema mapped tags to @taglist by unsing a specified
+ * merge @mode
+ * @see_also: #metadataparse_xmp_iter
+ * #metadataparse_xmp_iter_array
+ *
+ * Returns: nothing
+ */
void
-metadataparse_xmp_iter_simple (GstTagList * taglist, GstTagMergeMode mode,
- const char *schema, const char *path, const char *value,
- const SchemaMap * schema_map)
+metadataparse_xmp_iter_node_schema (GstTagList * taglist, GstTagMergeMode mode,
+ XmpPtr xmp, const char *schema, const char *path)
{
- GST_LOG (" %s = %s", path, value);
+ SchemaMap *schema_map = NULL;
- metadataparse_xmp_iter_add_to_tag_list (taglist, mode, path, value,
- schema_map, 0);
+ if (0 == strcmp (schema, "http://purl.org/dc/elements/1.1/")) {
+ schema_map = (SchemaMap *) & schema_map_dublin;
+ }
+ metadataparse_xmp_iter_array (taglist, mode, xmp, schema, path, schema_map);
}
+/*
+ * metadataparse_xmp_iter_array:
+ * @taglist: tag list in which extracted tags will be added
+ * @mode: tag list merge mode
+ * @xmp: handle to XMP data from lib exempi
+ * @schema: schema name string
+ * @path: schema path
+ * @schema_map: structure containing @schema description and map with GST tags
+ *
+ * This function looks all the tags into a @schema and call other functions in
+ * order to add the mapped ones to @taglist by using a specified merge @mode
+ * @see_also: #metadataparse_xmp_iter_node_schema
+ * #metadataparse_xmp_iter_simple_qual metadataparse_xmp_iter_simple
+ *
+ * Returns: nothing
+ */
+
void
metadataparse_xmp_iter_array (GstTagList * taglist, GstTagMergeMode mode,
XmpPtr xmp, const char *schema, const char *path,
@@ -362,10 +612,10 @@ metadataparse_xmp_iter_array (GstTagList * taglist, GstTagMergeMode mode,
if (strcmp (path, "") != 0) {
if (XMP_HAS_PROP_QUALIFIERS (opt)) {
/* ignore language qualifier, just get the first */
- metadataparse_xmp_iter_simple_qual (taglist, mode, schema, path,
- value, schema_map);
+ metadataparse_xmp_iter_simple_qual (taglist, mode, path, value,
+ schema_map);
} else {
- metadataparse_xmp_iter_simple (taglist, mode, schema, path, value,
+ metadataparse_xmp_iter_simple (taglist, mode, path, value,
schema_map);
}
}
@@ -408,64 +658,141 @@ done:
}
+/*
+ * metadataparse_xmp_iter_simple_qual:
+ * @taglist: tag list in which extracted tags will be added
+ * @mode: tag list merge mode
+ * @path: schema path
+ * @value: value of the (@path) tag
+ * @schema_map: structure containing @schema description and map with GST tags
+ *
+ * This function gets a XMP tag (@path) with quilifiers and try to add it
+ * to @taglist by calling #metadataparse_xmp_iter_add_to_tag_list
+ * @see_also: #metadataparse_xmp_iter_array
+ * #metadataparse_xmp_iter_simple #metadataparse_xmp_iter_add_to_tag_list
+ *
+ * Returns: nothing
+ */
+
void
-metadataparse_xmp_iter_node_schema (GstTagList * taglist, GstTagMergeMode mode,
- XmpPtr xmp, const char *schema, const char *path)
+metadataparse_xmp_iter_simple_qual (GstTagList * taglist, GstTagMergeMode mode,
+ const char *path, const char *value, const SchemaMap * schema_map)
{
- SchemaMap *schema_map = NULL;
+ GString *string = g_string_new (path);
- if (0 == strcmp (schema, "http://purl.org/dc/elements/1.1/")) {
- schema_map = (SchemaMap *) & schema_map_dublin;
+#ifndef GST_DISABLE_GST_DEBUG
+ gchar *ch;
+
+ /* remove the language qualifier */
+ ch = string->str + string->len - 3;
+ while (ch != string->str + schema_map->prefix_len) {
+ if (*ch == '[') {
+ *ch = '\0';
+ }
+ --ch;
}
+ GST_LOG (" %s = %s", string->str, value);
+#endif /* #ifndef GST_DISABLE_GST_DEBUG */
- metadataparse_xmp_iter_array (taglist, mode, xmp, schema, path, schema_map);
+ metadataparse_xmp_iter_add_to_tag_list (taglist, mode, path, value,
+ schema_map, XMP_PROP_HAS_QUALIFIERS);
+
+ g_string_free (string, TRUE);
}
+/*
+ * metadataparse_xmp_iter_simple:
+ * @taglist: tag list in which extracted tags will be added
+ * @mode: tag list merge mode
+ * @path: schema path
+ * @value: value of the (@path) tag
+ * @schema_map: structure containing @schema description and map with GST tags
+ *
+ * This function gets a simple XMP tag (@path) and try to add it to @taglist by
+ * calling # metadataparse_xmp_iter_add_to_tag_list
+ * @see_also: #metadataparse_xmp_iter_array
+ * #metadataparse_xmp_iter_simple_qual #metadataparse_xmp_iter_add_to_tag_list
+ *
+ * Returns: nothing
+ */
+
void
-metadataparse_xmp_iter (GstTagList * taglist, GstTagMergeMode mode, XmpPtr xmp)
+metadataparse_xmp_iter_simple (GstTagList * taglist, GstTagMergeMode mode,
+ const char *path, const char *value, const SchemaMap * schema_map)
{
- XmpStringPtr xstr_schema = xmp_string_new ();
- XmpStringPtr xstr_path = xmp_string_new ();
- XmpStringPtr xstr_prop = xmp_string_new ();
- uint32_t opt = 0;
- XmpIteratorPtr xmp_iter = NULL;
+ GST_LOG (" %s = %s", path, value);
- xmp_iter = xmp_iterator_new (xmp, NULL, NULL, XMP_ITER_JUSTCHILDREN);
+ metadataparse_xmp_iter_add_to_tag_list (taglist, mode, path, value,
+ schema_map, 0);
- if (NULL == xmp_iter)
+}
+
+/*
+ * metadataparse_xmp_iter_add_to_tag_list:
+ * @taglist: tag list in which extracted tags will be added
+ * @mode: tag list merge mode
+ * @path: schema path
+ * @value: value of the (@path) tag
+ * @schema_map: structure containing @schema description and map with GST tags
+ * @opt: indicates if the string (@path) has extras caracters like '[' and ']'
+ *
+ * This function gets a XMP tag (@path) and see if it is mapped to a GST tag by
+ * calling #metadataparse_xmp_get_tagsmap_from_path, if so, add it to @taglist
+ * by using a specified merge @mode
+ * @see_also: #metadataparse_xmp_iter_simple_qual
+ * #metadataparse_xmp_iter_simple #metadataparse_xmp_get_tagsmap_from_path
+ *
+ * Returns: nothing
+ */
+
+static void
+metadataparse_xmp_iter_add_to_tag_list (GstTagList * taglist,
+ GstTagMergeMode mode, const char *path, const char *value,
+ const SchemaMap * schema_map, const uint32_t opt)
+{
+
+ const SchemaTagMap *smaptag =
+ metadataparse_xmp_get_tagsmap_from_path (schema_map, path, opt);
+
+ if (NULL == smaptag)
goto done;
- while (xmp_iterator_next (xmp_iter, xstr_schema, xstr_path, xstr_prop, &opt)) {
- const char *schema = xmp_string_cstr (xstr_schema);
- const char *path = xmp_string_cstr (xstr_path);
+ if (NULL == smaptag->gst_tag)
+ goto done;
- if (XMP_IS_NODE_SCHEMA (opt)) {
- GST_LOG ("%s", schema);
- metadataparse_xmp_iter_node_schema (taglist, mode, xmp, schema, path);
- } else {
- GST_LOG ("Unexpected iteraction");
- }
+ GType type = gst_tag_get_type (smaptag->gst_tag);
+
+ switch (type) {
+ case G_TYPE_STRING:
+ gst_tag_list_add (taglist, mode, smaptag->gst_tag, value, NULL);
+ break;
+ default:
+ break;
}
done:
- if (xmp_iter)
- xmp_iterator_free (xmp_iter);
-
- if (xstr_prop)
- xmp_string_free (xstr_prop);
-
- if (xstr_path)
- xmp_string_free (xstr_path);
+ return;
- if (xstr_schema)
- xmp_string_free (xstr_schema);
}
+/*
+ * metadatamux_xmp_for_each_tag_in_list:
+ * @list: GStreamer tag list from which @tag belongs to
+ * @tag: GStreamer tag to be added to the XMP chunk
+ * @user_data: pointer to #XmpPtr in which the tag will be added
+ *
+ * This function designed to be called for each tag in GST tag list. This
+ * function adds get the tag value from tag @list and then add it to the XMP
+ * chunk by using #XmpPtr and related functions from lib exempi
+ * @see_also: #metadatamux_xmp_create_chunk_from_tag_list
+ *
+ * Returns: nothing
+ */
static void
-metadataxmp_for_each_tag_in_list (const GstTagList * list, const gchar * tag,
- gpointer user_data)
+metadatamux_xmp_for_each_tag_in_list (const GstTagList * list,
+ const gchar * tag, gpointer user_data)
{
XmpPtr xmp = (XmpPtr) user_data;
int i;
@@ -474,7 +801,7 @@ metadataxmp_for_each_tag_in_list (const GstTagList * list, const gchar * tag,
const SchemaMap *smap = schemas_map[i];
const SchemaTagMap *stagmap =
- metadataparse_get_tagsmap_from_gsttag (smap, tag);
+ metadatamux_xmp_get_tagsmap_from_gsttag (smap, tag);
if (stagmap) {
@@ -501,56 +828,4 @@ metadataxmp_for_each_tag_in_list (const GstTagList * list, const gchar * tag,
}
-void
-metadatamux_xmp_create_chunk_from_tag_list (guint8 ** buf, guint32 * size,
- const GstTagList * taglist)
-{
- GstBuffer *xmp_chunk = NULL;
- const GValue *val = NULL;
- XmpPtr xmp = NULL;
- XmpStringPtr xmp_str_buf = xmp_string_new ();
-
- if (!(buf && size))
- goto done;
- if (*buf) {
- g_free (*buf);
- *buf = NULL;
- }
- *size = 0;
-
- val = gst_tag_list_get_value_index (taglist, GST_TAG_XMP, 0);
- if (val) {
- xmp_chunk = gst_value_get_buffer (val);
- if (xmp_chunk)
- xmp = xmp_new (GST_BUFFER_DATA (xmp_chunk), GST_BUFFER_SIZE (xmp_chunk));
- }
-
- if (NULL == xmp)
- xmp = xmp_new_empty ();
-
- gst_tag_list_foreach (taglist, metadataxmp_for_each_tag_in_list, xmp);
-
- if (!xmp_serialize (xmp, xmp_str_buf, 0, 2)) {
- GST_ERROR ("failed to serialize xmp into chunk\n");
- } else if (xmp_str_buf) {
- unsigned int len = strlen (xmp_string_cstr (xmp_str_buf));
-
- *size = len + 1;
- *buf = malloc (*size);
- memcpy (*buf, xmp_string_cstr (xmp_str_buf), *size);
- } else {
- GST_ERROR ("failed to serialize xmp into chunk\n");
- }
-
-done:
-
- if (xmp_str_buf)
- xmp_string_free (xmp_str_buf);
-
- if (xmp)
- xmp_free (xmp);
-
- return;
-}
-
#endif /* else (ifndef HAVE_XMP) */
diff --git a/ext/metadata/metadataxmp.h b/ext/metadata/metadataxmp.h
index 73a53241..831b563f 100644
--- a/ext/metadata/metadataxmp.h
+++ b/ext/metadata/metadataxmp.h
@@ -50,14 +50,18 @@
G_BEGIN_DECLS
+/*
+ * external function prototypes
+ */
+
+extern gboolean metadata_xmp_init (void);
+
+extern void metadata_xmp_dispose (void);
+
extern void
metadataparse_xmp_tag_list_add (GstTagList * taglist, GstTagMergeMode mode,
GstAdapter * adapter, MetadataTagMapping mapping);
-extern gboolean metadataparse_xmp_init (void);
-
-extern void metadataparse_xmp_dispose (void);
-
extern void
metadatamux_xmp_create_chunk_from_tag_list (guint8 ** buf, guint32 *size,
const GstTagList * taglist);
diff --git a/ext/metadata/test/Makefile b/ext/metadata/test/Makefile
deleted file mode 100644
index dfcd3f73..00000000
--- a/ext/metadata/test/Makefile
+++ /dev/null
@@ -1,16 +0,0 @@
-
-CC=gcc
-CFLAGS=-c -Wall -g3 -O0 `pkg-config --cflags gstreamer-0.10 libglade-2.0 gtk+-2.0`
-CLIBS=-lgstinterfaces-0.10 -Wl -export-dynamic `pkg-config --libs gstreamer-0.10 libglade-2.0 gtk+-2.0`
-
-all: metadata_editor
-
-metadata_editor: metadata_editor.o
- $(CC) $(CLIBS) metadata_editor.o -o metadata_editor
-
-metadata_editor.o: metadata_editor.c
- $(CC) $(CFLAGS) metadata_editor.c
-
-clean:
- rm -rf *.o metadata_editor
-
diff --git a/ext/metadata/test/MetadataEditorMain.glade b/ext/metadata/test/MetadataEditorMain.glade
deleted file mode 100644
index 871d8e6b..00000000
--- a/ext/metadata/test/MetadataEditorMain.glade
+++ /dev/null
@@ -1,154 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
-<!--Generated with glade3 3.2.0 on Mon Dec 17 11:42:47 2007 by edlima@feisty-laptop-->
-<glade-interface>
- <widget class="GtkWindow" id="windowMain">
- <property name="width_request">800</property>
- <property name="height_request">600</property>
- <property name="events">GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="title" translatable="yes">Metadata Editor</property>
- <signal name="configure_event" handler="on_windowMain_configure_event"/>
- <signal name="delete_event" handler="on_windowMain_delete_event"/>
- <child>
- <widget class="GtkVPaned" id="vpanedMain">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <child>
- <widget class="GtkDrawingArea" id="drawingMain">
- <property name="width_request">200</property>
- <property name="height_request">100</property>
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <signal name="expose_event" handler="on_drawingMain_expose_event"/>
- </widget>
- <packing>
- <property name="resize">False</property>
- <property name="shrink">False</property>
- </packing>
- </child>
- <child>
- <widget class="GtkVBox" id="vboxMain">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <child>
- <widget class="GtkScrolledWindow" id="scrolledwindow">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
- <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
- <child>
- <widget class="GtkTreeView" id="treeMain">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="headers_clickable">True</property>
- <property name="rules_hint">True</property>
- <property name="enable_search">False</property>
- <property name="hover_expand">True</property>
- </widget>
- </child>
- </widget>
- </child>
- <child>
- <widget class="GtkVBox" id="vboxEditBnt">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <child>
- <widget class="GtkHBox" id="hboxEdit">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <child>
- <widget class="GtkEntry" id="entryTag">
- <property name="width_request">300</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- </packing>
- </child>
- <child>
- <widget class="GtkEntry" id="entryValue">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- </widget>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- </widget>
- </child>
- <child>
- <widget class="GtkHButtonBox" id="hbuttonboxBnt">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="homogeneous">True</property>
- <property name="layout_style">GTK_BUTTONBOX_SPREAD</property>
- <child>
- <widget class="GtkButton" id="buttonInsert">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">Insert</property>
- <signal name="clicked" handler="on_buttonInsert_clicked"/>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- </packing>
- </child>
- <child>
- <widget class="GtkButton" id="buttonSaveFile">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">Save File</property>
- <signal name="clicked" handler="on_buttonSaveFile_clicked"/>
- </widget>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <widget class="GtkCheckButton" id="checkbuttonCapture">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">Capture image from camera</property>
- <property name="draw_indicator">True</property>
- <signal name="toggled" handler="on_checkbuttonCapture_toggled"/>
- </widget>
- <packing>
- <property name="position">2</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="shrink">False</property>
- </packing>
- </child>
- </widget>
- </child>
- </widget>
-</glade-interface>
diff --git a/ext/metadata/test/metadata_editor.c b/ext/metadata/test/metadata_editor.c
deleted file mode 100644
index f24f9994..00000000
--- a/ext/metadata/test/metadata_editor.c
+++ /dev/null
@@ -1,1015 +0,0 @@
-/*
- * GStreamer
- * Copyright 2007 Edgard Lima <edgard.lima@indt.org.br>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Alternatively, the contents of this file may be used under the
- * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
- * which case the following provisions apply instead of the ones
- * mentioned above:
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <gst/gst.h>
-#include <glade/glade-xml.h>
-#include <gtk/gtk.h>
-#include <gst/interfaces/xoverlay.h>
-#include <gdk/gdkx.h>
-
-/*
- * Global constants
- */
-
-enum
-{
- COL_TAG = 0,
- COL_VALUE,
- NUM_COLS
-};
-
-#define ENC_ERROR (-1)
-#define ENC_DONE (0)
-#define ENC_UNKNOWN (1)
-
-
-/*
- * functions prototypes
- */
-
-/* gstreamer related functions */
-
-static void me_gst_cleanup_elements ();
-static int
-me_gst_setup_view_pipeline (const gchar * filename, GdkWindow * window);
-static int
-me_gst_setup_capture_pipeline (const gchar * src_file, const gchar * dest_file,
- gint * encode_status);
-static int
-me_gst_setup_encode_pipeline (const gchar * src_file, const gchar * dest_file,
- gint * encode_status);
-
-/* ui related functions */
-
-static void ui_refresh ();
-
-/*
- * Global Vars
- */
-
-GstElement *gst_source = NULL;
-GstElement *gst_metadata_demux = NULL;
-GstElement *gst_metadata_mux = NULL;
-GstElement *gst_image_dec = NULL;
-GstElement *gst_image_enc = NULL;
-GstElement *gst_video_scale = NULL;
-GstElement *gst_video_convert = NULL;
-GstElement *gst_video_sink = NULL;
-GstElement *gst_file_sink = NULL;
-GstElement *gst_pipeline = NULL;
-
-GstTagList *tag_list = NULL;
-
-GladeXML *ui_glade_xml = NULL;
-GtkWidget *ui_main_window = NULL;
-GtkWidget *ui_drawing = NULL;
-GtkWidget *ui_tree = NULL;
-
-GtkEntry *ui_entry_insert_tag = NULL;
-GtkEntry *ui_entry_insert_value = NULL;
-
-GtkToggleButton *ui_chk_bnt_capture = NULL;
-
-GString *filename = NULL;
-
-/*
- * Helper functions
- */
-
-static void
-insert_tag_on_tree (const GstTagList * list, const gchar * tag,
- gpointer user_data)
-{
- gchar *str = NULL;
- GtkTreeView *tree_view = NULL;
- GtkTreeStore *tree_store = NULL;
- GtkTreeIter iter;
-
- tree_view = GTK_TREE_VIEW (user_data);
-
- if (gst_tag_get_type (tag) == G_TYPE_STRING) {
- if (!gst_tag_list_get_string_index (list, tag, 0, &str))
- g_assert_not_reached ();
- } else {
- str = g_strdup_value_contents (gst_tag_list_get_value_index (list, tag, 0));
- }
-
- tree_store = GTK_TREE_STORE (gtk_tree_view_get_model (tree_view));
- gtk_tree_store_append (tree_store, &iter, NULL);
- gtk_tree_store_set (tree_store, &iter, COL_TAG, tag, COL_VALUE, str, -1);
-
- if (str)
- g_free (str);
-
-}
-
-static gboolean
-change_tag_list (GstTagList ** list, const gchar * tag, const gchar * value)
-{
- GType type;
- gboolean ret = FALSE;
-
- if (list == NULL || tag == NULL || value == NULL)
- goto done;
-
- if (!gst_tag_exists (tag)) {
- fprintf (stderr, "%s is not a GStreamer registered tag\n", tag);
- goto done;
- }
-
- if (*list == NULL)
- *list = gst_tag_list_new ();
-
- type = gst_tag_get_type (tag);
-
- if (type == GST_TYPE_FRACTION) {
- /* FIXME: Ask GStreamer guys to add GST_FRACTION support to TAGS */
- /* Even better: ask GLib guys to add this type */
- gint n, d;
-
- sscanf (value, "%d/%d", &n, &d);
- gst_tag_list_add (*list, GST_TAG_MERGE_REPLACE, tag, n, d, NULL);
- ret = TRUE;
- } else {
- switch (type) {
- case G_TYPE_STRING:
- gst_tag_list_add (*list, GST_TAG_MERGE_REPLACE, tag, value, NULL);
- ret = TRUE;
- break;
- case G_TYPE_FLOAT:
- {
- gfloat fv = (gfloat) g_strtod (value, NULL);
-
- gst_tag_list_add (*list, GST_TAG_MERGE_REPLACE, tag, fv, NULL);
- ret = TRUE;
- }
- break;
- case G_TYPE_INT:
- /* fall through */
- case G_TYPE_UINT:
- {
- gint iv = atoi (value);
-
- gst_tag_list_add (*list, GST_TAG_MERGE_REPLACE, tag, iv, NULL);
- ret = TRUE;
- }
- break;
- default:
- fprintf (stderr, "This app still doesn't handle type (%s)(%ld)\n",
- g_type_name (type), type);
- break;
- }
- }
-
-done:
-
- return ret;
-
-}
-
-
-/*
- * UI handling functions (mapped by glade)
- */
-
-gboolean
-on_windowMain_configure_event (GtkWidget * widget, GdkEventConfigure * event,
- gpointer user_data)
-{
- GstXOverlay *xoverlay;
-
- if (gst_video_sink) {
-
- xoverlay = GST_X_OVERLAY (gst_video_sink);
-
- if (xoverlay != NULL && GST_IS_X_OVERLAY (xoverlay)) {
- gst_x_overlay_expose (xoverlay);
- }
-
- }
-
- return FALSE;
-}
-
-gboolean
-on_drawingMain_expose_event (GtkWidget * widget, GdkEventExpose * event,
- gpointer data)
-{
- if (gst_video_sink) {
- gst_x_overlay_set_xwindow_id (GST_X_OVERLAY (gst_video_sink),
- GDK_WINDOW_XWINDOW (widget->window));
- }
- return FALSE;
-}
-
-void
-on_windowMain_delete_event (GtkWidget * widget, GdkEvent * event,
- gpointer user_data)
-{
- gst_element_set_state (gst_pipeline, GST_STATE_NULL);
- gst_element_get_state (gst_pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
- gtk_main_quit ();
-}
-
-void
-on_buttonInsert_clicked (GtkButton * button, gpointer user_data)
-{
- GtkTreeStore *store = NULL;
- GtkTreeIter iter;
- const gchar *tag = gtk_entry_get_text (ui_entry_insert_tag);
- const gchar *value = gtk_entry_get_text (ui_entry_insert_value);
-
- if (tag && value && tag[0] != '\0') {
-
- /* insert just new tags (the ones already in list should be modified) */
- if (gst_tag_list_get_tag_size (tag_list, tag)) {
- fprintf (stderr, "%s tag is already in the list try to modify it\n", tag);
- } else {
- if (change_tag_list (&tag_list, tag, value)) {
- /* just add to ui_tree if it has been added to tag_list */
- store =
- GTK_TREE_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (ui_tree)));
- gtk_tree_store_append (store, &iter, NULL);
- gtk_tree_store_set (store, &iter, COL_TAG, tag, COL_VALUE, value, -1);
- }
- }
-
- }
-
- return;
-
-}
-
-static void
-setup_new_filename (GString * str, const gchar * ext)
-{
- int i = 0;
-
- for (i = str->len - 1; i > 0; --i) {
- if (str->str[i] == '/') {
- ++i;
- break;
- }
- }
- g_string_insert (str, i, "_new_");
- if (ext) {
- int len = strlen (ext);
-
- if (len > str->len)
- g_string_append (str, ext);
- else if (strcasecmp (ext, &str->str[str->len - len]))
- g_string_append (str, ext);
-
- }
-}
-
-void
-on_buttonSaveFile_clicked (GtkButton * button, gpointer user_data)
-{
-
- GString *src_file = NULL;
- gint enc_status = ENC_UNKNOWN;
-
- gst_element_set_state (gst_pipeline, GST_STATE_NULL);
- gst_element_get_state (gst_pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
-
- src_file = g_string_new (filename->str);
-
- if (gtk_toggle_button_get_active (ui_chk_bnt_capture)) {
- setup_new_filename (filename, ".jpg");
- if (me_gst_setup_capture_pipeline (src_file->str, filename->str,
- &enc_status)) {
- goto done;
- }
- } else {
- setup_new_filename (filename, NULL);
- if (me_gst_setup_encode_pipeline (src_file->str, filename->str,
- &enc_status)) {
- goto done;
- }
- }
-
- ui_refresh ();
- remove (filename->str);
-
- if (tag_list && gst_metadata_mux) {
- GstTagSetter *setter = GST_TAG_SETTER (gst_metadata_mux);
-
- if (setter) {
- gst_element_set_state (gst_pipeline, GST_STATE_READY);
- gst_element_get_state (gst_pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
-
- gst_tag_setter_merge_tags (setter, tag_list, GST_TAG_MERGE_REPLACE);
-
- }
- }
-
- gst_element_set_state (gst_pipeline, GST_STATE_PLAYING);
- gst_element_get_state (gst_pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
-
- /* wait until finished */
- gtk_main ();
-
- gst_element_set_state (gst_pipeline, GST_STATE_NULL);
- gst_element_get_state (gst_pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
-
- if (enc_status == ENC_DONE) {
-
- /* view new file */
- if (tag_list) {
- gst_tag_list_free (tag_list);
- tag_list = NULL;
- }
-
- me_gst_setup_view_pipeline (filename->str, ui_drawing->window);
-
- gst_element_set_state (gst_pipeline, GST_STATE_PLAYING);
- gst_element_get_state (gst_pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
-
- }
-
-done:
-
- if (src_file)
- g_string_free (src_file, TRUE);
-
-}
-
-void
-on_checkbuttonCapture_toggled (GtkToggleButton * togglebutton,
- gpointer user_data)
-{
- if (gtk_toggle_button_get_active (togglebutton)) {
- }
-}
-
-/*
- * UI handling functions
- */
-
-void
-on_cell_edited (GtkCellRendererText * renderer, gchar * str_path,
- gchar * new_text, gpointer user_data)
-{
- GtkTreePath *path = NULL;
- GtkTreeIter iter;
- GtkTreeModel *model = NULL;
- const guint32 col_index = (guint32) user_data;
- const gchar *tag = gtk_entry_get_text (ui_entry_insert_tag);
-
- path = gtk_tree_path_new_from_string (str_path);
- model = gtk_tree_view_get_model (GTK_TREE_VIEW (ui_tree));
-
- if (change_tag_list (&tag_list, tag, new_text)) {
-
- if (gtk_tree_model_get_iter (model, &iter, path)) {
- gtk_tree_store_set (GTK_TREE_STORE (model), &iter, col_index, new_text,
- -1);
- gtk_entry_set_text (ui_entry_insert_value, new_text);
- }
-
- }
-
- if (path)
- gtk_tree_path_free (path);
-
-}
-
-static void
-on_tree_selection_changed (GtkTreeSelection * selection, gpointer data)
-{
- GtkTreeIter iter;
- GtkTreeModel *model;
-
- if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
- gchar *tag;
- gchar *value;
-
- gtk_tree_model_get (model, &iter, COL_TAG, &tag, -1);
- gtk_tree_model_get (model, &iter, COL_VALUE, &value, -1);
-
- gtk_entry_set_text (ui_entry_insert_tag, tag);
- gtk_entry_set_text (ui_entry_insert_value, value);
-
- g_free (value);
- g_free (tag);
-
- }
-
-}
-
-/*
- * UI helper functions
- */
-
-static int
-ui_add_columns (GtkTreeView * tree_view, const gchar * title, gint col_index,
- gboolean editable)
-{
- GtkCellRenderer *renderer;
- GtkTreeViewColumn *tree_col;
- int ret = 0;
-
- renderer = gtk_cell_renderer_text_new ();
-
- if (editable) {
- g_object_set (renderer, "editable", TRUE, NULL);
- g_signal_connect (G_OBJECT (renderer), "edited",
- G_CALLBACK (on_cell_edited), (gpointer) col_index);
- }
-
- if ((tree_col = gtk_tree_view_column_new_with_attributes (title, renderer,
- "text", col_index, NULL))) {
- gtk_tree_view_append_column (tree_view, tree_col);
- } else {
- fprintf (stderr, "UI: could not create column %s\n", title);
- ret = -201;
- goto done;
- }
-
-done:
-
- return ret;
-
-}
-
-
-static int
-ui_setup_tree_view (GtkTreeView * tree_view)
-{
- int ret = 0;
- GtkTreeStore *tree_store = NULL;
- GtkTreeSelection *select;
-
- if ((ret = ui_add_columns (tree_view, "tag", COL_TAG, FALSE)))
- goto done;
-
- if ((ret = ui_add_columns (tree_view, "value", COL_VALUE, TRUE)))
- goto done;
-
- tree_store = gtk_tree_store_new (NUM_COLS, G_TYPE_STRING, G_TYPE_STRING);
-
- gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (tree_store));
-
- select = gtk_tree_view_get_selection (tree_view);
- gtk_tree_selection_set_mode (select, GTK_SELECTION_SINGLE);
- g_signal_connect (G_OBJECT (select), "changed",
- G_CALLBACK (on_tree_selection_changed), NULL);
-
-done:
-
- if (tree_store)
- g_object_unref (tree_store);
-
- return ret;
-}
-
-static void
-ui_refresh ()
-{
- GtkTreeStore *store =
- GTK_TREE_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (ui_tree)));
- gtk_tree_store_clear (store);
- if (filename)
- gtk_window_set_title (GTK_WINDOW (ui_main_window), filename->str);
-}
-
-static int
-ui_create ()
-{
- int ret = 0;
-
- ui_glade_xml = glade_xml_new ("MetadataEditorMain.glade", NULL, NULL);
-
- if (!ui_glade_xml) {
- fprintf (stderr, "glade_xml_new failed\n");
- ret = -101;
- goto done;
- }
-
- ui_main_window = glade_xml_get_widget (ui_glade_xml, "windowMain");
-
- ui_drawing = glade_xml_get_widget (ui_glade_xml, "drawingMain");
-
- ui_tree = glade_xml_get_widget (ui_glade_xml, "treeMain");
-
- ui_entry_insert_tag =
- GTK_ENTRY (glade_xml_get_widget (ui_glade_xml, "entryTag"));
-
- ui_entry_insert_value =
- GTK_ENTRY (glade_xml_get_widget (ui_glade_xml, "entryValue"));
-
- ui_chk_bnt_capture =
- GTK_TOGGLE_BUTTON (glade_xml_get_widget (ui_glade_xml,
- "checkbuttonCapture"));
-
- if (!(ui_main_window && ui_drawing && ui_tree && ui_entry_insert_tag
- && ui_entry_insert_value && ui_chk_bnt_capture)) {
- fprintf (stderr, "Some widgets couldn't be created\n");
- ret = -105;
- goto done;
- }
-
- glade_xml_signal_autoconnect (ui_glade_xml);
-
- ui_setup_tree_view (GTK_TREE_VIEW (ui_tree));
-
- ui_refresh ();
-
- gtk_widget_show_all (ui_main_window);
-
-done:
-
- return ret;
-
-}
-
-/*
- * GStreamer functions
- */
-
-
-static gboolean
-me_gst_bus_callback_encode (GstBus * bus, GstMessage * message, gpointer data)
-{
- gint *encode_status = (gint *) data;
-
- fflush (stdout);
-
- switch (GST_MESSAGE_TYPE (message)) {
- case GST_MESSAGE_ERROR:
- {
- GError *err;
- gchar *debug;
-
- gst_message_parse_error (message, &err, &debug);
- fprintf (stderr, "Error: %s\n", err->message);
- g_error_free (err);
- g_free (debug);
-
- *encode_status = ENC_ERROR;
- gtk_main_quit ();
- }
- break;
- case GST_MESSAGE_TAG:
- {
- /* ignore, we alredy have the tag list */
- }
- break;
- case GST_MESSAGE_EOS:
- {
- *encode_status = ENC_DONE;
- gtk_main_quit ();
- }
- break;
- default:
- /* unhandled message */
- break;
- }
-
- /* we want to be notified again the next time there is a message
- * on the bus, so returning TRUE (FALSE means we want to stop watching
- * for messages on the bus and our callback should not be called again)
- */
- return TRUE;
-}
-
-static gboolean
-me_gst_bus_callback_view (GstBus * bus, GstMessage * message, gpointer data)
-{
- switch (GST_MESSAGE_TYPE (message)) {
- case GST_MESSAGE_ERROR:
- {
- GError *err;
- gchar *debug;
-
- gst_message_parse_error (message, &err, &debug);
- fprintf (stderr, "Error: %s\n", err->message);
- g_error_free (err);
- g_free (debug);
-
- gtk_main_quit ();
- }
- break;
- case GST_MESSAGE_TAG:
- {
- if (tag_list == NULL)
- gst_message_parse_tag (message, &tag_list);
- else {
- GstTagList *tl = NULL;
- GstTagList *ntl = NULL;
-
- gst_message_parse_tag (message, &tl);
- if (tl) {
- ntl = gst_tag_list_merge (tag_list, tl, GST_TAG_MERGE_PREPEND);
- if (ntl) {
- gst_tag_list_free (tag_list);
- tag_list = ntl;
- gst_tag_list_free (tl);
- }
- }
- }
- /* remove whole chunk tags */
- gst_tag_list_remove_tag (tag_list, "exif");
- gst_tag_list_remove_tag (tag_list, "iptc");
- gst_tag_list_remove_tag (tag_list, "xmp");
- }
- break;
- case GST_MESSAGE_EOS:
- if (tag_list) {
- gst_tag_list_foreach (tag_list, insert_tag_on_tree, ui_tree);
- }
- break;
- default:
- /* unhandled message */
- break;
- }
-
- /* we want to be notified again the next time there is a message
- * on the bus, so returning TRUE (FALSE means we want to stop watching
- * for messages on the bus and our callback should not be called again)
- */
- return TRUE;
-}
-
-static void
-me_gst_cleanup_elements ()
-{
- /* when adding an element to pipeline rember to set it to NULL or add extra ref */
-
- if (gst_source) {
- gst_object_unref (gst_source);
- gst_source = NULL;
- }
- if (gst_metadata_demux) {
- gst_object_unref (gst_metadata_demux);
- gst_metadata_demux = NULL;
- }
- if (gst_metadata_mux) {
- gst_object_unref (gst_metadata_mux);
- gst_metadata_mux = NULL;
- }
- if (gst_image_dec) {
- gst_object_unref (gst_image_dec);
- gst_image_dec = NULL;
- }
- if (gst_image_enc) {
- gst_object_unref (gst_image_enc);
- gst_image_enc = NULL;
- }
- if (gst_video_scale) {
- gst_object_unref (gst_video_scale);
- gst_video_scale = NULL;
- }
- if (gst_video_convert) {
- gst_object_unref (gst_video_convert);
- gst_video_convert = NULL;
- }
- if (gst_video_sink) {
- gst_object_unref (gst_video_sink);
- gst_video_sink = NULL;
- }
- if (gst_file_sink) {
- gst_object_unref (gst_file_sink);
- gst_file_sink = NULL;
- }
-
- if (gst_pipeline) {
- gst_element_set_state (gst_pipeline, GST_STATE_NULL);
- gst_element_get_state (gst_pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
- gst_object_unref (gst_pipeline);
- gst_pipeline = NULL;
- }
-
-}
-
-/* dummy function that looks the file extension */
-static gboolean
-is_png (const gchar * filename)
-{
- gboolean ret = FALSE;
- guint32 len;
-
- if (!filename)
- goto done;
-
- if ((len = strlen (filename)) < 4) /* at least ".png" */
- goto done;
-
- if (0 == strcasecmp (filename + (len - 4), ".png"))
- ret = TRUE;
-
-done:
-
- return ret;
-
-}
-
-static int
-me_gst_setup_capture_pipeline (const gchar * src_file, const gchar * dest_file,
- gint * encode_status)
-{
- int ret = 0;
- GstBus *bus = NULL;
- gboolean linked;
-
- *encode_status = ENC_ERROR;
-
- me_gst_cleanup_elements ();
-
- /* create elements */
- gst_source = gst_element_factory_make ("v4l2src", NULL);
- gst_video_convert = gst_element_factory_make ("ffmpegcolorspace", NULL);
- gst_image_enc = gst_element_factory_make ("jpegenc", NULL);
- gst_metadata_mux = gst_element_factory_make ("metadatamux", NULL);
- gst_file_sink = gst_element_factory_make ("filesink", NULL);
-
- if (!(gst_source && gst_video_convert && gst_image_enc && gst_metadata_mux
- && gst_file_sink)) {
- fprintf (stderr, "An element couldn't be created for ecoding\n");
- ret = -300;
- goto done;
- }
-
- /* create gst_pipeline */
- gst_pipeline = gst_pipeline_new (NULL);
-
- if (NULL == gst_pipeline) {
- fprintf (stderr, "Pipeline couldn't be created\n");
- ret = -305;
- goto done;
- }
-
- /* set elements's properties */
- g_object_set (gst_source, "num-buffers", 1, NULL);
- g_object_set (gst_file_sink, "location", dest_file, NULL);
-
- /* adding and linking elements */
- gst_bin_add_many (GST_BIN (gst_pipeline), gst_source, gst_video_convert,
- gst_image_enc, gst_metadata_mux, gst_file_sink, NULL);
-
- linked =
- gst_element_link_many (gst_source, gst_video_convert, gst_image_enc,
- gst_metadata_mux, gst_file_sink, NULL);
-
- /* now element are owned by pipeline (for videosink we keep a extra ref) */
- gst_source = gst_video_convert = gst_image_enc = gst_file_sink = NULL;
- gst_object_ref (gst_metadata_mux);
-
- if (!linked) {
- fprintf (stderr, "Elements couldn't be linked\n");
- ret = -310;
- goto done;
- }
-
- *encode_status = ENC_UNKNOWN;
-
- /* adding message bus */
- bus = gst_pipeline_get_bus (GST_PIPELINE (gst_pipeline));
- gst_bus_add_watch (bus, me_gst_bus_callback_encode, encode_status);
- gst_object_unref (bus);
-
-done:
-
- return ret;
-
-}
-
-static int
-me_gst_setup_encode_pipeline (const gchar * src_file, const gchar * dest_file,
- gint * encode_status)
-{
- int ret = 0;
- GstBus *bus = NULL;
- gboolean linked;
-
- *encode_status = ENC_ERROR;
-
- me_gst_cleanup_elements ();
-
- /* create elements */
- gst_source = gst_element_factory_make ("filesrc", NULL);
- gst_metadata_demux = gst_element_factory_make ("metadatademux", NULL);
- gst_metadata_mux = gst_element_factory_make ("metadatamux", NULL);
- gst_file_sink = gst_element_factory_make ("filesink", NULL);
-
-
- if (!(gst_source && gst_metadata_demux && gst_metadata_mux && gst_file_sink)) {
- fprintf (stderr, "An element couldn't be created for ecoding\n");
- ret = -300;
- goto done;
- }
-
-
- /* create gst_pipeline */
- gst_pipeline = gst_pipeline_new (NULL);
-
- if (NULL == gst_pipeline) {
- fprintf (stderr, "Pipeline couldn't be created\n");
- ret = -305;
- goto done;
- }
-
- /* set elements's properties */
- g_object_set (gst_source, "location", src_file, NULL);
- g_object_set (gst_file_sink, "location", dest_file, NULL);
-
- /* adding and linking elements */
- gst_bin_add_many (GST_BIN (gst_pipeline), gst_source, gst_metadata_demux,
- gst_metadata_mux, gst_file_sink, NULL);
-
- linked =
- gst_element_link_many (gst_source, gst_metadata_demux, gst_metadata_mux,
- gst_file_sink, NULL);
-
- /* now element are owned by pipeline (for videosink we keep a extra ref) */
- gst_source = gst_metadata_demux = gst_file_sink = NULL;
- gst_object_ref (gst_metadata_mux);
-
- if (!linked) {
- fprintf (stderr, "Elements couldn't be linked\n");
- ret = -310;
- goto done;
- }
-
- *encode_status = ENC_UNKNOWN;
-
- /* adding message bus */
- bus = gst_pipeline_get_bus (GST_PIPELINE (gst_pipeline));
- gst_bus_add_watch (bus, me_gst_bus_callback_encode, encode_status);
- gst_object_unref (bus);
-
-done:
-
- return ret;
-
-}
-
-static int
-me_gst_setup_view_pipeline (const gchar * filename, GdkWindow * window)
-{
- int ret = 0;
- GstBus *bus = NULL;
- gboolean linked;
-
- me_gst_cleanup_elements ();
-
- /* create elements */
- gst_source = gst_element_factory_make ("filesrc", NULL);
- gst_metadata_demux = gst_element_factory_make ("metadatademux", NULL);
- /* let's do a dummy stuff to avoid decodebin */
- if (is_png (filename))
- gst_image_dec = gst_element_factory_make ("pngdec", NULL);
- else
- gst_image_dec = gst_element_factory_make ("jpegdec", NULL);
- gst_video_scale = gst_element_factory_make ("videoscale", NULL);
- gst_video_convert = gst_element_factory_make ("ffmpegcolorspace", NULL);
- gst_video_sink = gst_element_factory_make ("ximagesink", NULL);
-
- if (!(gst_source && gst_metadata_demux && gst_image_dec && gst_video_scale
- && gst_video_convert && gst_video_sink)) {
- fprintf (stderr, "An element couldn't be created for viewing\n");
- ret = -400;
- goto done;
- }
-
- /* create gst_pipeline */
- gst_pipeline = gst_pipeline_new (NULL);
-
- if (NULL == gst_pipeline) {
- fprintf (stderr, "Pipeline couldn't be created\n");
- ret = -405;
- goto done;
- }
-
- /* set elements's properties */
- g_object_set (gst_source, "location", filename, NULL);
- g_object_set (gst_metadata_demux, "parse-only", TRUE, NULL);
- g_object_set (gst_video_sink, "force-aspect-ratio", TRUE, NULL);
-
-
- /* adding and linking elements */
- gst_bin_add_many (GST_BIN (gst_pipeline), gst_source, gst_metadata_demux,
- gst_image_dec, gst_video_scale, gst_video_convert, gst_video_sink, NULL);
-
- linked = gst_element_link_many (gst_source, gst_metadata_demux, gst_image_dec,
- gst_video_scale, gst_video_convert, gst_video_sink, NULL);
-
- /* now element are owned by pipeline (for videosink we keep a extra ref) */
- gst_source = gst_metadata_demux = gst_image_dec = gst_video_scale =
- gst_video_convert = NULL;
- gst_object_ref (gst_video_sink);
-
- if (window)
- gst_x_overlay_set_xwindow_id (GST_X_OVERLAY (gst_video_sink),
- GDK_WINDOW_XWINDOW (window));
-
- if (!linked) {
- fprintf (stderr, "Elements couldn't be linked\n");
- ret = -410;
- goto done;
- }
-
- /* adding message bus */
- bus = gst_pipeline_get_bus (GST_PIPELINE (gst_pipeline));
- gst_bus_add_watch (bus, me_gst_bus_callback_view, NULL);
- gst_object_unref (bus);
-
-
-done:
-
- return ret;
-
-}
-
-int
-main (int argc, char *argv[])
-{
- int ret = 0;
-
- if (argc != 2) {
- fprintf (stderr, "Give the name of a jpeg file as argument\n");
- ret = -5;
- goto done;
- }
-
- gst_init (&argc, &argv);
- gtk_init (&argc, &argv);
-
- /* filename for future usage (title and file name to be created) */
- if (filename)
- g_string_free (filename, TRUE);
- filename = g_string_new (argv[1]);
-
- /* create UI */
- if ((ret = ui_create ())) {
- goto done;
- }
-
- /* create pipeline */
- me_gst_setup_view_pipeline (argv[1], ui_drawing->window);
-
- gst_element_set_state (gst_pipeline, GST_STATE_PLAYING);
-
- gtk_main ();
-
-done:
-
- me_gst_cleanup_elements ();
-
- if (tag_list) {
- gst_tag_list_free (tag_list);
- tag_list = NULL;
- }
-
- if (filename) {
- g_string_free (filename, TRUE);
- filename = NULL;
- }
-
- return ret;
-}