Beispiel #1
0
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;
}
Beispiel #2
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)
{
    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;
}
Beispiel #3
0
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;
}
Beispiel #4
0
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;
}
Beispiel #5
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,
                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;
}
Beispiel #6
0
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;
}
Beispiel #7
0
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;
}
Beispiel #8
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;
}
Beispiel #9
0
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;
}