Beispiel #1
0
GdkPixbuf *
gst_video_thumbnailer_get_shot (const gchar  *location,
          GCancellable *cancellable)
{
    GstElement *playbin, *audio_sink, *video_sink;
    GstStateChangeReturn state;
    GdkPixbuf *shot = NULL;
    int count = 0;
    gchar *uri = g_strconcat ("file://", location, NULL);
    GMainContext *context = g_main_context_new ();

    g_main_context_push_thread_default  (context);

    playbin = gst_element_factory_make ("playbin", "playbin");
    audio_sink = gst_element_factory_make ("fakesink", "audiosink");
    video_sink = gst_element_factory_make ("fakesink", "videosink");

    g_object_set (playbin,
                  "uri", uri,
                  "audio-sink", audio_sink,
                  "video-sink", video_sink,
                  NULL);
    g_object_set (video_sink,
                  "sync", TRUE,
                  NULL);
    state = gst_element_set_state (playbin, GST_STATE_PAUSED);
    while (state == GST_STATE_CHANGE_ASYNC
           && count < 5
           && !g_cancellable_is_cancelled (cancellable)) {
        state = gst_element_get_state (playbin, NULL, 0, 1 * GST_SECOND);
        count++;

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


    if (g_cancellable_is_cancelled (cancellable)) {
        g_print ("Video %s was cancelled\n", uri);
        state = GST_STATE_CHANGE_FAILURE;
    }

    if (state != GST_STATE_CHANGE_FAILURE &&
        state != GST_STATE_CHANGE_ASYNC) {
        GstFormat format = GST_FORMAT_TIME;
        gint64 duration;

        if (gst_element_query_duration (playbin, &format, &duration)) {
            gint64 seekpos;
            GstBuffer *frame;

            if (duration > 0) {
                if (duration / (3 * GST_SECOND) > 90) {
                    seekpos = (rand () % (duration / (3 * GST_SECOND))) * GST_SECOND;
                } else {
                    seekpos = (rand () % (duration / (GST_SECOND))) * GST_SECOND;
                }
            } else {
                seekpos = 5 * GST_SECOND;
            }

            gst_element_seek_simple (playbin, GST_FORMAT_TIME,
                                     GST_SEEK_FLAG_FLUSH |
                                     GST_SEEK_FLAG_ACCURATE, seekpos);

            /* Wait for seek to complete */
            count = 0;
            state = gst_element_get_state (playbin, NULL, 0,
                                           0.2 * GST_SECOND);
            while (state == GST_STATE_CHANGE_ASYNC && count < 3) {
                state = gst_element_get_state (playbin, NULL, 0, 1 * GST_SECOND);
                count++;
            }

            g_object_get (playbin,
                          "frame", &frame,
                          NULL);
            if (frame == NULL) {
                g_warning ("No frame for %s", uri);
                shot = NULL;
                goto finish;
            }

            shot = convert_buffer_to_pixbuf (frame, cancellable);
        }
    }

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

 finish:

    g_main_context_pop_thread_default (context);
    g_main_context_unref (context);

    return shot;
}
Beispiel #2
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));
    }
  }
}