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; }
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); }
/* * 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)); } } }