示例#1
0
void image_jpeg_destroy(MediaScanImage *i) {
  if (i->_jpeg) {
    JPEGData *j = (JPEGData *)i->_jpeg;

    jpeg_destroy_decompress(j->cinfo);
    LOG_MEM("destroy JPEG cinfo @ %p\n", j->cinfo);
    free(j->cinfo);
    LOG_MEM("destroy JPEG error_pub @ %p\n", j->jpeg_error_pub);
    free(j->jpeg_error_pub);

    LOG_MEM("destroy JPEGData @ %p\n", i->_jpeg);
    free(i->_jpeg);
    i->_jpeg = NULL;
  }
}
示例#2
0
static void buf_dst_mgr_term(j_compress_ptr cinfo) {
  struct buf_dst_mgr *dst = (void *)cinfo->dest;

  size_t sz = BUF_SIZE - dst->jdst.free_in_buffer;

  if (sz > 0) {
    // Copy final buffer to image data
    buffer_append(dst->dbuf, dst->buf, sz);
  }

  LOG_MEM("destroy JPEG buf @ %p\n", dst->buf);
  free(dst->buf);

  LOG_MEM("buf_dst_mgr_term, copied final %zd bytes (total bytes %d)\n", sz, buffer_len(dst->dbuf));
}
示例#3
0
MediaScanResult *
result_create(void)
{
  MediaScanResult *r = (MediaScanResult *)calloc(sizeof(MediaScanResult), 1);
  if (r == NULL) {
    LOG_ERROR("Out of memory for new MediaScanResult object\n");
    return NULL;
  }
  
  LOG_MEM("new MediaScanResult @ %p\n", r);
  
  r->type = TYPE_UNKNOWN;
  r->flags = USE_EXTENSION;
  r->path = NULL;
  r->error = NULL;
  
  r->mime_type = NULL;
  r->dlna_profile = NULL;
  r->size = 0;
  r->mtime = 0;
  r->bitrate = 0;
  r->duration_ms = 0;
  
  r->type_data.audio = NULL;
  
  r->_avf = NULL;
  r->_fp = NULL;
  
  return r;
}
示例#4
0
// Destination manager to copy compressed data to a buffer
static void buf_dst_mgr_init(j_compress_ptr cinfo) {
  struct buf_dst_mgr *dst = (void *)cinfo->dest;

  // Temporary internal buffer
  dst->buf = (JOCTET *)malloc(BUF_SIZE);
  LOG_MEM("new JPEG buf @ %p\n", dst->buf);

  // Storage for full compressed data
  dst->dbuf = (Buffer *)malloc(sizeof(Buffer));
  LOG_MEM("new JPEG dbuf @ %p\n", dst->dbuf);
  buffer_init(dst->dbuf, BUF_SIZE);

  dst->off = dst->buf;
  dst->jdst.next_output_byte = dst->off;
  dst->jdst.free_in_buffer = BUF_SIZE;
}
示例#5
0
MediaScan *ms_create(void) {
  MediaScan *s = NULL;
  dlna_t *dlna = NULL;

  _init();

  s = (MediaScan *)calloc(sizeof(MediaScan), 1);
  if (s == NULL) {
    ms_errno = MSENO_MEMERROR;
    FATAL("Out of memory for new MediaScan object\n");
    return NULL;
  }

  LOG_MEM("new MediaScan @ %p\n", s);

  s->flags = MS_USE_EXTENSION | MS_FULL_SCAN;
  s->watch_interval = 600;      // 10 minutes

  s->thread = NULL;
  s->dbp = NULL;
  s->progress = progress_create();

  // List of all dirs found
  s->_dirq = malloc(sizeof(struct dirq));
  SIMPLEQ_INIT((struct dirq *)s->_dirq);

  // We can't use libdlna's init function because it loads everything in ffmpeg
  dlna = (dlna_t *)calloc(sizeof(dlna_t), 1);
  dlna->inited = 1;
  s->_dlna = (void *)dlna;
  dlna_register_all_media_profiles(dlna);

  return s;
}                               /* ms_create() */
示例#6
0
文件: Gbs_Cpu.cpp 项目: kode54/Cog
inline void Gbs_Core::write_io( int offset, int data )
{
	(void) LOG_MEM( offset + io_base, "<", data );
	
	ram [io_base - ram_addr + offset] = data;
	if ( (unsigned) offset < 0x80 )
		write_io_( offset, data );
}
示例#7
0
void image_png_destroy(MediaScanImage *i) {
  if (i->_png) {
    PNGData *p = (PNGData *)i->_png;

    png_destroy_read_struct(&p->png_ptr, &p->info_ptr, NULL);

    LOG_MEM("destroy PNGData @ %p\n", i->_png);
    free(i->_png);
    i->_png = NULL;
  }
}
示例#8
0
MediaScanVideo *video_create(void) {
  MediaScanVideo *v = (MediaScanVideo *)calloc(sizeof(MediaScanVideo), 1);
  if (v == NULL) {
    ms_errno = MSENO_MEMERROR;
    FATAL("Out of memory for new MediaScanVideo object\n");
    return NULL;
  }

  LOG_MEM("new MediaScanVideo @ %p\n", v);
  return v;
}
示例#9
0
void tag_destroy(MediaScanTag *t) {
  int i;

  // free items
  if (t->nitems) {
    for (i = 0; i < t->nitems; i++)
      tag_item_destroy(t->items[i]);
  }

  LOG_MEM("destroy MediaScanTag @ %p\n", t);
  free(t);
}
示例#10
0
MediaScanTag *tag_create(const char *type) {
  MediaScanTag *t = (MediaScanTag *)calloc(sizeof(MediaScanTag), 1);
  if (t == NULL) {
    ms_errno = MSENO_MEMERROR;
    FATAL("Out of memory for new MediaScanTag object\n");
    return NULL;
  }

  t->type = type;
  t->nitems = 0;

  LOG_MEM("new MediaScanTag @ %p\n", t);
  return t;
}
示例#11
0
static boolean buf_dst_mgr_empty(j_compress_ptr cinfo) {
  struct buf_dst_mgr *dst = (void *)cinfo->dest;

  // Copy tmp buffer to image buffer
  buffer_append(dst->dbuf, dst->buf, BUF_SIZE);

  // Reuse the tmp buffer for the next chunk
  dst->off = dst->buf;
  dst->jdst.next_output_byte = dst->off;
  dst->jdst.free_in_buffer = BUF_SIZE;

  LOG_MEM("buf_dst_mgr_empty, copied %d bytes (total now %d)\n", BUF_SIZE, buffer_len(dst->dbuf));

  return TRUE;
}
示例#12
0
文件: Gbs_Cpu.cpp 项目: kode54/Cog
int Gbs_Core::read_mem( addr_t addr )
{
	int result = *cpu.get_code( addr );
	if ( (unsigned) (addr - apu_.io_addr) < apu_.io_size )
		result = apu_.read_register( time(), addr );
	
#ifndef NDEBUG
	else if ( unsigned (addr - 0x8000) < 0x2000 || unsigned (addr - 0xE000) < 0x1F00 )
		dprintf( "Unmapped read $%04X\n", (unsigned) addr );
	else if ( unsigned (addr - 0xFF01) < 0xFF80 - 0xFF01 && addr != 0xFF70 && addr != 0xFF05 )
		dprintf( "Unmapped read $%04X\n", (unsigned) addr );
#endif

	return LOG_MEM( addr, ">", result );
}
示例#13
0
int image_png_read_header(MediaScanImage *i, MediaScanResult *r) {
  int x;
  PNGData *p = malloc(sizeof(PNGData));
  i->_png = (void *)p;
  LOG_MEM("new PNGData @ %p\n", i->_png);

  p->buf = (Buffer *)r->_buf;
  p->fp = r->_fp;
  p->path = r->path;

  p->png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp) p, image_png_error, image_png_warning);
  if (!p->png_ptr)
    FATAL("Could not initialize libpng\n");

  p->info_ptr = png_create_info_struct(p->png_ptr);
  if (!p->info_ptr) {
    png_destroy_read_struct(&p->png_ptr, (png_infopp) NULL, (png_infopp) NULL);
    FATAL("Could not initialize libpng\n");
  }

  if (setjmp(png_jmpbuf(p->png_ptr))) {
    image_png_destroy(i);
    return 0;
  }

  png_set_read_fn(p->png_ptr, p, image_png_read_buf);

  png_read_info(p->png_ptr, p->info_ptr);

  i->width = png_get_image_width(p->png_ptr, p->info_ptr);
  i->height = png_get_image_height(p->png_ptr, p->info_ptr);
  i->channels = png_get_channels(p->png_ptr, p->info_ptr);
  i->has_alpha = 1;
  r->mime_type = MIME_IMAGE_PNG;

  // Match with DLNA profile
  // DLNA does not support interlaced images
  if (png_get_interlace_type(p->png_ptr, p->info_ptr) == PNG_INTERLACE_NONE) {
    for (x = 0; png_profiles_mapping[x].profile; x++) {
      if (i->width <= png_profiles_mapping[x].max_width && i->height <= png_profiles_mapping[x].max_height) {
        r->dlna_profile = png_profiles_mapping[x].profile->id;
        break;
      }
    }
  }

  return 1;
}
示例#14
0
int Nsf_Impl::read_mem( addr_t addr )
{
    int result = low_ram [addr & (low_ram_size-1)]; // also handles wrap-around
    if ( addr & 0xE000 )
    {
        result = *cpu.get_code( addr );
        if ( addr < sram_addr )
        {
            if ( addr == apu.status_addr )
                result = apu.read_status( time() );
            else
                result = cpu_read( addr );
        }
    }
    return LOG_MEM( addr, ">", result );
}
示例#15
0
文件: Gbs_Cpu.cpp 项目: kode54/Cog
int Gbs_Core::read_io( int offset )
{
	int const io_base = 0xFF00;
	int result = ram [io_base - ram_addr + offset];
	
	if ( (unsigned) (offset - (apu_.io_addr - io_base)) < apu_.io_size )
	{
		result = apu_.read_register( time(), offset + io_base );
		(void) LOG_MEM( offset + io_base, ">", result );
	}
	else
	{
		check( result == read_mem( offset + io_base ) );
	}
	return result;
}
示例#16
0
void ms_destroy(MediaScan *s) {
  int i;

  if (s->thread) {
    // A thread is running, tell it to abort
    ms_abort(s);

    // thread_destroy will wait until the thread exits cleanly
    thread_destroy(s->thread);
    s->thread = NULL;
  }

  for (i = 0; i < s->npaths; i++) {
    free(s->paths[i]);
  }

  for (i = 0; i < s->nignore_exts; i++) {
    free(s->ignore_exts[i]);
  }

  for (i = 0; i < s->nignore_sdirs; i++) {
    free(s->ignore_sdirs[i]);
  }

  for (i = 0; i < s->nthumbspecs; i++) {
    free(s->thumbspecs[i]);
  }

  progress_destroy(s->progress);

  free(s->_dirq);
  free(s->_dlna);

  if (s->cachedir)
    free(s->cachedir);

  /* When we're done with the database, close it. */
  bdb_destroy(s);

  LOG_MEM("destroy MediaScan @ %p\n", s);
  free(s);
}                               /* ms_destroy() */
示例#17
0
void Nsf_Impl::write_mem( addr_t addr, int data )
{
    (void) LOG_MEM( addr, "<", data );

    int offset = addr - sram_addr;
    if ( (unsigned) offset < sram_size )
    {
        sram() [offset] = data;
    }
    else
    {
        // after sram because CPU handles most low_ram accesses internally already
        int temp = addr & (low_ram_size-1); // also handles wrap-around
        if ( !(addr & 0xE000) )
        {
            low_ram [temp] = data;
        }
        else
        {
            int bank = addr - banks_addr;
            if ( (unsigned) bank < bank_count )
            {
                write_bank( bank, data );
            }
            else if ( (unsigned) (addr - apu.io_addr) < apu.io_size )
            {
                apu.write_register( time(), addr, data );
            }
            else
            {
#if !NSF_EMU_APU_ONLY
                // 0x8000-0xDFFF is writable
                int i = addr - 0x8000;
                if ( (unsigned) i < fdsram_size && fds_enabled() )
                    fdsram() [i] = data;
                else
#endif
                    cpu_write( addr, data );
            }
        }
    }
}
示例#18
0
void
result_destroy(MediaScanResult *r)
{
  if (r->error)
    error_destroy(r->error);
  
  switch (r->type) {
    case TYPE_VIDEO:
      if (r->type_data.video)
        video_destroy((MediaScanVideo *)r->type_data.video);
      break;
    
    // XXX other type_data types
    
    default:
      break;
  }
    
  LOG_MEM("destroy MediaScanResult @ %p\n", r);
  free(r);
}
示例#19
0
///-------------------------------------------------------------------------------------------------
///  Begin a recursive scan of all paths previously provided to ms_add_path(). If async mode
///   is enabled, this call will return immediately. You must obtain the file descriptor using
///   ms_async_fd and this must be checked using an event loop or select(). When the fd becomes
///   readable you must call ms_async_process to trigger any necessary callbacks.
///
/// @author Andy Grundman
/// @date 03/15/2011
///
/// @param [in,out] s If non-null, the.
///
/// ### remarks .
///-------------------------------------------------------------------------------------------------
void ms_scan(MediaScan *s) {

  if (s->on_result == NULL) {
    LOG_ERROR("Result callback not set, aborting scan\n");
    goto out;
  }

  if (s->npaths == 0) {
    LOG_ERROR("No paths set, aborting scan\n");
    goto out;
  }

  if (s->async) {
    thread_data_type *thread_data;

    thread_data = (thread_data_type *)calloc(sizeof(thread_data_type), 1);
    LOG_MEM("new thread_data @ %p\n", thread_data);

    thread_data->lpDir = NULL;
    thread_data->s = s;

    s->thread = thread_create(do_scan, thread_data, s->async_fds);
    if (!s->thread) {
      LOG_ERROR("Unable to start async thread\n");
      goto out;
    }
  }
  else {
    thread_data_type thread_data;
    thread_data.s = s;
    thread_data.lpDir = NULL;
    do_scan(&thread_data);
  }

out:
  return;
}                               /* ms_scan() */
示例#20
0
文件: Gbs_Cpu.cpp 项目: kode54/Cog
void Gbs_Core::write_mem( addr_t addr, int data )
{
	(void) LOG_MEM( addr, "<", data );
	
	int offset = addr - ram_addr;
	if ( (unsigned) offset < 0x10000 - ram_addr )
	{
		ram [offset] = data;
		
		offset -= 0xE000 - ram_addr;
		if ( (unsigned) offset < 0x1F80 )
			write_io_inline( offset, data, 0xE000 );
	}
	else if ( (unsigned) (offset - (0x2000 - ram_addr)) < 0x2000 )
	{
		set_bank( data & 0xFF );
	}
#ifndef NDEBUG
	else if ( unsigned (addr - 0x8000) < 0x2000 || unsigned (addr - 0xE000) < 0x1F00 )
	{
		dprintf( "Unmapped write $%04X\n", (unsigned) addr );
	}
#endif
}
示例#21
0
// Called by ms_scan either in a thread or synchronously
static void *do_scan(void *userdata) {
  MediaScan *s = ((thread_data_type *)userdata)->s;
  int i;
  struct dirq *dir_head = (struct dirq *)s->_dirq;
  struct dirq_entry *dir_entry = NULL;
  struct fileq *file_head = NULL;
  struct fileq_entry *file_entry = NULL;
  char tmp_full_path[MAX_PATH_STR_LEN];

  // Initialize the cache database
  if (!init_bdb(s)) {
    MediaScanError *e = error_create("", MS_ERROR_CACHE, "Unable to initialize libmediascan cache");
    send_error(s, e);
    goto out;
  }

  if (s->flags & MS_CLEARDB) {
    reset_bdb(s);
  }

  if (s->progress == NULL) {
    MediaScanError *e = error_create("", MS_ERROR_TYPE_INVALID_PARAMS, "Progress object not created");
    send_error(s, e);
    goto out;
  }

  // Build a list of all directories and paths
  // We do this first so we can present an accurate scan eta later
  progress_start_phase(s->progress, "Discovering");

  for (i = 0; i < s->npaths; i++) {
    LOG_INFO("Scanning %s\n", s->paths[i]);
    recurse_dir(s, s->paths[i], 0);
  }

  // Scan all files found
  progress_start_phase(s->progress, "Scanning");

  while (!SIMPLEQ_EMPTY(dir_head)) {
    dir_entry = SIMPLEQ_FIRST(dir_head);

    file_head = dir_entry->files;
    while (!SIMPLEQ_EMPTY(file_head)) {
      // check if the scan has been aborted
      if (s->_want_abort) {
        LOG_DEBUG("Aborting scan\n");
        goto aborted;
      }

      file_entry = SIMPLEQ_FIRST(file_head);

      // Construct full path
      strcpy(tmp_full_path, dir_entry->dir);
#ifdef WIN32
      strcat(tmp_full_path, "\\");
#else
      strcat(tmp_full_path, "/");
#endif
      strcat(tmp_full_path, file_entry->file);

      ms_scan_file(s, tmp_full_path, file_entry->type);

      // Send progress update if necessary
      if (s->on_progress) {
        s->progress->done++;

        if (progress_update(s->progress, tmp_full_path))
          send_progress(s);
      }

      SIMPLEQ_REMOVE_HEAD(file_head, entries);
      free(file_entry->file);
      free(file_entry);
    }

    SIMPLEQ_REMOVE_HEAD(dir_head, entries);
    free(dir_entry->dir);
    free(dir_entry->files);
    free(dir_entry);
  }

  // Send final progress callback
  if (s->on_progress) {
    progress_update(s->progress, NULL);
    send_progress(s);
  }

  LOG_DEBUG("Finished scanning\n");

out:
  if (s->on_finish)
    send_finish(s);

aborted:
  if (s->async) {
    LOG_MEM("destroy thread_data @ %p\n", userdata);
    free(userdata);
  }

  return NULL;
}
示例#22
0
void video_destroy(MediaScanVideo *v) {
  LOG_MEM("destroy MediaScanVideo @ %p\n", v);
  free(v);
}
示例#23
0
MediaScanImage *video_create_image_from_frame(MediaScanVideo *v, MediaScanResult *r) {
  MediaScanImage *i = image_create();
  AVFormatContext *avf = (AVFormatContext *)r->_avf;
  av_codecs_t *codecs = (av_codecs_t *)v->_codecs;
  AVCodec *codec = (AVCodec *)v->_avc;
  AVFrame *frame = NULL;
  AVPacket packet;
  struct SwsContext *swsc = NULL;
  int got_picture;
  int64_t duration_tb = ((double)avf->duration / AV_TIME_BASE) / av_q2d(codecs->vs->time_base);
  uint8_t *src;
  int x, y;
  int ofs = 0;
  int no_keyframe_found = 0;
  int skipped_frames = 0;

  if ((avcodec_open(codecs->vc, codec)) < 0) {
    LOG_ERROR("Couldn't open video codec %s for thumbnail creation\n", codec->name);
    goto err;
  }

  frame = avcodec_alloc_frame();
  if (!frame) {
    LOG_ERROR("Couldn't allocate a video frame\n");
    goto err;
  }

  av_init_packet(&packet);

  i->path = v->path;
  i->width = v->width;
  i->height = v->height;

  // XXX select best video frame, for example:
  // * Skip frames of all the same color (e.g. blank intro frames
  // * Use edge detection to skip blurry frames
  //   * http://code.google.com/p/fast-edge/
  //   * http://en.wikipedia.org/wiki/Canny_edge_detector 
  // * Use a frame some percentage into the video, what percentage?
  // * If really ambitious, use OpenCV for finding a frame with a face?

  // XXX other ways to seek if this fails
  // XXX for now, seek 10% into the video
  av_seek_frame(avf, codecs->vsid, (int)((double)duration_tb * 0.1), 0);

  for (;;) {
    int ret;
    int rgb_bufsize;
    AVFrame *frame_rgb = NULL;
    uint8_t *rgb_buffer = NULL;

    // Give up if we already tried the first frame
    if (no_keyframe_found) {
      LOG_ERROR("Error decoding video frame for thumbnail: %s\n", v->path);
      goto err;
    }

    if ((ret = av_read_frame(avf, &packet)) < 0) {
      if (ret == AVERROR_EOF || skipped_frames > 200) {
        LOG_DEBUG("Couldn't find a keyframe, using first frame\n");
        no_keyframe_found = 1;
        av_seek_frame(avf, codecs->vsid, 0, 0);
        av_read_frame(avf, &packet);
      }
      else {
        LOG_ERROR("Couldn't read video frame (%s): ", v->path);
        print_averror(ret);
        goto err;
      }
    }

    // Skip frame if it's not from the video stream
    if (!no_keyframe_found && packet.stream_index != codecs->vsid) {
      av_free_packet(&packet);
      skipped_frames++;
      continue;
    }

    // Skip non-key-frames
    if (!no_keyframe_found && !(packet.flags & AV_PKT_FLAG_KEY)) {
      av_free_packet(&packet);
      skipped_frames++;
      continue;
    }

    // Skip invalid packets, not sure why this isn't an error from av_read_frame
    if (packet.pos < 0) {
      av_free_packet(&packet);
      skipped_frames++;
      continue;
    }

    LOG_DEBUG("Using video packet: pos %lld size %d, stream_index %d, duration %d\n",
              packet.pos, packet.size, packet.stream_index, packet.duration);

    if ((ret = avcodec_decode_video2(codecs->vc, frame, &got_picture, &packet)) < 0) {
      LOG_ERROR("Error decoding video frame for thumbnail: %s\n", v->path);
      print_averror(ret);
      goto err;
    }

    if (!got_picture) {
      if (skipped_frames > 200) {
        LOG_ERROR("Error decoding video frame for thumbnail: %s\n", v->path);
        goto err;
      }
      if (!no_keyframe_found) {
        // Try next frame
        av_free_packet(&packet);
        skipped_frames++;
        continue;
      }
    }

    // use swscale to convert from source format to RGBA in our buffer with no resizing
    // XXX what scaler is fastest here when not actually resizing?
    swsc = sws_getContext(i->width, i->height, codecs->vc->pix_fmt,
                          i->width, i->height, PIX_FMT_RGB24, SWS_FAST_BILINEAR, NULL, NULL, NULL);
    if (!swsc) {
      LOG_ERROR("Unable to get swscale context\n");
      goto err;
    }

    frame_rgb = avcodec_alloc_frame();
    if (!frame_rgb) {
      LOG_ERROR("Couldn't allocate a video frame\n");
      goto err;
    }

    // XXX There is probably a way to get sws_scale to write directly to i->_pixbuf in our RGBA format

    rgb_bufsize = avpicture_get_size(PIX_FMT_RGB24, i->width, i->height);
    rgb_buffer = av_malloc(rgb_bufsize);
    if (!rgb_buffer) {
      LOG_ERROR("Couldn't allocate an RGB video buffer\n");
      av_free(frame_rgb);
      goto err;
    }
    LOG_MEM("new rgb_buffer of size %d @ %p\n", rgb_bufsize, rgb_buffer);

    avpicture_fill((AVPicture *)frame_rgb, rgb_buffer, PIX_FMT_RGB24, i->width, i->height);

    // Convert image to RGB24
    sws_scale(swsc, frame->data, frame->linesize, 0, i->height, frame_rgb->data, frame_rgb->linesize);

    // Allocate space for our version of the image
    image_alloc_pixbuf(i, i->width, i->height);

    src = frame_rgb->data[0];
    ofs = 0;
    for (y = 0; y < i->height; y++) {
      for (x = 0; x < i->width * 3; x += 3) {
        i->_pixbuf[ofs++] = COL(src[x], src[x + 1], src[x + 2]);
      }
      src += i->width * 3;
    }

    // Free the frame
    LOG_MEM("destroy rgb_buffer @ %p\n", rgb_buffer);
    av_free(rgb_buffer);

    av_free(frame_rgb);

    // Done!
    goto out;
  }

err:
  image_destroy(i);
  i = NULL;

out:
  sws_freeContext(swsc);
  av_free_packet(&packet);
  if (frame)
    av_free(frame);

  avcodec_close(codecs->vc);

  return i;
}
示例#24
0
int image_jpeg_read_header(MediaScanImage *i, MediaScanResult *r) {
  int ret = 1;
  int x;

  JPEGData *j = malloc(sizeof(JPEGData));
  i->_jpeg = (void *)j;
  LOG_MEM("new JPEGData @ %p\n", i->_jpeg);

  j->cinfo = malloc(sizeof(struct jpeg_decompress_struct));
  j->jpeg_error_pub = malloc(sizeof(struct jpeg_error_mgr));
  LOG_MEM("new JPEG cinfo @ %p\n", j->cinfo);
  LOG_MEM("new JPEG error_pub @ %p\n", j->jpeg_error_pub);

  j->cinfo->err = jpeg_std_error(j->jpeg_error_pub);
  j->jpeg_error_pub->error_exit = libjpeg_error_handler;
  j->jpeg_error_pub->output_message = libjpeg_output_message;

  if (setjmp(setjmp_buffer)) {
    image_jpeg_destroy(i);
    return 0;
  }

  // Save filename in case any warnings/errors occur
  strncpy(Filename, r->path, FILENAME_LEN);
  if (strlen(r->path) > FILENAME_LEN)
    Filename[FILENAME_LEN] = 0;

  jpeg_create_decompress(j->cinfo);

  // Init custom source manager to read from existing buffer
  image_jpeg_buf_src(i, r);

  // Save APP1 marker for EXIF
  jpeg_save_markers(j->cinfo, 0xE1, 1024 * 64);

  jpeg_read_header(j->cinfo, TRUE);

  i->width = j->cinfo->image_width;
  i->height = j->cinfo->image_height;
  i->channels = j->cinfo->num_components;
  r->mime_type = MIME_IMAGE_JPEG;

  // Match with DLNA profile
  for (x = 0; jpeg_profiles_mapping[x].profile; x++) {
    if (i->width <= jpeg_profiles_mapping[x].max_width && i->height <= jpeg_profiles_mapping[x].max_height) {
      r->dlna_profile = jpeg_profiles_mapping[x].profile->id;
      break;
    }
  }

  // Process Exif tag
  if (j->cinfo->marker_list != NULL) {
    jpeg_saved_marker_ptr marker = j->cinfo->marker_list;

    while (marker != NULL) {
      if (marker->marker == 0xE1
          && marker->data[0] == 'E' && marker->data[1] == 'x' && marker->data[2] == 'i' && marker->data[3] == 'f') {
        ExifData *exif;

        result_create_tag(r, "Exif");

        LOG_DEBUG("Parsing EXIF tag of size %d\n", marker->data_length);
        exif = exif_data_new_from_data(marker->data, marker->data_length);
        LOG_MEM("new EXIF data @ %p\n", exif);
        if (exif != NULL) {
          exif_data_foreach_content(exif, parse_exif_ifd, (void *)r);
          LOG_MEM("destroy EXIF data @ %p\n", exif);
          exif_data_free(exif);
        }

        break;
      }

      marker = marker->next;
    }
  }

  return ret;
}
示例#25
0
int image_jpeg_load(MediaScanImage *i, MediaScanThumbSpec *spec_hint) {
  float scale_factor;
  int x, w, h, ofs;
  unsigned char *line[1], *ptr = NULL;

  JPEGData *j = (JPEGData *)i->_jpeg;

  if (setjmp(setjmp_buffer)) {
    // See if we have partially decoded an image and hit a fatal error, but still have a usable image
    if (ptr != NULL) {
      LOG_MEM("destroy JPEG load ptr @ %p\n", ptr);
      free(ptr);
      ptr = NULL;
    }

    if (j->cinfo->output_scanline > 0) {
      LOG_DEBUG("Fatal error but already processed %d scanlines, continuing...\n", j->cinfo->output_scanline);
      return 1;
    }

    image_jpeg_destroy(i);
    return 0;
  }

  // Abort on progressive JPEGs if memory_limit is in use,
  // as progressive JPEGs can use many MBs of memory and there
  // is no other easy way to alter libjpeg's memory use
  /* XXX
     if (i->memory_limit && j->cinfo->progressive_mode) {
     LOG_WARN("libmediascan will not decode progressive JPEGs when memory_limit is in use (%s)\n", i->path);
     image_jpeg_destroy(i);
     return 0;
     }
   */

  // XXX If reusing the object a second time, we need to read the header again

  j->cinfo->do_fancy_upsampling = FALSE;
  j->cinfo->do_block_smoothing = FALSE;

  // Choose optimal scaling factor
  jpeg_calc_output_dimensions(j->cinfo);
  scale_factor = (float)j->cinfo->output_width / spec_hint->width;
  if (scale_factor > ((float)j->cinfo->output_height / spec_hint->height))
    scale_factor = (float)j->cinfo->output_height / spec_hint->height;
  if (scale_factor > 1) {       // Avoid divide by 0
    j->cinfo->scale_denom *= (unsigned int)scale_factor;
    jpeg_calc_output_dimensions(j->cinfo);
  }

  w = j->cinfo->output_width;
  h = j->cinfo->output_height;

  // Change the original values to the scaled size
  i->width = w;
  i->height = h;

  LOG_DEBUG("Using JPEG scale factor %d/%d, new source dimensions %d x %d\n",
            j->cinfo->scale_num, j->cinfo->scale_denom, w, h);

  // Save filename in case any warnings/errors occur
  strncpy(Filename, i->path, FILENAME_LEN);
  if (strlen(i->path) > FILENAME_LEN)
    Filename[FILENAME_LEN] = 0;

  // Note: I tested libjpeg-turbo's JCS_EXT_XBGR but it writes zeros
  // instead of FF for alpha, doesn't support CMYK, etc

  jpeg_start_decompress(j->cinfo);

  // Allocate storage for decompressed image
  image_alloc_pixbuf(i, w, h);

  ofs = 0;

  ptr = (unsigned char *)malloc(w * j->cinfo->output_components);
  line[0] = ptr;
  LOG_MEM("new JPEG load ptr @ %p\n", ptr);

  if (j->cinfo->output_components == 3) { // RGB
    while (j->cinfo->output_scanline < j->cinfo->output_height) {
      jpeg_read_scanlines(j->cinfo, line, 1);
      for (x = 0; x < w; x++) {
        i->_pixbuf[ofs++] = COL(ptr[x + x + x], ptr[x + x + x + 1], ptr[x + x + x + 2]);
      }
    }
  }
  else if (j->cinfo->output_components == 4) {  // CMYK inverted (Photoshop)
    while (j->cinfo->output_scanline < j->cinfo->output_height) {
      JSAMPROW row = *line;
      jpeg_read_scanlines(j->cinfo, line, 1);
      for (x = 0; x < w; x++) {
        int c = *row++;
        int m = *row++;
        int y = *row++;
        int k = *row++;

        i->_pixbuf[ofs++] = COL((c * k) / MAXJSAMPLE, (m * k) / MAXJSAMPLE, (y * k) / MAXJSAMPLE);
      }
    }
  }
  else {                        // grayscale
    while (j->cinfo->output_scanline < j->cinfo->output_height) {
      jpeg_read_scanlines(j->cinfo, line, 1);
      for (x = 0; x < w; x++) {
        i->_pixbuf[ofs++] = COL(ptr[x], ptr[x], ptr[x]);
      }
    }
  }

  LOG_MEM("destroy JPEG load ptr @ %p\n", ptr);
  free(ptr);

  jpeg_finish_decompress(j->cinfo);

  return 1;
}
示例#26
0
// Compress the data from i->_pixbuf to i->data.
// Uses libjpeg-turbo if available (JCS_EXTENSIONS) for better performance
int image_jpeg_compress(MediaScanImage *i, MediaScanThumbSpec *spec) {
  struct jpeg_compress_struct cinfo;
  struct jpeg_error_mgr jerr;
  struct buf_dst_mgr dst;
  int quality = spec->jpeg_quality;
  int x;
#ifdef JCS_EXTENSIONS
  JSAMPROW *data = NULL;
#else
  volatile unsigned char *data = NULL;  // volatile = won't be rolled back if longjmp is called
  JSAMPROW row_pointer[1];
  int y, row_stride;
#endif

  if (!i->_pixbuf_size) {
    LOG_WARN("JPEG compression requires pixbuf data (%s)\n", i->path);
    exit(-1);
    return 0;
  }

  if (!quality)
    quality = DEFAULT_JPEG_QUALITY;

  cinfo.err = jpeg_std_error(&jerr);
  jpeg_create_compress(&cinfo);
  image_jpeg_buf_dest(&cinfo, &dst);

  cinfo.image_width = i->width;
  cinfo.image_height = i->height;
  cinfo.input_components = 3;
  cinfo.in_color_space = JCS_RGB; // output is always RGB even if source was grayscale

  if (setjmp(setjmp_buffer)) {
    if (data != NULL) {
      LOG_MEM("destroy JPEG data row @ %p\n", data);
      free((void *)data);
    }
    return 0;
  }

#ifdef JCS_EXTENSIONS
  // Use libjpeg-turbo support for direct reading from source buffer
  cinfo.input_components = 4;
  cinfo.in_color_space = JCS_EXT_XBGR;
#endif

  jpeg_set_defaults(&cinfo);
  jpeg_set_quality(&cinfo, quality, TRUE);
  jpeg_start_compress(&cinfo, TRUE);

#ifdef JCS_EXTENSIONS
  data = (JSAMPROW *)malloc(i->height * sizeof(JSAMPROW));
  LOG_MEM("new JPEG data row @ %p\n", data);

  for (x = 0; x < i->height; x++)
    data[x] = (JSAMPROW)&i->_pixbuf[x * i->width];

  while (cinfo.next_scanline < cinfo.image_height) {
    jpeg_write_scanlines(&cinfo, &data[cinfo.next_scanline], cinfo.image_height - cinfo.next_scanline);
  }

#else
  // Normal libjpeg
  row_stride = cinfo.image_width * 3;
  data = (unsigned char *)malloc(row_stride);
  LOG_MEM("new JPEG data row @ %p\n", data);

  y = 0;
  while (cinfo.next_scanline < cinfo.image_height) {
    for (x = 0; x < cinfo.image_width; x++) {
      data[x + x + x] = COL_RED(i->_pixbuf[y]);
      data[x + x + x + 1] = COL_GREEN(i->_pixbuf[y]);
      data[x + x + x + 2] = COL_BLUE(i->_pixbuf[y]);
      y++;
    }
    row_pointer[0] = (unsigned char *)data;
    jpeg_write_scanlines(&cinfo, row_pointer, 1);
  }
#endif

  jpeg_finish_compress(&cinfo);

  LOG_MEM("destroy JPEG data row @ %p\n", data);
  free((void *)data);

  jpeg_destroy_compress(&cinfo);

  // Attach compressed buffer to image
  i->_dbuf = (void *)dst.dbuf;

  return 1;
}