Add lot of documentation.

Original commit message from CVS:
Add lot of documentation.
This commit is contained in:
Edgard Lima 2008-01-25 17:45:28 +00:00
parent ef421fee72
commit 9e31b57dc3
25 changed files with 2116 additions and 679 deletions

View file

@ -1,3 +1,33 @@
2008-01-25 Edgard Lima <edgard.lima@indt.org.br>
* ext/metadata/TODO:
* ext/metadata/gstbasemetadata.c:
* ext/metadata/metadata.c:
* ext/metadata/metadata.h:
* ext/metadata/metadataexif.c:
* ext/metadata/metadataexif.h:
* ext/metadata/metadataiptc.c:
* ext/metadata/metadataiptc.h:
* ext/metadata/metadataparsejpeg.c:
* ext/metadata/metadataparsejpeg.h:
* ext/metadata/metadataparsepng.c:
* ext/metadata/metadataparsepng.h:
* ext/metadata/metadataparseutil.c:
* ext/metadata/metadataparseutil.h:
* ext/metadata/metadatatags.c:
* ext/metadata/metadatatags.h:
* ext/metadata/metadatatypes.c:
* ext/metadata/metadatatypes.h:
* ext/metadata/metadataxmp.c:
* ext/metadata/metadataxmp.h:
* ext/metadata/test/Makefile:
* ext/metadata/test/MetadataEditorMain.glade:
* ext/metadata/test/metadata_editor.c:
* tests/icles/Makefile.am:
* tests/icles/metadata_editor.c:
* tests/icles/metadata_editor.glade:
Add lot of documentation.
2008-01-25 Zaheer Abbas Merali <zaheerabbas at merali dot org> 2008-01-25 Zaheer Abbas Merali <zaheerabbas at merali dot org>
* sys/dvb/gstdvbsrc.c: * sys/dvb/gstdvbsrc.c:

View file

@ -26,3 +26,12 @@ OPEN ISSUES:
KNOWN BUGS 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

View file

@ -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->sinkpad);
gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad); gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
metadataparse_xmp_init (); metadata_xmp_init ();
/* init members */ /* init members */
gst_base_metadata_init_members (filter); gst_base_metadata_init_members (filter);
@ -1307,7 +1307,7 @@ gst_base_metadata_dispose (GObject * object)
gst_base_metadata_dispose_members (filter); gst_base_metadata_dispose_members (filter);
metadataparse_xmp_dispose (); metadata_xmp_dispose ();
G_OBJECT_CLASS (parent_class)->dispose (object); G_OBJECT_CLASS (parent_class)->dispose (object);
} }

View file

