mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 12:11:13 +00:00
Some IPTC tags mapped.
Original commit message from CVS: Some IPTC tags mapped.
This commit is contained in:
parent
0f2ac9fe8b
commit
21962cbffe
9 changed files with 227 additions and 52 deletions
12
ChangeLog
12
ChangeLog
|
@ -1,3 +1,15 @@
|
|||
2007-12-21 Edgard Lima,,,, <edgard.lima@indt.org.br>
|
||||
|
||||
* ext/metadata/TODO:
|
||||
* ext/metadata/gstbasemetadata.c:
|
||||
* ext/metadata/gstbasemetadata.h:
|
||||
* ext/metadata/metadata.h:
|
||||
* ext/metadata/metadataiptc.c:
|
||||
* ext/metadata/metadatamuxjpeg.c:
|
||||
* ext/metadata/metadataparsejpeg.c:
|
||||
* ext/metadata/metadataparseutil.c:
|
||||
Some IPTC tags mapped.
|
||||
|
||||
2007-12-21 Edgard Lima <edgard.lima@indt.org.br>
|
||||
|
||||
* ext/metadata/Makefile.am:
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
|
||||
This file contains a list of things to be done as well some open issues (questions) related to design/implementation.
|
||||
|
||||
* I (Edgard Lima - alima - edgard.lima@indt.org.br) will be on vacation until 05-Jan-2008. After that I will be back on it.
|
||||
INFO:
|
||||
|
||||
1- I (Edgard Lima - alima - edgard.lima@indt.org.br) will be on vacation until 05-Jan-2008. After that I will be back on it.
|
||||
2- to see what tags are mapped so far run 'grep -n GST_TAG *.[ch]' into this folder.
|
||||
|
||||
TODO:
|
||||
|
||||
1- Add individual tags IPTC and XMP (and more for EXIF)
|
||||
1- Add individual XMP tags (and more for EXIF and IPTC)
|
||||
2- Get properties like 'width' and 'height' from caps
|
||||
3- Review the code (in order to move to gst-plugins-good)
|
||||
4- Document how the plugin works (atchitecture and interaction beteween modules)
|
||||
|
@ -17,8 +20,9 @@ OPEN ISSUES:
|
|||
ex: file.jpeg has XMP, then we do filesrc ! metadataparse ! jpegdec ! pngenc ! metadatamux ! files
|
||||
is the metadata still valid? which fields are no valid anymore?
|
||||
3- Add GST_TYPE_FRACTION support for GStreamer TAGS
|
||||
4- currently, in JPEG files, if there is a Photoshop segment, everything inside it but IPTC will be lost. From the point of view of implementation it is easy, but I still don't now how to solve from the point of view of "designing". Anyway I think it is not so important.
|
||||
|
||||
KNOWN BUGS
|
||||
|
||||
1- exposure-time, exposure-program and fnumber can't be read from a file saved from scratch (whithout WHOLE_CHUNK from previous file)
|
||||
I believe it is a bu in libexif
|
||||
I believe it is a bug in libexif
|
||||
|
|
|
@ -263,10 +263,10 @@ gboolean
|
|||
gst_base_metadata_strip_push_buffer (GstBaseMetadata * base,
|
||||
gint64 offset_orig, GstBuffer ** prepend, GstBuffer ** buf)
|
||||
{
|
||||
MetadataChunk *strip = base->metadata->strip_chunks.chunk;
|
||||
MetadataChunk *inject = base->metadata->inject_chunks.chunk;
|
||||
const gsize strip_len = base->metadata->strip_chunks.len;
|
||||
const gsize inject_len = base->metadata->inject_chunks.len;
|
||||
MetadataChunk *strip = META_DATA_STRIP_CHUNKS (base->metadata).chunk;
|
||||
MetadataChunk *inject = META_DATA_INJECT_CHUNKS (base->metadata).chunk;
|
||||
const gsize strip_len = META_DATA_STRIP_CHUNKS (base->metadata).len;
|
||||
const gsize inject_len = META_DATA_INJECT_CHUNKS (base->metadata).len;
|
||||
|
||||
gboolean buffer_reallocated = FALSE;
|
||||
|
||||
|
@ -493,10 +493,10 @@ gboolean
|
|||
gst_base_metadata_translate_pos_to_orig (GstBaseMetadata * base,
|
||||
gint64 pos, gint64 * orig_pos, GstBuffer ** buf)
|
||||
{
|
||||
MetadataChunk *strip = base->metadata->strip_chunks.chunk;
|
||||
MetadataChunk *inject = base->metadata->inject_chunks.chunk;
|
||||
const gsize strip_len = base->metadata->strip_chunks.len;
|
||||
const gsize inject_len = base->metadata->inject_chunks.len;
|
||||
MetadataChunk *strip = META_DATA_STRIP_CHUNKS (base->metadata).chunk;
|
||||
MetadataChunk *inject = META_DATA_INJECT_CHUNKS (base->metadata).chunk;
|
||||
const gsize strip_len = META_DATA_STRIP_CHUNKS (base->metadata).len;
|
||||
const gsize inject_len = META_DATA_INJECT_CHUNKS (base->metadata).len;
|
||||
const gint64 duration_orig = base->duration_orig;
|
||||
const gint64 duration = base->duration;
|
||||
|
||||
|
@ -597,8 +597,8 @@ gst_base_metadata_calculate_offsets (GstBaseMetadata * base)
|
|||
int i, j;
|
||||
guint32 append_size;
|
||||
guint32 bytes_striped, bytes_inject;
|
||||
MetadataChunk *strip = base->metadata->strip_chunks.chunk;
|
||||
MetadataChunk *inject = base->metadata->inject_chunks.chunk;
|
||||
MetadataChunk *strip = META_DATA_STRIP_CHUNKS (base->metadata).chunk;
|
||||
MetadataChunk *inject = META_DATA_INJECT_CHUNKS (base->metadata).chunk;
|
||||
gsize strip_len;
|
||||
gsize inject_len;
|
||||
|
||||
|
@ -607,8 +607,8 @@ gst_base_metadata_calculate_offsets (GstBaseMetadata * base)
|
|||
|
||||
metadata_lazy_update (base->metadata);
|
||||
|
||||
strip_len = base->metadata->strip_chunks.len;
|
||||
inject_len = base->metadata->inject_chunks.len;
|
||||
strip_len = META_DATA_STRIP_CHUNKS (base->metadata).len;
|
||||
inject_len = META_DATA_INJECT_CHUNKS (base->metadata).len;
|
||||
|
||||
bytes_striped = 0;
|
||||
bytes_inject = 0;
|
||||
|
@ -1661,23 +1661,22 @@ done:
|
|||
}
|
||||
|
||||
void
|
||||
gst_base_metadata_set_option_flag (GstBaseMetadata * metadata,
|
||||
MetaOptions options)
|
||||
gst_base_metadata_set_option_flag (GstBaseMetadata * base, MetaOptions options)
|
||||
{
|
||||
metadata->options |= options;
|
||||
base->options |= options;
|
||||
}
|
||||
|
||||
void
|
||||
gst_base_metadata_unset_option_flag (GstBaseMetadata * metadata,
|
||||
gst_base_metadata_unset_option_flag (GstBaseMetadata * base,
|
||||
MetaOptions options)
|
||||
{
|
||||
metadata->options &= ~options;
|
||||
base->options &= ~options;
|
||||
}
|
||||
|
||||
MetaOptions
|
||||
gst_base_metadata_get_option_flag (const GstBaseMetadata * metadata)
|
||||
gst_base_metadata_get_option_flag (const GstBaseMetadata * base)
|
||||
{
|
||||
return metadata->options;
|
||||
return base->options;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1685,8 +1684,8 @@ gst_base_metadata_update_segment_with_new_buffer (GstBaseMetadata * base,
|
|||
guint8 ** buf, guint32 * size, MetadataChunkType type)
|
||||
{
|
||||
int i;
|
||||
MetadataChunk *inject = base->metadata->inject_chunks.chunk;
|
||||
const gsize inject_len = base->metadata->inject_chunks.len;
|
||||
MetadataChunk *inject = META_DATA_INJECT_CHUNKS (base->metadata).chunk;
|
||||
const gsize inject_len = META_DATA_INJECT_CHUNKS (base->metadata).len;
|
||||
|
||||
if (!(buf && size))
|
||||
goto done;
|
||||
|
@ -1714,7 +1713,8 @@ done:
|
|||
}
|
||||
|
||||
void
|
||||
gst_base_metadata_chunk_array_remove_zero_size (GstBaseMetadata * metadata)
|
||||
gst_base_metadata_chunk_array_remove_zero_size (GstBaseMetadata * base)
|
||||
{
|
||||
metadata_chunk_array_remove_zero_size (&metadata->metadata->inject_chunks);
|
||||
metadata_chunk_array_remove_zero_size (&META_DATA_INJECT_CHUNKS (base->
|
||||
metadata));
|
||||
}
|
||||
|
|
|
@ -156,20 +156,20 @@ extern GType
|
|||
gst_base_metadata_get_type (void);
|
||||
|
||||
extern void
|
||||
gst_base_metadata_set_option_flag(GstBaseMetadata *metadata, const MetaOptions options);
|
||||
gst_base_metadata_set_option_flag(GstBaseMetadata *base, const MetaOptions options);
|
||||
|
||||
extern void
|
||||
gst_base_metadata_unset_option_flag(GstBaseMetadata *metadata, const MetaOptions options);
|
||||
gst_base_metadata_unset_option_flag(GstBaseMetadata *base, const MetaOptions options);
|
||||
|
||||
extern MetaOptions
|
||||
gst_base_metadata_get_option_flag(const GstBaseMetadata *metadata);
|
||||
gst_base_metadata_get_option_flag(const GstBaseMetadata *base);
|
||||
|
||||
extern void
|
||||
gst_base_metadata_update_segment_with_new_buffer (GstBaseMetadata *metadata,
|
||||
gst_base_metadata_update_segment_with_new_buffer (GstBaseMetadata *base,
|
||||
guint8 ** buf, guint32 * size, MetadataChunkType type);
|
||||
|
||||
extern void
|
||||
gst_base_metadata_chunk_array_remove_zero_size (GstBaseMetadata *metadata);
|
||||
gst_base_metadata_chunk_array_remove_zero_size (GstBaseMetadata *base);
|
||||
|
||||
G_END_DECLS
|
||||
#endif /* __GST_BASE_METADATA_H__ */
|
||||
|
|
|
@ -106,6 +106,9 @@ typedef struct _tag_MetaData
|
|||
|
||||
#define META_DATA_IMG_TYPE(p) (p)->img_type
|
||||
|
||||
#define META_DATA_STRIP_CHUNKS(p) (p)->strip_chunks
|
||||
#define META_DATA_INJECT_CHUNKS(p) (p)->inject_chunks
|
||||
|
||||
extern void metadata_init (MetaData ** meta_data, const MetaOptions options);
|
||||
|
||||
extern void metadata_dispose (MetaData ** meta_data);
|
||||
|
|
|
@ -75,11 +75,74 @@ metadatamux_iptc_create_chunk_from_tag_list (guint8 ** buf, guint32 * size,
|
|||
#else /* ifndef HAVE_IPTC */
|
||||
|
||||
#include <iptc-data.h>
|
||||
#include <iptc-tag.h>
|
||||
#include <string.h>
|
||||
#include <gst/gsttaglist.h>
|
||||
|
||||
typedef struct _tag_MEUserData
|
||||
{
|
||||
GstTagList *taglist;
|
||||
GstTagMergeMode mode;
|
||||
} MEUserData;
|
||||
|
||||
typedef struct _tag_MapIntStr
|
||||
{
|
||||
IptcRecord record;
|
||||
IptcTag iptc;
|
||||
const gchar *str;
|
||||
} MapIntStr;
|
||||
|
||||
static void
|
||||
iptc_data_foreach_dataset_func (IptcDataSet * dataset, void *user_data);
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
static MapIntStr mappedTags[] = {
|
||||
{IPTC_RECORD_APP_2, IPTC_TAG_OBJECT_NAME, /*ASCII*/ GST_TAG_TITLE /*STRING*/},
|
||||
{IPTC_RECORD_APP_2, IPTC_TAG_BYLINE, /*ASCII*/ GST_TAG_COMPOSER /*STRING*/},
|
||||
{IPTC_RECORD_APP_2, IPTC_TAG_CAPTION, /*ASCII*/ GST_TAG_DESCRIPTION /*STRING*/},
|
||||
{IPTC_RECORD_APP_2, IPTC_TAG_COPYRIGHT_NOTICE, /*ASCII*/ GST_TAG_COPYRIGHT /*STRING*/},
|
||||
{0, 0, NULL}
|
||||
};
|
||||
/* *INDENT-ON* */
|
||||
|
||||
static const gchar *
|
||||
metadataparse_iptc_get_tag_from_iptc (IptcTag iptc, GType * type,
|
||||
IptcRecord * record)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while (mappedTags[i].iptc) {
|
||||
if (iptc == mappedTags[i].iptc) {
|
||||
*type = gst_tag_get_type (mappedTags[i].str);
|
||||
*record = mappedTags[i].record;
|
||||
break;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
return mappedTags[i].str;
|
||||
|
||||
}
|
||||
|
||||
static IptcTag
|
||||
metadataparse_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;
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
metadataparse_iptc_tag_list_add (GstTagList * taglist, GstTagMergeMode mode,
|
||||
GstAdapter * adapter, MetadataTagMapping mapping)
|
||||
|
@ -87,6 +150,7 @@ metadataparse_iptc_tag_list_add (GstTagList * taglist, GstTagMergeMode mode,
|
|||
const guint8 *buf;
|
||||
guint32 size;
|
||||
IptcData *iptc = NULL;
|
||||
MEUserData user_data = { taglist, mode };
|
||||
|
||||
if (adapter == NULL || (size = gst_adapter_available (adapter)) == 0) {
|
||||
goto done;
|
||||
|
@ -108,7 +172,7 @@ metadataparse_iptc_tag_list_add (GstTagList * taglist, GstTagMergeMode mode,
|
|||
}
|
||||
|
||||
iptc_data_foreach_dataset (iptc, iptc_data_foreach_dataset_func,
|
||||
(void *) taglist);
|
||||
(void *) &user_data);
|
||||
|
||||
done:
|
||||
|
||||
|
@ -123,20 +187,81 @@ static void
|
|||
iptc_data_foreach_dataset_func (IptcDataSet * dataset, void *user_data)
|
||||
{
|
||||
|
||||
char buf[256];
|
||||
GstTagList *taglist = (GstTagList *) 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", iptc_dataset_get_as_str (dataset, buf, 256));
|
||||
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
|
||||
metadatamux_iptc_create_chunk_from_tag_list (guint8 ** buf, guint32 * size,
|
||||
const GstTagList * taglist)
|
||||
{
|
||||
IptcData *iptc = NULL;
|
||||
GstBuffer *iptc_chunk = NULL;
|
||||
const GValue *val = NULL;
|
||||
|
||||
|
@ -152,14 +277,25 @@ metadatamux_iptc_create_chunk_from_tag_list (guint8 ** buf, guint32 * size,
|
|||
if (val) {
|
||||
iptc_chunk = gst_value_get_buffer (val);
|
||||
if (iptc_chunk) {
|
||||
*size = GST_BUFFER_SIZE (iptc_chunk);
|
||||
*buf = g_new (guint8, *size);
|
||||
memcpy (*buf, GST_BUFFER_DATA (iptc_chunk), *size);
|
||||
iptc = iptc_data_new_from_data (GST_BUFFER_DATA (iptc_chunk),
|
||||
GST_BUFFER_SIZE (iptc_chunk));
|
||||
}
|
||||
}
|
||||
|
||||
if (!iptc) {
|
||||
iptc = iptc_data_new ();
|
||||
}
|
||||
|
||||
gst_tag_list_foreach (taglist, metadataiptc_for_each_tag_in_list, iptc);
|
||||
|
||||
iptc_data_save (iptc, buf, size);
|
||||
|
||||
|
||||
done:
|
||||
|
||||
if (iptc)
|
||||
iptc_data_unref (iptc);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -75,6 +75,35 @@ metadatamux_wrap_chunk (MetadataChunk * chunk, const guint8 * buf,
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_IPTC
|
||||
static gboolean
|
||||
metadatamux_wrap_iptc_with_ps3 (unsigned char **buf, unsigned int *buf_size)
|
||||
{
|
||||
unsigned int out_size = *buf_size + 4096;
|
||||
unsigned char *outbuf = g_new (unsigned char, out_size);
|
||||
int size_written;
|
||||
gboolean ret = TRUE;
|
||||
|
||||
size_written =
|
||||
iptc_jpeg_ps3_save_iptc (NULL, 0, *buf, *buf_size, outbuf, out_size);
|
||||
|
||||
g_free (*buf);
|
||||
*buf = NULL;
|
||||
*buf_size = 0;
|
||||
|
||||
if (size_written < 0) {
|
||||
g_free (outbuf);
|
||||
ret = FALSE;
|
||||
} else {
|
||||
*buf_size = size_written;
|
||||
*buf = outbuf;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
#endif /* #ifdef HAVE_IPTC */
|
||||
|
||||
void
|
||||
metadatamux_jpeg_lazy_update (JpegMuxData * jpeg_data)
|
||||
{
|
||||
|
@ -93,21 +122,12 @@ metadatamux_jpeg_lazy_update (JpegMuxData * jpeg_data)
|
|||
case MD_CHUNK_IPTC:
|
||||
#ifdef HAVE_IPTC
|
||||
{
|
||||
unsigned int size = jpeg_data->inject_chunks->chunk[i].size + 256;
|
||||
unsigned char *buf = g_new (guint8, size);
|
||||
|
||||
size = iptc_jpeg_ps3_save_iptc (NULL, 0,
|
||||
jpeg_data->inject_chunks->chunk[i].data,
|
||||
jpeg_data->inject_chunks->chunk[i].size, buf, size);
|
||||
if (size > 0) {
|
||||
g_free (jpeg_data->inject_chunks->chunk[i].data);
|
||||
jpeg_data->inject_chunks->chunk[i].data = buf;
|
||||
jpeg_data->inject_chunks->chunk[i].size = size;
|
||||
if (metadatamux_wrap_iptc_with_ps3 (&jpeg_data->inject_chunks->
|
||||
chunk[i].data, &jpeg_data->inject_chunks->chunk[i].size)) {
|
||||
metadatamux_wrap_chunk (&jpeg_data->inject_chunks->chunk[i], NULL,
|
||||
0, 0xFF, 0xED);
|
||||
} else {
|
||||
GST_ERROR ("Invalid IPTC chunk\n");
|
||||
g_free (buf);
|
||||
/* FIXME: remove entry from list */
|
||||
}
|
||||
}
|
||||
|
@ -225,7 +245,6 @@ metadatamux_jpeg_reading (JpegMuxData * jpeg_data, guint8 ** buf,
|
|||
static const char JfifHeader[] = "JFIF";
|
||||
static const unsigned char ExifHeader[] =
|
||||
{ 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 };
|
||||
static const char IptcHeader[] = "Photoshop 3.0";
|
||||
static const char XmpHeader[] = "http://ns.adobe.com/xap/1.0/";
|
||||
|
||||
*next_start = *buf;
|
||||
|
|
|
@ -199,7 +199,7 @@ metadataparse_jpeg_reading (JpegParseData * jpeg_data, guint8 ** buf,
|
|||
static const char JfifHeader[] = "JFIF";
|
||||
static const unsigned char ExifHeader[] =
|
||||
{ 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 };
|
||||
static const char IptcHeader[] = "Photoshop 3.0";
|
||||
static const char PhotoshopHeader[] = "Photoshop 3.0";
|
||||
static const char XmpHeader[] = "http://ns.adobe.com/xap/1.0/";
|
||||
|
||||
*next_start = *buf;
|
||||
|
@ -354,7 +354,7 @@ metadataparse_jpeg_reading (JpegParseData * jpeg_data, guint8 ** buf,
|
|||
}
|
||||
|
||||
|
||||
if (0 == memcmp (IptcHeader, *buf, 14)) {
|
||||
if (0 == memcmp (PhotoshopHeader, *buf, 14)) {
|
||||
|
||||
if (!jpeg_data->parse_only) {
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
*/
|
||||
|
||||
#include "metadataparseutil.h"
|
||||
#include <string.h>
|
||||
|
||||
void
|
||||
metadataparse_util_tag_list_add_chunk (GstTagList * taglist,
|
||||
|
|
Loading…
Reference in a new issue