f32e39772d5f3db7a36a6241b7d74baa0c529019
[lms] / lightmediascanner / src / plugins / jpeg / jpeg.c
1 /**
2  * Copyright (C) 2007 by INdT
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  *
18  * @author Gustavo Sverzut Barbieri <gustavo.barbieri@openbossa.org>
19  */
20
21 /**
22  * @brief
23  *
24  * Reads EXIF tags from images.
25  *
26  * @todo: get GPS data.
27  * @todo: check if worth using mmap().
28  */
29
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33
34 #define _XOPEN_SOURCE 600
35 #include <lightmediascanner_plugin.h>
36 #include <lightmediascanner_utils.h>
37 #include <lightmediascanner_db.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <fcntl.h>
41 #include <unistd.h>
42 #include <time.h>
43 #include <stdlib.h>
44 #include <stdio.h>
45 #include <string.h>
46 #include <strings.h>
47
48 enum {
49     JPEG_MARKER_SOI = 0xd8,
50     JPEG_MARKER_JFIF = 0xe0,
51     JPEG_MARKER_EXIF = 0xe1,
52     JPEG_MARKER_COMM = 0xfe,
53     JPEG_MARKER_SOF0 = 0xc0,
54     JPEG_MARKER_SOS = 0xda
55 };
56
57 /**
58  * Process SOF JPEG, this contains width and height.
59  */
60 static int
61 _jpeg_sof_process(int fd, unsigned short *width, unsigned short *height)
62 {
63     unsigned char buf[6];
64
65     if (read(fd, buf, 6) != 6) {
66         perror("could not read() SOF data");
67         return -1;
68     }
69
70     *height = (buf[1] << 8) | buf[2];
71     *width = (buf[3] << 8) | buf[4];
72
73     return 0;
74 }
75
76 /**
77  * Process COM JPEG, this contains user comment.
78  */
79 static int
80 _jpeg_com_process(int fd, int len, struct lms_string_size *comment)
81 {
82     if (len < 1) {
83         comment->str = NULL;
84         comment->len = 0;
85         return 0;
86     }
87
88     comment->str = malloc(len + 1);
89     if (!comment->str) {
90         perror("malloc");
91         return -1;
92     }
93     if (read(fd, comment->str, len) != len) {
94         perror("read");
95         free(comment->str);
96         comment->str = NULL;
97         comment->len = 0;
98         return -2;
99     }
100     if (comment->str[len - 1] == '\0')
101         len--;
102     else
103         comment->str[len] = '\0';
104     comment->len = len;
105
106     lms_strstrip(comment->str, &comment->len);
107     if (comment->len == 0) {
108         free(comment->str);
109         comment->str = NULL;
110     }
111
112     return 0;
113 }
114
115 /**
116  * Walk JPEG markers in order to get useful information.
117  */
118 static int
119 _jpeg_info_get(int fd, int len, struct lms_image_info *info)
120 {
121     unsigned char buf[4];
122     int found;
123     off_t offset;
124
125     found = info->title.str ? 1 : 0;
126     offset = lseek(fd, len - 2, SEEK_CUR);
127     len = 0;
128     while (found < 2) {
129         offset = lseek(fd, offset + len, SEEK_SET);
130         if (offset == -1) {
131             perror("lseek");
132             return -1;
133         }
134
135         if (read(fd, buf, 4) != 4) {
136             perror("read");
137             return -2;
138         }
139
140         len = ((buf[2] << 8) | buf[3]) - 2;
141
142         if (buf[0] != 0xff) {
143             fprintf(stderr, "ERROR: expected 0xff marker, got %#x\n", buf[0]);
144             return -3;
145         }
146
147         if (buf[1] == JPEG_MARKER_SOF0) {
148             if (_jpeg_sof_process(fd, &info->width, &info->height) != 0)
149                 return -4;
150             found++;
151         } else if (buf[1] == JPEG_MARKER_COMM && !info->title.str) {
152             if (_jpeg_com_process(fd, len, &info->title) != 0)
153                 return -5;
154             found++;
155         } else if (buf[1] == JPEG_MARKER_SOS)
156             break;
157
158         len += 4; /* add read size */
159     }
160
161     return 0;
162 }
163
164 /**
165  * Read JPEG file start (0xffd8 marker) and return the next
166  * marker type and its length.
167  */
168 static int
169 _jpeg_data_get(int fd, int *type, int *len)
170 {
171     unsigned char buf[6];
172
173     if (lseek(fd, 0, SEEK_SET) != 0) {
174         perror("lseek");
175         return -1;
176     }
177
178     if (read(fd, buf, 6) != 6) {
179         perror("read");
180         return -2;
181     }
182
183     if (buf[0] != 0xff || buf[1] != JPEG_MARKER_SOI || buf[2] != 0xff) {
184         fprintf(stderr, "ERROR: not JPEG file (magic=%#x %#x %#x)\n",
185                 buf[0], buf[1], buf[2]);
186         return -3;
187     }
188
189     *type = buf[3];
190     *len = (buf[4] << 8) | buf[5];
191
192     return 0;
193 }
194
195 #define LE_4BYTE(a) ((a)[0] | ((a)[1] << 8) | ((a)[2] << 16) | ((a)[3] << 24))
196 #define BE_4BYTE(a) (((a)[0] << 24) | ((a)[1] << 16) | ((a)[2] << 8) | (a)[3])
197
198 #define LE_2BYTE(a) ((a)[0] | ((a)[1] << 8))
199 #define BE_2BYTE(a) (((a)[0] << 8) | (a)[1])
200
201 #define E_2BTYE(little_endian, a) ((little_endian) ? LE_2BYTE(a) : BE_2BYTE(a))
202 #define E_4BTYE(little_endian, a) ((little_endian) ? LE_4BYTE(a) : BE_4BYTE(a))
203
204 enum {
205     EXIF_TYPE_BYTE = 1, /* 8 bit unsigned */
206     EXIF_TYPE_ASCII = 2, /* 8 bit byte with 7-bit ASCII code, NULL terminated */
207     EXIF_TYPE_SHORT = 3, /* 2-byte unsigned integer */
208     EXIF_TYPE_LONG = 4, /* 4-byte unsigned integer */
209     EXIF_TYPE_RATIONAL = 5, /* 2 4-byte unsigned integer, 1st = numerator */
210     EXIF_TYPE_UNDEFINED = 7, /* 8-bit byte */
211     EXIF_TYPE_SLONG = 9, /* 4-byte signed integer (2'complement) */
212     EXIF_TYPE_SRATIONAL = 10 /* 2 4-byte signed integer, 1st = numerator */
213 };
214
215 enum {
216     EXIF_TAG_ORIENTATION = 0x0112,
217     EXIF_TAG_ARTIST = 0x013b,
218     EXIF_TAG_USER_COMMENT = 0x9286,
219     EXIF_TAG_IMAGE_DESCRIPTION = 0x010e,
220     EXIF_TAG_DATE_TIME = 0x0132,
221     EXIF_TAG_DATE_TIME_ORIGINAL = 0x9003,
222     EXIF_TAG_DATE_TIME_DIGITIZED = 0x9004,
223     EXIF_TAG_EXIF_IFD_POINTER = 0x8769
224 };
225
226
227 struct exif_ifd {
228     unsigned short tag;
229     unsigned short type;
230     unsigned int count;
231     unsigned int offset;
232 };
233
234 /**
235  * Read IFD from stream.
236  */
237 static int
238 _exif_ifd_get(int fd, int little_endian, struct exif_ifd *ifd)
239 {
240     unsigned char buf[12];
241
242     if (read(fd, buf, 12) != 12) {
243         perror("read");
244         return -1;
245     }
246
247     if (little_endian) {
248         ifd->tag = LE_2BYTE(buf);
249         ifd->type = LE_2BYTE(buf + 2);
250         ifd->count = LE_4BYTE(buf + 4);
251         ifd->offset = LE_4BYTE(buf + 8);
252     } else {
253         ifd->tag = BE_2BYTE(buf);
254         ifd->type = BE_2BYTE(buf + 2);
255         ifd->count = BE_4BYTE(buf + 4);
256         ifd->offset = BE_4BYTE(buf + 8);
257     }
258     return 0;
259 }
260
261 /**
262  * Get non-exif data based on Exif tag offset.
263  *
264  * This will setup the file description position and call _jpeg_info_get().
265  */
266 static int
267 _exif_extra_get(int fd, int abs_offset, int len, struct lms_image_info *info)
268 {
269     if (lseek(fd, abs_offset, SEEK_SET) == -1) {
270         perror("lseek");
271         return -1;
272     }
273
274     if (_jpeg_info_get(fd, len, info) != 0) {
275         fprintf(stderr, "ERROR: could not get image size.\n");
276         return -2;
277     }
278     return 0;
279 }
280
281 static int
282 _exif_text_encoding_get(int fd, unsigned int count, int offset, struct lms_string_size *s)
283 {
284     if (count <= 8)
285         return -1;
286
287     count -= 8; /* XXX don't just ignore character code, handle it. */
288     offset += 8;
289
290     if (lseek(fd, offset, SEEK_SET) == -1) {
291         perror("lseek");
292         return -2;
293     }
294
295     s->str = malloc(count + 1);
296
297     if (read(fd, s->str, count) != count) {
298         perror("read");
299         free(s->str);
300         s->str = NULL;
301         s->len = 0;
302         return -3;
303     }
304     s->str[count] = '\0';
305     s->len = count;
306
307     lms_strstrip(s->str, &s->len);
308     if (s->len == 0) {
309         free(s->str);
310         s->str = NULL;
311     }
312
313     return 0;
314 }
315
316 static int
317 _exif_text_ascii_get(int fd, unsigned int count, int offset, struct lms_string_size *s)
318 {
319     if (count < 1) {
320         s->str = NULL;
321         s->len = 0;
322         return 0;
323     }
324
325     if (lseek(fd, offset, SEEK_SET) == -1) {
326         perror("lseek");
327         return -1;
328     }
329
330     s->str = malloc(count);
331
332     if (read(fd, s->str, count) != count) {
333         perror("read");
334         free(s->str);
335         s->str = NULL;
336         s->len = 0;
337         return -1;
338     }
339     s->str[count - 1] = '\0';
340     s->len = count - 1;
341
342     lms_strstrip(s->str, &s->len);
343     if (s->len == 0) {
344         free(s->str);
345         s->str = NULL;
346     }
347
348     return 0;
349 }
350
351 static unsigned int
352 _exif_datetime_get(int fd, int offset)
353 {
354     char buf[20];
355     struct tm tm = {0};
356
357     if (lseek(fd, offset, SEEK_SET) == -1) {
358         perror("lseek");
359         return 0;
360     }
361
362     if (read(fd, buf, 20) != 20) {
363         perror("read");
364         return 0;
365     }
366
367     buf[19] = '\0';
368     if (strptime(buf, "%Y:%m:%d %H:%M:%S", &tm)) {
369         return mktime(&tm);
370     }
371     return 0;
372 }
373
374 static int _exif_private_ifd_get(int fd, int base_offset, int offset, int little_endian, struct lms_image_info *info);
375
376 /**
377  * Process IFD contents.
378  */
379 static int
380 _exif_ifd_process(int fd, int count, int ifd_offset, int tiff_base, int little_endian, struct lms_image_info *info)
381 {
382     int i, torig, tdig, tlast;
383
384     torig = tdig = tlast = 0;
385
386     for (i = 0; i < count; i++) {
387         struct exif_ifd ifd;
388
389         lseek(fd, ifd_offset + i * 12, SEEK_SET);
390         if (_exif_ifd_get(fd, little_endian, &ifd) != 0) {
391             fprintf(stderr, "ERROR: could not read Exif IFD.\n");
392             return -8;
393         }
394
395         switch (ifd.tag) {
396         case EXIF_TAG_ORIENTATION:
397             info->orientation = ifd.offset >> 16;
398             break;
399         case EXIF_TAG_ARTIST:
400             if (!info->artist.str)
401                 _exif_text_ascii_get(fd, ifd.count, tiff_base + ifd.offset,
402                                      &info->artist);
403             break;
404         case EXIF_TAG_USER_COMMENT:
405             if (!info->title.str)
406                 _exif_text_encoding_get(fd, ifd.count, tiff_base + ifd.offset,
407                                         &info->title);
408             break;
409         case EXIF_TAG_IMAGE_DESCRIPTION:
410             if (!info->title.str)
411                 _exif_text_ascii_get(fd, ifd.count, tiff_base + ifd.offset,
412                                      &info->title);
413             break;
414         case EXIF_TAG_DATE_TIME:
415             if (torig == 0 && info->date == 0)
416                 tlast = _exif_datetime_get(fd, tiff_base + ifd.offset);
417             break;
418         case EXIF_TAG_DATE_TIME_ORIGINAL:
419             if (torig == 0 && info->date == 0)
420                 torig = _exif_datetime_get(fd, tiff_base + ifd.offset);
421             break;
422         case EXIF_TAG_DATE_TIME_DIGITIZED:
423             if (torig == 0 && info->date == 0)
424                 tdig = _exif_datetime_get(fd, tiff_base + ifd.offset);
425             break;
426         case EXIF_TAG_EXIF_IFD_POINTER:
427             if (ifd.count == 1 && ifd.type == EXIF_TYPE_LONG)
428                 _exif_private_ifd_get(fd, ifd.offset, tiff_base,
429                                       little_endian, info);
430             break;
431         default:
432             /* ignore */
433             break;
434         }
435     }
436
437     if (info->date == 0) {
438         if (torig)
439             info->date = torig;
440         else if (tdig)
441             info->date = tdig;
442         else
443             info->date = tlast;
444     }
445
446     return 0;
447 }
448
449 /**
450  * Process Exif IFD (Exif Private Tag), with more specific info.
451  */
452 static int
453 _exif_private_ifd_get(int fd, int ifd_offset, int tiff_base, int little_endian, struct lms_image_info *info)
454 {
455     char buf[2];
456     unsigned int count;
457
458     if (lseek(fd, tiff_base + ifd_offset, SEEK_SET) == -1) {
459         perror("lseek");
460         return -1;
461     }
462
463     if (read(fd, buf, 2) != 2) {
464         perror("read");
465         return -1;
466     }
467
468     count = E_2BTYE(little_endian, buf);
469     return _exif_ifd_process(fd, count, ifd_offset + 2, tiff_base,
470                              little_endian, info);
471 }
472
473 /**
474  * Process file as it being Exif, will extract Exif as well as other
475  * JPEG markers (comment, size).
476  */
477 static int
478 _exif_data_get(int fd, int len, struct lms_image_info *info)
479 {
480     const unsigned char exif_hdr[6] = "Exif\0";
481     unsigned char buf[8];
482     unsigned int little_endian, offset, count;
483     off_t abs_offset, tiff_base;
484
485     abs_offset = lseek(fd, 0, SEEK_CUR);
486     if (abs_offset == -1) {
487         perror("lseek");
488         return -1;
489     }
490
491     if (read(fd, buf, 6) != 6) {
492         perror("read");
493         return -2;
494     }
495
496     memset(info, 0, sizeof(*info));
497     info->orientation = 1;
498
499     if (memcmp(buf, exif_hdr, 6) != 0)
500         return _exif_extra_get(fd, abs_offset, len, info);
501
502     if (read(fd, buf, 8) != 8) {
503         perror("read");
504         return -4;
505     }
506
507     if (buf[0] == 'I' && buf[1] == 'I') {
508         little_endian = 1;
509         offset = LE_4BYTE(buf + 4);
510     } else if (buf[0] == 'M' && buf[1] == 'M') {
511         little_endian = 0;
512         offset = BE_4BYTE(buf + 4);
513     } else {
514         fprintf(stderr, "ERROR: undefined byte sex \"%2.2s\".\n", buf);
515         return -5;
516     }
517
518     offset -= 8;
519     if (offset > 0 && lseek(fd, offset, SEEK_CUR) == -1) {
520         perror("lseek");
521         return -6;
522     }
523
524     tiff_base = abs_offset + 6; /* offsets are relative to TIFF base */
525
526     if (read(fd, buf, 2) != 2) {
527         perror("read");
528         return -7;
529     }
530     count = E_2BTYE(little_endian, buf);
531
532     _exif_ifd_process(fd, count, tiff_base + 8 + 2, tiff_base,
533                       little_endian, info);
534
535     return _exif_extra_get(fd, abs_offset, len, info);
536 }
537
538 /**
539  * Process file as it being JFIF
540  */
541 static int
542 _jfif_data_get(int fd, int len, struct lms_image_info *info)
543 {
544     unsigned char buf[4];
545
546     memset(info, 0, sizeof(*info));
547     info->orientation = 1;
548
549     /* JFIF provides no useful information, try to find out Exif */
550     if (lseek(fd, len - 2, SEEK_CUR) == -1) {
551         perror("lseek");
552         return -1;
553     }
554
555     if (read(fd, buf, 4) != 4) {
556         perror("read");
557         return -2;
558     }
559
560     len = ((buf[2] << 8) | buf[3]);
561     if (buf[0] != 0xff) {
562         fprintf(stderr, "ERROR: expected 0xff marker, got %#x\n", buf[0]);
563         return -3;
564     }
565
566     if (buf[1] == JPEG_MARKER_EXIF)
567         return _exif_data_get(fd, len, info);
568     else
569         return _jpeg_info_get(fd, len, info);
570 }
571
572 static const char _name[] = "jpeg";
573 static const struct lms_string_size _exts[] = {
574     LMS_STATIC_STRING_SIZE(".jpg"),
575     LMS_STATIC_STRING_SIZE(".jpeg"),
576     LMS_STATIC_STRING_SIZE(".jpe")
577 };
578
579 struct plugin {
580     struct lms_plugin plugin;
581     lms_db_image_t *img_db;
582 };
583
584 static void *
585 _match(struct plugin *p, const char *path, int len, int base)
586 {
587     int i;
588
589     i = lms_which_extension(path, len, _exts, LMS_ARRAY_SIZE(_exts));
590     if (i < 0)
591         return NULL;
592     else
593         return (void*)(i + 1);
594 }
595
596 static int
597 _parse(struct plugin *plugin, struct lms_context *ctxt, const struct lms_file_info *finfo, void *match)
598 {
599     struct lms_image_info info = {0};
600     int fd, type, len, r;
601
602     fd = open(finfo->path, O_RDONLY);
603     if (fd < 0) {
604         perror("open");
605         return -1;
606     }
607
608     if (_jpeg_data_get(fd, &type, &len) != 0) {
609         r = -2;
610         goto done;
611     }
612
613     if (type == JPEG_MARKER_EXIF) {
614         if (_exif_data_get(fd, len, &info) != 0) {
615             fprintf(stderr, "ERROR: could not get EXIF info (%s).\n",
616                     finfo->path);
617             r = -3;
618             goto done;
619         }
620     } else if (type == JPEG_MARKER_JFIF) {
621         if (_jfif_data_get(fd, len, &info) != 0) {
622             fprintf(stderr, "ERROR: could not get JPEG size (%s).\n",
623                     finfo->path);
624             r = -4;
625             goto done;
626         }
627     } else {
628         fprintf(stderr, "ERROR: unsupported JPEG marker %#x (%s)\n", type,
629                 finfo->path);
630         r = -6;
631         goto done;
632     }
633
634     if (info.date == 0)
635         info.date = finfo->mtime;
636
637     if (!info.title.str) {
638       int ext_idx;
639
640       ext_idx = ((int)match) - 1;
641       info.title.len = finfo->path_len - finfo->base - _exts[ext_idx].len;
642       info.title.str = malloc((info.title.len + 1) * sizeof(char));
643       memcpy(info.title.str, finfo->path + finfo->base, info.title.len);
644       info.title.str[info.title.len] = '\0';
645     }
646
647     if (info.title.str)
648       lms_charset_conv(ctxt->cs_conv, &info.title.str, &info.title.len);
649     if (info.artist.str)
650       lms_charset_conv(ctxt->cs_conv, &info.artist.str, &info.artist.len);
651
652     info.id = finfo->id;
653     r = lms_db_image_add(plugin->img_db, &info);
654
655   done:
656     if (info.title.str)
657         free(info.title.str);
658     if (info.artist.str)
659         free(info.artist.str);
660
661     posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
662     close(fd);
663
664     return r;
665 }
666
667 static int
668 _setup(struct plugin *plugin, struct lms_context *ctxt)
669 {
670     plugin->img_db = lms_db_image_new(ctxt->db);
671     if (!plugin->img_db)
672         return -1;
673
674     return 0;
675 }
676
677 static int
678 _start(struct plugin *plugin, struct lms_context *ctxt)
679 {
680     return lms_db_image_start(plugin->img_db);
681 }
682
683 static int
684 _finish(struct plugin *plugin, struct lms_context *ctxt)
685 {
686     if (plugin->img_db)
687         return lms_db_image_free(plugin->img_db);
688
689     return 0;
690 }
691
692
693 static int
694 _close(struct plugin *plugin)
695 {
696     free(plugin);
697     return 0;
698 }
699
700 API struct lms_plugin *
701 lms_plugin_open(void)
702 {
703     struct plugin *plugin;
704
705     plugin = malloc(sizeof(*plugin));
706     plugin->plugin.name = _name;
707     plugin->plugin.match = (lms_plugin_match_fn_t)_match;
708     plugin->plugin.parse = (lms_plugin_parse_fn_t)_parse;
709     plugin->plugin.close = (lms_plugin_close_fn_t)_close;
710     plugin->plugin.setup = (lms_plugin_setup_fn_t)_setup;
711     plugin->plugin.start = (lms_plugin_start_fn_t)_start;
712     plugin->plugin.finish = (lms_plugin_finish_fn_t)_finish;
713
714     return (struct lms_plugin *)plugin;
715 }