Blame src/shell-recorder-src.c

Packit Service ed5168
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
Packit Service ed5168
Packit Service ed5168
#include "config.h"
Packit Service ed5168
Packit Service ed5168
#define GST_USE_UNSTABLE_API
Packit Service ed5168
#include <gst/base/gstpushsrc.h>
Packit Service ed5168
Packit Service ed5168
#include "shell-recorder-src.h"
Packit Service ed5168
Packit Service ed5168
struct _ShellRecorderSrc
Packit Service ed5168
{
Packit Service ed5168
  GstPushSrc parent;
Packit Service ed5168
Packit Service ed5168
  GMutex mutex;
Packit Service ed5168
Packit Service ed5168
  GstCaps *caps;
Packit Service ed5168
  GMutex queue_lock;
Packit Service ed5168
  GCond queue_cond;
Packit Service ed5168
  GQueue *queue;
Packit Service ed5168
Packit Service ed5168
  gboolean eos;
Packit Service ed5168
  gboolean flushing;
Packit Service ed5168
  guint memory_used;
Packit Service ed5168
  guint memory_used_update_idle;
Packit Service ed5168
};
Packit Service ed5168
Packit Service ed5168
struct _ShellRecorderSrcClass
Packit Service ed5168
{
Packit Service ed5168
  GstPushSrcClass parent_class;
Packit Service ed5168
};
Packit Service ed5168
Packit Service ed5168
enum {
Packit Service ed5168
  PROP_0,
Packit Service ed5168
  PROP_CAPS,
Packit Service ed5168
  PROP_MEMORY_USED
Packit Service ed5168
};
Packit Service ed5168
Packit Service ed5168
#define shell_recorder_src_parent_class parent_class
Packit Service ed5168
G_DEFINE_TYPE(ShellRecorderSrc, shell_recorder_src, GST_TYPE_PUSH_SRC);
Packit Service ed5168
Packit Service ed5168
static void
Packit Service ed5168
shell_recorder_src_init (ShellRecorderSrc      *src)
Packit Service ed5168
{
Packit Service ed5168
  gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_TIME);
Packit Service ed5168
  gst_base_src_set_live (GST_BASE_SRC (src), TRUE);
Packit Service ed5168
Packit Service ed5168
  src->queue = g_queue_new ();
Packit Service ed5168
  g_mutex_init (&src->mutex);
Packit Service ed5168
  g_mutex_init (&src->queue_lock);
