Merge branch 'master' into 0.11
[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   reader->taglist = gst_tag_list_new ();
460   reader->buffer = buf;
461   reader->base_offset = base_offset;
462   reader->byte_order = byte_order;
463   reader->pending_tags = NULL;
464   if (reader->byte_order != G_LITTLE_ENDIAN &&
465       reader->byte_order != G_BIG_ENDIAN) {
466     GST_WARNING ("Unexpected byte order %d, using system default: %d",
467         reader->byte_order, G_BYTE_ORDER);
468     reader->byte_order = G_BYTE_ORDER;
469   }
470 }
471
472 static void
473 gst_exif_reader_add_pending_tag (GstExifReader * reader, GstExifTagData * data)
474 {
475   GstExifTagData *copy;
476
477   copy = g_slice_new (GstExifTagData);
478   memcpy (copy, data, sizeof (GstExifTagData));
479
480   reader->pending_tags = g_slist_prepend (reader->pending_tags, copy);
481 }
482
483 static GstExifTagData *
484 gst_exif_reader_get_pending_tag (GstExifReader * reader, gint tagid)
485 {
486   GSList *walker;
487
488   for (walker = reader->pending_tags; walker; walker = g_slist_next (walker)) {
489     GstExifTagData *data = (GstExifTagData *) walker->data;
490     if (data->tag == tagid)
491       return data;
492   }
493
494   return NULL;
495 }
496
497 static GstTagList *
498 gst_exif_reader_reset (GstExifReader * reader, gboolean return_taglist)
499 {
500   GstTagList *ret = NULL;
501   GSList *walker;
502
503   for (walker = reader->pending_tags; walker; walker = g_slist_next (walker)) {
504     GstExifTagData *data = (GstExifTagData *) walker->data;
505
506     g_slice_free (GstExifTagData, data);
507   }
508   g_slist_free (reader->pending_tags);
509
510   if (return_taglist) {
511     ret = reader->taglist;
512     reader->taglist = NULL;
513   }
514
515   if (reader->taglist) {
516     gst_tag_list_free (reader->taglist);
517   }
518
519   return ret;
520 }
521
522 /* GstExifWriter functions */
523
524 static void
525 gst_exif_writer_init (GstExifWriter * writer, gint byte_order)
526 {
527   gst_byte_writer_init (&writer->tagwriter);
528   gst_byte_writer_init (&writer->datawriter);
529
530   writer->byte_order = byte_order;
531   writer->tags_total = 0;
532   if (writer->byte_order != G_LITTLE_ENDIAN &&
533       writer->byte_order != G_BIG_ENDIAN) {
534     GST_WARNING ("Unexpected byte order %d, using system default: %d",
535         writer->byte_order, G_BYTE_ORDER);
536     writer->byte_order = G_BYTE_ORDER;
537   }
538 }
539
540 static GstBuffer *
541 gst_exif_writer_reset_and_get_buffer (GstExifWriter * writer)
542 {
543   GstBuffer *header;
544   GstBuffer *data;
545
546   header = gst_byte_writer_reset_and_get_buffer (&writer->tagwriter);
547   data = gst_byte_writer_reset_and_get_buffer (&writer->datawriter);
548
549   return gst_buffer_join (header, data);
550 }
551
552 /*
553  * Given the exif tag with the passed id, returns the map index of the tag
554  * corresponding to it. If use_complementary is true, then the complementary
555  * are also used in the search.
556  *
557  * Returns -1 if not found
558  */
559 static gint
560 exif_tag_map_find_reverse (guint16 exif_tag, const GstExifTagMatch * tag_map,
561     gboolean use_complementary)
562 {
563   gint i;
564
565   for (i = 0; tag_map[i].exif_tag != 0; i++) {
566     if (exif_tag == tag_map[i].exif_tag || (use_complementary &&
567             exif_tag == tag_map[i].complementary_tag)) {
568       return i;
569     }
570   }
571   return -1;
572 }
573
574 static gboolean
575 gst_tag_list_has_ifd_tags (const GstTagList * taglist,
576     const GstExifTagMatch * tag_map)
577 {
578   gint i;
579
580   for (i = 0; tag_map[i].exif_tag != 0; i++) {
581     if (tag_map[i].gst_tag == NULL) {
582       if (tag_map[i].exif_tag == EXIF_GPS_IFD_TAG &&
583           gst_tag_list_has_ifd_tags (taglist, tag_map_gps))
584         return TRUE;
585       if (tag_map[i].exif_tag == EXIF_IFD_TAG &&
586           gst_tag_list_has_ifd_tags (taglist, tag_map_exif))
587         return TRUE;
588       continue;
589     }
590
591     if (gst_tag_list_get_value_index (taglist, tag_map[i].gst_tag, 0)) {
592       return TRUE;
593     }
594   }
595   return FALSE;
596 }
597
598 /*
599  * Writes the tag entry.
600  *
601  * The tag entry is the tag id, the tag type,
602  * the count and the offset.
603  *
604  * The offset is the on the amount of data writen so far, as one
605  * can't predict the total bytes that the tag entries will take.
606  * This means those fields requires being updated later.
607  */
608 static void
609 gst_exif_writer_write_tag_header (GstExifWriter * writer,
610     guint16 exif_tag, guint16 exif_type, guint32 count, guint32 offset,
611     gboolean is_data)
612 {
613   GST_DEBUG ("Writing tag entry: id %x, type %u, count %u, offset %u",
614       exif_tag, exif_type, count, offset);
615
616   if (writer->byte_order == G_LITTLE_ENDIAN) {
617     gst_byte_writer_put_uint16_le (&writer->tagwriter, exif_tag);
618     gst_byte_writer_put_uint16_le (&writer->tagwriter, exif_type);
619     gst_byte_writer_put_uint32_le (&writer->tagwriter, count);
620     gst_byte_writer_put_uint32_le (&writer->tagwriter, offset);
621   } else if (writer->byte_order == G_BIG_ENDIAN) {
622     gst_byte_writer_put_uint16_be (&writer->tagwriter, exif_tag);
623     gst_byte_writer_put_uint16_be (&writer->tagwriter, exif_type);
624     gst_byte_writer_put_uint32_be (&writer->tagwriter, count);
625     if (is_data) {
626       gst_byte_writer_put_uint32_le (&writer->tagwriter, offset);
627     } else {
628       gst_byte_writer_put_uint32_be (&writer->tagwriter, offset);
629     }
630   } else {
631     g_assert_not_reached ();
632   }
633
634   writer->tags_total++;
635 }
636
637 static void
638 gst_exif_writer_write_rational_data (GstExifWriter * writer, guint32 frac_n,
639     guint32 frac_d)
640 {
641   if (writer->byte_order == G_LITTLE_ENDIAN) {
642     gst_byte_writer_put_uint32_le (&writer->datawriter, frac_n);
643     gst_byte_writer_put_uint32_le (&writer->datawriter, frac_d);
644   } else {
645     gst_byte_writer_put_uint32_be (&writer->datawriter, frac_n);
646     gst_byte_writer_put_uint32_be (&writer->datawriter, frac_d);
647   }
648 }
649
650 static void
651 gst_exif_writer_write_signed_rational_data (GstExifWriter * writer,
652     gint32 frac_n, gint32 frac_d)
653 {
654   if (writer->byte_order == G_LITTLE_ENDIAN) {
655     gst_byte_writer_put_int32_le (&writer->datawriter, frac_n);
656     gst_byte_writer_put_int32_le (&writer->datawriter, frac_d);
657   } else {
658     gst_byte_writer_put_int32_be (&writer->datawriter, frac_n);
659     gst_byte_writer_put_int32_be (&writer->datawriter, frac_d);
660   }
661 }
662
663 static void
664 gst_exif_writer_write_rational_tag (GstExifWriter * writer,
665     guint16 tag, guint32 frac_n, guint32 frac_d)
666 {
667   guint32 offset = gst_byte_writer_get_size (&writer->datawriter);
668
669   gst_exif_writer_write_tag_header (writer, tag, EXIF_TYPE_RATIONAL,
670       1, offset, FALSE);
671
672   gst_exif_writer_write_rational_data (writer, frac_n, frac_d);
673 }
674
675 static void
676 gst_exif_writer_write_signed_rational_tag (GstExifWriter * writer,
677     guint16 tag, gint32 frac_n, gint32 frac_d)
678 {
679   guint32 offset = gst_byte_writer_get_size (&writer->datawriter);
680
681   gst_exif_writer_write_tag_header (writer, tag, EXIF_TYPE_SRATIONAL,
682       1, offset, FALSE);
683
684   gst_exif_writer_write_signed_rational_data (writer, frac_n, frac_d);
685 }
686
687 static void
688 gst_exif_writer_write_rational_tag_from_double (GstExifWriter * writer,
689     guint16 tag, gdouble value)
690 {
691   gint frac_n;
692   gint frac_d;
693
694   gst_util_double_to_fraction (value, &frac_n, &frac_d);
695
696   gst_exif_writer_write_rational_tag (writer, tag, frac_n, frac_d);
697 }
698
699 static void
700 gst_exif_writer_write_signed_rational_tag_from_double (GstExifWriter * writer,
701     guint16 tag, gdouble value)
702 {
703   gint frac_n;
704   gint frac_d;
705
706   gst_util_double_to_fraction (value, &frac_n, &frac_d);
707
708   gst_exif_writer_write_signed_rational_tag (writer, tag, frac_n, frac_d);
709 }
710
711 static void
712 gst_exif_writer_write_byte_tag (GstExifWriter * writer, guint16 tag,
713     guint8 value)
714 {
715   guint32 offset = 0;
716
717   GST_WRITE_UINT8 ((guint8 *) & offset, value);
718   gst_exif_writer_write_tag_header (writer, tag, EXIF_TYPE_BYTE,
719       1, offset, TRUE);
720 }
721
722 static void
723 gst_exif_writer_write_short_tag (GstExifWriter * writer, guint16 tag,
724     guint16 value)
725 {
726   guint32 offset = 0;
727
728   if (writer->byte_order == G_LITTLE_ENDIAN) {
729     GST_WRITE_UINT16_LE ((guint8 *) & offset, value);
730   } else {
731     GST_WRITE_UINT16_BE ((guint8 *) & offset, value);
732   }
733
734   gst_exif_writer_write_tag_header (writer, tag, EXIF_TYPE_SHORT,
735       1, offset, TRUE);
736 }
737
738 static void
739 gst_exif_writer_write_long_tag (GstExifWriter * writer, guint16 tag,
740     guint32 value)
741 {
742   guint32 offset = 0;
743   if (writer->byte_order == G_LITTLE_ENDIAN) {
744     GST_WRITE_UINT32_LE ((guint8 *) & offset, value);
745   } else {
746     GST_WRITE_UINT32_BE ((guint8 *) & offset, value);
747   }
748
749   gst_exif_writer_write_tag_header (writer, tag, EXIF_TYPE_LONG,
750       1, offset, TRUE);
751 }
752
753
754 static void
755 write_exif_undefined_tag (GstExifWriter * writer, guint16 tag,
756     const guint8 * data, gint size)
757 {
758   guint32 offset = 0;
759
760   if (size > 4) {
761     /* we only use the data offset here, later we add up the
762      * resulting tag headers offset and the base offset */
763     offset = gst_byte_writer_get_size (&writer->datawriter);
764     gst_exif_writer_write_tag_header (writer, tag, EXIF_TYPE_UNDEFINED,
765         size, offset, FALSE);
766     gst_byte_writer_put_data (&writer->datawriter, data, size);
767   } else {
768     /* small enough to go in the offset */
769     memcpy ((guint8 *) & offset, data, size);
770     gst_exif_writer_write_tag_header (writer, tag, EXIF_TYPE_UNDEFINED,
771         size, offset, TRUE);
772   }
773 }
774
775 static void
776 write_exif_ascii_tag (GstExifWriter * writer, guint16 tag, const gchar * str)
777 {
778   gint size;
779   guint32 offset = 0;
780
781   size = strlen (str) + 1;
782
783   if (size > 4) {
784     /* we only use the data offset here, later we add up the
785      * resulting tag headers offset and the base offset */
786     offset = gst_byte_writer_get_size (&writer->datawriter);
787     gst_exif_writer_write_tag_header (writer, tag, EXIF_TYPE_ASCII,
788         size, offset, FALSE);
789     gst_byte_writer_put_string (&writer->datawriter, str);
790   } else {
791     /* small enough to go in the offset */
792     memcpy ((guint8 *) & offset, str, size);
793     gst_exif_writer_write_tag_header (writer, tag, EXIF_TYPE_ASCII,
794         size, offset, TRUE);
795   }
796 }
797
798 static void
799 write_exif_ascii_tag_from_taglist (GstExifWriter * writer,
800     const GstTagList * taglist, const GstExifTagMatch * exiftag)
801 {
802   gchar *str = NULL;
803   gboolean cleanup = FALSE;
804   const GValue *value;
805   gint tag_size = gst_tag_list_get_tag_size (taglist, exiftag->gst_tag);
806
807   if (tag_size != 1) {
808     /* FIXME support this by serializing them with a ','? */
809     GST_WARNING ("Multiple string tags not supported yet");
810     return;
811   }
812
813   value = gst_tag_list_get_value_index (taglist, exiftag->gst_tag, 0);
814
815   /* do some conversion if needed */
816   switch (G_VALUE_TYPE (value)) {
817     case G_TYPE_STRING:
818       str = (gchar *) g_value_get_string (value);
819       break;
820     default:
821       if (G_VALUE_TYPE (value) == GST_TYPE_DATE_TIME) {
822         GstDateTime *dt = (GstDateTime *) g_value_get_boxed (value);
823
824         if (dt == NULL) {
825           GST_WARNING ("NULL datetime received");
826           break;
827         }
828
829         str = g_strdup_printf ("%04d:%02d:%02d %02d:%02d:%02d",
830             gst_date_time_get_year (dt), gst_date_time_get_month (dt),
831             gst_date_time_get_day (dt), gst_date_time_get_hour (dt),
832             gst_date_time_get_minute (dt), gst_date_time_get_second (dt));
833
834         cleanup = TRUE;
835       } else {
836         GST_WARNING ("Conversion from %s to ascii string not supported",
837             G_VALUE_TYPE_NAME (value));
838       }
839       break;
840   }
841
842   if (str == NULL)
843     return;
844
845   write_exif_ascii_tag (writer, exiftag->exif_tag, str);
846   if (cleanup)
847     g_free (str);
848 }
849
850 static void
851 write_exif_undefined_tag_from_taglist (GstExifWriter * writer,
852     const GstTagList * taglist, const GstExifTagMatch * exiftag)
853 {
854   const GValue *value;
855   guint8 *data = NULL;
856   gsize size = 0;
857   gint tag_size = gst_tag_list_get_tag_size (taglist, exiftag->gst_tag);
858   GstBuffer *buf = NULL;
859
860   if (tag_size != 1) {
861     GST_WARNING ("Only the first item in the taglist will be serialized");
862     return;
863   }
864
865   value = gst_tag_list_get_value_index (taglist, exiftag->gst_tag, 0);
866
867   /* do some conversion if needed */
868   switch (G_VALUE_TYPE (value)) {
869     case G_TYPE_STRING:
870       data = (guint8 *) g_value_get_string (value);
871       size = strlen ((gchar *) data);   /* no need to +1, undefined doesn't require it */
872       break;
873     default:
874       if (G_VALUE_TYPE (value) == GST_TYPE_BUFFER) {
875         buf = gst_value_get_buffer (value);
876         data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
877       } else {
878         GST_WARNING ("Conversion from %s to raw data not supported",
879             G_VALUE_TYPE_NAME (value));
880       }
881       break;
882   }
883
884   if (size > 0)
885     write_exif_undefined_tag (writer, exiftag->exif_tag, data, size);
886
887   if (buf)
888     gst_buffer_unmap (buf, data, size);
889 }
890
891 static void
892 write_exif_rational_tag_from_taglist (GstExifWriter * writer,
893     const GstTagList * taglist, const GstExifTagMatch * exiftag)
894 {
895   const GValue *value;
896   gdouble num = 0;
897   gint tag_size = gst_tag_list_get_tag_size (taglist, exiftag->gst_tag);
898
899   if (tag_size != 1) {
900     GST_WARNING ("Only the first item in the taglist will be serialized");
901     return;
902   }
903
904   value = gst_tag_list_get_value_index (taglist, exiftag->gst_tag, 0);
905
906   /* do some conversion if needed */
907   switch (G_VALUE_TYPE (value)) {
908     case G_TYPE_DOUBLE:
909       num = g_value_get_double (value);
910       gst_exif_writer_write_rational_tag_from_double (writer, exiftag->exif_tag,
911           num);
912       break;
913     default:
914       if (G_VALUE_TYPE (value) == GST_TYPE_FRACTION) {
915         gst_exif_writer_write_rational_tag (writer, exiftag->exif_tag,
916             gst_value_get_fraction_numerator (value),
917             gst_value_get_fraction_denominator (value));
918       } else {
919         GST_WARNING ("Conversion from %s to rational not supported",
920             G_VALUE_TYPE_NAME (value));
921       }
922       break;
923   }
924 }
925
926 static void
927 write_exif_signed_rational_tag_from_taglist (GstExifWriter * writer,
928     const GstTagList * taglist, const GstExifTagMatch * exiftag)
929 {
930   const GValue *value;
931   gdouble num = 0;
932   gint tag_size = gst_tag_list_get_tag_size (taglist, exiftag->gst_tag);
933
934   if (tag_size != 1) {
935     GST_WARNING ("Only the first item in the taglist will be serialized");
936     return;
937   }
938
939   value = gst_tag_list_get_value_index (taglist, exiftag->gst_tag, 0);
940
941   /* do some conversion if needed */
942   switch (G_VALUE_TYPE (value)) {
943     case G_TYPE_DOUBLE:
944       num = g_value_get_double (value);
945       gst_exif_writer_write_signed_rational_tag_from_double (writer,
946           exiftag->exif_tag, num);
947       break;
948     default:
949       if (G_VALUE_TYPE (value) == GST_TYPE_FRACTION) {
950         gst_exif_writer_write_signed_rational_tag (writer, exiftag->exif_tag,
951             gst_value_get_fraction_numerator (value),
952             gst_value_get_fraction_denominator (value));
953       } else {
954         GST_WARNING ("Conversion from %s to signed rational not supported",
955             G_VALUE_TYPE_NAME (value));
956       }
957       break;
958   }
959 }
960
961 static void
962 write_exif_integer_tag_from_taglist (GstExifWriter * writer,
963     const GstTagList * taglist, const GstExifTagMatch * exiftag)
964 {
965   const GValue *value;
966   guint32 num = 0;
967   gint tag_size = gst_tag_list_get_tag_size (taglist, exiftag->gst_tag);
968
969   if (tag_size != 1) {
970     GST_WARNING ("Only the first item in the taglist will be serialized");
971     return;
972   }
973
974   value = gst_tag_list_get_value_index (taglist, exiftag->gst_tag, 0);
975
976   /* do some conversion if needed */
977   switch (G_VALUE_TYPE (value)) {
978     case G_TYPE_INT:
979       num = g_value_get_int (value);
980       break;
981     default:
982       GST_WARNING ("Conversion from %s to int not supported",
983           G_VALUE_TYPE_NAME (value));
984       break;
985   }
986
987   switch (exiftag->exif_type) {
988     case EXIF_TYPE_LONG:
989       gst_exif_writer_write_long_tag (writer, exiftag->exif_tag, num);
990       break;
991     case EXIF_TYPE_SHORT:
992       gst_exif_writer_write_short_tag (writer, exiftag->exif_tag, num);
993       break;
994     default:
995       break;
996   }
997 }
998
999 static void
1000 write_exif_tag_from_taglist (GstExifWriter * writer, const GstTagList * taglist,
1001     const GstExifTagMatch * exiftag)
1002 {
1003   GST_DEBUG ("Writing tag %s", exiftag->gst_tag);
1004
1005   /* check for special handling */
1006   if (exiftag->serialize) {
1007     exiftag->serialize (writer, taglist, exiftag);
1008     return;
1009   }
1010
1011   switch (exiftag->exif_type) {
1012     case EXIF_TYPE_ASCII:
1013       write_exif_ascii_tag_from_taglist (writer, taglist, exiftag);
1014       break;
1015     case EXIF_TYPE_UNDEFINED:
1016       write_exif_undefined_tag_from_taglist (writer, taglist, exiftag);
1017       break;
1018     case EXIF_TYPE_RATIONAL:
1019       write_exif_rational_tag_from_taglist (writer, taglist, exiftag);
1020       break;
1021     case EXIF_TYPE_SRATIONAL:
1022       write_exif_signed_rational_tag_from_taglist (writer, taglist, exiftag);
1023       break;
1024     case EXIF_TYPE_LONG:
1025     case EXIF_TYPE_SHORT:
1026       write_exif_integer_tag_from_taglist (writer, taglist, exiftag);
1027       break;
1028     default:
1029       GST_WARNING ("Unhandled tag type %d", exiftag->exif_type);
1030   }
1031 }
1032
1033 static void
1034 tagdata_copy (GstExifTagData * to, const GstExifTagData * from)
1035 {
1036   to->tag = from->tag;
1037   to->tag_type = from->tag_type;
1038   to->count = from->count;
1039   to->offset = from->offset;
1040   to->offset_as_data = from->offset_as_data;
1041 }
1042
1043 static void
1044 gst_exif_tag_rewrite_offsets (GstByteWriter * writer, gint byte_order,
1045     guint32 offset, gint num_tags, GstByteWriter * inner_ifds_data)
1046 {
1047   GstByteReader *reader;
1048   gint i;
1049   guint16 aux = G_MAXUINT16;
1050
1051   GST_LOG ("Rewriting tag entries offsets");
1052
1053   reader = (GstByteReader *) writer;
1054
1055   if (num_tags == -1) {
1056     if (byte_order == G_LITTLE_ENDIAN) {
1057       gst_byte_reader_get_uint16_le (reader, &aux);
1058     } else {
1059       gst_byte_reader_get_uint16_be (reader, &aux);
1060     }
1061     if (aux == G_MAXUINT16) {
1062       GST_WARNING ("Failed to read number of tags, won't rewrite offsets");
1063       return;
1064     }
1065     num_tags = (gint) aux;
1066   }
1067
1068   g_return_if_fail (num_tags != -1);
1069
1070   GST_DEBUG ("number of tags %d", num_tags);
1071
1072   for (i = 0; i < num_tags; i++) {
1073     guint16 type = 0;
1074     guint32 cur_offset = 0;
1075     gint byte_size = 0;
1076     guint32 count = 0;
1077     guint16 tag_id = 0;
1078
1079     g_assert (gst_byte_writer_get_pos (writer) <
1080         gst_byte_writer_get_size (writer));
1081
1082     /* read the type */
1083     if (byte_order == G_LITTLE_ENDIAN) {
1084       if (!gst_byte_reader_get_uint16_le (reader, &tag_id))
1085         break;
1086       if (!gst_byte_reader_get_uint16_le (reader, &type))
1087         break;
1088       if (!gst_byte_reader_get_uint32_le (reader, &count))
1089         break;
1090     } else {
1091       if (!gst_byte_reader_get_uint16_be (reader, &tag_id))
1092         break;
1093       if (!gst_byte_reader_get_uint16_be (reader, &type))
1094         break;
1095       if (!gst_byte_reader_get_uint32_be (reader, &count))
1096         break;
1097     }
1098
1099     GST_LOG ("Parsed tag %x of type %u and count %u", tag_id, type, count);
1100
1101     switch (type) {
1102       case EXIF_TYPE_BYTE:
1103       case EXIF_TYPE_ASCII:
1104       case EXIF_TYPE_UNDEFINED:
1105         byte_size = count;
1106         break;
1107       case EXIF_TYPE_SHORT:
1108         byte_size = count * 2;  /* 2 bytes */
1109         break;
1110       case EXIF_TYPE_LONG:
1111       case EXIF_TYPE_SLONG:
1112         byte_size = count * 4;  /* 4 bytes */
1113         break;
1114       case EXIF_TYPE_RATIONAL:
1115       case EXIF_TYPE_SRATIONAL:
1116         byte_size = count * 8;  /* 8 bytes */
1117         break;
1118       default:
1119         g_assert_not_reached ();
1120         break;
1121     }
1122
1123     /* adjust the offset if needed */
1124     if (byte_size > 4 || tag_id == EXIF_GPS_IFD_TAG || tag_id == EXIF_IFD_TAG) {
1125       if (byte_order == G_LITTLE_ENDIAN) {
1126         if (gst_byte_reader_peek_uint32_le (reader, &cur_offset)) {
1127           gst_byte_writer_put_uint32_le (writer, cur_offset + offset);
1128         }
1129       } else {
1130         if (gst_byte_reader_peek_uint32_be (reader, &cur_offset)) {
1131           gst_byte_writer_put_uint32_be (writer, cur_offset + offset);
1132         }
1133       }
1134       GST_DEBUG ("Rewriting tag offset from %u to (%u + %u) %u",
1135           cur_offset, cur_offset, offset, cur_offset + offset);
1136
1137       if ((tag_id == EXIF_GPS_IFD_TAG || tag_id == EXIF_IFD_TAG) &&
1138           inner_ifds_data != NULL) {
1139         /* needs special handling */
1140         if (!gst_byte_writer_set_pos (inner_ifds_data, cur_offset)) {
1141           GST_WARNING ("Failed to position writer to rewrite inner ifd "
1142               "offsets");
1143           continue;
1144         }
1145
1146         gst_exif_tag_rewrite_offsets (inner_ifds_data, byte_order, offset, -1,
1147             NULL);
1148       }
1149     } else {
1150       gst_byte_reader_skip (reader, 4);
1151       GST_DEBUG ("No need to rewrite tag offset");
1152     }
1153   }
1154   GST_LOG ("Done rewriting offsets");
1155 }
1156
1157 static void
1158 parse_exif_ascii_tag (GstExifReader * reader, const GstExifTagMatch * tag,
1159     guint32 count, guint32 offset, const guint8 * offset_as_data)
1160 {
1161   GType tagtype;
1162   gchar *str;
1163   guint32 real_offset;
1164
1165   if (count > 4) {
1166     guint8 *data;
1167     gsize size;
1168
1169     if (offset < reader->base_offset) {
1170       GST_WARNING ("Offset is smaller (%u) than base offset (%u)", offset,
1171           reader->base_offset);
1172       return;
1173     }
1174
1175     real_offset = offset - reader->base_offset;
1176
1177     data = gst_buffer_map (reader->buffer, &size, NULL, GST_MAP_READ);
1178     if (real_offset >= size) {
1179       GST_WARNING ("Invalid offset %u for buffer of size %u, not adding tag %s",
1180           real_offset, size, tag->gst_tag);
1181       gst_buffer_unmap (reader->buffer, data, size);
1182       return;
1183     }
1184
1185     str = g_strndup ((gchar *) (data + real_offset), count);
1186     gst_buffer_unmap (reader->buffer, data, size);
1187   } else {
1188     str = g_strndup ((gchar *) offset_as_data, count);
1189   }
1190
1191   tagtype = gst_tag_get_type (tag->gst_tag);
1192   if (tagtype == GST_TYPE_DATE_TIME) {
1193     gint year = 0, month = 1, day = 1, hour = 0, minute = 0, second = 0;
1194
1195     if (sscanf (str, "%04d:%02d:%02d %02d:%02d:%02d", &year, &month, &day,
1196             &hour, &minute, &second) > 0) {
1197       GstDateTime *d;
1198
1199       d = gst_date_time_new_local_time (year, month, day, hour, minute, second);
1200       gst_tag_list_add (reader->taglist, GST_TAG_MERGE_REPLACE,
1201           tag->gst_tag, d, NULL);
1202       gst_date_time_unref (d);
1203     } else {
1204       GST_WARNING ("Failed to parse %s into a datetime tag", str);
1205     }
1206   } else if (tagtype == G_TYPE_STRING) {
1207     gst_tag_list_add (reader->taglist, GST_TAG_MERGE_REPLACE, tag->gst_tag, str,
1208         NULL);
1209   } else {
1210     GST_WARNING ("No parsing function associated to %x(%s)", tag->exif_tag,
1211         tag->gst_tag);
1212   }
1213   g_free (str);
1214 }
1215
1216 static void
1217 parse_exif_long_tag (GstExifReader * reader, const GstExifTagMatch * tag,
1218     guint32 count, guint32 offset, const guint8 * offset_as_data)
1219 {
1220   GType tagtype;
1221
1222   if (count > 1) {
1223     GST_WARNING ("Long tags with more than one value are not supported");
1224     return;
1225   }
1226
1227   tagtype = gst_tag_get_type (tag->gst_tag);
1228   if (tagtype == G_TYPE_INT) {
1229     gst_tag_list_add (reader->taglist, GST_TAG_MERGE_REPLACE, tag->gst_tag,
1230         offset, NULL);
1231   } else {
1232     GST_WARNING ("No parsing function associated to %x(%s)", tag->exif_tag,
1233         tag->gst_tag);
1234   }
1235 }
1236
1237
1238 static void
1239 parse_exif_undefined_tag (GstExifReader * reader, const GstExifTagMatch * tag,
1240     guint32 count, guint32 offset, const guint8 * offset_as_data)
1241 {
1242   GType tagtype;
1243   guint8 *data;
1244   guint32 real_offset;
1245
1246   if (count > 4) {
1247     guint8 *bdata;
1248     gsize bsize;
1249
1250     if (offset < reader->base_offset) {
1251       GST_WARNING ("Offset is smaller (%u) than base offset (%u)", offset,
1252           reader->base_offset);
1253       return;
1254     }
1255
1256     real_offset = offset - reader->base_offset;
1257
1258     bdata = gst_buffer_map (reader->buffer, &bsize, NULL, GST_MAP_READ);
1259
1260     if (real_offset >= bsize) {
1261       GST_WARNING ("Invalid offset %u for buffer of size %u, not adding tag %s",
1262           real_offset, bsize, tag->gst_tag);
1263       gst_buffer_unmap (reader->buffer, bdata, bsize);
1264       return;
1265     }
1266
1267     /* +1 because it could be a string without the \0 */
1268     data = malloc (sizeof (guint8) * count + 1);
1269     memcpy (data, bdata + real_offset, count);
1270     data[count] = 0;
1271
1272     gst_buffer_unmap (reader->buffer, bdata, bsize);
1273   } else {
1274     data = malloc (sizeof (guint8) * count + 1);
1275     memcpy (data, (guint8 *) offset_as_data, count);
1276     data[count] = 0;
1277   }
1278
1279   tagtype = gst_tag_get_type (tag->gst_tag);
1280   if (tagtype == GST_TYPE_BUFFER) {
1281     GstBuffer *buf;
1282
1283     buf = gst_buffer_new ();
1284     gst_buffer_take_memory (buf,
1285         gst_memory_new_wrapped (0, data, g_free, count, 0, count));
1286     data = NULL;
1287
1288     gst_tag_list_add (reader->taglist, GST_TAG_MERGE_APPEND, tag->gst_tag,
1289         buf, NULL);
1290
1291     gst_buffer_unref (buf);
1292   } else if (tagtype == G_TYPE_STRING) {
1293     gst_tag_list_add (reader->taglist, GST_TAG_MERGE_REPLACE, tag->gst_tag,
1294         data, NULL);
1295   } else {
1296     GST_WARNING ("No parsing function associated to %x(%s)", tag->exif_tag,
1297         tag->gst_tag);
1298   }
1299   g_free (data);
1300 }
1301
1302 static gboolean
1303 exif_reader_read_rational_tag (GstExifReader * exif_reader,
1304     guint32 count, guint32 offset, gboolean is_signed,
1305     gint32 * _frac_n, gint32 * _frac_d)
1306 {
1307   GstByteReader data_reader;
1308   guint32 real_offset;
1309   gint32 frac_n = 0;
1310   gint32 frac_d = 0;
1311   guint8 *data;
1312   gsize size;
1313
1314   if (count > 1) {
1315     GST_WARNING ("Rationals with multiple entries are not supported");
1316   }
1317   if (offset < exif_reader->base_offset) {
1318     GST_WARNING ("Offset is smaller (%u) than base offset (%u)", offset,
1319         exif_reader->base_offset);
1320     return FALSE;
1321   }
1322
1323   real_offset = offset - exif_reader->base_offset;
1324
1325   data = gst_buffer_map (exif_reader->buffer, &size, NULL, GST_MAP_READ);
1326
1327   if (real_offset >= size) {
1328     GST_WARNING ("Invalid offset %u for buffer of size %u", real_offset, size);
1329     goto reader_fail;
1330   }
1331
1332   gst_byte_reader_init (&data_reader, data, size);
1333   if (!gst_byte_reader_set_pos (&data_reader, real_offset))
1334     goto reader_fail;
1335
1336   if (!is_signed) {
1337     guint32 aux_n = 0, aux_d = 0;
1338     if (exif_reader->byte_order == G_LITTLE_ENDIAN) {
1339       if (!gst_byte_reader_get_uint32_le (&data_reader, &aux_n) ||
1340           !gst_byte_reader_get_uint32_le (&data_reader, &aux_d))
1341         goto reader_fail;
1342     } else {
1343       if (!gst_byte_reader_get_uint32_be (&data_reader, &aux_n) ||
1344           !gst_byte_reader_get_uint32_be (&data_reader, &aux_d))
1345         goto reader_fail;
1346     }
1347     frac_n = (gint32) aux_n;
1348     frac_d = (gint32) aux_d;
1349   } else {
1350     if (exif_reader->byte_order == G_LITTLE_ENDIAN) {
1351       if (!gst_byte_reader_get_int32_le (&data_reader, &frac_n) ||
1352           !gst_byte_reader_get_int32_le (&data_reader, &frac_d))
1353         goto reader_fail;
1354     } else {
1355       if (!gst_byte_reader_get_int32_be (&data_reader, &frac_n) ||
1356           !gst_byte_reader_get_int32_be (&data_reader, &frac_d))
1357         goto reader_fail;
1358     }
1359   }
1360
1361   if (_frac_n)
1362     *_frac_n = frac_n;
1363   if (_frac_d)
1364     *_frac_d = frac_d;
1365
1366   gst_buffer_unmap (exif_reader->buffer, data, size);
1367
1368   return TRUE;
1369
1370 reader_fail:
1371   GST_WARNING ("Failed to read from byte reader. (Buffer too short?)");
1372   gst_buffer_unmap (exif_reader->buffer, data, size);
1373   return FALSE;
1374 }
1375
1376 static void
1377 parse_exif_rational_tag (GstExifReader * exif_reader,
1378     const gchar * gst_tag, guint32 count, guint32 offset, gdouble multiplier,
1379     gboolean is_signed)
1380 {
1381   GType type;
1382   gint32 frac_n = 0;
1383   gint32 frac_d = 1;
1384   gdouble value;
1385
1386   GST_DEBUG ("Reading fraction for tag %s...", gst_tag);
1387   if (!exif_reader_read_rational_tag (exif_reader, count, offset, is_signed,
1388           &frac_n, &frac_d))
1389     return;
1390   GST_DEBUG ("Read fraction for tag %s: %d/%d", gst_tag, frac_n, frac_d);
1391
1392   type = gst_tag_get_type (gst_tag);
1393   switch (type) {
1394     case G_TYPE_DOUBLE:
1395       gst_util_fraction_to_double (frac_n, frac_d, &value);
1396       value *= multiplier;
1397       GST_DEBUG ("Adding %s tag: %lf", gst_tag, value);
1398       gst_tag_list_add (exif_reader->taglist, GST_TAG_MERGE_REPLACE, gst_tag,
1399           value, NULL);
1400       break;
1401     default:
1402       if (type == GST_TYPE_FRACTION) {
1403         GValue fraction = { 0 };
1404
1405         g_value_init (&fraction, GST_TYPE_FRACTION);
1406         gst_value_set_fraction (&fraction, frac_n * multiplier, frac_d);
1407         gst_tag_list_add_value (exif_reader->taglist, GST_TAG_MERGE_REPLACE,
1408             gst_tag, &fraction);
1409         g_value_unset (&fraction);
1410       } else {
1411         GST_WARNING ("Can't convert from fraction into %s", g_type_name (type));
1412       }
1413   }
1414
1415 }
1416
1417 static GstBuffer *
1418 write_exif_ifd (const GstTagList * taglist, gboolean byte_order,
1419     guint32 base_offset, const GstExifTagMatch * tag_map)
1420 {
1421   GstExifWriter writer;
1422   gint i;
1423
1424   GST_DEBUG ("Formatting taglist %p as exif buffer. Byte order: %d, "
1425       "base_offset: %u", taglist, byte_order, base_offset);
1426
1427   g_assert (byte_order == G_LITTLE_ENDIAN || byte_order == G_BIG_ENDIAN);
1428
1429   if (!gst_tag_list_has_ifd_tags (taglist, tag_map)) {
1430     GST_DEBUG ("No tags for this ifd");
1431     return NULL;
1432   }
1433
1434   gst_exif_writer_init (&writer, byte_order);
1435
1436   /* write tag number as 0 */
1437   gst_byte_writer_put_uint16_le (&writer.tagwriter, 0);
1438
1439   /* write both tag headers and data
1440    * in ascending id order */
1441
1442   for (i = 0; tag_map[i].exif_tag != 0; i++) {
1443
1444     /* special cases have NULL gst tag */
1445     if (tag_map[i].gst_tag == NULL) {
1446       GstBuffer *inner_ifd = NULL;
1447       const GstExifTagMatch *inner_tag_map = NULL;
1448
1449       GST_LOG ("Inner ifd tag: %x", tag_map[i].exif_tag);
1450
1451       if (tag_map[i].exif_tag == EXIF_GPS_IFD_TAG) {
1452         inner_tag_map = tag_map_gps;
1453       } else if (tag_map[i].exif_tag == EXIF_IFD_TAG) {
1454         inner_tag_map = tag_map_exif;
1455       } else if (tag_map[i].exif_tag == EXIF_VERSION_TAG) {
1456         /* special case where we write the exif version */
1457         write_exif_undefined_tag (&writer, EXIF_VERSION_TAG, (guint8 *) "0230",
1458             4);
1459       } else if (tag_map[i].exif_tag == EXIF_FLASHPIX_VERSION_TAG) {
1460         /* special case where we write the flashpix version */
1461         write_exif_undefined_tag (&writer, EXIF_FLASHPIX_VERSION_TAG,
1462             (guint8 *) "0100", 4);
1463       }
1464
1465       if (inner_tag_map) {
1466         /* base offset and tagheader size are added when rewriting offset */
1467         inner_ifd = write_exif_ifd (taglist, byte_order,
1468             gst_byte_writer_get_size (&writer.datawriter), inner_tag_map);
1469       }
1470
1471       if (inner_ifd) {
1472         guint8 *data;
1473         gsize size;
1474
1475         GST_DEBUG ("Adding inner ifd: %x", tag_map[i].exif_tag);
1476         gst_exif_writer_write_tag_header (&writer, tag_map[i].exif_tag,
1477             EXIF_TYPE_LONG, 1,
1478             gst_byte_writer_get_size (&writer.datawriter), FALSE);
1479
1480         data = gst_buffer_map (inner_ifd, &size, NULL, GST_MAP_READ);
1481         gst_byte_writer_put_data (&writer.datawriter, data, size);
1482         gst_buffer_unmap (inner_ifd, data, size);
1483         gst_buffer_unref (inner_ifd);
1484       }
1485       continue;
1486     }
1487
1488     GST_LOG ("Checking tag %s", tag_map[i].gst_tag);
1489     if (gst_tag_list_get_value_index (taglist, tag_map[i].gst_tag, 0) == NULL)
1490       continue;
1491
1492     write_exif_tag_from_taglist (&writer, taglist, &tag_map[i]);
1493   }
1494
1495   /* Add the next IFD offset, we just set it to 0 because
1496    * there is no easy way to predict what it is going to be.
1497    * The user might rewrite the value if needed */
1498   gst_byte_writer_put_uint32_le (&writer.tagwriter, 0);
1499
1500   /* write the number of tags */
1501   gst_byte_writer_set_pos (&writer.tagwriter, 0);
1502   if (writer.byte_order == G_LITTLE_ENDIAN)
1503     gst_byte_writer_put_uint16_le (&writer.tagwriter, writer.tags_total);
1504   else
1505     gst_byte_writer_put_uint16_be (&writer.tagwriter, writer.tags_total);
1506
1507   GST_DEBUG ("Number of tags rewriten to %d", writer.tags_total);
1508
1509   /* now that we know the tag headers size, we can add the offsets */
1510   gst_exif_tag_rewrite_offsets (&writer.tagwriter, writer.byte_order,
1511       base_offset + gst_byte_writer_get_size (&writer.tagwriter),
1512       writer.tags_total, &writer.datawriter);
1513
1514   return gst_exif_writer_reset_and_get_buffer (&writer);
1515 }
1516
1517 static gboolean
1518 parse_exif_tag_header (GstByteReader * reader, gint byte_order,
1519     GstExifTagData * _tagdata)
1520 {
1521   g_assert (_tagdata);
1522
1523   /* read the fields */
1524   if (byte_order == G_LITTLE_ENDIAN) {
1525     if (!gst_byte_reader_get_uint16_le (reader, &_tagdata->tag) ||
1526         !gst_byte_reader_get_uint16_le (reader, &_tagdata->tag_type) ||
1527         !gst_byte_reader_get_uint32_le (reader, &_tagdata->count) ||
1528         !gst_byte_reader_get_data (reader, 4, &_tagdata->offset_as_data)) {
1529       return FALSE;
1530     }
1531     _tagdata->offset = GST_READ_UINT32_LE (_tagdata->offset_as_data);
1532   } else {
1533     if (!gst_byte_reader_get_uint16_be (reader, &_tagdata->tag) ||
1534         !gst_byte_reader_get_uint16_be (reader, &_tagdata->tag_type) ||
1535         !gst_byte_reader_get_uint32_be (reader, &_tagdata->count) ||
1536         !gst_byte_reader_get_data (reader, 4, &_tagdata->offset_as_data)) {
1537       return FALSE;
1538     }
1539     _tagdata->offset = GST_READ_UINT32_BE (_tagdata->offset_as_data);
1540   }
1541
1542   return TRUE;
1543 }
1544
1545 static gboolean
1546 parse_exif_ifd (GstExifReader * exif_reader, gint buf_offset,
1547     const GstExifTagMatch * tag_map)
1548 {
1549   GstByteReader reader;
1550   guint16 entries = 0;
1551   guint16 i;
1552   guint8 *data;
1553   gsize size;
1554
1555   g_return_val_if_fail (exif_reader->byte_order == G_LITTLE_ENDIAN
1556       || exif_reader->byte_order == G_BIG_ENDIAN, FALSE);
1557
1558   data = gst_buffer_map (exif_reader->buffer, &size, NULL, GST_MAP_READ);
1559
1560   gst_byte_reader_init (&reader, data, size);
1561   if (!gst_byte_reader_set_pos (&reader, buf_offset))
1562     goto invalid_offset;
1563
1564   /* read the IFD entries number */
1565   if (exif_reader->byte_order == G_LITTLE_ENDIAN) {
1566     if (!gst_byte_reader_get_uint16_le (&reader, &entries))
1567       goto read_error;
1568   } else {
1569     if (!gst_byte_reader_get_uint16_be (&reader, &entries))
1570       goto read_error;
1571   }
1572   GST_DEBUG ("Read number of entries: %u", entries);
1573
1574   /* iterate over the buffer and find the tags and stuff */
1575   for (i = 0; i < entries; i++) {
1576     GstExifTagData tagdata;
1577     gint map_index;
1578
1579     GST_LOG ("Reading entry: %u", i);
1580
1581     if (!parse_exif_tag_header (&reader, exif_reader->byte_order, &tagdata))
1582       goto read_error;
1583
1584     GST_DEBUG ("Parsed tag: id 0x%x, type %u, count %u, offset %u (0x%x)"
1585         ", buf size: %u", tagdata.tag, tagdata.tag_type, tagdata.count,
1586         tagdata.offset, tagdata.offset, gst_byte_reader_get_size (&reader));
1587
1588     map_index = exif_tag_map_find_reverse (tagdata.tag, tag_map, TRUE);
1589     if (map_index == -1) {
1590       GST_WARNING ("Unmapped exif tag: 0x%x", tagdata.tag);
1591       continue;
1592     }
1593
1594     /*
1595      * inner ifd tags handling, errors processing those are being ignored
1596      * and we try to continue the parsing
1597      */
1598     if (tagdata.tag == EXIF_GPS_IFD_TAG) {
1599       parse_exif_ifd (exif_reader,
1600           tagdata.offset - exif_reader->base_offset, tag_map_gps);
1601
1602       continue;
1603     }
1604     if (tagdata.tag == EXIF_IFD_TAG) {
1605       parse_exif_ifd (exif_reader,
1606           tagdata.offset - exif_reader->base_offset, tag_map_exif);
1607
1608       continue;
1609     }
1610     if (tagdata.tag == EXIF_VERSION_TAG ||
1611         tagdata.tag == EXIF_FLASHPIX_VERSION_TAG) {
1612       /* skip */
1613       continue;
1614     }
1615
1616     /* tags that need specialized deserialization */
1617     if (tag_map[map_index].deserialize) {
1618       i += tag_map[map_index].deserialize (exif_reader, &reader,
1619           &tag_map[map_index], &tagdata);
1620       continue;
1621     }
1622
1623     switch (tagdata.tag_type) {
1624       case EXIF_TYPE_ASCII:
1625         parse_exif_ascii_tag (exif_reader, &tag_map[map_index],
1626             tagdata.count, tagdata.offset, tagdata.offset_as_data);
1627         break;
1628       case EXIF_TYPE_RATIONAL:
1629         parse_exif_rational_tag (exif_reader, tag_map[map_index].gst_tag,
1630             tagdata.count, tagdata.offset, 1, FALSE);
1631         break;
1632       case EXIF_TYPE_SRATIONAL:
1633         parse_exif_rational_tag (exif_reader, tag_map[map_index].gst_tag,
1634             tagdata.count, tagdata.offset, 1, TRUE);
1635         break;
1636       case EXIF_TYPE_UNDEFINED:
1637         parse_exif_undefined_tag (exif_reader, &tag_map[map_index],
1638             tagdata.count, tagdata.offset, tagdata.offset_as_data);
1639       case EXIF_TYPE_LONG:
1640         parse_exif_long_tag (exif_reader, &tag_map[map_index],
1641             tagdata.count, tagdata.offset, tagdata.offset_as_data);
1642         break;
1643       default:
1644         GST_WARNING ("Unhandled tag type: %u", tagdata.tag_type);
1645         break;
1646     }
1647   }
1648
1649   /* check if the pending tags have something that can still be added */
1650   {
1651     GSList *walker;
1652     GstExifTagData *data;
1653
1654     for (walker = exif_reader->pending_tags; walker;
1655         walker = g_slist_next (walker)) {
1656       data = (GstExifTagData *) walker->data;
1657       switch (data->tag) {
1658         case EXIF_TAG_XRESOLUTION:
1659           parse_exif_rational_tag (exif_reader, GST_TAG_IMAGE_HORIZONTAL_PPI,
1660               data->count, data->offset, 1, FALSE);
1661           break;
1662         case EXIF_TAG_YRESOLUTION:
1663           parse_exif_rational_tag (exif_reader, GST_TAG_IMAGE_VERTICAL_PPI,
1664               data->count, data->offset, 1, FALSE);
1665           break;
1666         default:
1667           /* NOP */
1668           break;
1669       }
1670     }
1671   }
1672   gst_buffer_unmap (exif_reader->buffer, data, size);
1673
1674   return TRUE;
1675
1676 invalid_offset:
1677   {
1678     GST_WARNING ("Buffer offset invalid when parsing exif ifd");
1679     gst_buffer_unmap (exif_reader->buffer, data, size);
1680     return FALSE;
1681   }
1682 read_error:
1683   {
1684     GST_WARNING ("Failed to parse the exif ifd");
1685     gst_buffer_unmap (exif_reader->buffer, data, size);
1686     return FALSE;
1687   }
1688 }
1689
1690 /**
1691  * gst_tag_list_to_exif_buffer:
1692  * @taglist: The taglist
1693  * @byte_order: byte order used in writing (G_LITTLE_ENDIAN or G_BIG_ENDIAN)
1694  * @base_offset: Offset from the tiff header first byte
1695  *
1696  * Formats the tags in taglist on exif format. The resulting buffer contains
1697  * the tags IFD and is followed by the data pointed by the tag entries.
1698  *
1699  * Returns: A GstBuffer containing the tag entries followed by the tag data
1700  *
1701  * Since: 0.10.30
1702  */
1703 GstBuffer *
1704 gst_tag_list_to_exif_buffer (const GstTagList * taglist, gint byte_order,
1705     guint32 base_offset)
1706 {
1707   return write_exif_ifd (taglist, byte_order, base_offset, tag_map_ifd0);
1708 }
1709
1710 /**
1711  * gst_tag_list_to_exif_buffer_with_tiff_header:
1712  * @taglist: The taglist
1713  *
1714  * Formats the tags in taglist into exif structure, a tiff header
1715  * is put in the beginning of the buffer.
1716  *
1717  * Returns: A GstBuffer containing the data
1718  *
1719  * Since: 0.10.30
1720  */
1721 GstBuffer *
1722 gst_tag_list_to_exif_buffer_with_tiff_header (const GstTagList * taglist)
1723 {
1724   GstBuffer *ifd;
1725   GstByteWriter writer;
1726   gsize size;
1727   guint8 *data;
1728
1729   ifd = gst_tag_list_to_exif_buffer (taglist, G_BYTE_ORDER, 8);
1730   if (ifd == NULL) {
1731     GST_WARNING ("Failed to create exif buffer");
1732     return NULL;
1733   }
1734
1735   data = gst_buffer_map (ifd, &size, NULL, GST_MAP_READ);
1736
1737   size += TIFF_HEADER_SIZE;
1738
1739   /* TODO what is the correct endianness here? */
1740   gst_byte_writer_init_with_size (&writer, size, FALSE);
1741   /* TIFF header */
1742   if (G_BYTE_ORDER == G_LITTLE_ENDIAN) {
1743     gst_byte_writer_put_uint16_le (&writer, TIFF_LITTLE_ENDIAN);
1744     gst_byte_writer_put_uint16_le (&writer, 42);
1745     gst_byte_writer_put_uint32_le (&writer, 8);
1746   } else {
1747     gst_byte_writer_put_uint16_be (&writer, TIFF_BIG_ENDIAN);
1748     gst_byte_writer_put_uint16_be (&writer, 42);
1749     gst_byte_writer_put_uint32_be (&writer, 8);
1750   }
1751   if (!gst_byte_writer_put_data (&writer, data, size)) {
1752     GST_WARNING ("Byte writer size mismatch");
1753     /* reaching here is a programming error because we should have a buffer
1754      * large enough */
1755     g_assert_not_reached ();
1756     gst_buffer_unmap (ifd, data, size);
1757     gst_buffer_unref (ifd);
1758     gst_byte_writer_reset (&writer);
1759     return NULL;
1760   }
1761   gst_buffer_unmap (ifd, data, size);
1762   gst_buffer_unref (ifd);
1763
1764   return gst_byte_writer_reset_and_get_buffer (&writer);
1765 }
1766
1767 /**
1768  * gst_tag_list_from_exif_buffer:
1769  * @buffer: The exif buffer
1770  * @byte_order: byte order of the data
1771  * @base_offset: Offset from the tiff header to this buffer
1772  *
1773  * Parses the IFD and IFD tags data contained in the buffer and puts it
1774  * on a taglist. The base_offset is used to subtract from the offset in
1775  * the tag entries and be able to get the offset relative to the buffer
1776  * start
1777  *
1778  * Returns: The parsed taglist
1779  *
1780  * Since: 0.10.30
1781  */
1782 GstTagList *
1783 gst_tag_list_from_exif_buffer (GstBuffer * buffer, gint byte_order,
1784     guint32 base_offset)
1785 {
1786   GstExifReader reader;
1787   g_return_val_if_fail (byte_order == G_LITTLE_ENDIAN
1788       || byte_order == G_BIG_ENDIAN, NULL);
1789
1790   gst_exif_reader_init (&reader, byte_order, buffer, base_offset);
1791
1792   if (!parse_exif_ifd (&reader, 0, tag_map_ifd0))
1793     goto read_error;
1794
1795   return gst_exif_reader_reset (&reader, TRUE);
1796
1797 read_error:
1798   {
1799     gst_exif_reader_reset (&reader, FALSE);
1800     GST_WARNING ("Failed to parse the exif buffer");
1801     return NULL;
1802   }
1803 }
1804
1805 /**
1806  * gst_tag_list_from_exif_buffer_with_tiff_header:
1807  * @buffer: The exif buffer
1808  *
1809  * Parses the exif tags starting with a tiff header structure.
1810  *
1811  * Returns: The taglist
1812  *
1813  * Since: 0.10.30
1814  */
1815 GstTagList *
1816 gst_tag_list_from_exif_buffer_with_tiff_header (GstBuffer * buffer)
1817 {
1818   GstByteReader reader;
1819   guint16 fortytwo = 42;
1820   guint16 endianness = 0;
1821   guint32 offset;
1822   GstTagList *taglist = NULL;
1823   GstBuffer *subbuffer;
1824   guint8 *data, *sdata;
1825   gsize size, ssize;
1826
1827   data = gst_buffer_map (buffer, &size, NULL, GST_MAP_READ);
1828
1829   GST_LOG ("Parsing exif tags with tiff header of size %u", size);
1830
1831   gst_byte_reader_init (&reader, data, size);
1832
1833   GST_LOG ("Parsing the tiff header");
1834   if (!gst_byte_reader_get_uint16_be (&reader, &endianness)) {
1835     goto byte_reader_fail;
1836   }
1837
1838   if (endianness == TIFF_LITTLE_ENDIAN) {
1839     if (!gst_byte_reader_get_uint16_le (&reader, &fortytwo) ||
1840         !gst_byte_reader_get_uint32_le (&reader, &offset))
1841       goto byte_reader_fail;
1842   } else if (endianness == TIFF_BIG_ENDIAN) {
1843     if (!gst_byte_reader_get_uint16_be (&reader, &fortytwo) ||
1844         !gst_byte_reader_get_uint32_be (&reader, &offset))
1845       goto byte_reader_fail;
1846   } else
1847     goto invalid_endianness;
1848
1849   if (fortytwo != 42)
1850     goto invalid_magic;
1851
1852   subbuffer = gst_buffer_new_and_alloc (size - (TIFF_HEADER_SIZE - 2));
1853
1854   sdata = gst_buffer_map (subbuffer, &ssize, NULL, GST_MAP_WRITE);
1855   memcpy (sdata, data + TIFF_HEADER_SIZE, size - TIFF_HEADER_SIZE);
1856   gst_buffer_unmap (subbuffer, sdata, ssize);
1857
1858   taglist = gst_tag_list_from_exif_buffer (subbuffer,
1859       endianness == TIFF_LITTLE_ENDIAN ? G_LITTLE_ENDIAN : G_BIG_ENDIAN, 8);
1860
1861   gst_buffer_unref (subbuffer);
1862
1863 done:
1864   gst_buffer_unmap (buffer, data, size);
1865
1866   return taglist;
1867
1868 byte_reader_fail:
1869   {
1870     GST_WARNING ("Failed to read values from buffer");
1871     goto done;
1872   }
1873 invalid_endianness:
1874   {
1875     GST_WARNING ("Invalid endianness number %u", endianness);
1876     goto done;
1877   }
1878 invalid_magic:
1879   {
1880     GST_WARNING ("Invalid magic number %u, should be 42", fortytwo);
1881     goto done;
1882   }
1883 }
1884
1885 /* special serialization functions */
1886 EXIF_SERIALIZATION_DESERIALIZATION_MAP_STRING_TO_INT_FUNC (contrast,
1887     capturing_contrast);
1888 EXIF_SERIALIZATION_DESERIALIZATION_MAP_STRING_TO_INT_FUNC (exposure_mode,
1889     capturing_exposure_mode);
1890 EXIF_SERIALIZATION_DESERIALIZATION_MAP_STRING_TO_INT_FUNC (exposure_program,
1891     capturing_exposure_program);
1892 EXIF_SERIALIZATION_DESERIALIZATION_MAP_STRING_TO_INT_FUNC (gain_control,
1893     capturing_gain_adjustment);
1894 EXIF_SERIALIZATION_DESERIALIZATION_MAP_STRING_TO_INT_FUNC (metering_mode,
1895     capturing_metering_mode);
1896 EXIF_SERIALIZATION_DESERIALIZATION_MAP_STRING_TO_INT_FUNC (orientation,
1897     image_orientation);
1898 EXIF_SERIALIZATION_DESERIALIZATION_MAP_STRING_TO_INT_FUNC (saturation,
1899     capturing_saturation);
1900 EXIF_SERIALIZATION_DESERIALIZATION_MAP_STRING_TO_INT_FUNC (scene_capture_type,
1901     capturing_scene_capture_type);
1902 EXIF_SERIALIZATION_DESERIALIZATION_MAP_STRING_TO_INT_FUNC (sharpness,
1903     capturing_sharpness);
1904 EXIF_SERIALIZATION_DESERIALIZATION_MAP_STRING_TO_INT_FUNC (source,
1905     capturing_source);
1906 EXIF_SERIALIZATION_DESERIALIZATION_MAP_STRING_TO_INT_FUNC (white_balance,
1907     capturing_white_balance);
1908
1909 static void
1910 serialize_geo_coordinate (GstExifWriter * writer, const GstTagList * taglist,
1911     const GstExifTagMatch * exiftag)
1912 {
1913   gboolean latitude;
1914   gdouble value;
1915   gint degrees;
1916   gint minutes;
1917   gint seconds;
1918   guint32 offset;
1919
1920   latitude = exiftag->exif_tag == EXIF_TAG_GPS_LATITUDE;        /* exif tag for latitude */
1921   if (!gst_tag_list_get_double (taglist, exiftag->gst_tag, &value)) {
1922     GST_WARNING ("Failed to get double from tag list for tag: %s",
1923         exiftag->gst_tag);
1924     return;
1925   }
1926
1927   /* first write the Latitude or Longitude Ref */
1928   if (latitude) {
1929     if (value >= 0) {
1930       write_exif_ascii_tag (writer, exiftag->complementary_tag, "N");
1931     } else {
1932       value *= -1;
1933       write_exif_ascii_tag (writer, exiftag->complementary_tag, "S");
1934     }
1935   } else {
1936     if (value >= 0) {
1937       write_exif_ascii_tag (writer, exiftag->complementary_tag, "E");
1938     } else {
1939       value *= -1;
1940       write_exif_ascii_tag (writer, exiftag->complementary_tag, "W");
1941     }
1942   }
1943
1944   /* now write the degrees stuff */
1945   GST_LOG ("Converting geo location %lf to degrees", value);
1946   degrees = (gint) value;
1947   value -= degrees;
1948   minutes = (gint) (value * 60);
1949   value = (value * 60) - minutes;
1950   seconds = (gint) (value * 60);
1951   GST_LOG ("Converted geo location to %d.%d'%d'' degrees", degrees,
1952       minutes, seconds);
1953
1954   offset = gst_byte_writer_get_size (&writer->datawriter);
1955   gst_exif_writer_write_tag_header (writer, exiftag->exif_tag,
1956       EXIF_TYPE_RATIONAL, 3, offset, FALSE);
1957   gst_exif_writer_write_rational_data (writer, degrees, 1);
1958   gst_exif_writer_write_rational_data (writer, minutes, 1);
1959   gst_exif_writer_write_rational_data (writer, seconds, 1);
1960 }
1961
1962 static gint
1963 deserialize_geo_coordinate (GstExifReader * exif_reader,
1964     GstByteReader * reader, const GstExifTagMatch * exiftag,
1965     GstExifTagData * tagdata)
1966 {
1967   GstByteReader fractions_reader;
1968   gint multiplier;
1969   GstExifTagData next_tagdata;
1970   gint ret = 0;
1971   /* for the conversion */
1972   guint32 degrees_n = 0;
1973   guint32 degrees_d = 1;
1974   guint32 minutes_n = 0;
1975   guint32 minutes_d = 1;
1976   guint32 seconds_n = 0;
1977   guint32 seconds_d = 1;
1978   gdouble degrees;
1979   gdouble minutes;
1980   gdouble seconds;
1981   guint8 *data = NULL;
1982   gsize size = 0;
1983
1984   GST_LOG ("Starting to parse %s tag in exif 0x%x", exiftag->gst_tag,
1985       exiftag->exif_tag);
1986
1987   if (exiftag->complementary_tag != tagdata->tag) {
1988     /* First should come the 'Ref' tags */
1989     GST_WARNING ("Tag %d is not the 'Ref' tag for latitude nor longitude",
1990         tagdata->tag);
1991     return ret;
1992   }
1993
1994   if (tagdata->offset_as_data[0] == 'N' || tagdata->offset_as_data[0] == 'E') {
1995     multiplier = 1;
1996   } else if (tagdata->offset_as_data[0] == 'S'
1997       || tagdata->offset_as_data[0] == 'W') {
1998     multiplier = -1;
1999   } else {
2000     GST_WARNING ("Invalid LatitudeRef or LongitudeRef %c",
2001         tagdata->offset_as_data[0]);
2002     return ret;
2003   }
2004
2005   /* now read the following tag that must be the latitude or longitude */
2006   if (exif_reader->byte_order == G_LITTLE_ENDIAN) {
2007     if (!gst_byte_reader_peek_uint16_le (reader, &next_tagdata.tag))
2008       goto reader_fail;
2009   } else {
2010     if (!gst_byte_reader_peek_uint16_be (reader, &next_tagdata.tag))
2011       goto reader_fail;
2012   }
2013
2014   if (exiftag->exif_tag != next_tagdata.tag) {
2015     GST_WARNING ("This is not a geo cordinate tag");
2016     return ret;
2017   }
2018
2019   /* read the remaining tag entry data */
2020   if (!parse_exif_tag_header (reader, exif_reader->byte_order, &next_tagdata)) {
2021     ret = -1;
2022     goto reader_fail;
2023   }
2024
2025   ret = 1;
2026
2027   /* some checking */
2028   if (next_tagdata.tag_type != EXIF_TYPE_RATIONAL) {
2029     GST_WARNING ("Invalid type %d for geo coordinate (latitude/longitude)",
2030         next_tagdata.tag_type);
2031     return ret;
2032   }
2033   if (next_tagdata.count != 3) {
2034     GST_WARNING ("Geo coordinate should use 3 fractions, we have %u",
2035         next_tagdata.count);
2036     return ret;
2037   }
2038
2039   data = gst_buffer_map (exif_reader->buffer, &size, NULL, GST_MAP_READ);
2040
2041   /* now parse the fractions */
2042   gst_byte_reader_init (&fractions_reader, data, size);
2043
2044   if (!gst_byte_reader_set_pos (&fractions_reader,
2045           next_tagdata.offset - exif_reader->base_offset))
2046     goto reader_fail;
2047
2048   if (exif_reader->byte_order == G_LITTLE_ENDIAN) {
2049     if (!gst_byte_reader_get_uint32_le (&fractions_reader, &degrees_n) ||
2050         !gst_byte_reader_get_uint32_le (&fractions_reader, &degrees_d) ||
2051         !gst_byte_reader_get_uint32_le (&fractions_reader, &minutes_n) ||
2052         !gst_byte_reader_get_uint32_le (&fractions_reader, &minutes_d) ||
2053         !gst_byte_reader_get_uint32_le (&fractions_reader, &seconds_n) ||
2054         !gst_byte_reader_get_uint32_le (&fractions_reader, &seconds_d))
2055       goto reader_fail;
2056   } else {
2057     if (!gst_byte_reader_get_uint32_be (&fractions_reader, &degrees_n) ||
2058         !gst_byte_reader_get_uint32_be (&fractions_reader, &degrees_d) ||
2059         !gst_byte_reader_get_uint32_be (&fractions_reader, &minutes_n) ||
2060         !gst_byte_reader_get_uint32_be (&fractions_reader, &minutes_d) ||
2061         !gst_byte_reader_get_uint32_be (&fractions_reader, &seconds_n) ||
2062         !gst_byte_reader_get_uint32_be (&fractions_reader, &seconds_d))
2063       goto reader_fail;
2064   }
2065   gst_buffer_unmap (exif_reader->buffer, data, size);
2066
2067   GST_DEBUG ("Read degrees fraction for tag %s: %u/%u %u/%u %u/%u",
2068       exiftag->gst_tag, degrees_n, degrees_d, minutes_n, minutes_d,
2069       seconds_n, seconds_d);
2070
2071   gst_util_fraction_to_double (degrees_n, degrees_d, &degrees);
2072   gst_util_fraction_to_double (minutes_n, minutes_d, &minutes);
2073   gst_util_fraction_to_double (seconds_n, seconds_d, &seconds);
2074
2075   minutes += seconds / 60;
2076   degrees += minutes / 60;
2077   degrees *= multiplier;
2078
2079   GST_DEBUG ("Adding %s tag: %lf", exiftag->gst_tag, degrees);
2080   gst_tag_list_add (exif_reader->taglist, GST_TAG_MERGE_REPLACE,
2081       exiftag->gst_tag, degrees, NULL);
2082
2083   return ret;
2084
2085 reader_fail:
2086   GST_WARNING ("Failed to read fields from buffer (too short?)");
2087   if (data)
2088     gst_buffer_unmap (exif_reader->buffer, data, size);
2089   return ret;
2090 }
2091
2092
2093 static void
2094 serialize_geo_direction (GstExifWriter * writer, const GstTagList * taglist,
2095     const GstExifTagMatch * exiftag)
2096 {
2097   gdouble value;
2098
2099   if (!gst_tag_list_get_double (taglist, exiftag->gst_tag, &value)) {
2100     GST_WARNING ("Failed to get double from tag list for tag: %s",
2101         exiftag->gst_tag);
2102     return;
2103   }
2104
2105   /* first write the direction ref */
2106   write_exif_ascii_tag (writer, exiftag->complementary_tag, "T");
2107   gst_exif_writer_write_rational_tag_from_double (writer,
2108       exiftag->exif_tag, value);
2109 }
2110
2111 static gint
2112 deserialize_geo_direction (GstExifReader * exif_reader,
2113     GstByteReader * reader, const GstExifTagMatch * exiftag,
2114     GstExifTagData * tagdata)
2115 {
2116   GstExifTagData next_tagdata = { 0, };
2117   gint ret = 0;
2118
2119   GST_LOG ("Starting to parse %s tag in exif 0x%x", exiftag->gst_tag,
2120       exiftag->exif_tag);
2121
2122   if (exiftag->complementary_tag == tagdata->tag) {
2123     /* First should come the 'Ref' tags */
2124     if (tagdata->offset_as_data[0] == 'M') {
2125       GST_WARNING ("Magnetic direction is not supported");
2126       return ret;
2127     } else if (tagdata->offset_as_data[0] == 'T') {
2128       /* nop */
2129     } else {
2130       GST_WARNING ("Invalid Ref for direction or track %c",
2131           tagdata->offset_as_data[0]);
2132       return ret;
2133     }
2134   } else {
2135     GST_DEBUG ("No Direction Ref, using default=T");
2136     if (tagdata->tag == exiftag->exif_tag) {
2137       /* this is the main tag */
2138       tagdata_copy (&next_tagdata, tagdata);
2139     }
2140   }
2141
2142   if (next_tagdata.tag == 0) {
2143     /* now read the following tag that must be the exif_tag */
2144     if (exif_reader->byte_order == G_LITTLE_ENDIAN) {
2145       if (!gst_byte_reader_peek_uint16_le (reader, &next_tagdata.tag))
2146         goto reader_fail;
2147     } else {
2148       if (!gst_byte_reader_peek_uint16_be (reader, &next_tagdata.tag))
2149         goto reader_fail;
2150     }
2151
2152     if (exiftag->exif_tag != next_tagdata.tag) {
2153       GST_WARNING ("Unexpected tag");
2154       return ret;
2155     }
2156
2157     /* read the remaining tag entry data */
2158     if (!parse_exif_tag_header (reader, exif_reader->byte_order, &next_tagdata)) {
2159       ret = -1;
2160       goto reader_fail;
2161     }
2162     ret = 1;
2163   }
2164
2165   /* some checking */
2166   if (next_tagdata.tag_type != EXIF_TYPE_RATIONAL) {
2167     GST_WARNING ("Invalid type %d for 0x%x", next_tagdata.tag_type,
2168         next_tagdata.tag);
2169     return ret;
2170   }
2171   if (next_tagdata.count != 1) {
2172     GST_WARNING ("0x%x tag must have a single fraction, we have %u",
2173         next_tagdata.tag_type, next_tagdata.count);
2174     return ret;
2175   }
2176
2177   parse_exif_rational_tag (exif_reader,
2178       exiftag->gst_tag, next_tagdata.count, next_tagdata.offset, 1, FALSE);
2179
2180   return ret;
2181
2182 reader_fail:
2183   GST_WARNING ("Failed to read fields from buffer (too short?)");
2184   return ret;
2185 }
2186
2187
2188 static void
2189 serialize_geo_elevation (GstExifWriter * writer, const GstTagList * taglist,
2190     const GstExifTagMatch * exiftag)
2191 {
2192   gdouble value;
2193
2194   if (!gst_tag_list_get_double (taglist, exiftag->gst_tag, &value)) {
2195     GST_WARNING ("Failed to get double from tag list for tag: %s",
2196         exiftag->gst_tag);
2197     return;
2198   }
2199
2200   /* first write the Ref */
2201   gst_exif_writer_write_byte_tag (writer,
2202       exiftag->complementary_tag, value >= 0 ? 0 : 1);
2203
2204   if (value < 0)
2205     value *= -1;
2206
2207   /* now the value */
2208   gst_exif_writer_write_rational_tag_from_double (writer,
2209       exiftag->exif_tag, value);
2210 }
2211
2212 static gint
2213 deserialize_geo_elevation (GstExifReader * exif_reader,
2214     GstByteReader * reader, const GstExifTagMatch * exiftag,
2215     GstExifTagData * tagdata)
2216 {
2217   GstExifTagData next_tagdata = { 0, };
2218   gint multiplier = 1;
2219   gint ret = 0;
2220
2221   GST_LOG ("Starting to parse %s tag in exif 0x%x", exiftag->gst_tag,
2222       exiftag->exif_tag);
2223
2224   if (exiftag->complementary_tag == tagdata->tag) {
2225     if (tagdata->offset_as_data[0] == 0) {
2226       /* NOP */
2227     } else if (tagdata->offset_as_data[0] == 1) {
2228       multiplier = -1;
2229     } else {
2230       GST_WARNING ("Invalid GPSAltitudeRef %u", tagdata->offset_as_data[0]);
2231       return ret;
2232     }
2233   } else {
2234     GST_DEBUG ("No GPSAltitudeRef, using default=0");
2235     if (tagdata->tag == exiftag->exif_tag) {
2236       tagdata_copy (&next_tagdata, tagdata);
2237     }
2238   }
2239
2240   /* now read the following tag that must be the exif_tag */
2241   if (next_tagdata.tag == 0) {
2242     if (exif_reader->byte_order == G_LITTLE_ENDIAN) {
2243       if (!gst_byte_reader_peek_uint16_le (reader, &next_tagdata.tag))
2244         goto reader_fail;
2245     } else {
2246       if (!gst_byte_reader_peek_uint16_be (reader, &next_tagdata.tag))
2247         goto reader_fail;
2248     }
2249
2250     if (exiftag->exif_tag != next_tagdata.tag) {
2251       GST_WARNING ("Unexpected tag");
2252       return ret;
2253     }
2254
2255     /* read the remaining tag entry data */
2256     if (!parse_exif_tag_header (reader, exif_reader->byte_order, &next_tagdata)) {
2257       ret = -1;
2258       goto reader_fail;
2259     }
2260     ret = 1;
2261   }
2262
2263   /* some checking */
2264   if (next_tagdata.tag_type != EXIF_TYPE_RATIONAL) {
2265     GST_WARNING ("Invalid type %d for 0x%x", next_tagdata.tag_type,
2266         next_tagdata.tag);
2267     return ret;
2268   }
2269   if (next_tagdata.count != 1) {
2270     GST_WARNING ("0x%x tag must have a single fraction, we have %u",
2271         next_tagdata.tag_type, next_tagdata.count);
2272     return ret;
2273   }
2274
2275   parse_exif_rational_tag (exif_reader,
2276       exiftag->gst_tag, next_tagdata.count, next_tagdata.offset, multiplier,
2277       FALSE);
2278
2279   return ret;
2280
2281 reader_fail:
2282   GST_WARNING ("Failed to read fields from buffer (too short?)");
2283   return ret;
2284 }
2285
2286
2287 static void
2288 serialize_speed (GstExifWriter * writer, const GstTagList * taglist,
2289     const GstExifTagMatch * exiftag)
2290 {
2291   gdouble value;
2292
2293   if (!gst_tag_list_get_double (taglist, exiftag->gst_tag, &value)) {
2294     GST_WARNING ("Failed to get double from tag list for tag: %s",
2295         exiftag->gst_tag);
2296     return;
2297   }
2298
2299   /* first write the Ref */
2300   write_exif_ascii_tag (writer, exiftag->complementary_tag, "K");
2301
2302   /* now the value */
2303   gst_exif_writer_write_rational_tag_from_double (writer,
2304       exiftag->exif_tag, value * METERS_PER_SECOND_TO_KILOMETERS_PER_HOUR);
2305 }
2306
2307 static gint
2308 deserialize_speed (GstExifReader * exif_reader,
2309     GstByteReader * reader, const GstExifTagMatch * exiftag,
2310     GstExifTagData * tagdata)
2311 {
2312   GstExifTagData next_tagdata = { 0, };
2313   gdouble multiplier = 1;
2314   gint ret = 0;
2315
2316   GST_LOG ("Starting to parse %s tag in exif 0x%x", exiftag->gst_tag,
2317       exiftag->exif_tag);
2318
2319   if (exiftag->complementary_tag == tagdata->tag) {
2320     if (tagdata->offset_as_data[0] == 'K') {
2321       multiplier = KILOMETERS_PER_HOUR_TO_METERS_PER_SECOND;
2322     } else if (tagdata->offset_as_data[0] == 'M') {
2323       multiplier = MILES_PER_HOUR_TO_METERS_PER_SECOND;
2324     } else if (tagdata->offset_as_data[0] == 'N') {
2325       multiplier = KNOTS_TO_METERS_PER_SECOND;
2326     } else {
2327       GST_WARNING ("Invalid GPSSpeedRed %c", tagdata->offset_as_data[0]);
2328       return ret;
2329     }
2330   } else {
2331     GST_DEBUG ("No GPSSpeedRef, using default=K");
2332     multiplier = KILOMETERS_PER_HOUR_TO_METERS_PER_SECOND;
2333
2334     if (tagdata->tag == exiftag->exif_tag) {
2335       tagdata_copy (&next_tagdata, tagdata);
2336     }
2337   }
2338
2339   /* now read the following tag that must be the exif_tag */
2340   if (next_tagdata.tag == 0) {
2341     if (exif_reader->byte_order == G_LITTLE_ENDIAN) {
2342       if (!gst_byte_reader_peek_uint16_le (reader, &next_tagdata.tag))
2343         goto reader_fail;
2344     } else {
2345       if (!gst_byte_reader_peek_uint16_be (reader, &next_tagdata.tag))
2346         goto reader_fail;
2347     }
2348
2349     if (exiftag->exif_tag != next_tagdata.tag) {
2350       GST_WARNING ("Unexpected tag");
2351       return ret;
2352     }
2353
2354     /* read the remaining tag entry data */
2355     if (!parse_exif_tag_header (reader, exif_reader->byte_order, &next_tagdata)) {
2356       ret = -1;
2357       goto reader_fail;
2358     }
2359     ret = 1;
2360   }
2361
2362
2363   /* some checking */
2364   if (next_tagdata.tag_type != EXIF_TYPE_RATIONAL) {
2365     GST_WARNING ("Invalid type %d for 0x%x", next_tagdata.tag_type,
2366         next_tagdata.tag);
2367     return ret;
2368   }
2369   if (next_tagdata.count != 1) {
2370     GST_WARNING ("0x%x tag must have a single fraction, we have %u",
2371         next_tagdata.tag_type, next_tagdata.count);
2372     return ret;
2373   }
2374
2375   parse_exif_rational_tag (exif_reader,
2376       exiftag->gst_tag, next_tagdata.count, next_tagdata.offset, multiplier,
2377       FALSE);
2378
2379   return ret;
2380
2381 reader_fail:
2382   GST_WARNING ("Failed to read fields from buffer (too short?)");
2383   return ret;
2384 }
2385
2386 static void
2387 serialize_shutter_speed (GstExifWriter * writer, const GstTagList * taglist,
2388     const GstExifTagMatch * exiftag)
2389 {
2390   const GValue *value = NULL;
2391   gdouble num;
2392
2393   value = gst_tag_list_get_value_index (taglist, exiftag->gst_tag, 0);
2394   if (!value) {
2395     GST_WARNING ("Failed to get shutter speed from from tag list");
2396     return;
2397   }
2398   gst_util_fraction_to_double (gst_value_get_fraction_numerator (value),
2399       gst_value_get_fraction_denominator (value), &num);
2400
2401 #ifdef HAVE_LOG2
2402   num = -log2 (num);
2403 #else
2404   num = -log (num) / M_LN2;
2405 #endif
2406
2407   /* now the value */
2408   gst_exif_writer_write_signed_rational_tag_from_double (writer,
2409       exiftag->exif_tag, num);
2410 }
2411
2412 static gint
2413 deserialize_shutter_speed (GstExifReader * exif_reader,
2414     GstByteReader * reader, const GstExifTagMatch * exiftag,
2415     GstExifTagData * tagdata)
2416 {
2417   gint32 frac_n, frac_d;
2418   gdouble d;
2419   GValue value = { 0 };
2420
2421   GST_LOG ("Starting to parse %s tag in exif 0x%x", exiftag->gst_tag,
2422       exiftag->exif_tag);
2423
2424   if (!exif_reader_read_rational_tag (exif_reader, tagdata->count,
2425           tagdata->offset, TRUE, &frac_n, &frac_d))
2426     return 0;
2427
2428   gst_util_fraction_to_double (frac_n, frac_d, &d);
2429   d = pow (2, -d);
2430   gst_util_double_to_fraction (d, &frac_n, &frac_d);
2431
2432   g_value_init (&value, GST_TYPE_FRACTION);
2433   gst_value_set_fraction (&value, frac_n, frac_d);
2434   gst_tag_list_add_value (exif_reader->taglist, GST_TAG_MERGE_KEEP,
2435       exiftag->gst_tag, &value);
2436   g_value_unset (&value);
2437
2438   return 0;
2439 }
2440
2441 static void
2442 serialize_aperture_value (GstExifWriter * writer, const GstTagList * taglist,
2443     const GstExifTagMatch * exiftag)
2444 {
2445   gdouble num;
2446
2447   if (!gst_tag_list_get_double_index (taglist, exiftag->gst_tag, 0, &num)) {
2448     GST_WARNING ("Failed to get focal ratio from from tag list");
2449     return;
2450   }
2451 #ifdef HAVE_LOG2
2452   num = 2 * log2 (num);
2453 #else
2454   num = 2 * (log (num) / M_LN2);
2455 #endif
2456
2457   /* now the value */
2458   gst_exif_writer_write_rational_tag_from_double (writer,
2459       exiftag->exif_tag, num);
2460 }
2461
2462 static gint
2463 deserialize_aperture_value (GstExifReader * exif_reader,
2464     GstByteReader * reader, const GstExifTagMatch * exiftag,
2465     GstExifTagData * tagdata)
2466 {
2467   gint32 frac_n, frac_d;
2468   gdouble d;
2469
2470   GST_LOG ("Starting to parse %s tag in exif 0x%x", exiftag->gst_tag,
2471       exiftag->exif_tag);
2472
2473   if (!exif_reader_read_rational_tag (exif_reader, tagdata->count,
2474           tagdata->offset, FALSE, &frac_n, &frac_d))
2475     return 0;
2476
2477   gst_util_fraction_to_double (frac_n, frac_d, &d);
2478   d = pow (2, d / 2);
2479
2480   gst_tag_list_add (exif_reader->taglist, GST_TAG_MERGE_KEEP,
2481       exiftag->gst_tag, d, NULL);
2482
2483   return 0;
2484 }
2485
2486 static void
2487 serialize_sensitivity_type (GstExifWriter * writer, const GstTagList * taglist,
2488     const GstExifTagMatch * exiftag)
2489 {
2490   /* we only support ISOSpeed as the sensitivity type (3) */
2491   gst_exif_writer_write_short_tag (writer, exiftag->exif_tag, 3);
2492 }
2493
2494 static gint
2495 deserialize_sensitivity_type (GstExifReader * exif_reader,
2496     GstByteReader * reader, const GstExifTagMatch * exiftag,
2497     GstExifTagData * tagdata)
2498 {
2499   GstExifTagData *sensitivity = NULL;
2500   guint16 type_data;
2501
2502   if (exif_reader->byte_order == G_LITTLE_ENDIAN) {
2503     type_data = GST_READ_UINT16_LE (tagdata->offset_as_data);
2504   } else {
2505     type_data = GST_READ_UINT16_BE (tagdata->offset_as_data);
2506   }
2507
2508   if (type_data != 3) {
2509     GST_WARNING ("We only support SensitivityType=3");
2510     return 0;
2511   }
2512
2513   /* check the pending tags for the PhotographicSensitivity tag */
2514   sensitivity =
2515       gst_exif_reader_get_pending_tag (exif_reader,
2516       EXIF_TAG_PHOTOGRAPHIC_SENSITIVITY);
2517   if (sensitivity == NULL) {
2518     GST_WARNING ("PhotographicSensitivity tag not found");
2519     return 0;
2520   }
2521
2522   GST_LOG ("Starting to parse %s tag in exif 0x%x", exiftag->gst_tag,
2523       exiftag->exif_tag);
2524
2525   gst_tag_list_add (exif_reader->taglist, GST_TAG_MERGE_KEEP,
2526       GST_TAG_CAPTURING_ISO_SPEED, sensitivity->offset_as_data, NULL);
2527
2528   return 0;
2529 }
2530
2531 static void
2532 serialize_flash (GstExifWriter * writer, const GstTagList * taglist,
2533     const GstExifTagMatch * exiftag)
2534 {
2535   gboolean flash_fired;
2536   const gchar *flash_mode;
2537   guint16 tagvalue = 0;
2538
2539   if (!gst_tag_list_get_boolean_index (taglist, exiftag->gst_tag, 0,
2540           &flash_fired)) {
2541     GST_WARNING ("Failed to get flash fired from from tag list");
2542     return;
2543   }
2544
2545   if (flash_fired)
2546     tagvalue = 1;
2547
2548   if (gst_tag_list_peek_string_index (taglist, GST_TAG_CAPTURING_FLASH_MODE, 0,
2549           &flash_mode)) {
2550     guint16 mode = 0;
2551     if (strcmp (flash_mode, "auto") == 0) {
2552       mode = 3;
2553     } else if (strcmp (flash_mode, "always") == 0) {
2554       mode = 1;
2555     } else if (strcmp (flash_mode, "never") == 0) {
2556       mode = 2;
2557     }
2558
2559     tagvalue = tagvalue | (mode << 3);
2560   } else {
2561     GST_DEBUG ("flash-mode not available");
2562   }
2563
2564   gst_exif_writer_write_short_tag (writer, exiftag->exif_tag, tagvalue);
2565 }
2566
2567 static gint
2568 deserialize_flash (GstExifReader * exif_reader,
2569     GstByteReader * reader, const GstExifTagMatch * exiftag,
2570     GstExifTagData * tagdata)
2571 {
2572   guint16 value = 0;
2573   guint mode = 0;
2574   const gchar *mode_str = NULL;
2575
2576   GST_LOG ("Starting to parse %s tag in exif 0x%x", exiftag->gst_tag,
2577       exiftag->exif_tag);
2578
2579   if (exif_reader->byte_order == G_LITTLE_ENDIAN) {
2580     value = GST_READ_UINT16_LE (tagdata->offset_as_data);
2581   } else {
2582     value = GST_READ_UINT16_BE (tagdata->offset_as_data);
2583   }
2584
2585   /* check flash fired */
2586   if (value & 0x1) {
2587     gst_tag_list_add (exif_reader->taglist, GST_TAG_MERGE_REPLACE,
2588         GST_TAG_CAPTURING_FLASH_FIRED, TRUE, NULL);
2589   } else {
2590     gst_tag_list_add (exif_reader->taglist, GST_TAG_MERGE_REPLACE,
2591         GST_TAG_CAPTURING_FLASH_FIRED, FALSE, NULL);
2592   }
2593
2594   mode = (value >> 3) & 0x3;
2595   if (mode == 1) {
2596     mode_str = "always";
2597   } else if (mode == 2) {
2598     mode_str = "never";
2599   } else if (mode == 3) {
2600     mode_str = "auto";
2601   }
2602
2603   if (mode_str)
2604     gst_tag_list_add (exif_reader->taglist, GST_TAG_MERGE_REPLACE,
2605         GST_TAG_CAPTURING_FLASH_MODE, mode_str, NULL);
2606
2607   return 0;
2608 }
2609
2610 static gint
2611 deserialize_resolution (GstExifReader * exif_reader,
2612     GstByteReader * reader, const GstExifTagMatch * exiftag,
2613     GstExifTagData * tagdata)
2614 {
2615   GstExifTagData *xres = NULL;
2616   GstExifTagData *yres = NULL;
2617   guint16 unit;
2618   gdouble multiplier;
2619
2620   if (exif_reader->byte_order == G_LITTLE_ENDIAN) {
2621     unit = GST_READ_UINT16_LE (tagdata->offset_as_data);
2622   } else {
2623     unit = GST_READ_UINT16_BE (tagdata->offset_as_data);
2624   }
2625
2626   if (unit != 2 && unit != 3) {
2627     GST_WARNING ("Invalid resolution unit, ignoring PPI tags");
2628     return 0;
2629   }
2630
2631   xres = gst_exif_reader_get_pending_tag (exif_reader, EXIF_TAG_XRESOLUTION);
2632   yres = gst_exif_reader_get_pending_tag (exif_reader, EXIF_TAG_YRESOLUTION);
2633
2634   switch (unit) {
2635     case 2:                    /* inch */
2636       multiplier = 1;
2637       break;
2638     case 3:                    /* cm */
2639       multiplier = 1 / 2.54;
2640       break;
2641     default:
2642       multiplier = 1;
2643       g_assert_not_reached ();
2644       break;
2645   }
2646
2647   if (xres) {
2648     parse_exif_rational_tag (exif_reader, GST_TAG_IMAGE_HORIZONTAL_PPI,
2649         xres->count, xres->offset, multiplier, FALSE);
2650   }
2651   if (yres) {
2652     parse_exif_rational_tag (exif_reader, GST_TAG_IMAGE_VERTICAL_PPI,
2653         yres->count, yres->offset, multiplier, FALSE);
2654   }
2655
2656   return 0;
2657 }
2658
2659 static void
2660 serialize_scene_type (GstExifWriter * writer, const GstTagList * taglist,
2661     const GstExifTagMatch * exiftag)
2662 {
2663   const gchar *str;
2664   guint8 value = 0;
2665
2666   if (gst_tag_list_peek_string_index (taglist, GST_TAG_CAPTURING_SOURCE, 0,
2667           &str)) {
2668     if (strcmp (str, "dsc") == 0) {
2669       value = 0;
2670     }
2671   }
2672
2673   if (value != 0)
2674     write_exif_undefined_tag (writer, exiftag->exif_tag, &value, 1);
2675 }
2676
2677 static gint
2678 deserialize_scene_type (GstExifReader * exif_reader,
2679     GstByteReader * reader, const GstExifTagMatch * exiftag,
2680     GstExifTagData * tagdata)
2681 {
2682   guint8 value = 0;
2683
2684   GST_LOG ("Starting to parse %s tag in exif 0x%x", exiftag->gst_tag,
2685       exiftag->exif_tag);
2686
2687   value = GST_READ_UINT8 (tagdata->offset_as_data);
2688
2689   if (value == 1) {
2690     gst_tag_list_add (exif_reader->taglist, GST_TAG_MERGE_KEEP,
2691         GST_TAG_CAPTURING_SOURCE, "dsc", NULL);
2692   }
2693
2694   return 0;
2695 }
2696
2697 static gint
2698 deserialize_add_to_pending_tags (GstExifReader * exif_reader,
2699     GstByteReader * reader, const GstExifTagMatch * exiftag,
2700     GstExifTagData * tagdata)
2701 {
2702   GST_LOG ("Adding %s tag in exif 0x%x to pending tags", exiftag->gst_tag,
2703       exiftag->exif_tag);
2704
2705   /* add it to the pending tags, as we can only parse it when we find the
2706    * SensitivityType tag */
2707   gst_exif_reader_add_pending_tag (exif_reader, tagdata);
2708   return 0;
2709 }
2710
2711 #undef EXIF_SERIALIZATION_FUNC
2712 #undef EXIF_DESERIALIZATION_FUNC
2713 #undef EXIF_SERIALIZATION_DESERIALIZATION_FUNC