Example #1
0
gboolean  tumbler_util_guess_is_sparse (TumblerFileInfo *info)
{
  gchar *filename;
  struct stat sb;
  gboolean ret_val = FALSE;

  g_return_val_if_fail (TUMBLER_IS_FILE_INFO (info), FALSE);

  filename = g_filename_from_uri (tumbler_file_info_get_uri (info), NULL, NULL);

  if (G_LIKELY(filename))
  {
    stat (filename, &sb);

    g_free (filename);

    /* Test sparse files on regular ones */
    if (S_ISREG (sb.st_mode))
    {
      if (((TUMBLER_STAT_BLKSIZE * sb.st_blocks) / sb.st_size) < 0.8)
      {
        ret_val = TRUE;
      }
    }
  }

  return ret_val;
}
Example #2
0
static void
jpeg_thumbnailer_create (TumblerAbstractThumbnailer *thumbnailer,
                         GCancellable               *cancellable,
                         TumblerFileInfo            *info)
{
  TumblerThumbnailFlavor *flavor;
  TumblerImageData        data;
  TumblerThumbnail       *thumbnail;
  struct stat             statb;
  const gchar            *uri;
  GdkPixbuf              *pixbuf = NULL;
  gboolean                streaming_needed = TRUE;
  JOCTET                 *content;
  GError                 *error = NULL;
  GFile                  *file;
  gchar                  *path;
  gsize                   length;
  gint                    fd;
  gint                    height;
  gint                    width;
  gint                    size;

  g_return_if_fail (IS_JPEG_THUMBNAILER (thumbnailer));
  g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
  g_return_if_fail (TUMBLER_IS_FILE_INFO (info));

  /* do nothing if cancelled */
  if (g_cancellable_is_cancelled (cancellable)) 
    return;

  uri = tumbler_file_info_get_uri (info);

  /* try to open the source file for reading */
  file = g_file_new_for_uri (uri);

  thumbnail = tumbler_file_info_get_thumbnail (info);
  g_assert (thumbnail != NULL);

  flavor = tumbler_thumbnail_get_flavor (thumbnail);
  g_assert (flavor != NULL);

  tumbler_thumbnail_flavor_get_size (flavor, &width, &height);
  size = MIN (width, height);

#ifdef HAVE_MMAP
  if (g_file_is_native (file))
    {
      path = g_file_get_path (file);

      /* try to open the file at the given path */
      fd = open (path, O_RDONLY);
      if (G_LIKELY (fd >= 0))
        {
          /* determine the status of the file */
          if (G_LIKELY (fstat (fd, &statb) == 0 && statb.st_size > 0))
            {
              /* try to mmap the file */
              content = (JOCTET *) mmap (NULL, statb.st_size, PROT_READ, 
                                         MAP_SHARED, fd, 0);

              /* verify whether the mmap was successful */
              if (G_LIKELY (content != (JOCTET *) MAP_FAILED))
                {
                  /* try to load the embedded thumbnail first */
                  pixbuf = tvtj_jpeg_load_thumbnail (content, statb.st_size, size);
                  if (pixbuf == NULL)
                    {
                      /* fall back to loading and scaling the image itself */
                      pixbuf = tvtj_jpeg_load (content, statb.st_size, size);

                      if (pixbuf == NULL)
                        {
                          g_set_error (&error, TUMBLER_ERROR, 
                                       TUMBLER_ERROR_INVALID_FORMAT,
                                       _("Thumbnail could not be inferred from file contents"));
                        }
                    }

                  /* we have successfully mmapped the file. we may not have
                   * a thumbnail but trying to read the image from a stream
                   * won't help us here, so we don't need to attempt streaming
                   * as a fallback */
                  streaming_needed = FALSE;
                }

              /* unmap the file content */
              munmap ((void *) content, statb.st_size);
            }

          /* close the file */
          close (fd);
        }

      g_free (path);
    }
#endif

  if (streaming_needed)
    {
      g_file_load_contents (file, cancellable, (gchar **)&content, &length, 
                            NULL, &error);

      if (error == NULL)
        {
          pixbuf = tvtj_jpeg_load_thumbnail (content, length, size);

          if (pixbuf == NULL)
            {
              pixbuf = tvtj_jpeg_load (content, length, size);
              if (pixbuf == NULL)
                {
                  g_set_error (&error, TUMBLER_ERROR, TUMBLER_ERROR_INVALID_FORMAT,
                               _("Thumbnail could not be inferred from file contents"));
                }
            }
        }
    }

  /* either we have an error now or we have a valid thumbnail pixbuf */
  g_assert (error != NULL || pixbuf != NULL);

  if (pixbuf != NULL)
    {
      data.data = gdk_pixbuf_get_pixels (pixbuf);
      data.has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
      data.bits_per_sample = gdk_pixbuf_get_bits_per_sample (pixbuf);
      data.width = gdk_pixbuf_get_width (pixbuf);
      data.height = gdk_pixbuf_get_height (pixbuf);
      data.rowstride = gdk_pixbuf_get_rowstride (pixbuf);
      data.colorspace = (TumblerColorspace) gdk_pixbuf_get_colorspace (pixbuf);

      tumbler_thumbnail_save_image_data (thumbnail, &data, 
                                         tumbler_file_info_get_mtime (info), 
                                         NULL, &error);

      g_object_unref (pixbuf);
    }

  if (error != NULL)
    {
      g_signal_emit_by_name (thumbnailer, "error", uri, error->code, error->message);
      g_error_free (error);
    }
  else
    {
      g_signal_emit_by_name (thumbnailer, "ready", uri);
    }

  g_object_unref (flavor);
  g_object_unref (thumbnail);
  g_object_unref (file);
}
Example #3
0
/*
 * get uri
 * create playbin
 * check that we have video
 * for each potential location
 *   check for cancel
 *   grab snapshot
 *   interesting?
 *   return if so, other spin loop
 *
 * for future, check metadata for artwork and don't reject non-video streams (see totem)
 *
 * TODO: all error paths leak
 */