@ -41,32 +41,67 @@
* Boston, MA 02111-1307, USA. * 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 <string.h>
#include "metadata.h" #include "metadata.h"
/* /*
*static declarations * static helper functions declaration
*/ */
static MetadataParsingReturn static MetadataParsingReturn
metadata_parse_none (MetaData * meta_data, const guint8 * buf, metadata_parse_none (MetaData * meta_data, const guint8 * data,
guint32 * bufsize, guint8 ** next_start, guint32 * next_size); guint32 * data_size, guint8 ** next_start, guint32 * next_size);
/* /*
* extern functions implementations * extern functions implementations
*/ */
/* /*
* Init metadata handle vars. * metadata_init:
* This function must becalled before any other function from this module. * @meta_data: [in] metadata handler to be inited
* This functoin must not be called twice without call 'metadata_dispose' * @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. * beteween them.
* meta_data [in]: metadata handler to be inited * @see_also: #metadata_dispose #metadata_parse
* parse [in]: pass TRUE for demuxing and FALSE for muxing *
* options [in]: which types of metadata will be processed (EXIF, IPTC and/or XMP). * Returns: nothing
* Look at 'MetaOptions' to see the available options.
*/ */
void void
metadata_init (MetaData ** meta_data, const MetaOptions options) 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) { if ((*meta_data)->options & META_OPT_DEMUX) {
/* when parsing we will probably strip only 3 chunk (exif, iptc and xmp) /* 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. 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); metadata_chunk_array_init (&(*meta_data)->strip_chunks, 4);
/* at most 1 chunk will be injected (JPEG JFIF) */ /* at most 1 chunk will be injected (JPEG JFIF) */
metadata_chunk_array_init (&(*meta_data)->inject_chunks, 1); 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. * metadata_dispose:
* Call this function to free any resource allocated by 'metadata_init' * @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 void
metadata_dispose (MetaData ** meta_data) metadata_dispose (MetaData ** meta_data)
{ {
@ -151,27 +193,41 @@ metadata_dispose (MetaData ** meta_data)
} }
/* /*
* meta_data [in]: metata handle * metadata_parse:
* buf [in]: data to be parsed * @meta_data: [in] metadata handle
* bufsize [in]: size of data in bytes * @buf: [in] data to be parsed
* next_offset [out]: number of bytes to jump from the begining of 'buf' in the next call. * @buf_size: [in] size of @buf in bytes
* i.e, 0 (zero) mean that in the next call to function "buf" must have the same * @next_offset: [out] number of bytes to jump from the begining of @buf in
* data (probably resized, see 'size') * the next call. i.e, 0 (zero) mean that in the next call this function @buf
* size [out]: number of minimal bytes in buf for the next call to this function * must have the same data (probably resized, see @next_size)
* return: * @next_size: [out] number of minimal bytes in @buf for the next call to this
* META_PARSING_ERROR * function
* META_PARSING_DONE *
* META_PARSING_NEED_MORE_DATA (look 'next_offset' and 'size') * This function is used to parse the stream step-by-step incrementaly, which
* when this function returns 0 you have strip and inject chunks ready to use * means, discover the stream type and find the metadata chunks
* If you change the contents of strip and inject chunks, you have to call * (#META_OPT_DEMUX), or point out where metadata chunks should be written
* 'metadata_lazy_update' (this is the case when muxing) * (#META_OPT_MUX). It is important to notice that there could be both strip
* see MetaData->strip_chunks and MetaData->inject_chunks * 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 MetadataParsingReturn
metadata_parse (MetaData * meta_data, const guint8 * buf, 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; int ret = META_PARSING_DONE;
@ -180,7 +236,7 @@ metadata_parse (MetaData * meta_data, const guint8 * buf,
if (meta_data->state == STATE_NULL) { if (meta_data->state == STATE_NULL) {
ret = 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) if (ret == META_PARSING_DONE)
meta_data->state = STATE_READING; meta_data->state = STATE_READING;
else else
@ -192,24 +248,24 @@ metadata_parse (MetaData * meta_data, const guint8 * buf,
if (G_LIKELY (meta_data->options & META_OPT_DEMUX)) if (G_LIKELY (meta_data->options & META_OPT_DEMUX))
ret = ret =
metadataparse_jpeg_parse (&meta_data->format_data.jpeg_parse, 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); next_size);
else else
ret = ret =
metadatamux_jpeg_parse (&meta_data->format_data.jpeg_mux, 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); next_size);
break; break;
case IMG_PNG: case IMG_PNG:
if (G_LIKELY (meta_data->options & META_OPT_DEMUX)) if (G_LIKELY (meta_data->options & META_OPT_DEMUX))
ret = ret =
metadataparse_png_parse (&meta_data->format_data.png_parse, 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); next_size);
else else
ret = ret =
metadatamux_png_parse (&meta_data->format_data.png_mux, 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); next_size);
break; break;
default: 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: * This function is really importante in case o muxing 'cause:
* 1- 'cause gives the oportunity to muxers to wrapper new segments with apropriate bytes * 1- 'cause gives the oportunity to muxers to wrapper new segments with
* ex: in case of JPEG it can wrap the EXIF chunk (created using tags) with chunk id and chunk size * apropriate bytes
* 2- 'cause gives the oportunity to muxer to decide if some chunks should still be striped/injected * ex: in case of JPEG it can wrap the EXIF chunk (created using tags) with
* ex: if there is no EXIF chunk to be inserted, the muxer decides to not strip JFIF anymore * chunk id and chunk size
* see MetaData->strip_chunks and MetaData->inject_chunks * 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 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 static MetadataParsingReturn
metadata_parse_none (MetaData * meta_data, const guint8 * buf, 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; 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 */ /* we need at least 3 bytes to see if it is JPEG */
if (*bufsize < 3) { if (*buf_size < 3) {
*next_size = 3; *next_size = 3;
ret = META_PARSING_NEED_MORE_DATA; ret = META_PARSING_NEED_MORE_DATA;
goto done; 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 */ /* we need at least 8 bytes to see if it is PNG */
if (*bufsize < 8) { if (*buf_size < 8) {
*next_size = 8; *next_size = 8;
ret = META_PARSING_NEED_MORE_DATA; ret = META_PARSING_NEED_MORE_DATA;
goto done; goto done;

View file

@ -44,6 +44,10 @@
#ifndef __METADATA_H__ #ifndef __METADATA_H__
#define __METADATA_H__ #define __METADATA_H__
/*
* includes
*/
#include <gst/base/gstadapter.h> #include <gst/base/gstadapter.h>
#include "metadatatypes.h" #include "metadatatypes.h"
@ -54,16 +58,19 @@
G_BEGIN_DECLS G_BEGIN_DECLS
/* *INDENT-OFF* */ /*
* enum and types
*/
/* *INDENT-OFF* */
typedef enum _tag_MetaOptions typedef enum _tag_MetaOptions
{ {
META_OPT_EXIF = (1 << 0), META_OPT_EXIF = (1 << 0),
META_OPT_IPTC = (1 << 1), META_OPT_IPTC = (1 << 1),
META_OPT_XMP = (1 << 2), META_OPT_XMP = (1 << 2),
META_OPT_PARSE_ONLY = (1 << 3), /* only makes sense with META_OPT_DEMUX */ META_OPT_PARSE_ONLY = (1 << 3), /* only makes sense with META_OPT_DEMUX */
META_OPT_DEMUX = (1 << 4), META_OPT_DEMUX = (1 << 4), /* to operates in demuxing mode */
META_OPT_MUX = (1 << 5), META_OPT_MUX = (1 << 5), /* to operates in muxing mode */
META_OPT_ALL = (1 << 6) - 1 META_OPT_ALL = (1 << 6) - 1
} MetaOptions; } MetaOptions;
/* *INDENT-ON* */ /* *INDENT-ON* */
@ -104,18 +111,26 @@ typedef struct _tag_MetaData
} MetaData; } MetaData;
/*
* defines and macros
*/
#define META_DATA_IMG_TYPE(p) (p)->img_type #define META_DATA_IMG_TYPE(p) (p)->img_type
#define META_DATA_STRIP_CHUNKS(p) (p)->strip_chunks #define META_DATA_STRIP_CHUNKS(p) (p)->strip_chunks
#define META_DATA_INJECT_CHUNKS(p) (p)->inject_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_init (MetaData ** meta_data, const MetaOptions options);
extern void metadata_dispose (MetaData ** meta_data); extern void metadata_dispose (MetaData ** meta_data);
extern MetadataParsingReturn extern MetadataParsingReturn
metadata_parse (MetaData * meta_data, const guint8 * buf, 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); extern void metadata_lazy_update (MetaData * meta_data);

View file

@ -41,23 +41,52 @@
* Boston, MA 02111-1307, USA. * 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 "metadataexif.h"
#include "metadataparseutil.h" #include "metadataparseutil.h"
#include "metadatatags.h" #include "metadatatags.h"
/*
* defines
*/
GST_DEBUG_CATEGORY (gst_metadata_exif_debug); GST_DEBUG_CATEGORY (gst_metadata_exif_debug);
#define GST_CAT_DEFAULT gst_metadata_exif_debug #define GST_CAT_DEFAULT gst_metadata_exif_debug
/*
* Implementation when libexif isn't available at compilation time
*/
#ifndef HAVE_EXIF #ifndef HAVE_EXIF
/*
* extern functions implementations
*/
void void
metadataparse_exif_tag_list_add (GstTagList * taglist, GstTagMergeMode mode, metadataparse_exif_tag_list_add (GstTagList * taglist, GstTagMergeMode mode,
GstAdapter * adapter, MetadataTagMapping mapping) GstAdapter * adapter, MetadataTagMapping mapping)
{ {
if (mapping & METADATA_TAG_MAP_WHOLECHUNK) { if (mapping & METADATA_TAG_MAP_WHOLECHUNK) {
GST_LOG GST_LOG ("EXIF not defined, sending just one tag as whole chunk");
("EXIF not defined, here I should send just one tag as whole chunk");
metadataparse_util_tag_list_add_chunk (taglist, mode, GST_TAG_EXIF, metadataparse_util_tag_list_add_chunk (taglist, mode, GST_TAG_EXIF,
adapter); adapter);
} }
@ -73,11 +102,23 @@ metadatamux_exif_create_chunk_from_tag_list (guint8 ** buf, guint32 * size,
#else /* ifndef HAVE_EXIF */ #else /* ifndef HAVE_EXIF */
/*
* Implementation when libexif is available at compilation time
*/
/*
* includes
*/
#include <libexif/exif-data.h> #include <libexif/exif-data.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <math.h> #include <math.h>
/*
* enum and types
*/
typedef struct _tag_MEUserData typedef struct _tag_MEUserData
{ {
GstTagList *taglist; GstTagList *taglist;
@ -92,10 +133,9 @@ typedef struct _tag_MapIntStr
const gchar *str; const gchar *str;
} MapIntStr; } MapIntStr;
static void /*
exif_data_foreach_content_func (ExifContent * content, void *callback_data); * defines and static global vars
*/
static void exif_content_foreach_entry_func (ExifEntry * entry, void *);
/* *INDENT-OFF* */ /* *INDENT-OFF* */
static MapIntStr mappedTags[] = { static MapIntStr mappedTags[] = {
@ -131,41 +171,47 @@ static MapIntStr mappedTags[] = {
}; };
/* *INDENT-ON* */ /* *INDENT-ON* */
static const gchar * /*
metadataparse_exif_get_tag_from_exif (ExifTag exif, GType * type) * static helper functions declaration
{ */
int i = 0;
while (mappedTags[i].exif) { static const gchar *metadataparse_exif_get_tag_from_exif (ExifTag exif,
if (exif == mappedTags[i].exif) { GType * type);
*type = gst_tag_get_type (mappedTags[i].str);
break;
}
++i;
}
return mappedTags[i].str;
}
static ExifTag static ExifTag
metadataparse_exif_get_exif_from_tag (const gchar * tag, GType * type, metadatamux_exif_get_exif_from_tag (const gchar * tag, GType * type,
ExifIfd * ifd) ExifIfd * ifd);
{
int i = 0;
while (mappedTags[i].exif) { static void
if (0 == strcmp (mappedTags[i].str, tag)) { metadataparse_exif_data_foreach_content_func (ExifContent * content,
*type = gst_tag_get_type (tag); void *callback_data);
*ifd = mappedTags[i].ifd;
break;
}
++i;
}
return mappedTags[i].exif; static void
metadataparse_exif_content_foreach_entry_func (ExifEntry * entry,
void *user_data);
} static void
metadatamux_exif_for_each_tag_in_list (const GstTagList * list,
const gchar * tag, gpointer user_data);
/*
* 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 void
metadataparse_exif_tag_list_add (GstTagList * taglist, GstTagMergeMode mode, metadataparse_exif_tag_list_add (GstTagList * taglist, GstTagMergeMode mode,
@ -195,8 +241,8 @@ metadataparse_exif_tag_list_add (GstTagList * taglist, GstTagMergeMode mode,
goto done; goto done;
} }
exif_data_foreach_content (exif, exif_data_foreach_content_func, exif_data_foreach_content (exif,
(void *) &user_data); metadataparse_exif_data_foreach_content_func, (void *) &user_data);
done: done:
@ -207,44 +253,178 @@ done:
} }
/*
* 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)
{
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, metadatamux_exif_for_each_tag_in_list, ed);
exif_data_save_data (ed, buf, size);
done:
if (ed)
exif_data_unref (ed);
return;
}
/*
* 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;
while (mappedTags[i].exif) {
if (exif == mappedTags[i].exif) {
*type = gst_tag_get_type (mappedTags[i].str);
break;
}
++i;
}
return mappedTags[i].str;
}
/*
* 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 mappedTags[i].exif;
}
/*
* 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 static void
exif_data_foreach_content_func (ExifContent * content, void *user_data) metadataparse_exif_data_foreach_content_func (ExifContent * content,
void *user_data)
{ {
ExifIfd ifd = exif_content_get_ifd (content); ExifIfd ifd = exif_content_get_ifd (content);
GST_LOG ("\n Content %p: %s (ifd=%d)", content, exif_ifd_get_name (ifd), GST_LOG ("\n Content %p: %s (ifd=%d)", content, exif_ifd_get_name (ifd),
ifd); ifd);
exif_content_foreach_entry (content, exif_content_foreach_entry_func, exif_content_foreach_entry (content,
user_data); metadataparse_exif_content_foreach_entry_func, user_data);
} }
#if 0 /*
static gboolean * metadataparse_exif_content_foreach_entry_func:
exif_fast_mdc (glong n, glong d, gulong * m) * @entry: EXIF structure from libexif having a EXIF tag
{ * @user_data: pointer to #MEUserData
gboolean ret = FALSE; *
* This function designed to be called for each EXIF tag in a EXIF IFD. This
static const int a[] = * function gets the EXIF tag from @entry and then add to the tag list
{ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 39, 41, 43, 47, 49, 53, 0 }; * in @user_data by using a merge mode also specified in @user_data
int i = 0; * @see_also: #metadataparse_exif_data_foreach_content_func
*
*m = 1; * Returns: nothing
*/
while (a[i] <= n && a[i] <= d) {
while ((n % a[i] == 0) && (d % a[i]) == 0) {
*m *= a[i];
ret = TRUE;
}
++i;
}
return ret;
}
#endif
static void static void
exif_content_foreach_entry_func (ExifEntry * entry, void *user_data) metadataparse_exif_content_foreach_entry_func (ExifEntry * entry,
void *user_data)
{ {
char buf[2048]; char buf[2048];
MEUserData *meudata = (MEUserData *) user_data; 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 static void
metadataexif_for_each_tag_in_list (const GstTagList * list, const gchar * tag, metadatamux_exif_for_each_tag_in_list (const GstTagList * list,
gpointer user_data) const gchar * tag, gpointer user_data)
{ {
ExifData *ed = (ExifData *) user_data; ExifData *ed = (ExifData *) user_data;
ExifTag exif_tag; ExifTag exif_tag;
@ -468,7 +594,7 @@ metadataexif_for_each_tag_in_list (const GstTagList * list, const gchar * tag,
ExifIfd ifd; ExifIfd ifd;
const ExifByteOrder byte_order = exif_data_get_byte_order (ed); 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) if (!exif_tag)
goto done; goto done;
@ -505,7 +631,9 @@ metadataexif_for_each_tag_in_list (const GstTagList * list, const gchar * tag,
entry->tag == EXIF_TAG_Y_RESOLUTION) { entry->tag == EXIF_TAG_Y_RESOLUTION) {
ExifEntry *unit_entry = NULL; 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); ExifShort vsh = exif_get_short (unit_entry->data, byte_order);
if (vsh != 2) /* inches */ 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) */ #endif /* else (ifndef HAVE_EXIF) */

View file

@ -50,6 +50,10 @@
G_BEGIN_DECLS G_BEGIN_DECLS
/*
* external function prototypes
*/
extern void extern void
metadataparse_exif_tag_list_add (GstTagList * taglist, GstTagMergeMode mode, metadataparse_exif_tag_list_add (GstTagList * taglist, GstTagMergeMode mode,
GstAdapter * adapter, MetadataTagMapping mapping); GstAdapter * adapter, MetadataTagMapping mapping);

View file

@ -41,23 +41,51 @@
* Boston, MA 02111-1307, USA. * 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 "metadataiptc.h"
#include "metadataparseutil.h" #include "metadataparseutil.h"
#include "metadatatags.h" #include "metadatatags.h"
/*
* defines
*/
GST_DEBUG_CATEGORY (gst_metadata_iptc_debug); GST_DEBUG_CATEGORY (gst_metadata_iptc_debug);
#define GST_CAT_DEFAULT gst_metadata_iptc_debug #define GST_CAT_DEFAULT gst_metadata_iptc_debug
/*
* Implementation when libiptcdata isn't available at compilation time
*/
#ifndef HAVE_IPTC #ifndef HAVE_IPTC
/*
* extern functions implementations
*/
void void
metadataparse_iptc_tag_list_add (GstTagList * taglist, GstTagMergeMode mode, metadataparse_iptc_tag_list_add (GstTagList * taglist, GstTagMergeMode mode,
GstAdapter * adapter, MetadataTagMapping mapping) GstAdapter * adapter, MetadataTagMapping mapping)
{ {
if (mapping & METADATA_TAG_MAP_WHOLECHUNK) { if (mapping & METADATA_TAG_MAP_WHOLECHUNK) {
GST_LOG GST_LOG ("IPTC not defined, sending just one tag as whole chunk");
("IPTC not defined, here I should send just one tag as whole chunk");
metadataparse_util_tag_list_add_chunk (taglist, mode, GST_TAG_IPTC, metadataparse_util_tag_list_add_chunk (taglist, mode, GST_TAG_IPTC,
adapter); adapter);
} }
@ -74,11 +102,23 @@ metadatamux_iptc_create_chunk_from_tag_list (guint8 ** buf, guint32 * size,
#else /* ifndef HAVE_IPTC */ #else /* ifndef HAVE_IPTC */
/*
* Implementation when libiptcdata is available at compilation time
*/
/*
* includes
*/
#include <iptc-data.h> #include <iptc-data.h>
#include <iptc-tag.h> #include <iptc-tag.h>
#include <string.h> #include <string.h>
#include <gst/gsttaglist.h> #include <gst/gsttaglist.h>
/*
* enum and types
*/
typedef struct _tag_MEUserData typedef struct _tag_MEUserData
{ {
GstTagList *taglist; GstTagList *taglist;
@ -92,56 +132,62 @@ typedef struct _tag_MapIntStr
const gchar *str; const gchar *str;
} MapIntStr; } MapIntStr;
static void /*
iptc_data_foreach_dataset_func (IptcDataSet * dataset, void *user_data); * defines and static global vars
*/
/* *INDENT-OFF* */ /* *INDENT-OFF* */
static MapIntStr mappedTags[] = { static MapIntStr mappedTags[] = {
{IPTC_RECORD_APP_2, IPTC_TAG_OBJECT_NAME, /*ASCII*/ GST_TAG_TITLE /*STRING*/}, {IPTC_RECORD_APP_2, IPTC_TAG_OBJECT_NAME, /*ASCII*/
{IPTC_RECORD_APP_2, IPTC_TAG_BYLINE, /*ASCII*/ GST_TAG_COMPOSER /*STRING*/}, GST_TAG_TITLE /*STRING*/},
{IPTC_RECORD_APP_2, IPTC_TAG_CAPTION, /*ASCII*/ GST_TAG_DESCRIPTION /*STRING*/}, {IPTC_RECORD_APP_2, IPTC_TAG_BYLINE, /*ASCII*/
{IPTC_RECORD_APP_2, IPTC_TAG_COPYRIGHT_NOTICE, /*ASCII*/ GST_TAG_COPYRIGHT /*STRING*/}, 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} {0, 0, NULL}
}; };
/* *INDENT-ON* */ /* *INDENT-ON* */
static const gchar * /*
metadataparse_iptc_get_tag_from_iptc (IptcTag iptc, GType * type, * static helper functions declaration
IptcRecord * record) */
{
int i = 0;
while (mappedTags[i].iptc) { static const gchar *metadataparse_iptc_get_tag_from_iptc (IptcTag iptc,
if (iptc == mappedTags[i].iptc) { GType * type, IptcRecord * record);
*type = gst_tag_get_type (mappedTags[i].str);
*record = mappedTags[i].record;
break;
}
++i;
}
return mappedTags[i].str;
}
static IptcTag static IptcTag
metadataparse_iptc_get_iptc_from_tag (const gchar * tag, GType * type, metadatamux_iptc_get_iptc_from_tag (const gchar * tag, GType * type,
IptcRecord * record) IptcRecord * record);
{
int i = 0;
while (mappedTags[i].iptc) { static void
if (0 == strcmp (mappedTags[i].str, tag)) { metadataparse_iptc_data_foreach_dataset_func (IptcDataSet * dataset,
*type = gst_tag_get_type (tag); void *user_data);
*record = mappedTags[i].record;
break;
}
++i;
}
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 void
metadataparse_iptc_tag_list_add (GstTagList * taglist, GstTagMergeMode mode, metadataparse_iptc_tag_list_add (GstTagList * taglist, GstTagMergeMode mode,
@ -171,8 +217,8 @@ metadataparse_iptc_tag_list_add (GstTagList * taglist, GstTagMergeMode mode,
goto done; goto done;
} }
iptc_data_foreach_dataset (iptc, iptc_data_foreach_dataset_func, iptc_data_foreach_dataset (iptc,
(void *) &user_data); metadataparse_iptc_data_foreach_dataset_func, (void *) &user_data);
done: done:
@ -183,79 +229,17 @@ done:
} }
static void /*
iptc_data_foreach_dataset_func (IptcDataSet * dataset, void *user_data) * 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
char buf[1024]; * @taglist: list of tags to be added to IPTC chunk
MEUserData *meudata = (MEUserData *) user_data; *
GType type; * Get tags from @taglist, create a IPTC chunk based on it and save to @buf.
IptcRecord record; * Note: The IPTC chunk is NOT wrapped by any bytes specific to any file format
const gchar *tag = *
metadataparse_iptc_get_tag_from_iptc (dataset->tag, &type, &record); * Returns: nothing
const gchar *value = iptc_dataset_get_as_str (dataset, buf, 1024); */
if (!tag)
goto done;
gst_tag_list_add (meudata->taglist, meudata->mode, tag, value, NULL);
done:
GST_LOG ("name -> %s", iptc_tag_get_name (dataset->record, dataset->tag));
GST_LOG ("title -> %s", iptc_tag_get_title (dataset->record, dataset->tag));
GST_LOG ("description -> %s", iptc_tag_get_description (dataset->record,
dataset->tag));
GST_LOG ("value = %s", value);
GST_LOG ("record = %d", dataset->record);
return;
}
static void
metadataiptc_for_each_tag_in_list (const GstTagList * list, const gchar * tag,
gpointer user_data)
{
IptcData *iptc = (IptcData *) user_data;
IptcTag iptc_tag;
IptcRecord record;
GType type;
IptcDataSet *dataset = NULL;
gboolean new_dataset = FALSE;
gchar *tag_value = NULL;
iptc_tag = metadataparse_iptc_get_iptc_from_tag (tag, &type, &record);
if (!iptc_tag)
goto done;
dataset = iptc_data_get_dataset (iptc, record, iptc_tag);
if (!dataset) {
dataset = iptc_dataset_new ();
new_dataset = TRUE;
}
iptc_dataset_set_tag (dataset, record, iptc_tag);
if (gst_tag_list_get_string (list, tag, &tag_value)) {
iptc_dataset_set_data (dataset, tag_value, strlen (tag_value),
IPTC_DONT_VALIDATE);
g_free (tag_value);
tag_value = NULL;
}
if (new_dataset)
iptc_data_add_dataset (iptc, dataset);
done:
if (dataset)
iptc_dataset_unref (dataset);
}
void void
metadatamux_iptc_create_chunk_from_tag_list (guint8 ** buf, guint32 * size, metadatamux_iptc_create_chunk_from_tag_list (guint8 ** buf, guint32 * size,
@ -286,7 +270,7 @@ metadatamux_iptc_create_chunk_from_tag_list (guint8 ** buf, guint32 * size,
iptc = iptc_data_new (); iptc = iptc_data_new ();
} }
gst_tag_list_foreach (taglist, metadataiptc_for_each_tag_in_list, iptc); gst_tag_list_foreach (taglist, metadatamux_iptc_for_each_tag_in_list, iptc);
iptc_data_save (iptc, buf, size); iptc_data_save (iptc, buf, size);
@ -299,4 +283,182 @@ done:
return; 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
metadataparse_iptc_data_foreach_dataset_func (IptcDataSet * dataset,
void *user_data)
{
char buf[1024];
MEUserData *meudata = (MEUserData *) user_data;
GType type;
IptcRecord record;
const gchar *tag =
metadataparse_iptc_get_tag_from_iptc (dataset->tag, &type, &record);
const gchar *value = iptc_dataset_get_as_str (dataset, buf, 1024);
if (!tag)
goto done;
gst_tag_list_add (meudata->taglist, meudata->mode, tag, value, NULL);
done:
GST_LOG ("name -> %s", iptc_tag_get_name (dataset->record, dataset->tag));
GST_LOG ("title -> %s", iptc_tag_get_title (dataset->record, dataset->tag));
GST_LOG ("description -> %s", iptc_tag_get_description (dataset->record,
dataset->tag));
GST_LOG ("value = %s", value);
GST_LOG ("record = %d", dataset->record);
return;
}
/*
* 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
metadatamux_iptc_for_each_tag_in_list (const GstTagList * list,
const gchar * tag, gpointer user_data)
{
IptcData *iptc = (IptcData *) user_data;
IptcTag iptc_tag;
IptcRecord record;
GType type;
IptcDataSet *dataset = NULL;
gboolean new_dataset = FALSE;
gchar *tag_value = NULL;
iptc_tag = metadatamux_iptc_get_iptc_from_tag (tag, &type, &record);
if (!iptc_tag)
goto done;
dataset = iptc_data_get_dataset (iptc, record, iptc_tag);
if (!dataset) {
dataset = iptc_dataset_new ();
new_dataset = TRUE;
}
iptc_dataset_set_tag (dataset, record, iptc_tag);
if (gst_tag_list_get_string (list, tag, &tag_value)) {
iptc_dataset_set_data (dataset, tag_value, strlen (tag_value),
IPTC_DONT_VALIDATE);
g_free (tag_value);
tag_value = NULL;
}
if (new_dataset)
iptc_data_add_dataset (iptc, dataset);
done:
if (dataset)
iptc_dataset_unref (dataset);
}
#endif /* else (ifndef HAVE_IPTC) */ #endif /* else (ifndef HAVE_IPTC) */

View file

@ -50,6 +50,10 @@
G_BEGIN_DECLS G_BEGIN_DECLS
/*
* external function prototypes
*/
extern void extern void
metadataparse_iptc_tag_list_add (GstTagList * taglist, GstTagMergeMode mode, metadataparse_iptc_tag_list_add (GstTagList * taglist, GstTagMergeMode mode,
GstAdapter * adapter, MetadataTagMapping mapping); GstAdapter * adapter, MetadataTagMapping mapping);

View file

@ -41,14 +41,62 @@
* Boston, MA 02111-1307, USA. * 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 <string.h>
#include "metadataparsejpeg.h"
#ifdef HAVE_IPTC #ifdef HAVE_IPTC
#include <libiptcdata/iptc-jpeg.h> #include <libiptcdata/iptc-jpeg.h>
#endif #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 static MetadataParsingReturn
metadataparse_jpeg_reading (JpegParseData * jpeg_data, guint8 ** buf, metadataparse_jpeg_reading (JpegParseData * jpeg_data, guint8 ** buf,
guint32 * bufsize, const guint32 offset, const guint8 * step_buf, guint32 * bufsize, const guint32 offset, const guint8 * step_buf,
@ -72,13 +120,30 @@ static MetadataParsingReturn
metadataparse_jpeg_jump (JpegParseData * jpeg_data, guint8 ** buf, metadataparse_jpeg_jump (JpegParseData * jpeg_data, guint8 ** buf,
guint32 * bufsize, guint8 ** next_start, guint32 * next_size); 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) * metadataparse_jpeg_init:
{ * @jpeg_data: [in] jpeg data handler to be inited
/* nothing to do */ * @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 void
metadataparse_jpeg_init (JpegParseData * jpeg_data, GstAdapter ** exif_adpt, 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 void
metadataparse_jpeg_dispose (JpegParseData * jpeg_data) metadataparse_jpeg_dispose (JpegParseData * jpeg_data)
{ {
@ -108,6 +184,42 @@ metadataparse_jpeg_dispose (JpegParseData * jpeg_data)
jpeg_data->xmp_adapter = NULL; 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 MetadataParsingReturn
metadataparse_jpeg_parse (JpegParseData * jpeg_data, guint8 * buf, metadataparse_jpeg_parse (JpegParseData * jpeg_data, guint8 * buf,
guint32 * bufsize, const guint32 offset, guint8 ** next_start, 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 }; guint8 mark[2] = { 0x00, 0x00 };
const guint8 *step_buf = buf; 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; *next_start = buf;
if (jpeg_data->state == JPEG_PARSE_NULL) { 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) { if (*bufsize < 2) {
*next_size = (buf - *next_start) + 2; *next_size = (buf - *next_start) + 2;
ret = META_PARSING_NEED_MORE_DATA; 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 */ /* look for markers */
static MetadataParsingReturn static MetadataParsingReturn
@ -218,7 +401,8 @@ metadataparse_jpeg_reading (JpegParseData * jpeg_data, guint8 ** buf,
ret = META_PARSING_DONE; ret = META_PARSING_DONE;
jpeg_data->state = JPEG_PARSE_DONE; jpeg_data->state = JPEG_PARSE_DONE;
goto 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; ret = META_PARSING_DONE;
jpeg_data->state = JPEG_PARSE_DONE; jpeg_data->state = JPEG_PARSE_DONE;
goto done; goto done;
@ -264,7 +448,9 @@ metadataparse_jpeg_reading (JpegParseData * jpeg_data, guint8 ** buf,
if (!jpeg_data->parse_only) { if (!jpeg_data->parse_only) {
memset (&chunk, 0x00, sizeof (MetadataChunk)); 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.size = chunk_size + 2; /* chunk size plus app marker */
chunk.type = MD_CHUNK_EXIF; chunk.type = MD_CHUNK_EXIF;
@ -276,15 +462,15 @@ metadataparse_jpeg_reading (JpegParseData * jpeg_data, guint8 ** buf,
if (!jpeg_data->jfif_found) { if (!jpeg_data->jfif_found) {
/* only inject if no JFIF has been 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) { 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)); memset (&chunk, 0x00, sizeof (MetadataChunk));
chunk.offset_orig = 2; chunk.offset_orig = 2;
chunk.size = 18; chunk.size = 18;
@ -321,7 +507,7 @@ metadataparse_jpeg_reading (JpegParseData * jpeg_data, guint8 ** buf,
MetadataChunk chunk; MetadataChunk chunk;
memset (&chunk, 0x00, sizeof (MetadataChunk)); 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.size = chunk_size + 2; /* chunk size plus app marker */
chunk.type = MD_CHUNK_XMP; chunk.type = MD_CHUNK_XMP;
@ -344,7 +530,9 @@ metadataparse_jpeg_reading (JpegParseData * jpeg_data, guint8 ** buf,
} }
} }
#ifdef HAVE_IPTC #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 (chunk_size >= 16) { /* size2 "Photoshop 3.0" */
if (*bufsize < 14) { if (*bufsize < 14) {
@ -361,7 +549,7 @@ metadataparse_jpeg_reading (JpegParseData * jpeg_data, guint8 ** buf,
MetadataChunk chunk; MetadataChunk chunk;
memset (&chunk, 0x00, sizeof (MetadataChunk)); 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.size = chunk_size + 2; /* chunk size plus app marker */
chunk.type = MD_CHUNK_IPTC; 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 static MetadataParsingReturn
metadataparse_jpeg_exif (JpegParseData * jpeg_data, guint8 ** buf, metadataparse_jpeg_exif (JpegParseData * jpeg_data, guint8 ** buf,
guint32 * bufsize, guint8 ** next_start, guint32 * next_size) 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 #ifdef HAVE_IPTC
static MetadataParsingReturn static MetadataParsingReturn
metadataparse_jpeg_iptc (JpegParseData * jpeg_data, guint8 ** buf, 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); size = gst_adapter_available (*jpeg_data->iptc_adapter);
buf = gst_adapter_peek (*jpeg_data->iptc_adapter, size); 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); res = iptc_jpeg_ps3_find_iptc (buf, size, &iptc_len);
if (res < 0) { if (res < 0) {
@ -473,6 +708,14 @@ metadataparse_jpeg_iptc (JpegParseData * jpeg_data, guint8 ** buf,
} }
#endif #endif
/*
* metadataparse_jpeg_xmp:
*
* Look at #metadataparse_jpeg_exif. This function has the same behavior as
* that.
*
*/
static MetadataParsingReturn static MetadataParsingReturn
metadataparse_jpeg_xmp (JpegParseData * jpeg_data, guint8 ** buf, metadataparse_jpeg_xmp (JpegParseData * jpeg_data, guint8 ** buf,
guint32 * bufsize, guint8 ** next_start, guint32 * next_size) guint32 * bufsize, guint8 ** next_start, guint32 * next_size)
@ -490,6 +733,32 @@ metadataparse_jpeg_xmp (JpegParseData * jpeg_data, guint8 ** buf,
return ret; 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 static MetadataParsingReturn
metadataparse_jpeg_jump (JpegParseData * jpeg_data, guint8 ** buf, metadataparse_jpeg_jump (JpegParseData * jpeg_data, guint8 ** buf,
guint32 * bufsize, guint8 ** next_start, guint32 * next_size) guint32 * bufsize, guint8 ** next_start, guint32 * next_size)

View file

@ -44,12 +44,20 @@
#ifndef __METADATAPARSE_JPEG_H__ #ifndef __METADATAPARSE_JPEG_H__
#define __METADATAPARSE_JPEG_H__ #define __METADATAPARSE_JPEG_H__
/*
* includes
*/
#include <gst/base/gstadapter.h> #include <gst/base/gstadapter.h>
#include "metadatatypes.h" #include "metadatatypes.h"
G_BEGIN_DECLS G_BEGIN_DECLS
/*
* enum and types
*/
typedef enum _tag_JpegParseState typedef enum _tag_JpegParseState
{ {
JPEG_PARSE_NULL, JPEG_PARSE_NULL,
@ -79,6 +87,9 @@ typedef struct _tag_JpegParseData
gboolean jfif_found; gboolean jfif_found;
} JpegParseData; } JpegParseData;
/*
* external function prototypes
*/
extern void extern void
metadataparse_jpeg_init (JpegParseData * jpeg_data, GstAdapter ** exif_adpt, 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_dispose (JpegParseData * jpeg_data);
extern void metadataparse_jpeg_lazy_update (JpegParseData * jpeg_data);
extern MetadataParsingReturn extern MetadataParsingReturn
metadataparse_jpeg_parse (JpegParseData * jpeg_data, guint8 * buf, 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 G_END_DECLS
#endif /* __METADATAPARSE_JPEG_H__ */ #endif /* __METADATAPARSE_JPEG_H__ */

View file

@ -41,10 +41,45 @@
* Boston, MA 02111-1307, USA. * 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 "metadataparsepng.h"
#include <string.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 static MetadataParsingReturn
metadataparse_png_reading (PngParseData * png_data, guint8 ** buf, metadataparse_png_reading (PngParseData * png_data, guint8 ** buf,
guint32 * bufsize, const guint32 offset, const guint8 * step_buf, guint32 * bufsize, const guint32 offset, const guint8 * step_buf,
@ -58,13 +93,30 @@ static MetadataParsingReturn
metadataparse_png_jump (PngParseData * png_data, guint8 ** buf, metadataparse_png_jump (PngParseData * png_data, guint8 ** buf,
guint32 * bufsize, guint8 ** next_start, guint32 * next_size); 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) * metadataparse_png_init:
{ * @png_data: [in] png data handler to be inited
/* nothing to do */ * @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 void
metadataparse_png_init (PngParseData * png_data, GstAdapter ** exif_adpt, 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 void
metadataparse_png_dispose (PngParseData * png_data) metadataparse_png_dispose (PngParseData * png_data)
{ {
png_data->xmp_adapter = NULL; 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 MetadataParsingReturn
metadataparse_png_parse (PngParseData * png_data, guint8 * buf, metadataparse_png_parse (PngParseData * png_data, guint8 * buf,
guint32 * bufsize, const guint32 offset, guint8 ** next_start, guint32 * bufsize, const guint32 offset, guint8 ** next_start,
@ -98,10 +197,19 @@ metadataparse_png_parse (PngParseData * png_data, guint8 * buf,
guint8 mark[8]; guint8 mark[8];
const guint8 *step_buf = buf; 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; *next_start = buf;
if (png_data->state == PNG_PARSE_NULL) { 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) { if (*bufsize < 8) {
*next_size = (buf - *next_start) + 8; *next_size = (buf - *next_start) + 8;
ret = META_PARSING_NEED_MORE_DATA; ret = META_PARSING_NEED_MORE_DATA;
@ -117,9 +225,9 @@ metadataparse_png_parse (PngParseData * png_data, guint8 * buf,
mark[6] = READ (buf, *bufsize); mark[6] = READ (buf, *bufsize);
mark[7] = READ (buf, *bufsize); mark[7] = READ (buf, *bufsize);
if (mark[0] != 0x89 || mark[1] != 0x50 || mark[2] != 0x4E || mark[3] != 0x47 if (mark[0] != 0x89 || mark[1] != 0x50 || mark[2] != 0x4E ||
|| mark[4] != 0x0D || mark[5] != 0x0A || mark[6] != 0x1A mark[3] != 0x47 || mark[4] != 0x0D || mark[5] != 0x0A ||
|| mark[7] != 0x0A) { mark[6] != 0x1A || mark[7] != 0x0A) {
ret = META_PARSING_ERROR; ret = META_PARSING_ERROR;
goto done; 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 */ /* look for markers */
static MetadataParsingReturn static MetadataParsingReturn
@ -213,7 +383,7 @@ metadataparse_png_reading (PngParseData * png_data, guint8 ** buf,
memset (&chunk, 0x00, sizeof (MetadataChunk)); memset (&chunk, 0x00, sizeof (MetadataChunk));
chunk.offset_orig = (*buf - step_buf) + offset - 8; /* maker + size */ 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; chunk.type = MD_CHUNK_XMP;
metadata_chunk_array_append_sorted (png_data->strip_chunks, &chunk); 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 adapter has been provided, prepare to hold chunk */
if (png_data->xmp_adapter) { 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; *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; png_data->state = PNG_PARSE_XMP;
ret = META_PARSING_DONE; ret = META_PARSING_DONE;
goto done; goto done;
@ -244,14 +415,44 @@ done:
} }
static MetadataParsingReturn /*
metadataparse_png_jump (PngParseData * png_data, guint8 ** buf, * metadataparse_png_xmp:
guint32 * bufsize, guint8 ** next_start, guint32 * next_size) * @png_data: [in] png data handle
{ * @buf: [in] data to be parsed
png_data->state = PNG_PARSE_READING; * @bufsize: [in] size of @buf in bytes
return metadataparse_util_jump_chunk (&png_data->read, buf, * @next_start: look at #metadataparse_png_reading
bufsize, next_start, next_size); * @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 static MetadataParsingReturn
metadataparse_png_xmp (PngParseData * png_data, guint8 ** buf, metadataparse_png_xmp (PngParseData * png_data, guint8 ** buf,
@ -271,3 +472,38 @@ metadataparse_png_xmp (PngParseData * png_data, guint8 ** buf,
return ret; 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);
}

View file

@ -44,12 +44,20 @@
#ifndef __METADATAPARSE_PNG_H__ #ifndef __METADATAPARSE_PNG_H__
#define __METADATAPARSE_PNG_H__ #define __METADATAPARSE_PNG_H__
/*
* includes
*/
#include <gst/base/gstadapter.h> #include <gst/base/gstadapter.h>
#include "metadatatypes.h" #include "metadatatypes.h"
G_BEGIN_DECLS G_BEGIN_DECLS
/*
* enum and types
*/
typedef enum _tag_PngParseState typedef enum _tag_PngParseState
{ {
PNG_PARSE_NULL, PNG_PARSE_NULL,
@ -73,6 +81,9 @@ typedef struct _tag_PngParseData
guint32 read; guint32 read;
} PngParseData; } PngParseData;
/*
* external function prototypes
*/
extern void extern void
metadataparse_png_init (PngParseData * png_data, GstAdapter ** exif_adpt, 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_dispose (PngParseData * png_data);
extern void metadataparse_png_lazy_update (PngParseData * jpeg_data); extern void metadataparse_png_lazy_update (PngParseData * png_data);
extern MetadataParsingReturn extern MetadataParsingReturn
metadataparse_png_parse (PngParseData * png_data, guint8 * buf, 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 G_END_DECLS
#endif /* __METADATAPARSE_PNG_H__ */ #endif /* __METADATAPARSE_PNG_H__ */

View file

@ -41,9 +41,38 @@
* Boston, MA 02111-1307, USA. * 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 "metadataparseutil.h"
#include <string.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 void
metadataparse_util_tag_list_add_chunk (GstTagList * taglist, metadataparse_util_tag_list_add_chunk (GstTagList * taglist,
GstTagMergeMode mode, const gchar * name, GstAdapter * adapter) 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 MetadataParsingReturn
metadataparse_util_hold_chunk (guint32 * read, guint8 ** buf, metadataparse_util_hold_chunk (guint32 * read, guint8 ** buf,
guint32 * bufsize, guint8 ** next_start, guint32 * bufsize, guint8 ** next_start,
@ -95,6 +157,16 @@ metadataparse_util_hold_chunk (guint32 * read, guint8 ** buf,
return ret; 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 MetadataParsingReturn
metadataparse_util_jump_chunk (guint32 * read, guint8 ** buf, metadataparse_util_jump_chunk (guint32 * read, guint8 ** buf,
guint32 * bufsize, guint8 ** next_start, guint32 * next_size) guint32 * bufsize, guint8 ** next_start, guint32 * next_size)

View file

@ -44,6 +44,10 @@
#ifndef __GST_METADATAPARSE_UTIL_H__ #ifndef __GST_METADATAPARSE_UTIL_H__
#define __GST_METADATAPARSE_UTIL_H__ #define __GST_METADATAPARSE_UTIL_H__
/*
* includes
*/
#include <gst/gst.h> #include <gst/gst.h>
#include <gst/base/gstadapter.h> #include <gst/base/gstadapter.h>
@ -51,6 +55,10 @@
G_BEGIN_DECLS G_BEGIN_DECLS
/*
* external function prototypes
*/
extern void extern void
metadataparse_util_tag_list_add_chunk (GstTagList * taglist, metadataparse_util_tag_list_add_chunk (GstTagList * taglist,
GstTagMergeMode mode, const gchar * name, GstAdapter * adapter); GstTagMergeMode mode, const gchar * name, GstAdapter * adapter);

View file

@ -41,8 +41,67 @@
* Boston, MA 02111-1307, USA. * 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" #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 * 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 ();
}

View file

@ -44,17 +44,29 @@
#ifndef __GST_METADATA_TAGS_H__ #ifndef __GST_METADATA_TAGS_H__
#define __GST_METADATA_TAGS_H__ #define __GST_METADATA_TAGS_H__
/*
* includes
*/
#include <gst/gst.h> #include <gst/gst.h>
#include <gst/base/gstadapter.h> #include <gst/base/gstadapter.h>
G_BEGIN_DECLS G_BEGIN_DECLS
/*
* enum and types
*/
/* set bit to desired mapping */ /* set bit to desired mapping */
typedef enum { typedef enum {
METADATA_TAG_MAP_INDIVIDUALS = 1 << 0, METADATA_TAG_MAP_INDIVIDUALS = 1 << 0,
METADATA_TAG_MAP_WHOLECHUNK = 1 << 1 METADATA_TAG_MAP_WHOLECHUNK = 1 << 1
} MetadataTagMapping; } MetadataTagMapping;
/*
* defines
*/
#define GST_TAG_EXIF "exif" #define GST_TAG_EXIF "exif"
#define GST_TAG_IPTC "iptc" #define GST_TAG_IPTC "iptc"
@ -79,6 +91,10 @@ typedef enum {
#define GST_TAG_CAPTURE_CONTRAST "capture-contrast" #define GST_TAG_CAPTURE_CONTRAST "capture-contrast"
#define GST_TAG_CAPTURE_SATURATION "capture-saturation" #define GST_TAG_CAPTURE_SATURATION "capture-saturation"
/*
* external function prototypes
*/
extern void extern void
metadata_tags_register (void); metadata_tags_register (void);

View file

@ -41,10 +41,41 @@
* Boston, MA 02111-1307, USA. * 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 "metadatatypes.h"
#include <string.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 void
metadata_chunk_array_init (MetadataChunkArray * array, gsize alloc_size) 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; 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 void
metadata_chunk_array_free (MetadataChunkArray * array) 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 void
metadata_chunk_array_clear (MetadataChunkArray * array) 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 void
metadata_chunk_array_append (MetadataChunkArray * array, MetadataChunk * chunk) metadata_chunk_array_append (MetadataChunkArray * array, MetadataChunk * chunk)
{ {
@ -87,6 +152,19 @@ metadata_chunk_array_append (MetadataChunkArray * array, MetadataChunk * chunk)
++array->len; ++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 void
metadata_chunk_array_append_sorted (MetadataChunkArray * array, metadata_chunk_array_append_sorted (MetadataChunkArray * array,
MetadataChunk * chunk) 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 void
metadata_chunk_array_remove_zero_size (MetadataChunkArray * array) metadata_chunk_array_remove_zero_size (MetadataChunkArray * array)
{ {

View file

@ -44,10 +44,18 @@
#ifndef __METADATATYPES_H__ #ifndef __METADATATYPES_H__
#define __METADATATYPES_H__ #define __METADATATYPES_H__
/*
* includes
*/
#include <glib.h> #include <glib.h>
G_BEGIN_DECLS G_BEGIN_DECLS
/*
* enum and types
*/
/* *INDENT-OFF* */ /* *INDENT-OFF* */
typedef enum _tag_MetadataParsingReturn { typedef enum _tag_MetadataParsingReturn {
@ -78,10 +86,14 @@ typedef struct _tag_MetadataChunk
typedef struct _tag_MetadataChunkArray typedef struct _tag_MetadataChunkArray
{ {
MetadataChunk * chunk; MetadataChunk * chunk;
gsize len; gsize len; /* number of chunks into aray */
gsize allocated_len; gsize allocated_len; /* number of slots into the array to store chunks */
} MetadataChunkArray; } MetadataChunkArray;
/*
* external function prototypes
*/
extern void extern void
metadata_chunk_array_init(MetadataChunkArray * array, gsize alloc_size); metadata_chunk_array_init(MetadataChunkArray * array, gsize alloc_size);
@ -94,11 +106,9 @@ metadata_chunk_array_clear(MetadataChunkArray * array);
extern void extern void
metadata_chunk_array_append(MetadataChunkArray * array, MetadataChunk * chunk); 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 extern void
metadata_chunk_array_append_sorted(MetadataChunkArray * array, MetadataChunk * chunk); metadata_chunk_array_append_sorted(MetadataChunkArray * array,
MetadataChunk * chunk);
extern void extern void
metadata_chunk_array_remove_zero_size (MetadataChunkArray * array); metadata_chunk_array_remove_zero_size (MetadataChunkArray * array);

View file

@ -41,39 +41,75 @@
* Boston, MA 02111-1307, USA. * 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 "metadataxmp.h"
#include "metadataparseutil.h" #include "metadataparseutil.h"
#include "metadatatags.h" #include "metadatatags.h"
/*
* defines
*/
GST_DEBUG_CATEGORY (gst_metadata_xmp_debug); GST_DEBUG_CATEGORY (gst_metadata_xmp_debug);
#define GST_CAT_DEFAULT gst_metadata_xmp_debug #define GST_CAT_DEFAULT gst_metadata_xmp_debug
/*
* Implementation when lib exempi isn't available at compilation time
*/
#ifndef HAVE_XMP #ifndef HAVE_XMP
/*
* extern functions implementations
*/
gboolean
metadata_xmp_init (void)
{
return TRUE;
}
void
metadata_xmp_dispose (void)
{
return;
}
void void
metadataparse_xmp_tag_list_add (GstTagList * taglist, GstTagMergeMode mode, metadataparse_xmp_tag_list_add (GstTagList * taglist, GstTagMergeMode mode,
GstAdapter * adapter, MetadataTagMapping mapping) GstAdapter * adapter, MetadataTagMapping mapping)
{ {
if (mapping & METADATA_TAG_MAP_WHOLECHUNK) { if (mapping & METADATA_TAG_MAP_WHOLECHUNK) {
GST_LOG ("XMP not defined, here I should send just one tag as whole chunk"); GST_LOG ("XMP not defined, sending just one tag as whole chunk");
metadataparse_util_tag_list_add_chunk (taglist, mode, GST_TAG_XMP, adapter); metadataparse_util_tag_list_add_chunk (taglist, mode, GST_TAG_XMP, adapter);
} }
} }
gboolean
metadataparse_xmp_init (void)
{
return TRUE;
}
void
metadataparse_xmp_dispose (void)
{
return;
}
void void
metadatamux_xmp_create_chunk_from_tag_list (guint8 ** buf, guint32 * size, metadatamux_xmp_create_chunk_from_tag_list (guint8 ** buf, guint32 * size,
const GstTagList * taglist) const GstTagList * taglist)
@ -83,10 +119,20 @@ metadatamux_xmp_create_chunk_from_tag_list (guint8 ** buf, guint32 * size,
#else /* ifndef HAVE_XMP */ #else /* ifndef HAVE_XMP */
/*
* Implementation when lib exempi isn't available at compilation time
*/
/*
* includes
*/
#include <xmp.h> #include <xmp.h>
#include <string.h> #include <string.h>
#define XMP_SCHEMA_NODE 0x80000000UL /*
* enum and types
*/
typedef struct _tag_SchemaTagMap typedef struct _tag_SchemaTagMap
{ {
@ -102,6 +148,12 @@ typedef struct _tag_SchemaMap
const SchemaTagMap *tags_map; const SchemaTagMap *tags_map;
} SchemaMap; } SchemaMap;
/*
* defines and static global vars
*/
#define XMP_SCHEMA_NODE 0x80000000UL
static const SchemaTagMap schema_map_dublin_tags_map[] = { static const SchemaTagMap schema_map_dublin_tags_map[] = {
{"description", GST_TAG_DESCRIPTION}, {"description", GST_TAG_DESCRIPTION},
{"title", GST_TAG_TITLE}, {"title", GST_TAG_TITLE},
@ -109,7 +161,8 @@ static const SchemaTagMap schema_map_dublin_tags_map[] = {
{NULL, NULL} {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:", "dc:",
3, 3,
schema_map_dublin_tags_map schema_map_dublin_tags_map
@ -120,10 +173,22 @@ static const SchemaMap *schemas_map[] = {
NULL 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 static void
metadataparse_xmp_iter_add_to_tag_list (GstTagList * taglist, metadataparse_xmp_iter (GstTagList * taglist, GstTagMergeMode mode, XmpPtr xmp);
GstTagMergeMode mode, const char *path, const char *value,
const SchemaMap * schema_map, const uint32_t opt); static void
metadataparse_xmp_iter_node_schema (GstTagList * taglist, GstTagMergeMode mode,
XmpPtr xmp, const char *schema, const char *path);
static void static void
metadataparse_xmp_iter_array (GstTagList * taglist, GstTagMergeMode mode, metadataparse_xmp_iter_array (GstTagList * taglist, GstTagMergeMode mode,
@ -131,34 +196,75 @@ metadataparse_xmp_iter_array (GstTagList * taglist, GstTagMergeMode mode,
const SchemaMap * schema_map); const SchemaMap * schema_map);
static void static void
metadataparse_xmp_iter_node_schema (GstTagList * taglist, GstTagMergeMode mode, metadataparse_xmp_iter_simple_qual (GstTagList * taglist, GstTagMergeMode mode,
XmpPtr xmp, const char *schema, const char *path); const char *path, const char *value, const SchemaMap * schema_map);
static void static void
metadataparse_xmp_iter_simple (GstTagList * taglist, GstTagMergeMode mode, metadataparse_xmp_iter_simple (GstTagList * taglist, GstTagMergeMode mode,
const char *schema, const char *path, const char *value, const char *path, const char *value, const SchemaMap * schema_map);
const SchemaMap * schema_map);
static void static void
metadataparse_xmp_iter_simple_qual (GstTagList * taglist, GstTagMergeMode mode, metadataparse_xmp_iter_add_to_tag_list (GstTagList * taglist,
const char *schema, const char *path, const char *value, GstTagMergeMode mode, const char *path, const char *value,
const SchemaMap * schema_map); const SchemaMap * schema_map, const uint32_t opt);
static void 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 gboolean
metadataparse_xmp_init (void) metadata_xmp_init (void)
{ {
return xmp_init (); 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 void
metadataparse_xmp_dispose (void) metadata_xmp_dispose (void)
{ {
xmp_terminate (); 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 void
metadataparse_xmp_tag_list_add (GstTagList * taglist, GstTagMergeMode mode, metadataparse_xmp_tag_list_add (GstTagList * taglist, GstTagMergeMode mode,
GstAdapter * adapter, MetadataTagMapping mapping) GstAdapter * adapter, MetadataTagMapping mapping)
@ -172,8 +278,9 @@ metadataparse_xmp_tag_list_add (GstTagList * taglist, GstTagMergeMode mode,
} }
/* add chunk tag */ /* 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); metadataparse_util_tag_list_add_chunk (taglist, mode, GST_TAG_XMP, adapter);
}
if (!(mapping & METADATA_TAG_MAP_INDIVIDUALS)) if (!(mapping & METADATA_TAG_MAP_INDIVIDUALS))
goto done; goto done;
@ -196,32 +303,96 @@ done:
} }
static const SchemaTagMap * /*
metadataparse_get_tagsmap_from_gsttag (const SchemaMap * schema_map, * metadatamux_xmp_create_chunk_from_tag_list:
const gchar * tag) * @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; GstBuffer *xmp_chunk = NULL;
int i; const GValue *val = NULL;
XmpPtr xmp = NULL;
XmpStringPtr xmp_str_buf = xmp_string_new ();
if (NULL == schema_map) if (!(buf && size))
goto done; 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 (NULL == xmp)
if (0 == strcmp (schema_map->tags_map[i].gst_tag, tag)) { xmp = xmp_new_empty ();
tags_map = (SchemaTagMap *) & schema_map->tags_map[i];
break; 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: 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 * 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) const gchar * path, uint32_t opt)
{ {
@ -238,7 +409,7 @@ metadataparse_get_tagsmap_from_path (const SchemaMap * schema_map,
string = g_string_new (path); string = g_string_new (path);
/* remove the language qualifier */ /* remove the language qualifier "[xxx]" */
ch = string->str + string->len - 3; ch = string->str + string->len - 3;
while (ch != string->str + schema_map->prefix_len) { while (ch != string->str + schema_map->prefix_len) {
if (*ch == '[') { if (*ch == '[') {
@ -266,160 +437,63 @@ done:
} }
static void /*
metadataparse_xmp_iter_add_to_tag_list (GstTagList * taglist, * metadatamux_xmp_get_tagsmap_from_gsttag:
GstTagMergeMode mode, const char *path, const char *value, * @schema_map: Structure containg a map beteween GST tags and tags into a XMP
const SchemaMap * schema_map, const uint32_t opt) * 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>
*/
static const SchemaTagMap *
metadatamux_xmp_get_tagsmap_from_gsttag (const SchemaMap * schema_map,
const gchar * tag)
{ {
SchemaTagMap *tags_map = NULL;
int i;
const SchemaTagMap *smaptag = if (NULL == schema_map)
metadataparse_get_tagsmap_from_path (schema_map, path, opt);
if (NULL == smaptag)
goto done; goto done;
if (NULL == smaptag->gst_tag)
goto done;
GType type = gst_tag_get_type (smaptag->gst_tag); for (i = 0; schema_map->tags_map[i].gst_tag; i++) {
if (0 == strcmp (schema_map->tags_map[i].gst_tag, tag)) {
switch (type) { tags_map = (SchemaTagMap *) & schema_map->tags_map[i];
case G_TYPE_STRING:
gst_tag_list_add (taglist, mode, smaptag->gst_tag, value, NULL);
break;
default:
break; break;
}
} }
done: done:
return; return tags_map;
} }
void /*
metadataparse_xmp_iter_simple_qual (GstTagList * taglist, GstTagMergeMode mode, * metadataparse_xmp_iter:
const char *schema, const char *path, const char *value, * @taglist: tag list in which extracted tags will be added
const SchemaMap * schema_map) * @mode: tag list merge mode
{ * @xmp: handle to XMP data from lib exempi
GString *string = g_string_new (path); *
gchar *ch; * 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
/* remove the language qualifier */ * to add all XMP mapped tags to @taglist by unsing a specified merge @mode
ch = string->str + string->len - 3; * @see_also: #metadataparse_xmp_tag_list_add
while (ch != string->str + schema_map->prefix_len) { * #metadataparse_xmp_iter_node_schema
if (*ch == '[') { *
*ch = '\0'; * Returns: nothing
} */
--ch;
}
GST_LOG (" %s = %s", string->str, value);
metadataparse_xmp_iter_add_to_tag_list (taglist, mode, path, value,
schema_map, XMP_PROP_HAS_QUALIFIERS);
g_string_free (string, TRUE);
}
void
metadataparse_xmp_iter_simple (GstTagList * taglist, GstTagMergeMode mode,
const char *schema, const char *path, const char *value,
const SchemaMap * schema_map)
{
GST_LOG (" %s = %s", path, value);
metadataparse_xmp_iter_add_to_tag_list (taglist, mode, path, value,
schema_map, 0);
}
void
metadataparse_xmp_iter_array (GstTagList * taglist, GstTagMergeMode mode,
XmpPtr xmp, const char *schema, const char *path,
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;
xmp_iter = xmp_iterator_new (xmp, schema, path, 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);
const char *value = xmp_string_cstr (xstr_prop);
if (XMP_IS_NODE_SCHEMA (opt)) {
GST_LOG ("Unexpected iteraction");
} else if (XMP_IS_PROP_SIMPLE (opt)) {
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);
} else {
metadataparse_xmp_iter_simple (taglist, mode, schema, path, value,
schema_map);
}
}
} else if (XMP_IS_PROP_ARRAY (opt)) {
/* FIXME: array with merge mode */
GstTagMergeMode new_mode = mode;
#if 0
//const gchar *tag = ;
if (mode == GST_TAG_MERGE_REPLACE) {
//gst_tag_list_remove_tag(taglist, );
}
#endif
if (XMP_IS_ARRAY_ALTTEXT (opt)) {
metadataparse_xmp_iter_array (taglist, new_mode, xmp, schema, path,
schema_map);
xmp_iterator_skip (xmp_iter, XMP_ITER_SKIPSUBTREE);
} else {
metadataparse_xmp_iter_array (taglist, new_mode, xmp, schema, path,
schema_map);
xmp_iterator_skip (xmp_iter, XMP_ITER_SKIPSUBTREE);
}
}
}
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);
if (xstr_schema)
xmp_string_free (xstr_schema);
}
void
metadataparse_xmp_iter_node_schema (GstTagList * taglist, GstTagMergeMode mode,
XmpPtr xmp, const char *schema, const char *path)
{
SchemaMap *schema_map = NULL;
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);
}
void void
metadataparse_xmp_iter (GstTagList * taglist, GstTagMergeMode mode, XmpPtr xmp) metadataparse_xmp_iter (GstTagList * taglist, GstTagMergeMode mode, XmpPtr xmp)
@ -462,10 +536,263 @@ done:
xmp_string_free (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_node_schema (GstTagList * taglist, GstTagMergeMode mode,
XmpPtr xmp, const char *schema, const char *path)
{
SchemaMap *schema_map = NULL;
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,
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;
xmp_iter = xmp_iterator_new (xmp, schema, path, 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);
const char *value = xmp_string_cstr (xstr_prop);
if (XMP_IS_NODE_SCHEMA (opt)) {
GST_LOG ("Unexpected iteraction");
} else if (XMP_IS_PROP_SIMPLE (opt)) {
if (strcmp (path, "") != 0) {
if (XMP_HAS_PROP_QUALIFIERS (opt)) {
/* ignore language qualifier, just get the first */
metadataparse_xmp_iter_simple_qual (taglist, mode, path, value,
schema_map);
} else {
metadataparse_xmp_iter_simple (taglist, mode, path, value,
schema_map);
}
}
} else if (XMP_IS_PROP_ARRAY (opt)) {
/* FIXME: array with merge mode */
GstTagMergeMode new_mode = mode;
#if 0
//const gchar *tag = ;
if (mode == GST_TAG_MERGE_REPLACE) {
//gst_tag_list_remove_tag(taglist, );
}
#endif
if (XMP_IS_ARRAY_ALTTEXT (opt)) {
metadataparse_xmp_iter_array (taglist, new_mode, xmp, schema, path,
schema_map);
xmp_iterator_skip (xmp_iter, XMP_ITER_SKIPSUBTREE);
} else {
metadataparse_xmp_iter_array (taglist, new_mode, xmp, schema, path,
schema_map);
xmp_iterator_skip (xmp_iter, XMP_ITER_SKIPSUBTREE);
}
}
}
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);
if (xstr_schema)
xmp_string_free (xstr_schema);
}
/*
* 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_simple_qual (GstTagList * taglist, GstTagMergeMode mode,
const char *path, const char *value, const SchemaMap * schema_map)
{
GString *string = g_string_new (path);
#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_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_simple (GstTagList * taglist, GstTagMergeMode mode,
const char *path, const char *value, const SchemaMap * schema_map)
{
GST_LOG (" %s = %s", path, value);
metadataparse_xmp_iter_add_to_tag_list (taglist, mode, path, value,
schema_map, 0);
}
/*
* 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 static void
metadataxmp_for_each_tag_in_list (const GstTagList * list, const gchar * tag, metadataparse_xmp_iter_add_to_tag_list (GstTagList * taglist,
gpointer user_data) 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;
if (NULL == smaptag->gst_tag)
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:
break;
}
done:
return;
}
/*
* 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
metadatamux_xmp_for_each_tag_in_list (const GstTagList * list,
const gchar * tag, gpointer user_data)
{ {
XmpPtr xmp = (XmpPtr) user_data; XmpPtr xmp = (XmpPtr) user_data;
int i; 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 SchemaMap *smap = schemas_map[i];
const SchemaTagMap *stagmap = const SchemaTagMap *stagmap =
metadataparse_get_tagsmap_from_gsttag (smap, tag); metadatamux_xmp_get_tagsmap_from_gsttag (smap, tag);
if (stagmap) { 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) */ #endif /* else (ifndef HAVE_XMP) */

View file

@ -50,14 +50,18 @@
G_BEGIN_DECLS G_BEGIN_DECLS
/*
* external function prototypes
*/
extern gboolean metadata_xmp_init (void);
extern void metadata_xmp_dispose (void);
extern void extern void
metadataparse_xmp_tag_list_add (GstTagList * taglist, GstTagMergeMode mode, metadataparse_xmp_tag_list_add (GstTagList * taglist, GstTagMergeMode mode,
GstAdapter * adapter, MetadataTagMapping mapping); GstAdapter * adapter, MetadataTagMapping mapping);
extern gboolean metadataparse_xmp_init (void);
extern void metadataparse_xmp_dispose (void);
extern void extern void
metadatamux_xmp_create_chunk_from_tag_list (guint8 ** buf, guint32 *size, metadatamux_xmp_create_chunk_from_tag_list (guint8 ** buf, guint32 *size,
const GstTagList * taglist); const GstTagList * taglist);

View file

@ -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

View file

@ -11,9 +11,27 @@ else
GST_SOUNDTOUCH_TESTS = GST_SOUNDTOUCH_TESTS =
endif endif
if USE_METADATA
GST_METADATA_TESTS = metadata_editor
metadata_editor_SOURCES = metadata_editor.c
metadata_editor_CFLAGS = $(GST_CFLAGS) \
`pkg-config --cflags gstreamer-0.10 libglade-2.0 gtk+-2.0`
metadata_editor_LDADD = $(GST_LIBS)
metadata_editor_LDFLAGS = $(GST_PLUGIN_LDFLAGS) \
-Wl -export-dynamic \
-lgstinterfaces-0.10 \
`pkg-config --libs gstreamer-0.10 libglade-2.0 gtk+-2.0`
else
GST_METADATA_TESTS =
endif
equalizer_test_SOURCES = equalizer-test.c equalizer_test_SOURCES = equalizer-test.c
equalizer_test_CFLAGS = $(GST_CFLAGS) equalizer_test_CFLAGS = $(GST_CFLAGS)
equalizer_test_LDADD = $(GST_LIBS) equalizer_test_LDADD = $(GST_LIBS)
equalizer_test_LDFLAGS = $(GST_PLUGIN_LDFLAGS) equalizer_test_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
noinst_PROGRAMS = $(GST_SOUNDTOUCH_TESTS) equalizer-test noinst_PROGRAMS = $(GST_SOUNDTOUCH_TESTS) $(GST_METADATA_TESTS) equalizer-test

View file

@ -523,7 +523,7 @@ ui_create ()
{ {
int ret = 0; int ret = 0;
ui_glade_xml = glade_xml_new ("MetadataEditorMain.glade", NULL, NULL); ui_glade_xml = glade_xml_new ("metadata_editor.glade", NULL, NULL);
if (!ui_glade_xml) { if (!ui_glade_xml) {
fprintf (stderr, "glade_xml_new failed\n"); fprintf (stderr, "glade_xml_new failed\n");