rtsp: Port to GIO
[platform/upstream/gstreamer.git] / gst-libs / gst / tag / gstexiftag.c
1 /* GStreamer
2  * Copyright (C) 2010 Thiago Santos <thiago.sousa.santos@collabora.co.uk>
3  *
4  * gstexiftag.c: library for reading / modifying exif tags
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 /**
23  * SECTION:gsttagexif
24  * @short_description: tag mappings and support functions for plugins
25  *                     dealing with exif tags
26  * @see_also: #GstTagList
27  *
28  * Contains utility function to parse #GstTagList<!-- -->s from exif
29  * buffers and to create exif buffers from #GstTagList<!-- -->s
30  *
31  * Note that next IFD fields on the created exif buffers are set to 0.
32  */
33
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif
37 #include <gst/gsttagsetter.h>
38 #include <gst/base/gstbytewriter.h>
39 #include "gsttageditingprivate.h"
40
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <gst/math-compat.h>
45
46 /* Some useful constants */
47 #define TIFF_LITTLE_ENDIAN  0x4949
48 #define TIFF_BIG_ENDIAN     0x4D4D
49 #define TIFF_HEADER_SIZE    8
50 #define EXIF_TAG_ENTRY_SIZE (2 + 2 + 4 + 4)
51
52 /* Exif tag types */
53 #define EXIF_TYPE_BYTE       1
54 #define EXIF_TYPE_ASCII      2
55 #define EXIF_TYPE_SHORT      3
56 #define EXIF_TYPE_LONG       4
57 #define EXIF_TYPE_RATIONAL   5
58 #define EXIF_TYPE_UNDEFINED  7
59 #define EXIF_TYPE_SLONG      9
60 #define EXIF_TYPE_SRATIONAL 10
61
62 typedef struct _GstExifTagMatch GstExifTagMatch;
63 typedef struct _GstExifWriter GstExifWriter;
64 typedef struct _GstExifReader GstExifReader;
65 typedef struct _GstExifTagData GstExifTagData;
66
67 typedef void (*GstExifSerializationFunc) (GstExifWriter * writer,
68     const GstTagList * taglist, const GstExifTagMatch * exiftag);
69
70 /*
71  * Function used to deserialize tags that don't follow the usual
72  * deserialization conversions. Usually those that have 'Ref' complementary
73  * tags.
74  *
75  * Those functions receive a exif tag data in the parameters, plus the taglist
76  * and the reader and buffer if they need to get more information to build
77  * its tags. There are lots of parameters, but this is needed to make it
78  * versatile. Explanation of them follows:
79  *
80  * exif_reader: The #GstExifReader with the reading parameter and taglist for
81  * results.
82  * reader: The #GstByteReader pointing to the start of the next tag entry in
83  * the ifd, useful for tags that use other complementary tags.
84  * the buffer start
85  * exiftag: The #GstExifTagMatch that contains this tag info
86  * tagdata: values from the already parsed tag
87  */
88 typedef gint (*GstExifDeserializationFunc) (GstExifReader * exif_reader,
89     GstByteReader * reader, const GstExifTagMatch * exiftag,
90     GstExifTagData * tagdata);
91
92 #define EXIF_SERIALIZATION_FUNC(name) \
93 static void serialize_ ## name (GstExifWriter * writer, \
94     const GstTagList * taglist, const GstExifTagMatch * exiftag)
95
96 #define EXIF_DESERIALIZATION_FUNC(name) \
97 static gint deserialize_ ## name (GstExifReader * exif_reader, \
98     GstByteReader * reader, const GstExifTagMatch * exiftag, \
99     GstExifTagData * tagdata)
100
101 #define EXIF_SERIALIZATION_DESERIALIZATION_FUNC(name) \
102   EXIF_SERIALIZATION_FUNC (name); \
103   EXIF_DESERIALIZATION_FUNC (name)
104
105 /*
106  * A common case among serialization/deserialization routines is that
107  * the gstreamer tag is a string (with a predefined set of allowed values)
108  * and exif is an int. These macros cover these cases
109  */
110 #define EXIF_SERIALIZATION_MAP_STRING_TO_INT_FUNC(name,funcname) \
111 static void \
112 serialize_ ## name (GstExifWriter * writer, const GstTagList * taglist, \
113     const GstExifTagMatch * exiftag) \
114 { \
115   gchar *str = NULL; \
116   gint exif_value; \
117 \
118   if (!gst_tag_list_get_string_index (taglist, exiftag->gst_tag, 0, &str)) { \
119     GST_WARNING ("No %s tag present in taglist", exiftag->gst_tag); \
120     return; \
121   } \
122 \
123   exif_value = __exif_tag_ ## funcname ## _to_exif_value (str); \
124   if (exif_value == -1) { \
125     g_free (str); \
126     return; \
127   } \
128   g_free (str); \
129 \
130   switch (exiftag->exif_type) { \
131     case EXIF_TYPE_SHORT: \
132       gst_exif_writer_write_short_tag (writer, exiftag->exif_tag, exif_value); \
133       break; \
134     case EXIF_TYPE_LONG: \
135       gst_exif_writer_write_long_tag (writer, exiftag->exif_tag, exif_value); \
136       break; \
137     case EXIF_TYPE_UNDEFINED: \
138     { \
139         guint8 data = (guint8) exif_value; \
140         write_exif_undefined_tag (writer, exiftag->exif_tag, &data, 1); \
141     } \
142       break; \
143     default: \
144       g_assert_not_reached (); \
145       GST_WARNING ("Unmapped serialization for type %d", exiftag->exif_type); \
146       break; \
147    } \
148 }
149
150 #define EXIF_DESERIALIZATION_MAP_STRING_TO_INT_FUNC(name,funcname) \
151 static gint \
152 deserialize_ ## name (GstExifReader * exif_reader, \
153     GstByteReader * reader, const GstExifTagMatch * exiftag, \
154     GstExifTagData * tagdata) \
155 { \
156   const gchar *str = NULL; \
157   gint value; \
158 \
159   GST_LOG ("Starting to parse %s tag in exif 0x%x", exiftag->gst_tag, \
160       exiftag->exif_tag); \
161 \
162   /* validate tag */ \
163   if (tagdata->count != 1) { \
164     GST_WARNING ("0x%X has unexpected count", tagdata->count); \
165     return 0; \
166   } \
167 \
168   if (tagdata->tag_type == EXIF_TYPE_SHORT) { \
169     if (exif_reader->byte_order == G_LITTLE_ENDIAN) { \
170       value = GST_READ_UINT16_LE (tagdata->offset_as_data); \
171     } else { \
172       value = GST_READ_UINT16_BE (tagdata->offset_as_data); \
173     } \
174   } else if (tagdata->tag_type == EXIF_TYPE_UNDEFINED) { \
175     value = GST_READ_UINT8 (tagdata->offset_as_data); \
176   } else { \
177     GST_WARNING ("0x%X has unexpected type %d", exiftag->exif_tag, \
178         tagdata->tag_type); \
179     return 0; \
180   } \
181 \
182   str = __exif_tag_## funcname ## _from_exif_value (value); \
183   if (str == NULL) { \
184     GST_WARNING ("Invalid value for tag 0x%X: %d", tagdata->tag, value); \
185     return 0; \
186   } \
187   gst_tag_list_add (exif_reader->taglist, GST_TAG_MERGE_REPLACE, \
188       exiftag->gst_tag, str, NULL); \
189 \
190   return 0; \
191 }
192
193 #define EXIF_SERIALIZATION_DESERIALIZATION_MAP_STRING_TO_INT_FUNC(name,funcname) \
194   EXIF_SERIALIZATION_MAP_STRING_TO_INT_FUNC(name,funcname); \
195   EXIF_DESERIALIZATION_MAP_STRING_TO_INT_FUNC(name,funcname);
196
197 struct _GstExifTagMatch
198 {
199   const gchar *gst_tag;
200   guint16 exif_tag;
201   guint16 exif_type;
202
203   /* for tags that need special handling */
204   guint16 complementary_tag;
205   GstExifSerializationFunc serialize;
206   GstExifDeserializationFunc deserialize;
207 };
208
209 struct _GstExifTagData
210 {
211   guint16 tag;
212   guint16 tag_type;
213   guint32 count;
214   guint32 offset;
215   const guint8 *offset_as_data;
216 };
217
218 /*
219  * Holds the info and variables necessary to write
220  * the exif tags properly
221  */
222 struct _GstExifWriter
223 {
224   GstByteWriter tagwriter;
225   GstByteWriter datawriter;
226
227   gint byte_order;
228   guint tags_total;
229 };
230
231 struct _GstExifReader
232 {
233   GstTagList *taglist;
234   GstBuffer *buffer;
235   guint32 base_offset;
236   gint byte_order;
237
238   /* tags waiting for their complementary tags */
239   GSList *pending_tags;
240 };
241
242 EXIF_SERIALIZATION_DESERIALIZATION_FUNC (aperture_value);
243 EXIF_SERIALIZATION_DESERIALIZATION_FUNC (contrast);
244 EXIF_SERIALIZATION_DESERIALIZATION_FUNC (exposure_program);
245 EXIF_SERIALIZATION_DESERIALIZATION_FUNC (exposure_mode);
246 EXIF_SERIALIZATION_DESERIALIZATION_FUNC (flash);
247 EXIF_SERIALIZATION_DESERIALIZATION_FUNC (gain_control);
248 EXIF_SERIALIZATION_DESERIALIZATION_FUNC (geo_coordinate);
249 EXIF_SERIALIZATION_DESERIALIZATION_FUNC (geo_direction);
250 EXIF_SERIALIZATION_DESERIALIZATION_FUNC (geo_elevation);
251 EXIF_SERIALIZATION_DESERIALIZATION_FUNC (metering_mode);
252 EXIF_SERIALIZATION_DESERIALIZATION_FUNC (orientation);
253 EXIF_SERIALIZATION_DESERIALIZATION_FUNC (saturation);
254 EXIF_SERIALIZATION_DESERIALIZATION_FUNC (scene_capture_type);
255 EXIF_SERIALIZATION_DESERIALIZATION_FUNC (scene_type);
256 EXIF_SERIALIZATION_DESERIALIZATION_FUNC (sensitivity_type);
257 EXIF_SERIALIZATION_DESERIALIZATION_FUNC (sharpness);
258 EXIF_SERIALIZATION_DESERIALIZATION_FUNC (shutter_speed);
259 EXIF_SERIALIZATION_DESERIALIZATION_FUNC (source);
260 EXIF_SERIALIZATION_DESERIALIZATION_FUNC (speed);
261 EXIF_SERIALIZATION_DESERIALIZATION_FUNC (white_balance);
262
263 EXIF_DESERIALIZATION_FUNC (resolution);
264 EXIF_DESERIALIZATION_FUNC (add_to_pending_tags);
265
266 /* FIXME copyright tag has a weird "artist\0editor\0" format that is
267  * not yet handled */
268
269 /* exif tag numbers */
270 #define EXIF_TAG_GPS_LATITUDE_REF 0x1
271 #define EXIF_TAG_GPS_LATITUDE 0x2
272 #define EXIF_TAG_GPS_LONGITUDE_REF 0x3
273 #define EXIF_TAG_GPS_LONGITUDE 0x4
274 #define EXIF_TAG_GPS_ALTITUDE_REF 0x5
275 #define EXIF_TAG_GPS_ALTITUDE 0x6
276 #define EXIF_TAG_GPS_SPEED_REF 0xC
277 #define EXIF_TAG_GPS_SPEED 0xD
278 #define EXIF_TAG_GPS_TRACK_REF 0xE
279 #define EXIF_TAG_GPS_TRACK 0xF
280 #define EXIF_TAG_GPS_IMAGE_DIRECTION_REF 0x10
281 #define EXIF_TAG_GPS_IMAGE_DIRECTION 0x11
282 #define EXIF_TAG_GPS_HORIZONTAL_POSITIONING_ERROR 0x1F
283 #define EXIF_TAG_IMAGE_DESCRIPTION 0x10E
284 #define EXIF_TAG_MAKE 0x10F
285 #define EXIF_TAG_MODEL 0x110
286 #define EXIF_TAG_ORIENTATION 0x112
287 #define EXIF_TAG_XRESOLUTION 0x11A
288 #define EXIF_TAG_YRESOLUTION 0x11B
289 #define EXIF_TAG_RESOLUTION_UNIT 0x128
290 #define EXIF_TAG_SOFTWARE 0x131
291 #define EXIF_TAG_DATE_TIME 0x132
292 #define EXIF_TAG_ARTIST 0x13B
293 #define EXIF_TAG_COPYRIGHT 0x8298
294 #define EXIF_TAG_EXPOSURE_TIME 0x829A
295 #define EXIF_TAG_F_NUMBER 0x829D
296 #define EXIF_TAG_EXPOSURE_PROGRAM 0x8822
297 #define EXIF_TAG_PHOTOGRAPHIC_SENSITIVITY 0x8827
298 #define EXIF_TAG_SENSITIVITY_TYPE 0x8830
299 #define EXIF_TAG_ISO_SPEED 0x8833
300 #define EXIF_TAG_DATE_TIME_ORIGINAL 0x9003
301 #define EXIF_TAG_DATE_TIME_DIGITIZED 0x9004
302 #define EXIF_TAG_SHUTTER_SPEED_VALUE 0x9201
303 #define EXIF_TAG_APERTURE_VALUE 0x9202
304 #define EXIF_TAG_EXPOSURE_BIAS 0x9204
305 #define EXIF_TAG_METERING_MODE 0x9207
306 #define EXIF_TAG_FLASH 0x9209
307 #define EXIF_TAG_FOCAL_LENGTH 0x920A
308 #define EXIF_TAG_MAKER_NOTE 0x927C
309 #define EXIF_TAG_FILE_SOURCE 0xA300
310 #define EXIF_TAG_SCENE_TYPE 0xA301
311 #define EXIF_TAG_EXPOSURE_MODE 0xA402
312 #define EXIF_TAG_WHITE_BALANCE 0xA403
313 #define EXIF_TAG_DIGITAL_ZOOM_RATIO 0xA404
314 #define EXIF_TAG_SCENE_CAPTURE_TYPE 0xA406
315 #define EXIF_TAG_GAIN_CONTROL 0xA407
316 #define EXIF_TAG_CONTRAST 0xA408
317 #define EXIF_TAG_SATURATION 0xA409
318 #define EXIF_TAG_SHARPNESS 0xA40A
319
320 /* IFD pointer tags */
321 #define EXIF_IFD_TAG 0x8769
322 #define EXIF_GPS_IFD_TAG 0x8825
323
324 /* version tags */
325 #define EXIF_VERSION_TAG 0x9000
326 #define EXIF_FLASHPIX_VERSION_TAG 0xA000
327
328 /* useful macros for speed tag */
329 #define METERS_PER_SECOND_TO_KILOMETERS_PER_HOUR (3.6)
330 #define KILOMETERS_PER_HOUR_TO_METERS_PER_SECOND (1/3.6)
331 #define MILES_PER_HOUR_TO_METERS_PER_SECOND (0.44704)
332 #define KNOTS_TO_METERS_PER_SECOND (0.514444)
333
334 /*
335  * Should be kept in ascending id order
336  *
337  * {gst-tag, exif-tag, exig-type, complementary-exif-tag, serialization-func,
338  *     deserialization-func}
339  */
340 static const GstExifTagMatch tag_map_ifd0[] = {
341   {GST_TAG_IMAGE_HORIZONTAL_PPI, EXIF_TAG_XRESOLUTION, EXIF_TYPE_RATIONAL,
342       0, NULL, deserialize_add_to_pending_tags},
343   {GST_TAG_IMAGE_VERTICAL_PPI, EXIF_TAG_YRESOLUTION, EXIF_TYPE_RATIONAL,
344       0, NULL, deserialize_add_to_pending_tags},
345   {NULL, EXIF_TAG_RESOLUTION_UNIT, EXIF_TYPE_SHORT, 0, NULL,
346       deserialize_resolution},
347   {GST_TAG_DESCRIPTION, EXIF_TAG_IMAGE_DESCRIPTION, EXIF_TYPE_ASCII, 0, NULL,
348       NULL},
349   {GST_TAG_DEVICE_MANUFACTURER, EXIF_TAG_MAKE, EXIF_TYPE_ASCII, 0, NULL, NULL},
350   {GST_TAG_DEVICE_MODEL, EXIF_TAG_MODEL, EXIF_TYPE_ASCII, 0, NULL, NULL},
351   {GST_TAG_IMAGE_ORIENTATION, EXIF_TAG_ORIENTATION, EXIF_TYPE_SHORT, 0,
352         serialize_orientation,
353       deserialize_orientation},
354   {GST_TAG_APPLICATION_NAME, EXIF_TAG_SOFTWARE, EXIF_TYPE_ASCII, 0, NULL, NULL},
355   {GST_TAG_DATE_TIME, EXIF_TAG_DATE_TIME, EXIF_TYPE_ASCII, 0, NULL, NULL},
356   {GST_TAG_ARTIST, EXIF_TAG_ARTIST, EXIF_TYPE_ASCII, 0, NULL, NULL},
357   {GST_TAG_COPYRIGHT, EXIF_TAG_COPYRIGHT, EXIF_TYPE_ASCII, 0, NULL, NULL},
358   {NULL, EXIF_IFD_TAG, EXIF_TYPE_LONG, 0, NULL, NULL},
359   {NULL, EXIF_GPS_IFD_TAG, EXIF_TYPE_LONG, 0, NULL, NULL},
360   {NULL, 0, 0, 0, NULL, NULL}
361 };
362
363 static const GstExifTagMatch tag_map_exif[] = {
364   {GST_TAG_CAPTURING_SHUTTER_SPEED, EXIF_TAG_EXPOSURE_TIME, EXIF_TYPE_RATIONAL,
365         0,
366       NULL, NULL},
367   {GST_TAG_CAPTURING_FOCAL_RATIO, EXIF_TAG_F_NUMBER, EXIF_TYPE_RATIONAL, 0,
368         NULL,
369       NULL},
370   {GST_TAG_CAPTURING_EXPOSURE_PROGRAM, EXIF_TAG_EXPOSURE_PROGRAM,
371         EXIF_TYPE_SHORT, 0, serialize_exposure_program,
372       deserialize_exposure_program},
373
374   /* don't need the serializer as we always write the iso speed alone */
375   {GST_TAG_CAPTURING_ISO_SPEED, EXIF_TAG_PHOTOGRAPHIC_SENSITIVITY,
376         EXIF_TYPE_SHORT, 0, NULL,
377       deserialize_add_to_pending_tags},
378
379   {GST_TAG_CAPTURING_ISO_SPEED, EXIF_TAG_SENSITIVITY_TYPE, EXIF_TYPE_SHORT, 0,
380       serialize_sensitivity_type, deserialize_sensitivity_type},
381   {GST_TAG_CAPTURING_ISO_SPEED, EXIF_TAG_ISO_SPEED, EXIF_TYPE_LONG, 0, NULL,
382       NULL},
383   {NULL, EXIF_VERSION_TAG, EXIF_TYPE_UNDEFINED, 0, NULL, NULL},
384   {GST_TAG_DATE_TIME, EXIF_TAG_DATE_TIME_ORIGINAL, EXIF_TYPE_ASCII, 0, NULL,
385       NULL},
386   {GST_TAG_CAPTURING_SHUTTER_SPEED, EXIF_TAG_SHUTTER_SPEED_VALUE,
387         EXIF_TYPE_SRATIONAL, 0,
388       serialize_shutter_speed, deserialize_shutter_speed},
389   {GST_TAG_CAPTURING_FOCAL_RATIO, EXIF_TAG_APERTURE_VALUE, EXIF_TYPE_RATIONAL,
390         0,
391       serialize_aperture_value, deserialize_aperture_value},
392   {GST_TAG_CAPTURING_EXPOSURE_COMPENSATION, EXIF_TAG_EXPOSURE_BIAS,
393       EXIF_TYPE_SRATIONAL, 0, NULL, NULL},
394   {GST_TAG_CAPTURING_METERING_MODE, EXIF_TAG_METERING_MODE, EXIF_TYPE_SHORT, 0,
395       serialize_metering_mode, deserialize_metering_mode},
396   {GST_TAG_CAPTURING_FLASH_FIRED, EXIF_TAG_FLASH, EXIF_TYPE_SHORT, 0,
397       serialize_flash, deserialize_flash},
398   {GST_TAG_CAPTURING_FOCAL_LENGTH, EXIF_TAG_FOCAL_LENGTH, EXIF_TYPE_RATIONAL, 0,
399       NULL, NULL},
400   {GST_TAG_APPLICATION_DATA, EXIF_TAG_MAKER_NOTE, EXIF_TYPE_UNDEFINED, 0, NULL,
401       NULL},
402   {NULL, EXIF_FLASHPIX_VERSION_TAG, EXIF_TYPE_UNDEFINED, 0, NULL, NULL},
403   {GST_TAG_CAPTURING_SOURCE, EXIF_TAG_FILE_SOURCE, EXIF_TYPE_UNDEFINED,
404       0, serialize_source, deserialize_source},
405   {GST_TAG_CAPTURING_SOURCE, EXIF_TAG_SCENE_TYPE, EXIF_TYPE_UNDEFINED,
406       0, serialize_scene_type, deserialize_scene_type},
407   {GST_TAG_CAPTURING_EXPOSURE_MODE, EXIF_TAG_EXPOSURE_MODE, EXIF_TYPE_SHORT,
408       0, serialize_exposure_mode, deserialize_exposure_mode},
409   {GST_TAG_CAPTURING_WHITE_BALANCE, EXIF_TAG_WHITE_BALANCE, EXIF_TYPE_SHORT,
410       0, serialize_white_balance, deserialize_white_balance},
411   {GST_TAG_CAPTURING_DIGITAL_ZOOM_RATIO, EXIF_TAG_DIGITAL_ZOOM_RATIO,
412         EXIF_TYPE_RATIONAL, 0, NULL,
413       NULL},
414   {GST_TAG_CAPTURING_SCENE_CAPTURE_TYPE, EXIF_TAG_SCENE_CAPTURE_TYPE,
415         EXIF_TYPE_SHORT, 0, serialize_scene_capture_type,
416       deserialize_scene_capture_type},
417   {GST_TAG_CAPTURING_GAIN_ADJUSTMENT, EXIF_TAG_GAIN_CONTROL,
418         EXIF_TYPE_SHORT, 0, serialize_gain_control,
419       deserialize_gain_control},
420   {GST_TAG_CAPTURING_CONTRAST, EXIF_TAG_CONTRAST, EXIF_TYPE_SHORT, 0,
421       serialize_contrast, deserialize_contrast},
422   {GST_TAG_CAPTURING_SATURATION, EXIF_TAG_SATURATION, EXIF_TYPE_SHORT, 0,
423       serialize_saturation, deserialize_saturation},
424   {GST_TAG_CAPTURING_SHARPNESS, EXIF_TAG_SHARPNESS, EXIF_TYPE_SHORT, 0,
425       serialize_sharpness, deserialize_sharpness},
426   {NULL, 0, 0, 0, NULL, NULL}
427 };
428
429 static const GstExifTagMatch tag_map_gps[] = {
430   {GST_TAG_GEO_LOCATION_LATITUDE, EXIF_TAG_GPS_LATITUDE, EXIF_TYPE_RATIONAL,
431         EXIF_TAG_GPS_LATITUDE_REF,
432       serialize_geo_coordinate, deserialize_geo_coordinate},
433   {GST_TAG_GEO_LOCATION_LONGITUDE, EXIF_TAG_GPS_LONGITUDE, EXIF_TYPE_RATIONAL,
434         EXIF_TAG_GPS_LONGITUDE_REF,
435       serialize_geo_coordinate, deserialize_geo_coordinate},
436   {GST_TAG_GEO_LOCATION_ELEVATION, EXIF_TAG_GPS_ALTITUDE, EXIF_TYPE_RATIONAL,
437         EXIF_TAG_GPS_ALTITUDE_REF,
438       serialize_geo_elevation, deserialize_geo_elevation},
439   {GST_TAG_GEO_LOCATION_MOVEMENT_SPEED, EXIF_TAG_GPS_SPEED, EXIF_TYPE_RATIONAL,
440         EXIF_TAG_GPS_SPEED_REF,
441       serialize_speed, deserialize_speed},
442   {GST_TAG_GEO_LOCATION_MOVEMENT_DIRECTION, EXIF_TAG_GPS_TRACK,
443         EXIF_TYPE_RATIONAL, EXIF_TAG_GPS_TRACK_REF,
444       serialize_geo_direction, deserialize_geo_direction},
445   {GST_TAG_GEO_LOCATION_CAPTURE_DIRECTION, EXIF_TAG_GPS_IMAGE_DIRECTION,
446         EXIF_TYPE_RATIONAL, EXIF_TAG_GPS_IMAGE_DIRECTION_REF,
447       serialize_geo_direction, deserialize_geo_direction},
448   {GST_TAG_GEO_LOCATION_HORIZONTAL_ERROR,
449         EXIF_TAG_GPS_HORIZONTAL_POSITIONING_ERROR,
450       EXIF_TYPE_RATIONAL, 0, NULL, NULL},
451   {NULL, 0, 0, 0, NULL, NULL}
452 };
453
454 /* GstExifReader functions */
455 static void
456 gst_exif_reader_init (GstExifReader * reader, gint byte_order,
457     GstBuffer * buf, guint32 base_offset)
458 {
459   ensure_exif_tags ();
460
461   reader->taglist = gst_tag_list_new_empty ();
462   reader->buffer = buf;
463   reader->base_offset = base_offset;
464   reader->byte_order = byte_order;
465   reader->pending_tags = NULL;
466   if (reader->byte_order != G_LITTLE_ENDIAN &&
467       reader->byte_order != G_BIG_ENDIAN) {
468     GST_WARNING ("Unexpected byte order %d, using system default: %d",
469         reader->byte_order, G_BYTE_ORDER);
470     reader->byte_order = G_BYTE_ORDER;
471   }
472 }
473
474 static void
475 gst_exif_reader_add_pending_tag (GstExifReader * reader, GstExifTagData * data)
476 {
477   GstExifTagData *copy;
478
479   copy = g_slice_new (GstExifTagData);
480   memcpy (copy, data, sizeof (GstExifTagData));
481
482   reader->pending_tags = g_slist_prepend (reader->pending_tags, copy);
483 }
484
485 static GstExifTagData *
486 gst_exif_reader_get_pending_tag (GstExifReader * reader, gint tagid)
487 {
488   GSList *walker;
489
490   for (walker = reader->pending_tags; walker; walker = g_slist_next (walker)) {
491     GstExifTagData *data = (GstExifTagData *) walker->data;
492     if (data->tag == tagid)
493       return data;
494   }
495
496   return NULL;
497 }
498
499 static GstTagList *
500 gst_exif_reader_reset (GstExifReader * reader, gboolean return_taglist)
501 {
502   GstTagList *ret = NULL;
503   GSList *walker;
504
505   for (walker = reader->pending_tags; walker; walker = g_slist_next (walker)) {
506     GstExifTagData *data = (GstExifTagData *) walker->data;
507
508     g_slice_free (GstExifTagData, data);
509   }
510   g_slist_free (reader->pending_tags);
511
512   if (return_taglist) {
513     ret = reader->taglist;
514     reader->taglist = NULL;
515   }
516
517   if (reader->taglist) {
518     gst_tag_list_free (reader->taglist);
519   }
520
521   return ret;
522 }
523
524 /* GstExifWriter functions */
525
526 static void
527 gst_exif_writer_init (GstExifWriter * writer, gint byte_order)
528 {
529   ensure_exif_tags ();
530
531   gst_byte_writer_init (&writer->tagwriter);
532   gst_byte_writer_init (&writer->datawriter);
533
534   writer->byte_order = byte_order;
535   writer->tags_total = 0;
536   if (writer->byte_order != G_LITTLE_ENDIAN &&
537       writer->byte_order != G_BIG_ENDIAN) {
538     GST_WARNING ("Unexpected byte order %d, using system default: %d",
539         writer->byte_order, G_BYTE_ORDER);
540     writer->byte_order = G_BYTE_ORDER;
541   }
542 }
543
544 static GstBuffer *
545 gst_exif_writer_reset_and_get_buffer (GstExifWriter * writer)
546 {
547   GstBuffer *header;
548   GstBuffer *data;
549
550   header = gst_byte_writer_reset_and_get_buffer (&writer->tagwriter);
551   data = gst_byte_writer_reset_and_get_buffer (&writer->datawriter);
552
553   return gst_buffer_join (header, data);
554 }
555
556 /*
557  * Given the exif tag with the passed id, returns the map index of the tag
558  * corresponding to it. If use_complementary is true, then the complementary
559  * are also used in the search.
560  *
561  * Returns -1 if not found
562  */
563 static gint
564 exif_tag_map_find_reverse (guint16 exif_tag, const GstExifTagMatch * tag_map,
565     gboolean use_complementary)
566 {
567   gint i;
568
569   for (i = 0; tag_map[i].exif_tag != 0; i++) {
570     if (exif_tag == tag_map[i].exif_tag || (use_complementary &&
571             exif_tag == tag_map[i].complementary_tag)) {
572       return i;
573     }
574   }
575   return -1;
576 }
577
578 static gboolean
579 gst_tag_list_has_ifd_tags (const GstTagList * taglist,
580     const GstExifTagMatch * tag_map)
581 {
582   gint i;
583
584   for (i = 0; tag_map[i].exif_tag != 0; i++) {
585     if (tag_map[i].gst_tag == NULL) {
586       if (tag_map[i].exif_tag == EXIF_GPS_IFD_TAG &&
587           gst_tag_list_has_ifd_tags (taglist, tag_map_gps))
588         return TRUE;
589       if (tag_map[i].exif_tag == EXIF_IFD_TAG &&
590           gst_tag_list_has_ifd_tags (taglist, tag_map_exif))
591         return TRUE;
592       continue;
593     }
594
595     if (gst_tag_list_get_value_index (taglist, tag_map[i].gst_tag, 0)) {
596       return TRUE;
597     }
598   }
599   return FALSE;
600 }
601
602 /*
603  * Writes the tag entry.
604  *
605  * The tag entry is the tag id, the tag type,
606  * the count and the offset.
607  *
608  * The offset is the on the amount of data writen so far, as one
609  * can't predict the total bytes that the tag entries will take.
610  * This means those fields requires being updated later.
611  */
612 static void
613 gst_exif_writer_write_tag_header (GstExifWriter * writer,
614     guint16 exif_tag, guint16 exif_type, guint32 count, guint32 offset,
615     const guint32 * offset_data)
616 {
617   GST_DEBUG ("Writing tag entry: id %x, type %u, count %u, offset %u",
618       exif_tag, exif_type, count, offset);
619
620   if (writer->byte_order == G_LITTLE_ENDIAN) {
621     gst_byte_writer_put_uint16_le (&writer->tagwriter, exif_tag);
622     gst_byte_writer_put_uint16_le (&writer->tagwriter, exif_type);
623     gst_byte_writer_put_uint32_le (&writer->tagwriter, count);
624     if (offset_data != NULL) {
625       gst_byte_writer_put_data (&writer->tagwriter, (guint8 *) offset_data, 4);
626     } else {
627       gst_byte_writer_put_uint32_le (&writer->tagwriter, offset);
628     }
629   } else if (writer->byte_order == G_BIG_ENDIAN) {
630     gst_byte_writer_put_uint16_be (&writer->tagwriter, exif_tag);
631     gst_byte_writer_put_uint16_be (&writer->tagwriter, exif_type);
632     gst_byte_writer_put_uint32_be (&writer->tagwriter, count);
633     if (offset_data != NULL) {
634       gst_byte_writer_put_data (&writer->tagwriter, (guint8 *) offset_data, 4);
635     } else {
636       gst_byte_writer_put_uint32_be (&writer->tagwriter, offset);
637     }
638   } else {
639     g_assert_not_reached ();
640   }
641
642   writer->tags_total++;
643 }
644
645 static void
646 gst_exif_writer_write_rational_data (GstExifWriter * writer, guint32 frac_n,
647     guint32 frac_d)
648 {
649   if (writer->byte_order == G_LITTLE_ENDIAN) {
650     gst_byte_writer_put_uint32_le (&writer->datawriter, frac_n);
651     gst_byte_writer_put_uint32_le (&writer->datawriter, frac_d);
652   } else {
653     gst_byte_writer_put_uint32_be (&writer->datawriter, frac_n);
654     gst_byte_writer_put_uint32_be (&writer->datawriter, frac_d);
655   }
656 }
657
658 static void
659 gst_exif_writer_write_signed_rational_data (GstExifWriter * writer,
660     gint32 frac_n, gint32 frac_d)
661 {
662   if (writer->byte_order == G_LITTLE_ENDIAN) {
663     gst_byte_writer_put_int32_le (&writer->datawriter, frac_n);
664     gst_byte_writer_put_int32_le (&writer->datawriter, frac_d);
665   } else {
666     gst_byte_writer_put_int32_be (&writer->datawriter, frac_n);
667     gst_byte_writer_put_int32_be (&writer->datawriter, frac_d);
668   }
669 }
670
671 static void
672 gst_exif_writer_write_rational_tag (GstExifWriter * writer,
673     guint16 tag, guint32 frac_n, guint32 frac_d)
674 {
675   guint32 offset = gst_byte_writer_get_size (&writer->datawriter);
676
677   gst_exif_writer_write_tag_header (writer, tag, EXIF_TYPE_RATIONAL,
678       1, offset, NULL);
679
680   gst_exif_writer_write_rational_data (writer, frac_n, frac_d);
681 }
682
683 static void
684 gst_exif_writer_write_signed_rational_tag (GstExifWriter * writer,
685     guint16 tag, gint32 frac_n, gint32 frac_d)
686 {
687   guint32 offset = gst_byte_writer_get_size (&writer->datawriter);
688
689   gst_exif_writer_write_tag_header (writer, tag, EXIF_TYPE_SRATIONAL,
690       1, offset, NULL);
691
692   gst_exif_writer_write_signed_rational_data (writer, frac_n, frac_d);
693 }
694
695 static void
696 gst_exif_writer_write_rational_tag_from_double (GstExifWriter * writer,
697     guint16 tag, gdouble value)
698 {
699   gint frac_n;
700   gint frac_d;
701
702   gst_util_double_to_fraction (value, &frac_n, &frac_d);
703
704   gst_exif_writer_write_rational_tag (writer, tag, frac_n, frac_d);
705 }
706
707 static void
708 gst_exif_writer_write_signed_rational_tag_from_double (GstExifWriter * writer,
709     guint16 tag, gdouble value)
710 {
711   gint frac_n;
712   gint frac_d;
713
714   gst_util_double_to_fraction (value, &frac_n, &frac_d);
715
716   gst_exif_writer_write_signed_rational_tag (writer, tag, frac_n, frac_d);
717 }
718
719 static void
720 gst_exif_writer_write_byte_tag (GstExifWriter * writer, guint16 tag,
721     guint8 value)
722 {
723   guint32 offset = 0;
724
725   GST_WRITE_UINT8 ((guint8 *) & offset, value);
726   gst_exif_writer_write_tag_header (writer, tag, EXIF_TYPE_BYTE,
727       1, offset, &offset);
728 }
729
730 static void
731 gst_exif_writer_write_short_tag (GstExifWriter * writer, guint16 tag,
732     guint16 value)
733 {
734   guint32 offset = 0;
735
736   if (writer->byte_order == G_LITTLE_ENDIAN) {
737     GST_WRITE_UINT16_LE ((guint8 *) & offset, value);
738   } else {
739     GST_WRITE_UINT16_BE ((guint8 *) & offset, value);
740   }
741
742   gst_exif_writer_write_tag_header (writer, tag, EXIF_TYPE_SHORT,
743       1, offset, &offset);
744 }
745
746 static void
747 gst_exif_writer_write_long_tag (GstExifWriter * writer, guint16 tag,
748     guint32 value)
749 {
750   guint32 offset = 0;
751   if (writer->byte_order == G_LITTLE_ENDIAN) {
752     GST_WRITE_UINT32_LE ((guint8 *) & offset, value);
753   } else {
754     GST_WRITE_UINT32_BE ((guint8 *) & offset, value);
755   }
756
757   gst_exif_writer_write_tag_header (writer, tag, EXIF_TYPE_LONG,
758       1, offset, &offset);
759 }
760
761
762 static void
763 write_exif_undefined_tag (GstExifWriter * writer, guint16 tag,
764     const guint8 * data, gint size)
765 {
766   guint32 offset = 0;
767
768   if (size > 4) {
769     /* we only use the data offset here, later we add up the
770      * resulting tag headers offset and the base offset */
771     offset = gst_byte_writer_get_size (&writer->datawriter);
772     gst_exif_writer_write_tag_header (writer, tag, EXIF_TYPE_UNDEFINED,
773         size, offset, NULL);
774     gst_byte_writer_put_data (&writer->datawriter, data, size);
775   } else {
776     /* small enough to go in the offset */
777     memcpy ((guint8 *) & offset, data, size);
778     gst_exif_writer_write_tag_header (writer, tag, EXIF_TYPE_UNDEFINED,
779         size, offset, &offset);
780   }
781 }
782
783 static void
784 write_exif_ascii_tag (GstExifWriter * writer, guint16 tag, const gchar * str)
785 {
786   guint32 offset = 0;
787   gchar *ascii_str;
788   gsize ascii_size;
789   GError *error = NULL;
790
791   ascii_str = g_convert (str, -1, "latin1", "utf8", NULL, &ascii_size, &error);
792
793   if (error) {
794     GST_WARNING ("Failed to convert exif tag to ascii: 0x%x - %s. Error: %s",
795         tag, str, error->message);
796     g_error_free (error);
797     g_free (ascii_str);
798     return;
799   }
800
801   /* add the \0 at the end */
802   ascii_size++;
803
804   if (ascii_size > 4) {
805     /* we only use the data offset here, later we add up the
806      * resulting tag headers offset and the base offset */
807     offset = gst_byte_writer_get_size (&writer->datawriter);
808     gst_exif_writer_write_tag_header (writer, tag, EXIF_TYPE_ASCII,
809         ascii_size, offset, NULL);
810     gst_byte_writer_put_string (&writer->datawriter, ascii_str);
811   } else {
812     /* small enough to go in the offset */
813     memcpy ((guint8 *) & offset, ascii_str, ascii_size);
814     gst_exif_writer_write_tag_header (writer, tag, EXIF_TYPE_ASCII,
815         ascii_size, offset, &offset);
816   }
817
818   g_free (ascii_str);
819 }
820
821 static void
822 write_exif_ascii_tag_from_taglist (GstExifWriter * writer,
823     const GstTagList * taglist, const GstExifTagMatch * exiftag)
824 {
825   gchar *str = NULL;
826   gboolean cleanup = FALSE;
827   const GValue *value;
828   gint tag_size = gst_tag_list_get_tag_size (taglist, exiftag->gst_tag);
829
830   if (tag_size != 1) {
831     /* FIXME support this by serializing them with a ','? */
832     GST_WARNING ("Multiple string tags not supported yet");
833     return;
834   }
835
836   value = gst_tag_list_get_value_index (taglist, exiftag->gst_tag, 0);
837
838   /* do some conversion if needed */
839   switch (G_VALUE_TYPE (value)) {
840     case G_TYPE_STRING:
841       str = (gchar *) g_value_get_string (value);
842       break;
843     default:
844       if (G_VALUE_TYPE (value) == GST_TYPE_DATE_TIME) {
845         GstDateTime *dt = (GstDateTime *) g_value_get_boxed (value);
846
847         if (dt == NULL) {
848           GST_WARNING ("NULL datetime received");
849           break;
850         }
851
852         str = g_strdup_printf ("%04d:%02d:%02d %02d:%02d:%02d",
853             gst_date_time_get_year (dt), gst_date_time_get_month (dt),
854             gst_date_time_get_day (dt), gst_date_time_get_hour (dt),
855             gst_date_time_get_minute (dt), gst_date_time_get_second (dt));
856
857         cleanup = TRUE;
858       } else {
859         GST_WARNING ("Conversion from %s to ascii string not supported",
860             G_VALUE_TYPE_NAME (value));
861       }
862       break;
863   }
864
865   if (str == NULL)
866     return;
867
868   write_exif_ascii_tag (writer, exiftag->exif_tag, str);
869   if (cleanup)
870     g_free (str);
871 }
872
873 static void
874 write_exif_undefined_tag_from_taglist (GstExifWriter * writer,
875     const GstTagList * taglist, const GstExifTagMatch * exiftag)
876 {
877   const GValue *value;
878   guint8 *data = NULL;
879   gsize size = 0;
880   gint tag_size = gst_tag_list_get_tag_size (taglist, exiftag->gst_tag);
881   GstBuffer *buf = NULL;
882
883   if (tag_size != 1) {
884     GST_WARNING ("Only the first item in the taglist will be serialized");
885     return;
886   }
887
888   value = gst_tag_list_get_value_index (taglist, exiftag->gst_tag, 0);
889
890   /* do some conversion if needed */
891   switch (G_VALUE_TYPE (value)) {
892     case G_TYPE_STRING:
893       data = (guint8 *) g_value_get_string (value);
894       size = strlen ((gchar *) data);   /* no need to +1, undefined doesn't require it */
895       break;
896     default:
897       if (G_VALUE_TYPE (value) == GST_TYPE_BUFFER) {
898         buf = gst_value_get_buffer (value);
899         data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
900       } else {
901         GST_WARNING ("Conversion from %s to raw data not supported",
902             G_VALUE_TYPE_NAME (value));
903       }
904       break;
905   }
906
907   if (size > 0)
908     write_exif_undefined_tag (writer, exiftag->exif_tag, data, size);
909
910   if (buf)
911     gst_buffer_unmap (buf, data, size);
912 }
913
914 static void
915 write_exif_rational_tag_from_taglist (GstExifWriter * writer,
916     const GstTagList * taglist, const GstExifTagMatch * exiftag)
917 {
918   const GValue *value;
919   gdouble num = 0;
920   gint tag_size = gst_tag_list_get_tag_size (taglist, exiftag->gst_tag);
921
922   if (tag_size != 1) {
923     GST_WARNING ("Only the first item in the taglist will be serialized");
924     return;
925   }
926
927   value = gst_tag_list_get_value_index (taglist, exiftag->gst_tag, 0);
928
929   /* do some conversion if needed */
930   switch (G_VALUE_TYPE (value)) {
931     case G_TYPE_DOUBLE:
932       num = g_value_get_double (value);
933       gst_exif_writer_write_rational_tag_from_double (writer, exiftag->exif_tag,
934           num);
935       break;
936     default:
937       if (G_VALUE_TYPE (value) == GST_TYPE_FRACTION) {
938         gst_exif_writer_write_rational_tag (writer, exiftag->exif_tag,
939             gst_value_get_fraction_numerator (value),
940             gst_value_get_fraction_denominator (value));
941       } else {
942         GST_WARNING ("Conversion from %s to rational not supported",
943             G_VALUE_TYPE_NAME (value));
944       }
945       break;
946   }
947 }
948
949 static void
950 write_exif_signed_rational_tag_from_taglist (GstExifWriter * writer,
951     const GstTagList * taglist, const GstExifTagMatch * exiftag)
952 {
953   const GValue *value;
954   gdouble num = 0;
955   gint tag_size = gst_tag_list_get_tag_size (taglist, exiftag->gst_tag);
956
957   if (tag_size != 1) {
958     GST_WARNING ("Only the first item in the taglist will be serialized");
959     return;
960   }
961
962   value = gst_tag_list_get_value_index (taglist, exiftag->gst_tag, 0);
963
964   /* do some conversion if needed */
965   switch (G_VALUE_TYPE (value)) {
966     case G_TYPE_DOUBLE:
967       num = g_value_get_double (value);
968       gst_exif_writer_write_signed_rational_tag_from_double (writer,
969           exiftag->exif_tag, num);
970       break;
971     default:
972       if (G_VALUE_TYPE (value) == GST_TYPE_FRACTION) {
973         gst_exif_writer_write_signed_rational_tag (writer, exiftag->exif_tag,
974             gst_value_get_fraction_numerator (value),
975             gst_value_get_fraction_denominator (value));
976       } else {
977         GST_WARNING ("Conversion from %s to signed rational not supported",
978             G_VALUE_TYPE_NAME (value));
979       }
980       break;
981   }
982 }
983
984 static void
985 write_exif_integer_tag_from_taglist (GstExifWriter * writer,
986     const GstTagList * taglist, const GstExifTagMatch * exiftag)
987 {
988   const GValue *value;
989   guint32 num = 0;
990   gint tag_size = gst_tag_list_get_tag_size (taglist, exiftag->gst_tag);
991
992   if (tag_size != 1) {
993     GST_WARNING ("Only the first item in the taglist will be serialized");
994     return;
995   }
996
997   value = gst_tag_list_get_value_index (taglist, exiftag->gst_tag, 0);
998
999   /* do some conversion if needed */
1000   switch (G_VALUE_TYPE (value)) {
1001     case G_TYPE_INT:
1002       num = g_value_get_int (value);
1003       break;
1004     default:
1005       GST_WARNING ("Conversion from %s to int not supported",
1006           G_VALUE_TYPE_NAME (value));
1007       break;
1008   }
1009
1010   switch (exiftag->exif_type) {
1011     case EXIF_TYPE_LONG:
1012       gst_exif_writer_write_long_tag (writer, exiftag->exif_tag, num);
1013       break;
1014     case EXIF_TYPE_SHORT:
1015       gst_exif_writer_write_short_tag (writer, exiftag->exif_tag, num);
1016       break;
1017     default:
1018       break;
1019   }
1020 }
1021
1022 static void
1023 write_exif_tag_from_taglist (GstExifWriter * writer, const GstTagList * taglist,
1024     const GstExifTagMatch * exiftag)
1025 {
1026   GST_DEBUG ("Writing tag %s", exiftag->gst_tag);
1027
1028   /* check for special handling */
1029   if (exiftag->serialize) {
1030     exiftag->serialize (writer, taglist, exiftag);
1031     return;
1032   }
1033
1034   switch (exiftag->exif_type) {
1035     case EXIF_TYPE_ASCII:
1036       write_exif_ascii_tag_from_taglist (writer, taglist, exiftag);
1037       break;
1038     case EXIF_TYPE_UNDEFINED:
1039       write_exif_undefined_tag_from_taglist (writer, taglist, exiftag);
1040       break;
1041     case EXIF_TYPE_RATIONAL:
1042       write_exif_rational_tag_from_taglist (writer, taglist, exiftag);
1043       break;
1044     case EXIF_TYPE_SRATIONAL:
1045       write_exif_signed_rational_tag_from_taglist (writer, taglist, exiftag);
1046       break;
1047     case EXIF_TYPE_LONG:
1048     case EXIF_TYPE_SHORT:
1049       write_exif_integer_tag_from_taglist (writer, taglist, exiftag);
1050       break;
1051     default:
1052       GST_WARNING ("Unhandled tag type %d", exiftag->exif_type);
1053   }
1054 }
1055
1056 static void
1057 tagdata_copy (GstExifTagData * to, const GstExifTagData * from)
1058 {
1059   to->tag = from->tag;
1060   to->tag_type = from->tag_type;
1061   to->count = from->count;
1062   to->offset = from->offset;
1063   to->offset_as_data = from->offset_as_data;
1064 }
1065
1066 static void
1067 gst_exif_tag_rewrite_offsets (GstByteWriter * writer, gint byte_order,
1068     guint32 offset, gint num_tags, GstByteWriter * inner_ifds_data)
1069 {
1070   GstByteReader *reader;
1071   gint i;
1072   guint16 aux = G_MAXUINT16;
1073
1074   GST_LOG ("Rewriting tag entries offsets");
1075
1076   reader = (GstByteReader *) writer;
1077
1078   if (num_tags == -1) {
1079     if (byte_order == G_LITTLE_ENDIAN) {
1080       gst_byte_reader_get_uint16_le (reader, &aux);
1081     } else {
1082       gst_byte_reader_get_uint16_be (reader, &aux);
1083     }
1084     if (aux == G_MAXUINT16) {
1085       GST_WARNING ("Failed to read number of tags, won't rewrite offsets");
1086       return;
1087     }
1088     num_tags = (gint) aux;
1089   }
1090
1091   g_return_if_fail (num_tags != -1);
1092
1093   GST_DEBUG ("number of tags %d", num_tags);
1094
1095   for (i = 0; i < num_tags; i++) {
1096     guint16 type = 0;
1097     guint32 cur_offset = 0;
1098     gint byte_size = 0;
1099     guint32 count = 0;
1100     guint16 tag_id = 0;
1101
1102     g_assert (gst_byte_writer_get_pos (writer) <
1103         gst_byte_writer_get_size (writer));
1104
1105     /* read the type */
1106     if (byte_order == G_LITTLE_ENDIAN) {
1107       if (!gst_byte_reader_get_uint16_le (reader, &tag_id))
1108         break;
1109       if (!gst_byte_reader_get_uint16_le (reader, &type))
1110         break;
1111       if (!gst_byte_reader_get_uint32_le (reader, &count))
1112         break;
1113     } else {
1114       if (!gst_byte_reader_get_uint16_be (reader, &tag_id))
1115         break;
1116       if (!gst_byte_reader_get_uint16_be (reader, &type))
1117         break;
1118       if (!gst_byte_reader_get_uint32_be (reader, &count))
1119         break;
1120     }
1121
1122     GST_LOG ("Parsed tag %x of type %u and count %u", tag_id, type, count);
1123
1124     switch (type) {
1125       case EXIF_TYPE_BYTE:
1126       case EXIF_TYPE_ASCII:
1127       case EXIF_TYPE_UNDEFINED:
1128         byte_size = count;
1129         break;
1130       case EXIF_TYPE_SHORT:
1131         byte_size = count * 2;  /* 2 bytes */
1132         break;
1133       case EXIF_TYPE_LONG:
1134       case EXIF_TYPE_SLONG:
1135         byte_size = count * 4;  /* 4 bytes */
1136         break;
1137       case EXIF_TYPE_RATIONAL:
1138       case EXIF_TYPE_SRATIONAL:
1139         byte_size = count * 8;  /* 8 bytes */
1140         break;
1141       default:
1142         g_assert_not_reached ();
1143         break;
1144     }
1145
1146     /* adjust the offset if needed */
1147     if (byte_size > 4 || tag_id == EXIF_GPS_IFD_TAG || tag_id == EXIF_IFD_TAG) {
1148       if (byte_order == G_LITTLE_ENDIAN) {
1149         if (gst_byte_reader_peek_uint32_le (reader, &cur_offset)) {
1150           gst_byte_writer_put_uint32_le (writer, cur_offset + offset);
1151         }
1152       } else {
1153         if (gst_byte_reader_peek_uint32_be (reader, &cur_offset)) {
1154           gst_byte_writer_put_uint32_be (writer, cur_offset + offset);
1155         }
1156       }
1157       GST_DEBUG ("Rewriting tag offset from %u to (%u + %u) %u",
1158           cur_offset, cur_offset, offset, cur_offset + offset);
1159
1160       if ((tag_id == EXIF_GPS_IFD_TAG || tag_id == EXIF_IFD_TAG) &&
1161           inner_ifds_data != NULL) {
1162         /* needs special handling */
1163         if (!gst_byte_writer_set_pos (inner_ifds_data, cur_offset)) {
1164           GST_WARNING ("Failed to position writer to rewrite inner ifd "
1165               "offsets");
1166           continue;
1167         }
1168
1169         gst_exif_tag_rewrite_offsets (inner_ifds_data, byte_order, offset, -1,
1170             NULL);
1171       }
1172     } else {
1173       gst_byte_reader_skip (reader, 4);
1174       GST_DEBUG ("No need to rewrite tag offset");
1175     }
1176   }
1177   GST_LOG ("Done rewriting offsets");
1178 }
1179
1180 static void
1181 parse_exif_ascii_tag (GstExifReader * reader, const GstExifTagMatch * tag,
1182     guint32 count, guint32 offset, const guint8 * offset_as_data)
1183 {
1184   GType tagtype;
1185   gchar *str;
1186   gchar *utfstr;
1187   guint32 real_offset;
1188   GError *error = NULL;
1189
1190   if (count > 4) {
1191     guint8 *data;
1192     gsize size;
1193
1194     if (offset < reader->base_offset) {
1195       GST_WARNING ("Offset is smaller (%u) than base offset (%u)", offset,
1196           reader->base_offset);
1197       return;
1198     }
1199
1200     real_offset = offset - reader->base_offset;
1201
1202     data = gst_buffer_map (reader->buffer, &size, NULL, GST_MAP_READ);
1203     if (real_offset >= size) {
1204       GST_WARNING ("Invalid offset %u for buffer of size %" G_GSIZE_FORMAT
1205           ", not adding tag %s", real_offset, size, tag->gst_tag);
1206       gst_buffer_unmap (reader->buffer, data, size);
1207       return;
1208     }
1209
1210     str = g_strndup ((gchar *) (data + real_offset), count);
1211     gst_buffer_unmap (reader->buffer, data, size);
1212   } else {
1213     str = g_strndup ((gchar *) offset_as_data, count);
1214   }
1215
1216   /* convert from ascii to utf8 */
1217   if (g_utf8_validate (str, -1, NULL)) {
1218     GST_DEBUG ("Exif string is already on utf8: %s", str);
1219     utfstr = str;
1220   } else {
1221     GST_DEBUG ("Exif string isn't utf8, trying to convert from latin1: %s",
1222         str);
1223     utfstr = g_convert (str, count, "utf8", "latin1", NULL, NULL, &error);
1224     g_free (str);
1225     if (error) {
1226       GST_WARNING ("Skipping tag %d:%s. Failed to convert ascii string "
1227           "to utf8 : %s - %s", tag->exif_tag, tag->gst_tag, str,
1228           error->message);
1229       g_error_free (error);
1230       g_free (utfstr);
1231       return;
1232     }
1233   }
1234
1235   tagtype = gst_tag_get_type (tag->gst_tag);
1236   if (tagtype == GST_TYPE_DATE_TIME) {
1237     gint year = 0, month = 1, day = 1, hour = 0, minute = 0, second = 0;
1238
1239     if (sscanf (utfstr, "%04d:%02d:%02d %02d:%02d:%02d", &year, &month, &day,
1240             &hour, &minute, &second) > 0) {
1241       GstDateTime *d;
1242
1243       d = gst_date_time_new_local_time (year, month, day, hour, minute, second);
1244       gst_tag_list_add (reader->taglist, GST_TAG_MERGE_REPLACE,
1245           tag->gst_tag, d, NULL);
1246       gst_date_time_unref (d);
1247     } else {
1248       GST_WARNING ("Failed to parse %s into a datetime tag", str);
1249     }
1250   } else if (tagtype == G_TYPE_STRING) {
1251     gst_tag_list_add (reader->taglist, GST_TAG_MERGE_REPLACE, tag->gst_tag,
1252         utfstr, NULL);
1253   } else {
1254     GST_WARNING ("No parsing function associated to %x(%s)", tag->exif_tag,
1255         tag->gst_tag);
1256   }
1257   g_free (utfstr);
1258 }
1259
1260 static void
1261 parse_exif_long_tag (GstExifReader * reader, const GstExifTagMatch * tag,
1262     guint32 count, guint32 offset, const guint8 * offset_as_data)
1263 {
1264   GType tagtype;
1265
1266   if (count > 1) {
1267     GST_WARNING ("Long tags with more than one value are not supported");
1268     return;
1269   }
1270
1271   tagtype = gst_tag_get_type (tag->gst_tag);
1272   if (tagtype == G_TYPE_INT) {
1273     gst_tag_list_add (reader->taglist, GST_TAG_MERGE_REPLACE, tag->gst_tag,
1274         offset, NULL);
1275   } else {
1276     GST_WARNING ("No parsing function associated to %x(%s)", tag->exif_tag,
1277         tag->gst_tag);
1278   }
1279 }
1280
1281
1282 static void
1283 parse_exif_undefined_tag (GstExifReader * reader, const GstExifTagMatch * tag,
1284     guint32 count, guint32 offset, const guint8 * offset_as_data)
1285 {
1286   GType tagtype;
1287   guint8 *data;
1288   guint32 real_offset;
1289
1290   if (count > 4) {
1291     guint8 *bdata;
1292     gsize bsize;
1293
1294     if (offset < reader->base_offset) {
1295       GST_WARNING ("Offset is smaller (%u) than base offset (%u)", offset,
1296           reader->base_offset);
1297       return;
1298     }
1299
1300     real_offset = offset - reader->base_offset;
1301
1302     bdata = gst_buffer_map (reader->buffer, &bsize, NULL, GST_MAP_READ);
1303
1304     if (real_offset >= bsize) {
1305       GST_WARNING ("Invalid offset %u for buffer of size %" G_GSIZE_FORMAT
1306           ", not adding tag %s", real_offset, bsize, tag->gst_tag);
1307       gst_buffer_unmap (reader->buffer, bdata, bsize);
1308       return;
1309     }
1310
1311     /* +1 because it could be a string without the \0 */
1312     data = malloc (sizeof (guint8) * count + 1);
1313     memcpy (data, bdata + real_offset, count);
1314     data[count] = 0;
1315
1316     gst_buffer_unmap (reader->buffer, bdata, bsize);
1317   } else {
1318     data = malloc (sizeof (guint8) * count + 1);
1319     memcpy (data, (guint8 *) offset_as_data, count);
1320     data[count] = 0;
1321   }
1322
1323   tagtype = gst_tag_get_type (tag->gst_tag);
1324   if (tagtype == GST_TYPE_BUFFER) {
1325     GstBuffer *buf;
1326
1327     buf = gst_buffer_new ();
1328     gst_buffer_take_memory (buf, -1,
1329         gst_memory_new_wrapped (0, data, g_free, count, 0, count));
1330     data = NULL;
1331
1332     gst_tag_list_add (reader->taglist, GST_TAG_MERGE_APPEND, tag->gst_tag,
1333         buf, NULL);
1334
1335     gst_buffer_unref (buf);
1336   } else if (tagtype == G_TYPE_STRING) {
1337     gst_tag_list_add (reader->taglist, GST_TAG_MERGE_REPLACE, tag->gst_tag,
1338         data, NULL);
1339   } else {
1340     GST_WARNING ("No parsing function associated to %x(%s)", tag->exif_tag,
1341         tag->gst_tag);
1342   }
1343   g_free (data);
1344 }
1345
1346 static gboolean
1347 exif_reader_read_rational_tag (GstExifReader * exif_reader,
1348     guint32 count, guint32 offset, gboolean is_signed,
1349     gint32 * _frac_n, gint32 * _frac_d)
1350 {
1351   GstByteReader data_reader;
1352   guint32 real_offset;
1353   gint32 frac_n = 0;
1354   gint32 frac_d = 0;
1355   guint8 *data;
1356   gsize size;
1357
1358   if (count > 1) {
1359     GST_WARNING ("Rationals with multiple entries are not supported");
1360   }
1361   if (offset < exif_reader->base_offset) {
1362     GST_WARNING ("Offset is smaller (%u) than base offset (%u)", offset,
1363         exif_reader->base_offset);
1364     return FALSE;
1365   }
1366
1367   real_offset = offset - exif_reader->base_offset;
1368
1369   data = gst_buffer_map (exif_reader->buffer, &size, NULL, GST_MAP_READ);
1370
1371   if (real_offset >= size) {
1372     GST_WARNING ("Invalid offset %u for buffer of size %" G_GSIZE_FORMAT,
1373         real_offset, size);
1374     goto reader_fail;
1375   }
1376
1377   gst_byte_reader_init (&data_reader, data, size);
1378   if (!gst_byte_reader_set_pos (&data_reader, real_offset))
1379     goto reader_fail;
1380
1381   if (!is_signed) {
1382     guint32 aux_n = 0, aux_d = 0;
1383     if (exif_reader->byte_order == G_LITTLE_ENDIAN) {
1384       if (!gst_byte_reader_get_uint32_le (&data_reader, &aux_n) ||
1385           !gst_byte_reader_get_uint32_le (&data_reader, &aux_d))
1386         goto reader_fail;
1387     } else {
1388       if (!gst_byte_reader_get_uint32_be (&data_reader, &aux_n) ||
1389           !gst_byte_reader_get_uint32_be (&data_reader, &aux_d))
1390         goto reader_fail;
1391     }
1392     frac_n = (gint32) aux_n;
1393     frac_d = (gint32) aux_d;
1394   } else {
1395     if (exif_reader->byte_order == G_LITTLE_ENDIAN) {
1396       if (!gst_byte_reader_get_int32_le (&data_reader, &frac_n) ||
1397           !gst_byte_reader_get_int32_le (&data_reader, &frac_d))
1398         goto reader_fail;
1399     } else {
1400       if (!gst_byte_reader_get_int32_be (&data_reader, &frac_n) ||
1401           !gst_byte_reader_get_int32_be (&data_reader, &frac_d))
1402         goto reader_fail;
1403     }
1404   }
1405
1406   if (_frac_n)
1407     *_frac_n = frac_n;
1408   if (_frac_d)
1409     *_frac_d = frac_d;
1410
1411   gst_buffer_unmap (exif_reader->buffer, data, size);
1412
1413   return TRUE;
1414
1415 reader_fail:
1416   GST_WARNING ("Failed to read from byte reader. (Buffer too short?)");
1417   gst_buffer_unmap (exif_reader->buffer, data, size);
1418   return FALSE;
1419 }
1420
1421 static void
1422 parse_exif_rational_tag (GstExifReader * exif_reader,
1423     const gchar * gst_tag, guint32 count, guint32 offset, gdouble multiplier,
1424     gboolean is_signed)
1425 {
1426   GType type;
1427   gint32 frac_n = 0;
1428   gint32 frac_d = 1;
1429   gdouble value;
1430
1431   GST_DEBUG ("Reading fraction for tag %s...", gst_tag);
1432   if (!exif_reader_read_rational_tag (exif_reader, count, offset, is_signed,
1433           &frac_n, &frac_d))
1434     return;
1435   GST_DEBUG ("Read fraction for tag %s: %d/%d", gst_tag, frac_n, frac_d);
1436
1437   type = gst_tag_get_type (gst_tag);
1438   switch (type) {
1439     case G_TYPE_DOUBLE:
1440       gst_util_fraction_to_double (frac_n, frac_d, &value);
1441       value *= multiplier;
1442       GST_DEBUG ("Adding %s tag: %lf", gst_tag, value);
1443       gst_tag_list_add (exif_reader->taglist, GST_TAG_MERGE_REPLACE, gst_tag,
1444           value, NULL);
1445       break;
1446     default:
1447       if (type == GST_TYPE_FRACTION) {
1448         GValue fraction = { 0 };
1449
1450         g_value_init (&fraction, GST_TYPE_FRACTION);
1451         gst_value_set_fraction (&fraction, frac_n * multiplier, frac_d);
1452         gst_tag_list_add_value (exif_reader->taglist, GST_TAG_MERGE_REPLACE,
1453             gst_tag, &fraction);
1454         g_value_unset (&fraction);
1455       } else {
1456         GST_WARNING ("Can't convert from fraction into %s", g_type_name (type));
1457       }
1458   }
1459
1460 }
1461
1462 static GstBuffer *
1463 write_exif_ifd (const GstTagList * taglist, guint byte_order,
1464     guint32 base_offset, const GstExifTagMatch * tag_map)
1465 {
1466   GstExifWriter writer;
1467   gint i;
1468
1469   GST_DEBUG ("Formatting taglist %p as exif buffer. Byte order: %d, "
1470       "base_offset: %u", taglist, byte_order, base_offset);
1471
1472   g_assert (byte_order == G_LITTLE_ENDIAN || byte_order == G_BIG_ENDIAN);
1473
1474   if (!gst_tag_list_has_ifd_tags (taglist, tag_map)) {
1475     GST_DEBUG ("No tags for this ifd");
1476     return NULL;
1477   }
1478
1479   gst_exif_writer_init (&writer, byte_order);
1480
1481   /* write tag number as 0 */
1482   gst_byte_writer_put_uint16_le (&writer.tagwriter, 0);
1483
1484   /* write both tag headers and data
1485    * in ascending id order */
1486
1487   for (i = 0; tag_map[i].exif_tag != 0; i++) {
1488
1489     /* special cases have NULL gst tag */
1490     if (tag_map[i].gst_tag == NULL) {
1491       GstBuffer *inner_ifd = NULL;
1492       const GstExifTagMatch *inner_tag_map = NULL;
1493
1494       GST_LOG ("Inner ifd tag: %x", tag_map[i].exif_tag);
1495
1496       if (tag_map[i].exif_tag == EXIF_GPS_IFD_TAG) {
1497         inner_tag_map = tag_map_gps;
1498       } else if (tag_map[i].exif_tag == EXIF_IFD_TAG) {
1499         inner_tag_map = tag_map_exif;
1500       } else if (tag_map[i].exif_tag == EXIF_VERSION_TAG) {
1501         /* special case where we write the exif version */
1502         write_exif_undefined_tag (&writer, EXIF_VERSION_TAG, (guint8 *) "0230",
1503             4);
1504       } else if (tag_map[i].exif_tag == EXIF_FLASHPIX_VERSION_TAG) {
1505         /* special case where we write the flashpix version */
1506         write_exif_undefined_tag (&writer, EXIF_FLASHPIX_VERSION_TAG,
1507             (guint8 *) "0100", 4);
1508       }
1509
1510       if (inner_tag_map) {
1511         /* base offset and tagheader size are added when rewriting offset */
1512         inner_ifd = write_exif_ifd (taglist, byte_order,
1513             gst_byte_writer_get_size (&writer.datawriter), inner_tag_map);
1514       }
1515
1516       if (inner_ifd) {
1517         guint8 *data;
1518         gsize size;
1519
1520         GST_DEBUG ("Adding inner ifd: %x", tag_map[i].exif_tag);
1521         gst_exif_writer_write_tag_header (&writer, tag_map[i].exif_tag,
1522             EXIF_TYPE_LONG, 1,
1523             gst_byte_writer_get_size (&writer.datawriter), NULL);
1524
1525         data = gst_buffer_map (inner_ifd, &size, NULL, GST_MAP_READ);
1526         gst_byte_writer_put_data (&writer.datawriter, data, size);
1527         gst_buffer_unmap (inner_ifd, data, size);
1528         gst_buffer_unref (inner_ifd);
1529       }
1530       continue;
1531     }
1532
1533     GST_LOG ("Checking tag %s", tag_map[i].gst_tag);
1534     if (gst_tag_list_get_value_index (taglist, tag_map[i].gst_tag, 0) == NULL)
1535       continue;
1536
1537     write_exif_tag_from_taglist (&writer, taglist, &tag_map[i]);
1538   }
1539
1540   /* Add the next IFD offset, we just set it to 0 because
1541    * there is no easy way to predict what it is going to be.
1542    * The user might rewrite the value if needed */
1543   gst_byte_writer_put_uint32_le (&writer.tagwriter, 0);
1544
1545   /* write the number of tags */
1546   gst_byte_writer_set_pos (&writer.tagwriter, 0);
1547   if (writer.byte_order == G_LITTLE_ENDIAN)
1548     gst_byte_writer_put_uint16_le (&writer.tagwriter, writer.tags_total);
1549   else
1550     gst_byte_writer_put_uint16_be (&writer.tagwriter, writer.tags_total);
1551
1552   GST_DEBUG ("Number of tags rewritten to %d", writer.tags_total);
1553
1554   /* now that we know the tag headers size, we can add the offsets */
1555   gst_exif_tag_rewrite_offsets (&writer.tagwriter, writer.byte_order,
1556       base_offset + gst_byte_writer_get_size (&writer.tagwriter),
1557       writer.tags_total, &writer.datawriter);
1558
1559   return gst_exif_writer_reset_and_get_buffer (&writer);
1560 }
1561
1562 static gboolean
1563 parse_exif_tag_header (GstByteReader * reader, gint byte_order,
1564     GstExifTagData * _tagdata)
1565 {
1566   g_assert (_tagdata);
1567
1568   /* read the fields */
1569   if (byte_order == G_LITTLE_ENDIAN) {
1570     if (!gst_byte_reader_get_uint16_le (reader, &_tagdata->tag) ||
1571         !gst_byte_reader_get_uint16_le (reader, &_tagdata->tag_type) ||
1572         !gst_byte_reader_get_uint32_le (reader, &_tagdata->count) ||
1573         !gst_byte_reader_get_data (reader, 4, &_tagdata->offset_as_data)) {
1574       return FALSE;
1575     }
1576     _tagdata->offset = GST_READ_UINT32_LE (_tagdata->offset_as_data);
1577   } else {
1578     if (!gst_byte_reader_get_uint16_be (reader, &_tagdata->tag) ||
1579         !gst_byte_reader_get_uint16_be (reader, &_tagdata->tag_type) ||
1580         !gst_byte_reader_get_uint32_be (reader, &_tagdata->count) ||
1581         !gst_byte_reader_get_data (reader, 4, &_tagdata->offset_as_data)) {
1582       return FALSE;
1583     }
1584     _tagdata->offset = GST_READ_UINT32_BE (_tagdata->offset_as_data);
1585   }
1586
1587   return TRUE;
1588 }
1589
1590 static gboolean
1591 parse_exif_ifd (GstExifReader * exif_reader, gint buf_offset,
1592     const GstExifTagMatch * tag_map)
1593 {
1594   GstByteReader reader;
1595   guint16 entries = 0;
1596   guint16 i;
1597   guint8 *data;
1598   gsize size;
1599
1600   g_return_val_if_fail (exif_reader->byte_order == G_LITTLE_ENDIAN
1601       || exif_reader->byte_order == G_BIG_ENDIAN, FALSE);
1602
1603   data = gst_buffer_map (exif_reader->buffer, &size, NULL, GST_MAP_READ);
1604
1605   gst_byte_reader_init (&reader, data, size);
1606   if (!gst_byte_reader_set_pos (&reader, buf_offset))
1607     goto invalid_offset;
1608
1609   /* read the IFD entries number */
1610   if (exif_reader->byte_order == G_LITTLE_ENDIAN) {
1611     if (!gst_byte_reader_get_uint16_le (&reader, &entries))
1612       goto read_error;
1613   } else {
1614     if (!gst_byte_reader_get_uint16_be (&reader, &entries))
1615       goto read_error;
1616   }
1617   GST_DEBUG ("Read number of entries: %u", entries);
1618
1619   /* iterate over the buffer and find the tags and stuff */
1620   for (i = 0; i < entries; i++) {
1621     GstExifTagData tagdata;
1622     gint map_index;
1623
1624     GST_LOG ("Reading entry: %u", i);
1625
1626     if (!parse_exif_tag_header (&reader, exif_reader->byte_order, &tagdata))
1627       goto read_error;
1628
1629     GST_DEBUG ("Parsed tag: id 0x%x, type %u, count %u, offset %u (0x%x)"
1630         ", buf size: %u", tagdata.tag, tagdata.tag_type, tagdata.count,
1631         tagdata.offset, tagdata.offset, gst_byte_reader_get_size (&reader));
1632
1633     map_index = exif_tag_map_find_reverse (tagdata.tag, tag_map, TRUE);
1634     if (map_index == -1) {
1635       GST_WARNING ("Unmapped exif tag: 0x%x", tagdata.tag);
1636       continue;
1637     }
1638
1639     /*
1640      * inner ifd tags handling, errors processing those are being ignored
1641      * and we try to continue the parsing
1642      */
1643     if (tagdata.tag == EXIF_GPS_IFD_TAG) {
1644       parse_exif_ifd (exif_reader,
1645           tagdata.offset - exif_reader->base_offset, tag_map_gps);
1646
1647       continue;
1648     }
1649     if (tagdata.tag == EXIF_IFD_TAG) {
1650       parse_exif_ifd (exif_reader,
1651           tagdata.offset - exif_reader->base_offset, tag_map_exif);
1652
1653       continue;
1654     }
1655     if (tagdata.tag == EXIF_VERSION_TAG ||
1656         tagdata.tag == EXIF_FLASHPIX_VERSION_TAG) {
1657       /* skip */
1658       continue;
1659     }
1660
1661     /* tags that need specialized deserialization */
1662     if (tag_map[map_index].deserialize) {
1663       i += tag_map[map_index].deserialize (exif_reader, &reader,
1664           &tag_map[map_index], &tagdata);
1665       continue;
1666     }
1667
1668     switch (tagdata.tag_type) {
1669       case EXIF_TYPE_ASCII:
1670         parse_exif_ascii_tag (exif_reader, &tag_map[map_index],
1671             tagdata.count, tagdata.offset, tagdata.offset_as_data);
1672         break;
1673       case EXIF_TYPE_RATIONAL:
1674         parse_exif_rational_tag (exif_reader, tag_map[map_index].gst_tag,
1675             tagdata.count, tagdata.offset, 1, FALSE);
1676         break;
1677       case EXIF_TYPE_SRATIONAL:
1678         parse_exif_rational_tag (exif_reader, tag_map[map_index].gst_tag,
1679             tagdata.count, tagdata.offset, 1, TRUE);
1680         break;
1681       case EXIF_TYPE_UNDEFINED:
1682         parse_exif_undefined_tag (exif_reader, &tag_map[map_index],
1683             tagdata.count, tagdata.offset, tagdata.offset_as_data);
1684       case EXIF_TYPE_LONG:
1685         parse_exif_long_tag (exif_reader, &tag_map[map_index],
1686             tagdata.count, tagdata.offset, tagdata.offset_as_data);
1687         break;
1688       default:
1689         GST_WARNING ("Unhandled tag type: %u", tagdata.tag_type);
1690         break;
1691     }
1692   }
1693
1694   /* check if the pending tags have something that can still be added */
1695   {
1696     GSList *walker;
1697     GstExifTagData *data;
1698
1699     for (walker = exif_reader->pending_tags; walker;
1700         walker = g_slist_next (walker)) {
1701       data = (GstExifTagData *) walker->data;
1702       switch (data->tag) {
1703         case EXIF_TAG_XRESOLUTION:
1704           parse_exif_rational_tag (exif_reader, GST_TAG_IMAGE_HORIZONTAL_PPI,
1705               data->count, data->offset, 1, FALSE);
1706           break;
1707         case EXIF_TAG_YRESOLUTION:
1708           parse_exif_rational_tag (exif_reader, GST_TAG_IMAGE_VERTICAL_PPI,
1709               data->count, data->offset, 1, FALSE);
1710           break;
1711         default:
1712           /* NOP */
1713           break;
1714       }
1715     }
1716   }
1717   gst_buffer_unmap (exif_reader->buffer, data, size);
1718
1719   return TRUE;
1720
1721 invalid_offset:
1722   {
1723     GST_WARNING ("Buffer offset invalid when parsing exif ifd");
1724     gst_buffer_unmap (exif_reader->buffer, data, size);
1725     return FALSE;
1726   }
1727 read_error:
1728   {
1729     GST_WARNING ("Failed to parse the exif ifd");
1730     gst_buffer_unmap (exif_reader->buffer, data, size);
1731     return FALSE;
1732   }
1733 }
1734
1735 /**
1736  * gst_tag_list_to_exif_buffer:
1737  * @taglist: The taglist
1738  * @byte_order: byte order used in writing (G_LITTLE_ENDIAN or G_BIG_ENDIAN)
1739  * @base_offset: Offset from the tiff header first byte
1740  *
1741  * Formats the tags in taglist on exif format. The resulting buffer contains
1742  * the tags IFD and is followed by the data pointed by the tag entries.
1743  *
1744  * Returns: A GstBuffer containing the tag entries followed by the tag data
1745  *
1746  * Since: 0.10.30
1747  */
1748 GstBuffer *
1749 gst_tag_list_to_exif_buffer (const GstTagList * taglist, gint byte_order,
1750     guint32 base_offset)
1751 {
1752   return write_exif_ifd (taglist, byte_order, base_offset, tag_map_ifd0);
1753 }
1754
1755 /**
1756  * gst_tag_list_to_exif_buffer_with_tiff_header:
1757  * @taglist: The taglist
1758  *
1759  * Formats the tags in taglist into exif structure, a tiff header
1760  * is put in the beginning of the buffer.
1761  *
1762  * Returns: A GstBuffer containing the data
1763  *
1764  * Since: 0.10.30
1765  */
1766 GstBuffer *
1767 gst_tag_list_to_exif_buffer_with_tiff_header (const GstTagList * taglist)
1768 {
1769   GstBuffer *ifd;
1770   GstByteWriter writer;
1771   gsize size;
1772   guint8 *data;
1773
1774   ifd = gst_tag_list_to_exif_buffer (taglist, G_BYTE_ORDER, 8);
1775   if (ifd == NULL) {
1776     GST_WARNING ("Failed to create exif buffer");
1777     return NULL;
1778   }
1779
1780   data = gst_buffer_map (ifd, &size, NULL, GST_MAP_READ);
1781
1782   size += TIFF_HEADER_SIZE;
1783
1784   /* TODO what is the correct endianness here? */
1785   gst_byte_writer_init_with_size (&writer, size, FALSE);
1786   /* TIFF header */
1787   if (G_BYTE_ORDER == G_LITTLE_ENDIAN) {
1788     gst_byte_writer_put_uint16_le (&writer, TIFF_LITTLE_ENDIAN);
1789     gst_byte_writer_put_uint16_le (&writer, 42);
1790     gst_byte_writer_put_uint32_le (&writer, 8);
1791   } else {
1792     gst_byte_writer_put_uint16_be (&writer, TIFF_BIG_ENDIAN);
1793     gst_byte_writer_put_uint16_be (&writer, 42);
1794     gst_byte_writer_put_uint32_be (&writer, 8);
1795   }
1796   if (!gst_byte_writer_put_data (&writer, data, size)) {
1797     GST_WARNING ("Byte writer size mismatch");
1798     /* reaching here is a programming error because we should have a buffer
1799      * large enough */
1800     g_assert_not_reached ();
1801     gst_buffer_unmap (ifd, data, size);
1802     gst_buffer_unref (ifd);
1803     gst_byte_writer_reset (&writer);
1804     return NULL;
1805   }
1806   gst_buffer_unmap (ifd, data, size);
1807   gst_buffer_unref (ifd);
1808
1809   return gst_byte_writer_reset_and_get_buffer (&writer);
1810 }
1811
1812 /**
1813  * gst_tag_list_from_exif_buffer:
1814  * @buffer: The exif buffer
1815  * @byte_order: byte order of the data
1816  * @base_offset: Offset from the tiff header to this buffer
1817  *
1818  * Parses the IFD and IFD tags data contained in the buffer and puts it
1819  * on a taglist. The base_offset is used to subtract from the offset in
1820  * the tag entries and be able to get the offset relative to the buffer
1821  * start
1822  *
1823  * Returns: The parsed taglist
1824  *
1825  * Since: 0.10.30
1826  */
1827 GstTagList *
1828 gst_tag_list_from_exif_buffer (GstBuffer * buffer, gint byte_order,
1829     guint32 base_offset)
1830 {
1831   GstExifReader reader;
1832   g_return_val_if_fail (byte_order == G_LITTLE_ENDIAN
1833       || byte_order == G_BIG_ENDIAN, NULL);
1834
1835   gst_exif_reader_init (&reader, byte_order, buffer, base_offset);
1836
1837   if (!parse_exif_ifd (&reader, 0, tag_map_ifd0))
1838     goto read_error;
1839
1840   return gst_exif_reader_reset (&reader, TRUE);
1841
1842 read_error:
1843   {
1844     gst_exif_reader_reset (&reader, FALSE);
1845     GST_WARNING ("Failed to parse the exif buffer");
1846     return NULL;
1847   }
1848 }
1849
1850 /**
1851  * gst_tag_list_from_exif_buffer_with_tiff_header:
1852  * @buffer: The exif buffer
1853  *
1854  * Parses the exif tags starting with a tiff header structure.
1855  *
1856  * Returns: The taglist
1857  *
1858  * Since: 0.10.30
1859  */
1860 GstTagList *
1861 gst_tag_list_from_exif_buffer_with_tiff_header (GstBuffer * buffer)
1862 {
1863   GstByteReader reader;
1864   guint16 fortytwo = 42;
1865   guint16 endianness = 0;
1866   guint32 offset;
1867   GstTagList *taglist = NULL;
1868   GstBuffer *subbuffer;
1869   guint8 *data, *sdata;
1870   gsize size, ssize;
1871
1872   data = gst_buffer_map (buffer, &size, NULL, GST_MAP_READ);
1873
1874   GST_LOG ("Parsing exif tags with tiff header of size %" G_GSIZE_FORMAT, size);
1875
1876   gst_byte_reader_init (&reader, data, size);
1877
1878   GST_LOG ("Parsing the tiff header");
1879   if (!gst_byte_reader_get_uint16_be (&reader, &endianness)) {
1880     goto byte_reader_fail;
1881   }
1882
1883   if (endianness == TIFF_LITTLE_ENDIAN) {
1884     if (!gst_byte_reader_get_uint16_le (&reader, &fortytwo) ||
1885         !gst_byte_reader_get_uint32_le (&reader, &offset))
1886       goto byte_reader_fail;
1887   } else if (endianness == TIFF_BIG_ENDIAN) {
1888     if (!gst_byte_reader_get_uint16_be (&reader, &fortytwo) ||
1889         !gst_byte_reader_get_uint32_be (&reader, &offset))
1890       goto byte_reader_fail;
1891   } else
1892     goto invalid_endianness;
1893
1894   if (fortytwo != 42)
1895     goto invalid_magic;
1896
1897   subbuffer = gst_buffer_new_and_alloc (size - (TIFF_HEADER_SIZE - 2));
1898
1899   sdata = gst_buffer_map (subbuffer, &ssize, NULL, GST_MAP_WRITE);
1900   memcpy (sdata, data + TIFF_HEADER_SIZE, size - TIFF_HEADER_SIZE);
1901   gst_buffer_unmap (subbuffer, sdata, ssize);
1902
1903   taglist = gst_tag_list_from_exif_buffer (subbuffer,
1904       endianness == TIFF_LITTLE_ENDIAN ? G_LITTLE_ENDIAN : G_BIG_ENDIAN, 8);
1905
1906   gst_buffer_unref (subbuffer);
1907
1908 done:
1909   gst_buffer_unmap (buffer, data, size);
1910
1911   return taglist;
1912
1913 byte_reader_fail:
1914   {
1915     GST_WARNING ("Failed to read values from buffer");
1916     goto done;
1917   }
1918 invalid_endianness:
1919   {
1920     GST_WARNING ("Invalid endianness number %u", endianness);
1921     goto done;
1922   }
1923 invalid_magic:
1924   {
1925     GST_WARNING ("Invalid magic number %u, should be 42", fortytwo);
1926     goto done;
1927   }
1928 }
1929
1930 /* special serialization functions */
1931 EXIF_SERIALIZATION_DESERIALIZATION_MAP_STRING_TO_INT_FUNC (contrast,
1932     capturing_contrast);
1933 EXIF_SERIALIZATION_DESERIALIZATION_MAP_STRING_TO_INT_FUNC (exposure_mode,
1934     capturing_exposure_mode);
1935 EXIF_SERIALIZATION_DESERIALIZATION_MAP_STRING_TO_INT_FUNC (exposure_program,
1936     capturing_exposure_program);
1937 EXIF_SERIALIZATION_DESERIALIZATION_MAP_STRING_TO_INT_FUNC (gain_control,
1938     capturing_gain_adjustment);
1939 EXIF_SERIALIZATION_DESERIALIZATION_MAP_STRING_TO_INT_FUNC (metering_mode,
1940     capturing_metering_mode);
1941 EXIF_SERIALIZATION_DESERIALIZATION_MAP_STRING_TO_INT_FUNC (orientation,
1942     image_orientation);
1943 EXIF_SERIALIZATION_DESERIALIZATION_MAP_STRING_TO_INT_FUNC (saturation,
1944     capturing_saturation);
1945 EXIF_SERIALIZATION_DESERIALIZATION_MAP_STRING_TO_INT_FUNC (scene_capture_type,
1946     capturing_scene_capture_type);
1947 EXIF_SERIALIZATION_DESERIALIZATION_MAP_STRING_TO_INT_FUNC (sharpness,
1948     capturing_sharpness);
1949 EXIF_SERIALIZATION_DESERIALIZATION_MAP_STRING_TO_INT_FUNC (source,
1950     capturing_source);
1951 EXIF_SERIALIZATION_DESERIALIZATION_MAP_STRING_TO_INT_FUNC (white_balance,
1952     capturing_white_balance);
1953
1954 static void
1955 serialize_geo_coordinate (GstExifWriter * writer, const GstTagList * taglist,
1956     const GstExifTagMatch * exiftag)
1957 {
1958   gboolean latitude;
1959   gdouble value;
1960   gint degrees;
1961   gint minutes;
1962   gint seconds;
1963   guint32 offset;
1964
1965   latitude = exiftag->exif_tag == EXIF_TAG_GPS_LATITUDE;        /* exif tag for latitude */
1966   if (!gst_tag_list_get_double (taglist, exiftag->gst_tag, &value)) {
1967     GST_WARNING ("Failed to get double from tag list for tag: %s",
1968         exiftag->gst_tag);
1969     return;
1970   }
1971
1972   /* first write the Latitude or Longitude Ref */
1973   if (latitude) {
1974     if (value >= 0) {
1975       write_exif_ascii_tag (writer, exiftag->complementary_tag, "N");
1976     } else {
1977       value *= -1;
1978       write_exif_ascii_tag (writer, exiftag->complementary_tag, "S");
1979     }
1980   } else {
1981     if (value >= 0) {
1982       write_exif_ascii_tag (writer, exiftag->complementary_tag, "E");
1983     } else {
1984       value *= -1;
1985       write_exif_ascii_tag (writer, exiftag->complementary_tag, "W");
1986     }
1987   }
1988
1989   /* now write the degrees stuff */
1990   GST_LOG ("Converting geo location %lf to degrees", value);
1991   degrees = (gint) value;
1992   value -= degrees;
1993   minutes = (gint) (value * 60);
1994   value = (value * 60) - minutes;
1995   seconds = (gint) (value * 60);
1996   GST_LOG ("Converted geo location to %d.%d'%d'' degrees", degrees,
1997       minutes, seconds);
1998
1999   offset = gst_byte_writer_get_size (&writer->datawriter);
2000   gst_exif_writer_write_tag_header (writer, exiftag->exif_tag,
2001       EXIF_TYPE_RATIONAL, 3, offset, NULL);
2002   gst_exif_writer_write_rational_data (writer, degrees, 1);
2003   gst_exif_writer_write_rational_data (writer, minutes, 1);
2004   gst_exif_writer_write_rational_data (writer, seconds, 1);
2005 }
2006
2007 static gint
2008 deserialize_geo_coordinate (GstExifReader * exif_reader,
2009     GstByteReader * reader, const GstExifTagMatch * exiftag,
2010     GstExifTagData * tagdata)
2011 {
2012   GstByteReader fractions_reader;
2013   gint multiplier;
2014   GstExifTagData next_tagdata;
2015   gint ret = 0;
2016   /* for the conversion */
2017   guint32 degrees_n = 0;
2018   guint32 degrees_d = 1;
2019   guint32 minutes_n = 0;
2020   guint32 minutes_d = 1;
2021   guint32 seconds_n = 0;
2022   guint32 seconds_d = 1;
2023   gdouble degrees;
2024   gdouble minutes;
2025   gdouble seconds;
2026   guint8 *data = NULL;
2027   gsize size = 0;
2028
2029   GST_LOG ("Starting to parse %s tag in exif 0x%x", exiftag->gst_tag,
2030       exiftag->exif_tag);
2031
2032   if (exiftag->complementary_tag != tagdata->tag) {
2033     /* First should come the 'Ref' tags */
2034     GST_WARNING ("Tag %d is not the 'Ref' tag for latitude nor longitude",
2035         tagdata->tag);
2036     return ret;
2037   }
2038
2039   if (tagdata->offset_as_data[0] == 'N' || tagdata->offset_as_data[0] == 'E') {
2040     multiplier = 1;
2041   } else if (tagdata->offset_as_data[0] == 'S'
2042       || tagdata->offset_as_data[0] == 'W') {
2043     multiplier = -1;
2044   } else {
2045     GST_WARNING ("Invalid LatitudeRef or LongitudeRef %c",
2046         tagdata->offset_as_data[0]);
2047     return ret;
2048   }
2049
2050   /* now read the following tag that must be the latitude or longitude */
2051   if (exif_reader->byte_order == G_LITTLE_ENDIAN) {
2052     if (!gst_byte_reader_peek_uint16_le (reader, &next_tagdata.tag))
2053       goto reader_fail;
2054   } else {
2055     if (!gst_byte_reader_peek_uint16_be (reader, &next_tagdata.tag))
2056       goto reader_fail;
2057   }
2058
2059   if (exiftag->exif_tag != next_tagdata.tag) {
2060     GST_WARNING ("This is not a geo coordinate tag");
2061     return ret;
2062   }
2063
2064   /* read the remaining tag entry data */
2065   if (!parse_exif_tag_header (reader, exif_reader->byte_order, &next_tagdata)) {
2066     ret = -1;
2067     goto reader_fail;
2068   }
2069
2070   ret = 1;
2071
2072   /* some checking */
2073   if (next_tagdata.tag_type != EXIF_TYPE_RATIONAL) {
2074     GST_WARNING ("Invalid type %d for geo coordinate (latitude/longitude)",
2075         next_tagdata.tag_type);
2076     return ret;
2077   }
2078   if (next_tagdata.count != 3) {
2079     GST_WARNING ("Geo coordinate should use 3 fractions, we have %u",
2080         next_tagdata.count);
2081     return ret;
2082   }
2083
2084   data = gst_buffer_map (exif_reader->buffer, &size, NULL, GST_MAP_READ);
2085
2086   /* now parse the fractions */
2087   gst_byte_reader_init (&fractions_reader, data, size);
2088
2089   if (!gst_byte_reader_set_pos (&fractions_reader,
2090           next_tagdata.offset - exif_reader->base_offset))
2091     goto reader_fail;
2092
2093   if (exif_reader->byte_order == G_LITTLE_ENDIAN) {
2094     if (!gst_byte_reader_get_uint32_le (&fractions_reader, &degrees_n) ||
2095         !gst_byte_reader_get_uint32_le (&fractions_reader, &degrees_d) ||
2096         !gst_byte_reader_get_uint32_le (&fractions_reader, &minutes_n) ||
2097         !gst_byte_reader_get_uint32_le (&fractions_reader, &minutes_d) ||
2098         !gst_byte_reader_get_uint32_le (&fractions_reader, &seconds_n) ||
2099         !gst_byte_reader_get_uint32_le (&fractions_reader, &seconds_d))
2100       goto reader_fail;
2101   } else {
2102     if (!gst_byte_reader_get_uint32_be (&fractions_reader, &degrees_n) ||
2103         !gst_byte_reader_get_uint32_be (&fractions_reader, &degrees_d) ||
2104         !gst_byte_reader_get_uint32_be (&fractions_reader, &minutes_n) ||
2105         !gst_byte_reader_get_uint32_be (&fractions_reader, &minutes_d) ||
2106         !gst_byte_reader_get_uint32_be (&fractions_reader, &seconds_n) ||
2107         !gst_byte_reader_get_uint32_be (&fractions_reader, &seconds_d))
2108       goto reader_fail;
2109   }
2110   gst_buffer_unmap (exif_reader->buffer, data, size);
2111
2112   GST_DEBUG ("Read degrees fraction for tag %s: %u/%u %u/%u %u/%u",
2113       exiftag->gst_tag, degrees_n, degrees_d, minutes_n, minutes_d,
2114       seconds_n, seconds_d);
2115
2116   gst_util_fraction_to_double (degrees_n, degrees_d, &degrees);
2117   gst_util_fraction_to_double (minutes_n, minutes_d, &minutes);
2118   gst_util_fraction_to_double (seconds_n, seconds_d, &seconds);
2119
2120   minutes += seconds / 60;
2121   degrees += minutes / 60;
2122   degrees *= multiplier;
2123
2124   GST_DEBUG ("Adding %s tag: %lf", exiftag->gst_tag, degrees);
2125   gst_tag_list_add (exif_reader->taglist, GST_TAG_MERGE_REPLACE,
2126       exiftag->gst_tag, degrees, NULL);
2127
2128   return ret;
2129
2130 reader_fail:
2131   GST_WARNING ("Failed to read fields from buffer (too short?)");
2132   if (data)
2133     gst_buffer_unmap (exif_reader->buffer, data, size);
2134   return ret;
2135 }
2136
2137
2138 static void
2139 serialize_geo_direction (GstExifWriter * writer, const GstTagList * taglist,
2140     const GstExifTagMatch * exiftag)
2141 {
2142   gdouble value;
2143
2144   if (!gst_tag_list_get_double (taglist, exiftag->gst_tag, &value)) {
2145     GST_WARNING ("Failed to get double from tag list for tag: %s",
2146         exiftag->gst_tag);
2147     return;
2148   }
2149
2150   /* first write the direction ref */
2151   write_exif_ascii_tag (writer, exiftag->complementary_tag, "T");
2152   gst_exif_writer_write_rational_tag_from_double (writer,
2153       exiftag->exif_tag, value);
2154 }
2155
2156 static gint
2157 deserialize_geo_direction (GstExifReader * exif_reader,
2158     GstByteReader * reader, const GstExifTagMatch * exiftag,
2159     GstExifTagData * tagdata)
2160 {
2161   GstExifTagData next_tagdata = { 0, };
2162   gint ret = 0;
2163
2164   GST_LOG ("Starting to parse %s tag in exif 0x%x", exiftag->gst_tag,
2165       exiftag->exif_tag);
2166
2167   if (exiftag->complementary_tag == tagdata->tag) {
2168     /* First should come the 'Ref' tags */
2169     if (tagdata->offset_as_data[0] == 'M') {
2170       GST_WARNING ("Magnetic direction is not supported");
2171       return ret;
2172     } else if (tagdata->offset_as_data[0] == 'T') {
2173       /* nop */
2174     } else {
2175       GST_WARNING ("Invalid Ref for direction or track %c",
2176           tagdata->offset_as_data[0]);
2177       return ret;
2178     }
2179   } else {
2180     GST_DEBUG ("No Direction Ref, using default=T");
2181     if (tagdata->tag == exiftag->exif_tag) {
2182       /* this is the main tag */
2183       tagdata_copy (&next_tagdata, tagdata);
2184     }
2185   }
2186
2187   if (next_tagdata.tag == 0) {
2188     /* now read the following tag that must be the exif_tag */
2189     if (exif_reader->byte_order == G_LITTLE_ENDIAN) {
2190       if (!gst_byte_reader_peek_uint16_le (reader, &next_tagdata.tag))
2191         goto reader_fail;
2192     } else {
2193       if (!gst_byte_reader_peek_uint16_be (reader, &next_tagdata.tag))
2194         goto reader_fail;
2195     }
2196
2197     if (exiftag->exif_tag != next_tagdata.tag) {
2198       GST_WARNING ("Unexpected tag");
2199       return ret;
2200     }
2201
2202     /* read the remaining tag entry data */
2203     if (!parse_exif_tag_header (reader, exif_reader->byte_order, &next_tagdata)) {
2204       ret = -1;
2205       goto reader_fail;
2206     }
2207     ret = 1;
2208   }
2209
2210   /* some checking */
2211   if (next_tagdata.tag_type != EXIF_TYPE_RATIONAL) {
2212     GST_WARNING ("Invalid type %d for 0x%x", next_tagdata.tag_type,
2213         next_tagdata.tag);
2214     return ret;
2215   }
2216   if (next_tagdata.count != 1) {
2217     GST_WARNING ("0x%x tag must have a single fraction, we have %u",
2218         next_tagdata.tag_type, next_tagdata.count);
2219     return ret;
2220   }
2221
2222   parse_exif_rational_tag (exif_reader,
2223       exiftag->gst_tag, next_tagdata.count, next_tagdata.offset, 1, FALSE);
2224
2225   return ret;
2226
2227 reader_fail:
2228   GST_WARNING ("Failed to read fields from buffer (too short?)");
2229   return ret;
2230 }
2231
2232
2233 static void
2234 serialize_geo_elevation (GstExifWriter * writer, const GstTagList * taglist,
2235     const GstExifTagMatch * exiftag)
2236 {
2237   gdouble value;
2238
2239   if (!gst_tag_list_get_double (taglist, exiftag->gst_tag, &value)) {
2240     GST_WARNING ("Failed to get double from tag list for tag: %s",
2241         exiftag->gst_tag);
2242     return;
2243   }
2244
2245   /* first write the Ref */
2246   gst_exif_writer_write_byte_tag (writer,
2247       exiftag->complementary_tag, value >= 0 ? 0 : 1);
2248
2249   if (value < 0)
2250     value *= -1;
2251
2252   /* now the value */
2253   gst_exif_writer_write_rational_tag_from_double (writer,
2254       exiftag->exif_tag, value);
2255 }
2256
2257 static gint
2258 deserialize_geo_elevation (GstExifReader * exif_reader,
2259     GstByteReader * reader, const GstExifTagMatch * exiftag,
2260     GstExifTagData * tagdata)
2261 {
2262   GstExifTagData next_tagdata = { 0, };
2263   gint multiplier = 1;
2264   gint ret = 0;
2265
2266   GST_LOG ("Starting to parse %s tag in exif 0x%x", exiftag->gst_tag,
2267       exiftag->exif_tag);
2268
2269   if (exiftag->complementary_tag == tagdata->tag) {
2270     if (tagdata->offset_as_data[0] == 0) {
2271       /* NOP */
2272     } else if (tagdata->offset_as_data[0] == 1) {
2273       multiplier = -1;
2274     } else {
2275       GST_WARNING ("Invalid GPSAltitudeRef %u", tagdata->offset_as_data[0]);
2276       return ret;
2277     }
2278   } else {
2279     GST_DEBUG ("No GPSAltitudeRef, using default=0");
2280     if (tagdata->tag == exiftag->exif_tag) {
2281       tagdata_copy (&next_tagdata, tagdata);
2282     }
2283   }
2284
2285   /* now read the following tag that must be the exif_tag */
2286   if (next_tagdata.tag == 0) {
2287     if (exif_reader->byte_order == G_LITTLE_ENDIAN) {
2288       if (!gst_byte_reader_peek_uint16_le (reader, &next_tagdata.tag))
2289         goto reader_fail;
2290     } else {
2291       if (!gst_byte_reader_peek_uint16_be (reader, &next_tagdata.tag))
2292         goto reader_fail;
2293     }
2294
2295     if (exiftag->exif_tag != next_tagdata.tag) {
2296       GST_WARNING ("Unexpected tag");
2297       return ret;
2298     }
2299
2300     /* read the remaining tag entry data */
2301     if (!parse_exif_tag_header (reader, exif_reader->byte_order, &next_tagdata)) {
2302       ret = -1;
2303       goto reader_fail;
2304     }
2305     ret = 1;
2306   }
2307
2308   /* some checking */
2309   if (next_tagdata.tag_type != EXIF_TYPE_RATIONAL) {
2310     GST_WARNING ("Invalid type %d for 0x%x", next_tagdata.tag_type,
2311         next_tagdata.tag);
2312     return ret;
2313   }
2314   if (next_tagdata.count != 1) {
2315     GST_WARNING ("0x%x tag must have a single fraction, we have %u",
2316         next_tagdata.tag_type, next_tagdata.count);
2317     return ret;
2318   }
2319
2320   parse_exif_rational_tag (exif_reader,
2321       exiftag->gst_tag, next_tagdata.count, next_tagdata.offset, multiplier,
2322       FALSE);
2323
2324   return ret;
2325
2326 reader_fail:
2327   GST_WARNING ("Failed to read fields from buffer (too short?)");
2328   return ret;
2329 }
2330
2331
2332 static void
2333 serialize_speed (GstExifWriter * writer, const GstTagList * taglist,
2334     const GstExifTagMatch * exiftag)
2335 {
2336   gdouble value;
2337
2338   if (!gst_tag_list_get_double (taglist, exiftag->gst_tag, &value)) {
2339     GST_WARNING ("Failed to get double from tag list for tag: %s",
2340         exiftag->gst_tag);
2341     return;
2342   }
2343
2344   /* first write the Ref */
2345   write_exif_ascii_tag (writer, exiftag->complementary_tag, "K");
2346
2347   /* now the value */
2348   gst_exif_writer_write_rational_tag_from_double (writer,
2349       exiftag->exif_tag, value * METERS_PER_SECOND_TO_KILOMETERS_PER_HOUR);
2350 }
2351
2352 static gint
2353 deserialize_speed (GstExifReader * exif_reader,
2354     GstByteReader * reader, const GstExifTagMatch * exiftag,
2355     GstExifTagData * tagdata)
2356 {
2357   GstExifTagData next_tagdata = { 0, };
2358   gdouble multiplier = 1;
2359   gint ret = 0;
2360
2361   GST_LOG ("Starting to parse %s tag in exif 0x%x", exiftag->gst_tag,
2362       exiftag->exif_tag);
2363
2364   if (exiftag->complementary_tag == tagdata->tag) {
2365     if (tagdata->offset_as_data[0] == 'K') {
2366       multiplier = KILOMETERS_PER_HOUR_TO_METERS_PER_SECOND;
2367     } else if (tagdata->offset_as_data[0] == 'M') {
2368       multiplier = MILES_PER_HOUR_TO_METERS_PER_SECOND;
2369     } else if (tagdata->offset_as_data[0] == 'N') {
2370       multiplier = KNOTS_TO_METERS_PER_SECOND;
2371     } else {
2372       GST_WARNING ("Invalid GPSSpeedRed %c", tagdata->offset_as_data[0]);
2373       return ret;
2374     }
2375   } else {
2376     GST_DEBUG ("No GPSSpeedRef, using default=K");
2377     multiplier = KILOMETERS_PER_HOUR_TO_METERS_PER_SECOND;
2378
2379     if (tagdata->tag == exiftag->exif_tag) {
2380       tagdata_copy (&next_tagdata, tagdata);
2381     }
2382   }
2383
2384   /* now read the following tag that must be the exif_tag */
2385   if (next_tagdata.tag == 0) {
2386     if (exif_reader->byte_order == G_LITTLE_ENDIAN) {
2387       if (!gst_byte_reader_peek_uint16_le (reader, &next_tagdata.tag))
2388         goto reader_fail;
2389     } else {
2390       if (!gst_byte_reader_peek_uint16_be (reader, &next_tagdata.tag))
2391         goto reader_fail;
2392     }
2393
2394     if (exiftag->exif_tag != next_tagdata.tag) {
2395       GST_WARNING ("Unexpected tag");
2396       return ret;
2397     }
2398
2399     /* read the remaining tag entry data */
2400     if (!parse_exif_tag_header (reader, exif_reader->byte_order, &next_tagdata)) {
2401       ret = -1;
2402       goto reader_fail;
2403     }
2404     ret = 1;
2405   }
2406
2407
2408   /* some checking */
2409   if (next_tagdata.tag_type != EXIF_TYPE_RATIONAL) {
2410     GST_WARNING ("Invalid type %d for 0x%x", next_tagdata.tag_type,
2411         next_tagdata.tag);
2412     return ret;
2413   }
2414   if (next_tagdata.count != 1) {
2415     GST_WARNING ("0x%x tag must have a single fraction, we have %u",
2416         next_tagdata.tag_type, next_tagdata.count);
2417     return ret;
2418   }
2419
2420   parse_exif_rational_tag (exif_reader,
2421       exiftag->gst_tag, next_tagdata.count, next_tagdata.offset, multiplier,
2422       FALSE);
2423
2424   return ret;
2425
2426 reader_fail:
2427   GST_WARNING ("Failed to read fields from buffer (too short?)");
2428   return ret;
2429 }
2430
2431 static void
2432 serialize_shutter_speed (GstExifWriter * writer, const GstTagList * taglist,
2433     const GstExifTagMatch * exiftag)
2434 {
2435   const GValue *value = NULL;
2436   gdouble num;
2437
2438   value = gst_tag_list_get_value_index (taglist, exiftag->gst_tag, 0);
2439   if (!value) {
2440     GST_WARNING ("Failed to get shutter speed from from tag list");
2441     return;
2442   }
2443   gst_util_fraction_to_double (gst_value_get_fraction_numerator (value),
2444       gst_value_get_fraction_denominator (value), &num);
2445
2446 #ifdef HAVE_LOG2
2447   num = -log2 (num);
2448 #else
2449   num = -log (num) / M_LN2;
2450 #endif
2451
2452   /* now the value */
2453   gst_exif_writer_write_signed_rational_tag_from_double (writer,
2454       exiftag->exif_tag, num);
2455 }
2456
2457 static gint
2458 deserialize_shutter_speed (GstExifReader * exif_reader,
2459     GstByteReader * reader, const GstExifTagMatch * exiftag,
2460     GstExifTagData * tagdata)
2461 {
2462   gint32 frac_n, frac_d;
2463   gdouble d;
2464   GValue value = { 0 };
2465
2466   GST_LOG ("Starting to parse %s tag in exif 0x%x", exiftag->gst_tag,
2467       exiftag->exif_tag);
2468
2469   if (!exif_reader_read_rational_tag (exif_reader, tagdata->count,
2470           tagdata->offset, TRUE, &frac_n, &frac_d))
2471     return 0;
2472
2473   gst_util_fraction_to_double (frac_n, frac_d, &d);
2474   d = pow (2, -d);
2475   gst_util_double_to_fraction (d, &frac_n, &frac_d);
2476
2477   g_value_init (&value, GST_TYPE_FRACTION);
2478   gst_value_set_fraction (&value, frac_n, frac_d);
2479   gst_tag_list_add_value (exif_reader->taglist, GST_TAG_MERGE_KEEP,
2480       exiftag->gst_tag, &value);
2481   g_value_unset (&value);
2482
2483   return 0;
2484 }
2485
2486 static void
2487 serialize_aperture_value (GstExifWriter * writer, const GstTagList * taglist,
2488     const GstExifTagMatch * exiftag)
2489 {
2490   gdouble num;
2491
2492   if (!gst_tag_list_get_double_index (taglist, exiftag->gst_tag, 0, &num)) {
2493     GST_WARNING ("Failed to get focal ratio from from tag list");
2494     return;
2495   }
2496 #ifdef HAVE_LOG2
2497   num = 2 * log2 (num);
2498 #else
2499   num = 2 * (log (num) / M_LN2);
2500 #endif
2501
2502   /* now the value */
2503   gst_exif_writer_write_rational_tag_from_double (writer,
2504       exiftag->exif_tag, num);
2505 }
2506
2507 static gint
2508 deserialize_aperture_value (GstExifReader * exif_reader,
2509     GstByteReader * reader, const GstExifTagMatch * exiftag,
2510     GstExifTagData * tagdata)
2511 {
2512   gint32 frac_n, frac_d;
2513   gdouble d;
2514
2515   GST_LOG ("Starting to parse %s tag in exif 0x%x", exiftag->gst_tag,
2516       exiftag->exif_tag);
2517
2518   if (!exif_reader_read_rational_tag (exif_reader, tagdata->count,
2519           tagdata->offset, FALSE, &frac_n, &frac_d))
2520     return 0;
2521
2522   gst_util_fraction_to_double (frac_n, frac_d, &d);
2523   d = pow (2, d / 2);
2524
2525   gst_tag_list_add (exif_reader->taglist, GST_TAG_MERGE_KEEP,
2526       exiftag->gst_tag, d, NULL);
2527
2528   return 0;
2529 }
2530
2531 static void
2532 serialize_sensitivity_type (GstExifWriter * writer, const GstTagList * taglist,
2533     const GstExifTagMatch * exiftag)
2534 {
2535   /* we only support ISOSpeed as the sensitivity type (3) */
2536   gst_exif_writer_write_short_tag (writer, exiftag->exif_tag, 3);
2537 }
2538
2539 static gint
2540 deserialize_sensitivity_type (GstExifReader * exif_reader,
2541     GstByteReader * reader, const GstExifTagMatch * exiftag,
2542     GstExifTagData * tagdata)
2543 {
2544   GstExifTagData *sensitivity = NULL;
2545   guint16 type_data;
2546
2547   if (exif_reader->byte_order == G_LITTLE_ENDIAN) {
2548     type_data = GST_READ_UINT16_LE (tagdata->offset_as_data);
2549   } else {
2550     type_data = GST_READ_UINT16_BE (tagdata->offset_as_data);
2551   }
2552
2553   if (type_data != 3) {
2554     GST_WARNING ("We only support SensitivityType=3");
2555     return 0;
2556   }
2557
2558   /* check the pending tags for the PhotographicSensitivity tag */
2559   sensitivity =
2560       gst_exif_reader_get_pending_tag (exif_reader,
2561       EXIF_TAG_PHOTOGRAPHIC_SENSITIVITY);
2562   if (sensitivity == NULL) {
2563     GST_WARNING ("PhotographicSensitivity tag not found");
2564     return 0;
2565   }
2566
2567   GST_LOG ("Starting to parse %s tag in exif 0x%x", exiftag->gst_tag,
2568       exiftag->exif_tag);
2569
2570   gst_tag_list_add (exif_reader->taglist, GST_TAG_MERGE_KEEP,
2571       GST_TAG_CAPTURING_ISO_SPEED, sensitivity->offset_as_data, NULL);
2572
2573   return 0;
2574 }
2575
2576 static void
2577 serialize_flash (GstExifWriter * writer, const GstTagList * taglist,
2578     const GstExifTagMatch * exiftag)
2579 {
2580   gboolean flash_fired;
2581   const gchar *flash_mode;
2582   guint16 tagvalue = 0;
2583
2584   if (!gst_tag_list_get_boolean_index (taglist, exiftag->gst_tag, 0,
2585           &flash_fired)) {
2586     GST_WARNING ("Failed to get flash fired from from tag list");
2587     return;
2588   }
2589
2590   if (flash_fired)
2591     tagvalue = 1;
2592
2593   if (gst_tag_list_peek_string_index (taglist, GST_TAG_CAPTURING_FLASH_MODE, 0,
2594           &flash_mode)) {
2595     guint16 mode = 0;
2596     if (strcmp (flash_mode, "auto") == 0) {
2597       mode = 3;
2598     } else if (strcmp (flash_mode, "always") == 0) {
2599       mode = 1;
2600     } else if (strcmp (flash_mode, "never") == 0) {
2601       mode = 2;
2602     }
2603
2604     tagvalue = tagvalue | (mode << 3);
2605   } else {
2606     GST_DEBUG ("flash-mode not available");
2607   }
2608
2609   gst_exif_writer_write_short_tag (writer, exiftag->exif_tag, tagvalue);
2610 }
2611
2612 static gint
2613 deserialize_flash (GstExifReader * exif_reader,
2614     GstByteReader * reader, const GstExifTagMatch * exiftag,
2615     GstExifTagData * tagdata)
2616 {
2617   guint16 value = 0;
2618   guint mode = 0;
2619   const gchar *mode_str = NULL;
2620
2621   GST_LOG ("Starting to parse %s tag in exif 0x%x", exiftag->gst_tag,
2622       exiftag->exif_tag);
2623
2624   if (exif_reader->byte_order == G_LITTLE_ENDIAN) {
2625     value = GST_READ_UINT16_LE (tagdata->offset_as_data);
2626   } else {
2627     value = GST_READ_UINT16_BE (tagdata->offset_as_data);
2628   }
2629
2630   /* check flash fired */
2631   if (value & 0x1) {
2632     gst_tag_list_add (exif_reader->taglist, GST_TAG_MERGE_REPLACE,
2633         GST_TAG_CAPTURING_FLASH_FIRED, TRUE, NULL);
2634   } else {
2635     gst_tag_list_add (exif_reader->taglist, GST_TAG_MERGE_REPLACE,
2636         GST_TAG_CAPTURING_FLASH_FIRED, FALSE, NULL);
2637   }
2638
2639   mode = (value >> 3) & 0x3;
2640   if (mode == 1) {
2641     mode_str = "always";
2642   } else if (mode == 2) {
2643     mode_str = "never";
2644   } else if (mode == 3) {
2645     mode_str = "auto";
2646   }
2647
2648   if (mode_str)
2649     gst_tag_list_add (exif_reader->taglist, GST_TAG_MERGE_REPLACE,
2650         GST_TAG_CAPTURING_FLASH_MODE, mode_str, NULL);
2651
2652   return 0;
2653 }
2654
2655 static gint
2656 deserialize_resolution (GstExifReader * exif_reader,
2657     GstByteReader * reader, const GstExifTagMatch * exiftag,
2658     GstExifTagData * tagdata)
2659 {
2660   GstExifTagData *xres = NULL;
2661   GstExifTagData *yres = NULL;
2662   guint16 unit;
2663   gdouble multiplier;
2664
2665   if (exif_reader->byte_order == G_LITTLE_ENDIAN) {
2666     unit = GST_READ_UINT16_LE (tagdata->offset_as_data);
2667   } else {
2668     unit = GST_READ_UINT16_BE (tagdata->offset_as_data);
2669   }
2670
2671   switch (unit) {
2672     case 2:                    /* inch */
2673       multiplier = 1;
2674       break;
2675     case 3:                    /* cm */
2676       multiplier = 1 / 2.54;
2677       break;
2678     default:
2679       GST_WARNING ("Invalid resolution unit, ignoring PPI tags");
2680       return 0;
2681   }
2682
2683   xres = gst_exif_reader_get_pending_tag (exif_reader, EXIF_TAG_XRESOLUTION);
2684   if (xres) {
2685     parse_exif_rational_tag (exif_reader, GST_TAG_IMAGE_HORIZONTAL_PPI,
2686         xres->count, xres->offset, multiplier, FALSE);
2687   }
2688   yres = gst_exif_reader_get_pending_tag (exif_reader, EXIF_TAG_YRESOLUTION);
2689   if (yres) {
2690     parse_exif_rational_tag (exif_reader, GST_TAG_IMAGE_VERTICAL_PPI,
2691         yres->count, yres->offset, multiplier, FALSE);
2692   }
2693
2694   return 0;
2695 }
2696
2697 static void
2698 serialize_scene_type (GstExifWriter * writer, const GstTagList * taglist,
2699     const GstExifTagMatch * exiftag)
2700 {
2701   const gchar *str;
2702   guint8 value = 0;
2703
2704   if (gst_tag_list_peek_string_index (taglist, GST_TAG_CAPTURING_SOURCE, 0,
2705           &str)) {
2706     if (strcmp (str, "dsc") == 0) {
2707       value = 1;
2708     }
2709   }
2710
2711   if (value != 0)
2712     write_exif_undefined_tag (writer, exiftag->exif_tag, &value, 1);
2713 }
2714
2715 static gint
2716 deserialize_scene_type (GstExifReader * exif_reader,
2717     GstByteReader * reader, const GstExifTagMatch * exiftag,
2718     GstExifTagData * tagdata)
2719 {
2720   guint8 value = 0;
2721
2722   GST_LOG ("Starting to parse %s tag in exif 0x%x", exiftag->gst_tag,
2723       exiftag->exif_tag);
2724
2725   value = GST_READ_UINT8 (tagdata->offset_as_data);
2726
2727   if (value == 1) {
2728     gst_tag_list_add (exif_reader->taglist, GST_TAG_MERGE_KEEP,
2729         GST_TAG_CAPTURING_SOURCE, "dsc", NULL);
2730   }
2731
2732   return 0;
2733 }
2734
2735 static gint
2736 deserialize_add_to_pending_tags (GstExifReader * exif_reader,
2737     GstByteReader * reader, const GstExifTagMatch * exiftag,
2738     GstExifTagData * tagdata)
2739 {
2740   GST_LOG ("Adding %s tag in exif 0x%x to pending tags", exiftag->gst_tag,
2741       exiftag->exif_tag);
2742
2743   /* add it to the pending tags, as we can only parse it when we find the
2744    * SensitivityType tag */
2745   gst_exif_reader_add_pending_tag (exif_reader, tagdata);
2746   return 0;
2747 }
2748
2749 #undef EXIF_SERIALIZATION_FUNC
2750 #undef EXIF_DESERIALIZATION_FUNC
2751 #undef EXIF_SERIALIZATION_DESERIALIZATION_FUNC