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