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, °rees_n) ||
2234 !gst_byte_reader_get_uint32_le (&fractions_reader, °rees_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, °rees_n) ||
2242 !gst_byte_reader_get_uint32_be (&fractions_reader, °rees_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, °rees);
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