static void
gst_thumbnailer_create (TumblerAbstractThumbnailer *thumbnailer,
                        GCancellable               *cancellable,
                        TumblerFileInfo            *info)
{
  /* These positions are taken from Totem */
  const double positions[] = {
    1.0 / 3.0,
    2.0 / 3.0,
    0.1,
    0.9,
    0.5
  };
  GstElement             *playbin;
  gint64                  duration;
  unsigned int            i;
  GstBuffer              *frame;
  GdkPixbuf              *shot;
  TumblerThumbnail       *thumbnail;
  TumblerThumbnailFlavor *flavour;
  TumblerImageData        data;
  GError                 *error = NULL;

  g_return_if_fail (IS_GST_THUMBNAILER (thumbnailer));
  g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
  g_return_if_fail (TUMBLER_IS_FILE_INFO (info));

  /* Check for early cancellation */
  if (g_cancellable_is_cancelled (cancellable))
    return;

  playbin = make_pipeline (info, cancellable);
  if (playbin == NULL) {
    /* TODO: emit an error, but the specification won't let me. */
    return;
  }

  duration = get_duration (playbin);

  /* Now we have a pipeline that we know has video and is paused, ready for
     seeking */
  for (i = 0; i < G_N_ELEMENTS (positions); i++) {
    /* Check if we've been cancelled */
    if (g_cancellable_is_cancelled (cancellable)) {
      gst_element_set_state (playbin, GST_STATE_NULL);
      g_object_unref (playbin);
      return;
    }

    LOG ("trying position %f", positions[i]);

    gst_element_seek_simple (playbin,
                             GST_FORMAT_TIME,
                             GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT,
                             (gint64)(positions[i] * duration));

    if (gst_element_get_state (playbin, NULL, NULL, 1 * GST_SECOND) == GST_STATE_CHANGE_FAILURE) {
      LOG ("Could not seek");
      return;
    }

    g_object_get (playbin, "frame", &frame, NULL);

    if (frame == NULL) {
      LOG ("No frame found!");
      continue;
    }

    thumbnail = tumbler_file_info_get_thumbnail (info);
    flavour = tumbler_thumbnail_get_flavor (thumbnail);
    /* This frees the buffer for us */
    shot = convert_buffer_to_pixbuf (frame, cancellable, flavour);
    g_object_unref (flavour);

    /* If it's not interesting, throw it away and try again*/
    if (is_interesting (shot)) {
      /* Got an interesting image, break out */
      LOG ("Found an interesting image");
      break;
    }

    /*
     * If we've still got positions to try, free the current uninteresting
     * shot. Otherwise we'll make do with what we have.
     */
    if (i + 1 < G_N_ELEMENTS (positions) && shot) {
      g_object_unref (shot);
      shot = NULL;
    }

    /* Spin mainloop so we can pick up the cancels */
    while (g_main_context_pending (NULL)) {
      g_main_context_iteration (NULL, FALSE);
    }
  }

  gst_element_set_state (playbin, GST_STATE_NULL);
  g_object_unref (playbin);

  if (shot) {
    data.data = gdk_pixbuf_get_pixels (shot);
    data.has_alpha = gdk_pixbuf_get_has_alpha (shot);
    data.bits_per_sample = gdk_pixbuf_get_bits_per_sample (shot);
    data.width = gdk_pixbuf_get_width (shot);
    data.height = gdk_pixbuf_get_height (shot);
    data.rowstride = gdk_pixbuf_get_rowstride (shot);
    data.colorspace = (TumblerColorspace) gdk_pixbuf_get_colorspace (shot);

    tumbler_thumbnail_save_image_data (thumbnail, &data,
                                       tumbler_file_info_get_mtime (info),
                                       NULL, &error);

    g_object_unref (shot);

    if (error != NULL) {
      g_signal_emit_by_name (thumbnailer, "error",
                             tumbler_file_info_get_uri (info),
                             error->code, error->message);
      g_error_free (error);
    } else {
      g_signal_emit_by_name (thumbnailer, "ready",
                             tumbler_file_info_get_uri (info));
    }
  }
}