Packit Service ed5168
  g_cond_init (&src->queue_cond);
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
static gboolean
Packit Service ed5168
shell_recorder_src_memory_used_update_idle (gpointer data)
Packit Service ed5168
{
Packit Service ed5168
  ShellRecorderSrc *src = data;
Packit Service ed5168
Packit Service ed5168
  g_mutex_lock (&src->mutex);
Packit Service ed5168
  src->memory_used_update_idle = 0;
Packit Service ed5168
  g_mutex_unlock (&src->mutex);
Packit Service ed5168
Packit Service ed5168
  g_object_notify (G_OBJECT (src), "memory-used");
Packit Service ed5168
Packit Service ed5168
  return FALSE;
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
/* The memory_used property is used to monitor buffer usage,
Packit Service ed5168
 * so we marshal notification back to the main loop thread.
Packit Service ed5168
 */
Packit Service ed5168
static void
Packit Service ed5168
shell_recorder_src_update_memory_used (ShellRecorderSrc *src,
Packit Service ed5168
				       int               delta)
Packit Service ed5168
{
Packit Service ed5168
  g_mutex_lock (&src->mutex);
Packit Service ed5168
  src->memory_used += delta;
Packit Service ed5168
  if (src->memory_used_update_idle == 0)
Packit Service ed5168
    {
Packit Service ed5168
      src->memory_used_update_idle = g_idle_add (shell_recorder_src_memory_used_update_idle, src);
Packit Service ed5168
      g_source_set_name_by_id (src->memory_used_update_idle, "[gnome-shell] shell_recorder_src_memory_used_update_idle");
Packit Service ed5168
    }
Packit Service ed5168
  g_mutex_unlock (&src->mutex);
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
/* _negotiate() is called when we have to decide on a format. We
Packit Service ed5168
 * use the configured format */
Packit Service ed5168
static gboolean
Packit Service ed5168
shell_recorder_src_negotiate (GstBaseSrc * base_src)
Packit Service ed5168
{
Packit Service ed5168
  ShellRecorderSrc *src = SHELL_RECORDER_SRC (base_src);
Packit Service ed5168
  gboolean result;
Packit Service ed5168
Packit Service ed5168
  result = gst_base_src_set_caps (base_src, src->caps);
Packit Service ed5168
Packit Service ed5168
  return result;
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
static gboolean
Packit Service ed5168
shell_recorder_src_unlock (GstBaseSrc * base_src)
Packit Service ed5168
{
Packit Service ed5168
  ShellRecorderSrc *src = SHELL_RECORDER_SRC (base_src);
Packit Service ed5168
Packit Service ed5168
  g_mutex_lock (&src->queue_lock);
Packit Service ed5168
  src->flushing = TRUE;
Packit Service ed5168
  g_cond_signal (&src->queue_cond);
Packit Service ed5168
  g_mutex_unlock (&src->queue_lock);
Packit Service ed5168
Packit Service ed5168
  return TRUE;
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
static gboolean
Packit Service ed5168
shell_recorder_src_unlock_stop (GstBaseSrc * base_src)
Packit Service ed5168
{
Packit Service ed5168
  ShellRecorderSrc *src = SHELL_RECORDER_SRC (base_src);
Packit Service ed5168
Packit Service ed5168
  g_mutex_lock (&src->queue_lock);
Packit Service ed5168
  src->flushing = FALSE;
Packit Service ed5168
  g_cond_signal (&src->queue_cond);
Packit Service ed5168
  g_mutex_unlock (&src->queue_lock);
Packit Service ed5168
Packit Service ed5168
  return TRUE;
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
static gboolean
Packit Service ed5168
shell_recorder_src_start (GstBaseSrc * base_src)
Packit Service ed5168
{
Packit Service ed5168
  ShellRecorderSrc *src = SHELL_RECORDER_SRC (base_src);
Packit Service ed5168
Packit Service ed5168
  g_mutex_lock (&src->queue_lock);
Packit Service ed5168
  src->flushing = FALSE;
Packit Service ed5168
  src->eos = FALSE;
Packit Service ed5168
  g_cond_signal (&src->queue_cond);
Packit Service ed5168
  g_mutex_unlock (&src->queue_lock);
Packit Service ed5168
Packit Service ed5168
  return TRUE;
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
static gboolean
Packit Service ed5168
shell_recorder_src_stop (GstBaseSrc * base_src)
Packit Service ed5168
{
Packit Service ed5168
  ShellRecorderSrc *src = SHELL_RECORDER_SRC (base_src);
Packit Service ed5168
Packit Service ed5168
  g_mutex_lock (&src->queue_lock);
Packit Service ed5168
  src->flushing = TRUE;
Packit Service ed5168
  src->eos = FALSE;
Packit Service ed5168
  g_queue_foreach (src->queue, (GFunc) gst_buffer_unref, NULL);
Packit Service ed5168
  g_queue_clear (src->queue);
Packit Service ed5168
  g_cond_signal (&src->queue_cond);
Packit Service ed5168
  g_mutex_unlock (&src->queue_lock);
Packit Service ed5168
Packit Service ed5168
  return TRUE;
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
static gboolean
Packit Service ed5168
shell_recorder_src_send_event (GstElement * element, GstEvent * event)
Packit Service ed5168
{
Packit Service ed5168
  ShellRecorderSrc *src = SHELL_RECORDER_SRC (element);
Packit Service ed5168
  gboolean res;
Packit Service ed5168
Packit Service ed5168
  if (GST_EVENT_TYPE (event) == GST_EVENT_EOS)
Packit Service ed5168
    {
Packit Service ed5168
      shell_recorder_src_close (src);
Packit Service ed5168
      gst_event_unref (event);
Packit Service ed5168
      res = TRUE;
Packit Service ed5168
    }
Packit Service ed5168
  else
Packit Service ed5168
    {
Packit Service ed5168
      res = GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS, send_event, (element,
Packit Service ed5168
              event), FALSE);
Packit Service ed5168
    }
Packit Service ed5168
  return res;
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
/* The create() virtual function is responsible for returning the next buffer.
Packit Service ed5168
 * We just pop buffers off of the queue and block if necessary.
Packit Service ed5168
 */
Packit Service ed5168
static GstFlowReturn
Packit Service ed5168
shell_recorder_src_create (GstPushSrc  *push_src,
Packit Service ed5168
			   GstBuffer  **buffer_out)
Packit Service ed5168
{
Packit Service ed5168
  ShellRecorderSrc *src = SHELL_RECORDER_SRC (push_src);
Packit Service ed5168
  GstBuffer *buffer;
Packit Service ed5168
Packit Service ed5168
  g_mutex_lock (&src->queue_lock);
Packit Service ed5168
  while (TRUE) {
Packit Service ed5168
    /* int the flushing state we just return FLUSHING */
Packit Service ed5168
    if (src->flushing) {
Packit Service ed5168
      g_mutex_unlock (&src->queue_lock);
Packit Service ed5168
      return GST_FLOW_FLUSHING;
Packit Service ed5168
    }
Packit Service ed5168
Packit Service ed5168
    buffer = g_queue_pop_head (src->queue);
Packit Service ed5168
Packit Service ed5168
    /* we have a buffer, exit the loop to handle it */
Packit Service ed5168
    if (buffer != NULL)
Packit Service ed5168
      break;
Packit Service ed5168
Packit Service ed5168
    /* no buffer, check EOS */
Packit Service ed5168
    if (src->eos) {
Packit Service ed5168
      g_mutex_unlock (&src->queue_lock);
Packit Service ed5168
      return GST_FLOW_EOS;
Packit Service ed5168
    }
Packit Service ed5168
    /* wait for something to happen and try again */
Packit Service ed5168
    g_cond_wait (&src->queue_cond, &src->queue_lock);
Packit Service ed5168
  }
Packit Service ed5168
  g_mutex_unlock (&src->queue_lock);
Packit Service ed5168
Packit Service ed5168
  shell_recorder_src_update_memory_used (src,
Packit Service ed5168
					 - (int)(gst_buffer_get_size(buffer) / 1024));
Packit Service ed5168
Packit Service ed5168
  *buffer_out = buffer;
Packit Service ed5168
Packit Service ed5168
  return GST_FLOW_OK;
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
static void
Packit Service ed5168
shell_recorder_src_set_caps (ShellRecorderSrc *src,
Packit Service ed5168
			     const GstCaps    *caps)
Packit Service ed5168
{
Packit Service ed5168
  if (caps == src->caps)
Packit Service ed5168
    return;
Packit Service ed5168
Packit Service ed5168
  if (src->caps != NULL)
Packit Service ed5168
    {
Packit Service ed5168
      gst_caps_unref (src->caps);
Packit Service ed5168
      src->caps = NULL;
Packit Service ed5168
    }
Packit Service ed5168
Packit Service ed5168
  if (caps)
Packit Service ed5168
    {
Packit Service ed5168
      /* The capabilities will be negotated with the downstream element
Packit Service ed5168
       * and set on the pad when the first buffer is pushed.
Packit Service ed5168
       */
Packit Service ed5168
      src->caps = gst_caps_copy (caps);
Packit Service ed5168
    }
Packit Service ed5168
  else
Packit Service ed5168
    src->caps = NULL;
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
static void
Packit Service ed5168
shell_recorder_src_finalize (GObject *object)
Packit Service ed5168
{
Packit Service ed5168
  ShellRecorderSrc *src = SHELL_RECORDER_SRC (object);
Packit Service ed5168
Packit Service ed5168
  if (src->memory_used_update_idle)
Packit Service ed5168
    g_source_remove (src->memory_used_update_idle);
Packit Service ed5168
Packit Service ed5168
  shell_recorder_src_set_caps (src, NULL);
Packit Service ed5168
  g_queue_free_full (src->queue, (GDestroyNotify) gst_buffer_unref);
Packit Service ed5168
Packit Service ed5168
  g_mutex_clear (&src->mutex);
Packit Service ed5168
  g_mutex_clear (&src->queue_lock);
Packit Service ed5168
  g_cond_clear (&src->queue_cond);
Packit Service ed5168
Packit Service ed5168
  G_OBJECT_CLASS (shell_recorder_src_parent_class)->finalize (object);
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
static void
Packit Service ed5168
shell_recorder_src_set_property (GObject      *object,
Packit Service ed5168
				 guint         prop_id,
Packit Service ed5168
				 const GValue *value,
Packit Service ed5168
				 GParamSpec   *pspec)
Packit Service ed5168
{
Packit Service ed5168
  ShellRecorderSrc *src = SHELL_RECORDER_SRC (object);
Packit Service ed5168
Packit Service ed5168
  switch (prop_id)
Packit Service ed5168
    {
Packit Service ed5168
    case PROP_CAPS:
Packit Service ed5168
      shell_recorder_src_set_caps (src, gst_value_get_caps (value));
Packit Service ed5168
      break;
Packit Service ed5168
    default:
Packit Service ed5168
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Packit Service ed5168
      break;
Packit Service ed5168
    }
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
static void
Packit Service ed5168
shell_recorder_src_get_property (GObject         *object,
Packit Service ed5168
				 guint            prop_id,
Packit Service ed5168
				 GValue          *value,
Packit Service ed5168
				 GParamSpec      *pspec)
Packit Service ed5168
{
Packit Service ed5168
  ShellRecorderSrc *src = SHELL_RECORDER_SRC (object);
Packit Service ed5168
Packit Service ed5168
  switch (prop_id)
Packit Service ed5168
    {
Packit Service ed5168
    case PROP_CAPS:
Packit Service ed5168
      gst_value_set_caps (value, src->caps);
Packit Service ed5168
      break;
Packit Service ed5168
    case PROP_MEMORY_USED:
Packit Service ed5168
      g_mutex_lock (&src->mutex);
Packit Service ed5168
      g_value_set_uint (value, src->memory_used);
Packit Service ed5168
      g_mutex_unlock (&src->mutex);
Packit Service ed5168
      break;
Packit Service ed5168
    default:
Packit Service ed5168
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Packit Service ed5168
      break;
Packit Service ed5168
    }
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
static void
Packit Service ed5168
shell_recorder_src_class_init (ShellRecorderSrcClass *klass)
Packit Service ed5168
{
Packit Service ed5168
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
Packit Service ed5168
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
Packit Service ed5168
  GstBaseSrcClass *base_src_class = GST_BASE_SRC_CLASS (klass);
Packit Service ed5168
  GstPushSrcClass *push_src_class = GST_PUSH_SRC_CLASS (klass);
Packit Service ed5168
Packit Service ed5168
  static GstStaticPadTemplate src_template =
Packit Service ed5168
    GST_STATIC_PAD_TEMPLATE ("src",
Packit Service ed5168
			     GST_PAD_SRC,
Packit Service ed5168
			     GST_PAD_ALWAYS,
Packit Service ed5168
			     GST_STATIC_CAPS_ANY);
Packit Service ed5168
Packit Service ed5168
  object_class->finalize = shell_recorder_src_finalize;
Packit Service ed5168
  object_class->set_property = shell_recorder_src_set_property;
Packit Service ed5168
  object_class->get_property = shell_recorder_src_get_property;
Packit Service ed5168
Packit Service ed5168
  g_object_class_install_property (object_class,
Packit Service ed5168
                                   PROP_CAPS,
Packit Service ed5168
                                   g_param_spec_boxed ("caps",
Packit Service ed5168
						       "Caps",
Packit Service ed5168
						       "Fixed GstCaps for the source",
Packit Service ed5168
						       GST_TYPE_CAPS,
Packit Service ed5168
						       G_PARAM_READWRITE));
Packit Service ed5168
  g_object_class_install_property (object_class,
Packit Service ed5168
                                   PROP_MEMORY_USED,
Packit Service ed5168
                                   g_param_spec_uint ("memory-used",
Packit Service ed5168
						     "Memory Used",
Packit Service ed5168
						     "Memory currently used by the queue (in kB)",
Packit Service ed5168
						      0, G_MAXUINT, 0,
Packit Service ed5168
						      G_PARAM_READABLE));
Packit Service ed5168
  gst_element_class_add_pad_template (element_class,
Packit Service ed5168
				      gst_static_pad_template_get (&src_template));
Packit Service ed5168
Packit Service ed5168
  gst_element_class_set_details_simple (element_class,
Packit Service ed5168
					"ShellRecorderSrc",
Packit Service ed5168
					"Generic/Src",
Packit Service ed5168
					"Feed screen capture data to a pipeline",
Packit Service ed5168
					"Owen Taylor <otaylor@redhat.com>");
Packit Service ed5168
Packit Service ed5168
  element_class->send_event = shell_recorder_src_send_event;
Packit Service ed5168
Packit Service ed5168
  base_src_class->negotiate = shell_recorder_src_negotiate;
Packit Service ed5168
  base_src_class->unlock = shell_recorder_src_unlock;
Packit Service ed5168
  base_src_class->unlock_stop = shell_recorder_src_unlock_stop;
Packit Service ed5168
  base_src_class->start = shell_recorder_src_start;
Packit Service ed5168
  base_src_class->stop = shell_recorder_src_stop;
Packit Service ed5168
Packit Service ed5168
  push_src_class->create = shell_recorder_src_create;
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
/**
Packit Service ed5168
 * shell_recorder_src_add_buffer:
Packit Service ed5168
 *
Packit Service ed5168
 * Adds a buffer to the internal queue to be pushed out at the next opportunity.
Packit Service ed5168
 * There is no flow control, so arbitrary amounts of memory may be used by
Packit Service ed5168
 * the buffers on the queue. The buffer contents must match the #GstCaps
Packit Service ed5168
 * set in the :caps property.
Packit Service ed5168
 */
Packit Service ed5168
void
Packit Service ed5168
shell_recorder_src_add_buffer (ShellRecorderSrc *src,
Packit Service ed5168
			       GstBuffer        *buffer)
Packit Service ed5168
{
Packit Service ed5168
  g_return_if_fail (SHELL_IS_RECORDER_SRC (src));
Packit Service ed5168
  g_return_if_fail (src->caps != NULL);
Packit Service ed5168
Packit Service ed5168
  shell_recorder_src_update_memory_used (src,
Packit Service ed5168
					 (int)(gst_buffer_get_size(buffer) / 1024));
Packit Service ed5168
Packit Service ed5168
  g_mutex_lock (&src->queue_lock);
Packit Service ed5168
  g_queue_push_tail (src->queue, gst_buffer_ref (buffer));
Packit Service ed5168
  g_cond_signal (&src->queue_cond);
Packit Service ed5168
  g_mutex_unlock (&src->queue_lock);
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
/**
Packit Service ed5168
 * shell_recorder_src_close:
Packit Service ed5168
 *
Packit Service ed5168
 * Indicates the end of the input stream. Once all previously added buffers have
Packit Service ed5168
 * been pushed out an end-of-stream message will be sent.
Packit Service ed5168
 */
Packit Service ed5168
void
Packit Service ed5168
shell_recorder_src_close (ShellRecorderSrc *src)
Packit Service ed5168
{
Packit Service ed5168
  /* We can't send a message to the source immediately or buffers that haven't
Packit Service ed5168
   * been pushed yet will be discarded. Instead mark ourselves EOS, which will
Packit Service ed5168
   * make us send an event once everything has been pushed.
Packit Service ed5168
   */
Packit Service ed5168
  g_mutex_lock (&src->queue_lock);
Packit Service ed5168
  src->eos = TRUE;
Packit Service ed5168
  g_cond_signal (&src->queue_cond);
Packit Service ed5168
  g_mutex_unlock (&src->queue_lock);
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
static gboolean
Packit Service ed5168
plugin_init (GstPlugin *plugin)
Packit Service ed5168
{
Packit Service ed5168
  gst_element_register(plugin, "shellrecordersrc", GST_RANK_NONE,
Packit Service ed5168
		       SHELL_TYPE_RECORDER_SRC);
Packit Service ed5168
Packit Service ed5168
  return TRUE;
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
/**
Packit Service ed5168
 * shell_recorder_src_register:
Packit Service ed5168
 *
Packit Service ed5168
 * Registers a plugin holding our single element to use privately in
Packit Service ed5168
 * this application. Can safely be called multiple times.
Packit Service ed5168
 */
Packit Service ed5168
void
Packit Service ed5168
shell_recorder_src_register (void)
Packit Service ed5168
{
Packit Service ed5168
  static gboolean registered = FALSE;
Packit Service ed5168
  if (registered)
Packit Service ed5168
    return;
Packit Service ed5168
Packit Service ed5168
  gst_plugin_register_static (GST_VERSION_MAJOR, GST_VERSION_MINOR,
Packit Service ed5168
			      "shellrecorder",
Packit Service ed5168
			      "Plugin for ShellRecorder",
Packit Service ed5168
			      plugin_init,
Packit Service ed5168
			      "0.1",
Packit Service ed5168
			      "LGPL",
Packit Service ed5168
			      "gnome-shell", "gnome-shell", "http://live.gnome.org/GnomeShell");
Packit Service ed5168
Packit Service ed5168
  registered = TRUE;
Packit Service ed5168
}