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