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