static pixmap_t * fa_image_from_video(const char *url0, const image_meta_t *im, char *errbuf, size_t errlen, int *cache_control, cancellable_t *c) { static char *stated_url; static fa_stat_t fs; time_t stattime = 0; time_t mtime = 0; pixmap_t *pm = NULL; char cacheid[512]; char *url = mystrdupa(url0); char *tim = strchr(url, '#'); const char *siz; *tim++ = 0; int secs = atoi(tim); hts_mutex_lock(&image_from_video_mutex[0]); if(strcmp(url, stated_url ?: "")) { free(stated_url); stated_url = NULL; if(fa_stat(url, &fs, errbuf, errlen)) { hts_mutex_unlock(&image_from_video_mutex[0]); return NULL; } stated_url = strdup(url); } stattime = fs.fs_mtime; hts_mutex_unlock(&image_from_video_mutex[0]); if(im->im_req_width < 100 && im->im_req_height < 100) { siz = "min"; } else if(im->im_req_width < 200 && im->im_req_height < 200) { siz = "mid"; } else { siz = "max"; } snprintf(cacheid, sizeof(cacheid), "%s-%s", url0, siz); buf_t *b = blobcache_get(cacheid, "videothumb", 0, 0, NULL, &mtime); if(b != NULL && mtime == stattime) { pm = pixmap_alloc_coded(b->b_ptr, b->b_size, PIXMAP_JPEG); buf_release(b); return pm; } buf_release(b); if(ONLY_CACHED(cache_control)) { snprintf(errbuf, errlen, "Not cached"); return NULL; } hts_mutex_lock(&image_from_video_mutex[1]); pm = fa_image_from_video2(url, im, cacheid, errbuf, errlen, secs, stattime, c); hts_mutex_unlock(&image_from_video_mutex[1]); return pm; }
/** * Load entire image into memory using fileaccess load method. * Faster than open+read+close. */ static pixmap_t * fa_imageloader2(const char *url, const char **vpaths, char *errbuf, size_t errlen) { uint8_t *p; size_t size; meminfo_t mi; pixmap_type_t fmt; int width = -1, height = -1, orientation = 0; if((p = fa_load(url, &size, vpaths, errbuf, errlen, NULL)) == NULL) return NULL; mi.data = p; mi.size = size; /* Probe format */ if((p[6] == 'J' && p[7] == 'F' && p[8] == 'I' && p[9] == 'F') || (p[6] == 'E' && p[7] == 'x' && p[8] == 'i' && p[9] == 'f')) { jpeginfo_t ji; if(jpeg_info(&ji, jpeginfo_mem_reader, &mi, JPEG_INFO_DIMENSIONS | JPEG_INFO_ORIENTATION, p, size, errbuf, errlen)) { free(p); return NULL; } fmt = PIXMAP_JPEG; width = ji.ji_width; height = ji.ji_height; orientation = ji.ji_orientation; jpeg_info_clear(&ji); } else if(!memcmp(pngsig, p, 8)) { fmt = PIXMAP_PNG; } else if(!memcmp(gif87sig, p, sizeof(gif87sig)) || !memcmp(gif89sig, p, sizeof(gif89sig))) { fmt = PIXMAP_GIF; } else { snprintf(errbuf, errlen, "%s: unknown format", url); free(p); return NULL; } pixmap_t *pm = pixmap_alloc_coded(p, size, fmt); pm->pm_width = width; pm->pm_height = height; pm->pm_orientation = orientation; free(p); return pm; }
static pixmap_t * fa_image_from_video(const char *url0, const image_meta_t *im, char *errbuf, size_t errlen, int *cache_control, fa_load_cb_t *cb, void *opaque) { static char *stated_url; static fa_stat_t fs; time_t stattime = 0; time_t mtime = 0; pixmap_t *pm = NULL; char cacheid[512]; void *data; size_t datasize; char *url = mystrdupa(url0); char *tim = strchr(url, '#'); *tim++ = 0; int secs = atoi(tim); hts_mutex_lock(&image_from_video_mutex); if(strcmp(url, stated_url ?: "")) { free(stated_url); stated_url = NULL; if(fa_stat(url, &fs, errbuf, errlen)) { hts_mutex_unlock(&image_from_video_mutex); return NULL; } stated_url = strdup(url); } stattime = fs.fs_mtime; hts_mutex_unlock(&image_from_video_mutex); snprintf(cacheid, sizeof(cacheid), "%s-%d-%d-3", url0, im->im_req_width, im->im_req_height); data = blobcache_get(cacheid, "videothumb", &datasize, 0, 0, NULL, &mtime); if(data != NULL && mtime == stattime) { pm = pixmap_alloc_coded(data, datasize, PIXMAP_PNG); free(data); return pm; } if(ONLY_CACHED(cache_control)) { snprintf(errbuf, errlen, "Not cached"); return NULL; } hts_mutex_lock(&image_from_video_mutex); pm = fa_image_from_video2(url, im, cacheid, errbuf, errlen, secs, stattime, cb, opaque); hts_mutex_unlock(&image_from_video_mutex); return pm; }
static pixmap_t * fa_image_from_video(const char *url0, const image_meta_t *im) { pixmap_t *pm = NULL; char cacheid[512]; void *data; size_t datasize; snprintf(cacheid, sizeof(cacheid), "%s-%d-%d", url0, im->req_width, im->req_height); data = blobcache_get(cacheid, "videothumb", &datasize, 0); if(data != NULL) { pm = pixmap_alloc_coded(data, datasize, CODEC_ID_PNG); free(data); return pm; } hts_mutex_lock(&image_from_video_mutex); pm = fa_image_from_video2(url0, im, cacheid); hts_mutex_unlock(&image_from_video_mutex); return pm; }
/** * Load entire image into memory using fileaccess load method. * Faster than open+read+close. */ static pixmap_t * fa_imageloader2(const char *url, const char **vpaths, char *errbuf, size_t errlen, int *cache_control, cancellable_t *c) { buf_t *buf; jpeg_meminfo_t mi; pixmap_type_t fmt; int width = -1, height = -1, orientation = 0; buf = fa_load(url, FA_LOAD_VPATHS(vpaths), FA_LOAD_ERRBUF(errbuf, errlen), FA_LOAD_CACHE_CONTROL(cache_control), FA_LOAD_CANCELLABLE(c), NULL); if(buf == NULL || buf == NOT_MODIFIED) return (pixmap_t *)buf; const uint8_t *p = buf_c8(buf); mi.data = p; mi.size = buf->b_size; if(buf->b_size < 16) goto bad; /* Probe format */ if((p[6] == 'J' && p[7] == 'F' && p[8] == 'I' && p[9] == 'F') || (p[6] == 'E' && p[7] == 'x' && p[8] == 'i' && p[9] == 'f')) { jpeginfo_t ji; if(jpeg_info(&ji, jpeginfo_mem_reader, &mi, JPEG_INFO_DIMENSIONS | JPEG_INFO_ORIENTATION, p, buf->b_size, errbuf, errlen)) { buf_release(buf); return NULL; } fmt = PIXMAP_JPEG; width = ji.ji_width; height = ji.ji_height; orientation = ji.ji_orientation; jpeg_info_clear(&ji); } else if(!memcmp(pngsig, p, 8)) { fmt = PIXMAP_PNG; } else if(!memcmp(gif87sig, p, sizeof(gif87sig)) || !memcmp(gif89sig, p, sizeof(gif89sig))) { fmt = PIXMAP_GIF; } else if(!memcmp(svgsig1, p, sizeof(svgsig1)) || !memcmp(svgsig2, p, sizeof(svgsig2))) { fmt = PIXMAP_SVG; } else { bad: snprintf(errbuf, errlen, "Unknown format"); buf_release(buf); return NULL; } pixmap_t *pm = pixmap_alloc_coded(p, buf->b_size, fmt); if(pm != NULL) { pm->pm_width = width; pm->pm_height = height; pm->pm_orientation = orientation; } else { snprintf(errbuf, errlen, "Out of memory"); } buf_release(buf); return pm; }
pixmap_t * fa_imageloader(const char *url, const struct image_meta *im, const char **vpaths, char *errbuf, size_t errlen, int *cache_control, cancellable_t *c) { uint8_t p[16]; int r; int width = -1, height = -1, orientation = 0; fa_handle_t *fh; pixmap_t *pm; pixmap_type_t fmt; #if ENABLE_LIBAV if(strchr(url, '#')) return fa_image_from_video(url, im, errbuf, errlen, cache_control, c); #endif if(!im->im_want_thumb) return fa_imageloader2(url, vpaths, errbuf, errlen, cache_control, c); fa_open_extra_t foe = { .foe_c = c }; if((fh = fa_open_vpaths(url, vpaths, errbuf, errlen, FA_BUFFERED_SMALL, &foe)) == NULL) return NULL; if(ONLY_CACHED(cache_control)) { snprintf(errbuf, errlen, "Not cached"); return NULL; } if(fa_read(fh, p, sizeof(p)) != sizeof(p)) { snprintf(errbuf, errlen, "File too short"); fa_close(fh); return NULL; } /* Probe format */ if((p[6] == 'J' && p[7] == 'F' && p[8] == 'I' && p[9] == 'F') || (p[6] == 'E' && p[7] == 'x' && p[8] == 'i' && p[9] == 'f')) { jpeginfo_t ji; if(jpeg_info(&ji, jpeginfo_reader, fh, JPEG_INFO_DIMENSIONS | JPEG_INFO_ORIENTATION | (im->im_want_thumb ? JPEG_INFO_THUMBNAIL : 0), p, sizeof(p), errbuf, errlen)) { fa_close(fh); return NULL; } if(im->im_want_thumb && ji.ji_thumbnail) { pixmap_t *pm = pixmap_dup(ji.ji_thumbnail); fa_close(fh); jpeg_info_clear(&ji); return pm; } fmt = PIXMAP_JPEG; width = ji.ji_width; height = ji.ji_height; orientation = ji.ji_orientation; jpeg_info_clear(&ji); } else if(!memcmp(pngsig, p, 8)) { fmt = PIXMAP_PNG; } else if(!memcmp(gif87sig, p, sizeof(gif87sig)) || !memcmp(gif89sig, p, sizeof(gif89sig))) { fmt = PIXMAP_GIF; } else if(!memcmp(svgsig1, p, sizeof(svgsig1)) || !memcmp(svgsig2, p, sizeof(svgsig2))) { fmt = PIXMAP_SVG; } else { snprintf(errbuf, errlen, "Unknown format"); fa_close(fh); return NULL; } int64_t s = fa_fsize(fh); if(s < 0) { snprintf(errbuf, errlen, "Can't read from non-seekable file"); fa_close(fh); return NULL; } pm = pixmap_alloc_coded(NULL, s, fmt); if(pm == NULL) { snprintf(errbuf, errlen, "Out of memory"); fa_close(fh); return NULL; } pm->pm_width = width; pm->pm_height = height; pm->pm_orientation = orientation; fa_seek(fh, SEEK_SET, 0); r = fa_read(fh, pm->pm_data, pm->pm_size); fa_close(fh); if(r != pm->pm_size) { pixmap_release(pm); snprintf(errbuf, errlen, "Read error"); return NULL; } return pm; }
static int parse_app1(jpeginfo_t *ji, const uint8_t *buf, size_t len, int flags) { int bigendian; int ifdbase; int ifd = 0; int thumbnail_jpeg_offset = -1; int thumbnail_jpeg_size = -1; #define EXIF8(off) buf[off] #define EXIF16(off) (bigendian ? \ ((buf[off] << 8) | buf[off + 1]) : (buf[off + 1] << 8 | buf[off])) #define EXIF32(off) (bigendian ? \ ((buf[off ] << 24) | (buf[off+1] << 16) | (buf[off+2] << 8) | (buf[off+3])):\ ((buf[off+3] << 24) | (buf[off+2] << 16) | (buf[off+1] << 8) | (buf[off ]))) #define IFDTAG(ifd, tag) (((ifd) << 16) | tag) // Exif Header if(len < 6 || memcmp(exifheader, buf, 6)) return 0; // Don't fail here, just skip buf += 6; len -= 6; // TIFF header if(len < 8) return -1; if(buf[0] == 'M' && buf[1] == 'M') bigendian = 1; else if(buf[0] == 'I' && buf[1] == 'I') bigendian = 0; else return -1; // printf(" EXIF/TIFF %s endian\n", bigendian ? "Big" : "Little"); if(EXIF16(2) != 0x2a) return -1; ifdbase = EXIF32(4); while(ifdbase) { // printf(" IDF Offset = %d\n", ifdbase); if(len < ifdbase + 2) return -1; int i, entries = EXIF16(ifdbase); // printf(" %d entries\n", entries); if(len < ifdbase + 2 + entries * 12 + 4) return -1; for(i = 0; i < entries; i++) { uint16_t tag = EXIF16(ifdbase + 2 + i * 12 + 0); uint16_t type = EXIF16(ifdbase + 2 + i * 12 + 2); // uint32_t c = EXIF32(ifdbase + 2 + i * 12 + 4); int po = ifdbase + 2 + i * 12 + 8; int value = 0; const char *str = NULL; switch(type) { case 1: value = (uint8_t) EXIF8(po); break; case 2: value = (uint32_t) EXIF32(po); str = (const char *)buf + value; break; case 3: value = (uint16_t) EXIF16(po); break; case 4: value = (uint32_t) EXIF32(po); break; case 6: value = (int8_t) EXIF8(po); break; case 8: value = (int16_t) EXIF8(po); break; } // printf(" IFD%d %04x (%d) == %d\n", ifd, tag, type, value); switch(IFDTAG(ifd, tag)) { case IFDTAG(1, 0x201): // JPEG Thumbnail offset thumbnail_jpeg_offset = value; break; case IFDTAG(1, 0x202): // JPEG Thumbnail size thumbnail_jpeg_size = value; break; case IFDTAG(0, 0x112): // Orientation ji->ji_orientation = value; break; case IFDTAG(0, 0x132): // Datetime ji->ji_time = jpeg_time(str); break; case IFDTAG(0, 0x10f): // Manufacturer rstr_release(ji->ji_manufacturer); ji->ji_manufacturer = rstr_alloc(str); break; case IFDTAG(0, 0x110): // Equipment rstr_release(ji->ji_equipment); ji->ji_equipment = rstr_alloc(str); break; default: break; } } ifd++; ifdbase = EXIF32(ifdbase + 2 + entries * 12); } if(flags & JPEG_INFO_THUMBNAIL && thumbnail_jpeg_offset != -1 && thumbnail_jpeg_size != -1 && thumbnail_jpeg_offset + thumbnail_jpeg_size <= len) { // printf(" Thumbnail @ %d, %d bytes\n", thumbnail_jpeg_offset, thumbnail_jpeg_size); ji->ji_thumbnail = pixmap_alloc_coded(buf + thumbnail_jpeg_offset, thumbnail_jpeg_size, PIXMAP_JPEG); ji->ji_thumbnail->pm_flags |= PIXMAP_THUMBNAIL; ji->ji_thumbnail->pm_orientation = ji->ji_orientation; } return 0; }
/** * Load entire image into memory using fileaccess load method. * Faster than open+read+close. */ static pixmap_t * fa_imageloader2(const char *url, const char **vpaths, char *errbuf, size_t errlen, int *cache_control, fa_load_cb_t *cb, void *opaque) { uint8_t *p; size_t size; jpeg_meminfo_t mi; pixmap_type_t fmt; int width = -1, height = -1, orientation = 0; p = fa_load(url, &size, vpaths, errbuf, errlen, cache_control, 0, cb, opaque); if(p == NULL || p == NOT_MODIFIED) return (pixmap_t *)p; mi.data = p; mi.size = size; if(size < 16) goto bad; /* Probe format */ if((p[6] == 'J' && p[7] == 'F' && p[8] == 'I' && p[9] == 'F') || (p[6] == 'E' && p[7] == 'x' && p[8] == 'i' && p[9] == 'f')) { jpeginfo_t ji; if(jpeg_info(&ji, jpeginfo_mem_reader, &mi, JPEG_INFO_DIMENSIONS | JPEG_INFO_ORIENTATION, p, size, errbuf, errlen)) { free(p); return NULL; } fmt = PIXMAP_JPEG; width = ji.ji_width; height = ji.ji_height; orientation = ji.ji_orientation; jpeg_info_clear(&ji); } else if(!memcmp(pngsig, p, 8)) { fmt = PIXMAP_PNG; } else if(!memcmp(gif87sig, p, sizeof(gif87sig)) || !memcmp(gif89sig, p, sizeof(gif89sig))) { fmt = PIXMAP_GIF; } else if(!memcmp(svgsig1, p, sizeof(svgsig1)) || !memcmp(svgsig2, p, sizeof(svgsig2))) { fmt = PIXMAP_SVG; } else { bad: snprintf(errbuf, errlen, "Unknown format"); free(p); return NULL; } pixmap_t *pm = pixmap_alloc_coded(p, size, fmt); if(pm != NULL) { pm->pm_width = width; pm->pm_height = height; pm->pm_orientation = orientation; } else { snprintf(errbuf, errlen, "Out of memory"); } free(p); return pm; }
pixmap_t * fa_imageloader(const char *url, const struct image_meta *im, const char **vpaths, char *errbuf, size_t errlen) { uint8_t p[16]; int r; enum CodecID codec; int width = -1, height = -1, orientation = 0; AVIOContext *avio; pixmap_t *pm; if(strchr(url, '#')) { pm = fa_image_from_video(url, im); if(pm == NULL) snprintf(errbuf, errlen, "%s: Unable to extract image", url); return pm; } if(!im->want_thumb) return fa_imageloader2(url, vpaths, errbuf, errlen); if((avio = fa_libav_open_vpaths(url, 32768, vpaths)) == NULL) { snprintf(errbuf, errlen, "%s: Unable to open file", url); return NULL; } if(avio_read(avio, p, sizeof(p)) != sizeof(p)) { snprintf(errbuf, errlen, "%s: file too short", url); fa_libav_close(avio); return NULL; } /* Probe format */ if((p[6] == 'J' && p[7] == 'F' && p[8] == 'I' && p[9] == 'F') || (p[6] == 'E' && p[7] == 'x' && p[8] == 'i' && p[9] == 'f')) { jpeginfo_t ji; if(jpeg_info(&ji, jpeginfo_reader, avio, JPEG_INFO_DIMENSIONS | JPEG_INFO_ORIENTATION | (im->want_thumb ? JPEG_INFO_THUMBNAIL : 0), p, sizeof(p), errbuf, errlen)) { fa_libav_close(avio); return NULL; } if(im->want_thumb && ji.ji_thumbnail) { pixmap_t *pm = pixmap_dup(ji.ji_thumbnail); fa_libav_close(avio); jpeg_info_clear(&ji); return pm; } codec = CODEC_ID_MJPEG; width = ji.ji_width; height = ji.ji_height; orientation = ji.ji_orientation; jpeg_info_clear(&ji); } else if(!memcmp(pngsig, p, 8)) { codec = CODEC_ID_PNG; } else if(!memcmp(gif87sig, p, sizeof(gif87sig)) || !memcmp(gif89sig, p, sizeof(gif89sig))) { codec = CODEC_ID_GIF; } else { snprintf(errbuf, errlen, "%s: unknown format", url); fa_libav_close(avio); return NULL; } size_t s = avio_size(avio); if(s < 0) { snprintf(errbuf, errlen, "%s: Can't read from non-seekable file", url); fa_libav_close(avio); return NULL; } pm = pixmap_alloc_coded(NULL, s, codec); if(pm == NULL) { snprintf(errbuf, errlen, "%s: no memory", url); fa_libav_close(avio); return NULL; } pm->pm_width = width; pm->pm_height = height; pm->pm_orientation = orientation; avio_seek(avio, SEEK_SET, 0); r = avio_read(avio, pm->pm_data, pm->pm_size); fa_libav_close(avio); if(r != pm->pm_size) { pixmap_release(pm); snprintf(errbuf, errlen, "%s: read error", url); return NULL; } return pm; }