gnome-shell-3.6.3.1/src/shell-recorder-src.c

No issues found

  1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
  2 
  3 #include "config.h"
  4 
  5 #define GST_USE_UNSTABLE_API
  6 #include <gst/base/gstpushsrc.h>
  7 
  8 #include "shell-recorder-src.h"
  9 
 10 struct _ShellRecorderSrc
 11 {
 12   GstPushSrc parent;
 13 
 14   GMutex mutex_data;
 15   GMutex *mutex;
 16 
 17   GstClock *clock;
 18   GstClockTime last_frame_time;
 19 
 20   GstCaps *caps;
 21   GAsyncQueue *queue;
 22   gboolean closed;
 23   guint memory_used;
 24   guint memory_used_update_idle;
 25 };
 26 
 27 struct _ShellRecorderSrcClass
 28 {
 29   GstPushSrcClass parent_class;
 30 };
 31 
 32 enum {
 33   PROP_0,
 34   PROP_CAPS,
 35   PROP_MEMORY_USED
 36 };
 37 
 38 /* Special marker value once the source is closed */
 39 #define RECORDER_QUEUE_END ((GstBuffer *)1)
 40 
 41 G_DEFINE_TYPE(ShellRecorderSrc, shell_recorder_src, GST_TYPE_PUSH_SRC);
 42 
 43 static void
 44 shell_recorder_src_init (ShellRecorderSrc      *src)
 45 {
 46   gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_TIME);
 47   gst_base_src_set_live (GST_BASE_SRC (src), TRUE);
 48 
 49   src->clock = gst_system_clock_obtain ();
 50   src->last_frame_time = 0;
 51 
 52   src->queue = g_async_queue_new ();
 53   src->mutex = &src->mutex_data;
 54   g_mutex_init (src->mutex);
 55 }
 56 
 57 static gboolean
 58 shell_recorder_src_memory_used_update_idle (gpointer data)
 59 {
 60   ShellRecorderSrc *src = data;
 61 
 62   g_mutex_lock (src->mutex);
 63   src->memory_used_update_idle = 0;
 64   g_mutex_unlock (src->mutex);
 65 
 66   g_object_notify (G_OBJECT (src), "memory-used");
 67 
 68   return FALSE;
 69 }
 70 
 71 /* The memory_used property is used to monitor buffer usage,
 72  * so we marshal notification back to the main loop thread.
 73  */
 74 static void
 75 shell_recorder_src_update_memory_used (ShellRecorderSrc *src,
 76 				       int               delta)
 77 {
 78   g_mutex_lock (src->mutex);
 79   src->memory_used += delta;
 80   if (src->memory_used_update_idle == 0)
 81     src->memory_used_update_idle = g_idle_add (shell_recorder_src_memory_used_update_idle, src);
 82   g_mutex_unlock (src->mutex);
 83 }
 84 
 85 /* The create() virtual function is responsible for returning the next buffer.
 86  * We just pop buffers off of the queue and block if necessary.
 87  */
 88 static GstFlowReturn
 89 shell_recorder_src_create (GstPushSrc  *push_src,
 90 			   GstBuffer  **buffer_out)
 91 {
 92   ShellRecorderSrc *src = SHELL_RECORDER_SRC (push_src);
 93   GstBuffer *buffer;
 94 
 95   if (src->closed)
 96     return GST_FLOW_EOS;
 97 
 98   buffer = g_async_queue_pop (src->queue);
 99 
100   if (src->last_frame_time == 0)
101     src->last_frame_time = gst_clock_get_time (GST_CLOCK (src->clock));
102 
103   if (buffer == RECORDER_QUEUE_END)
104     {
105       /* Returning UNEXPECTED here will cause a EOS message to be sent */
106       src->closed = TRUE;
107       return GST_FLOW_EOS;
108     }
109 
110   shell_recorder_src_update_memory_used (src,
111 					 - (int)(gst_buffer_get_size(buffer) / 1024));
112 
113   *buffer_out = buffer;
114   GST_BUFFER_DURATION(*buffer_out) = GST_CLOCK_DIFF (src->last_frame_time, gst_clock_get_time (GST_CLOCK (src->clock)));
115 
116   src->last_frame_time = gst_clock_get_time (GST_CLOCK (src->clock));
117 
118   return GST_FLOW_OK;
119 }
120 
121 static void
122 shell_recorder_src_set_caps (ShellRecorderSrc *src,
123 			     const GstCaps    *caps)
124 {
125   if (caps == src->caps)
126     return;
127 
128   if (src->caps != NULL)
129     {
130       gst_caps_unref (src->caps);
131       src->caps = NULL;
132     }
133 
134   if (caps)
135     {
136       /* The capabilities will be negotated with the downstream element
137        * and set on the pad when the first buffer is pushed.
138        */
139       src->caps = gst_caps_copy (caps);
140     }
141   else
142     src->caps = NULL;
143 }
144 
145 static void
146 shell_recorder_src_finalize (GObject *object)
147 {
148   ShellRecorderSrc *src = SHELL_RECORDER_SRC (object);
149 
150   if (src->memory_used_update_idle)
151     g_source_remove (src->memory_used_update_idle);
152 
153   shell_recorder_src_set_caps (src, NULL);
154   g_async_queue_unref (src->queue);
155 
156   g_mutex_clear (src->mutex);
157 
158   gst_object_unref (src->clock);
159 
160   G_OBJECT_CLASS (shell_recorder_src_parent_class)->finalize (object);
161 }
162 
163 static void
164 shell_recorder_src_set_property (GObject      *object,
165 				 guint         prop_id,
166 				 const GValue *value,
167 				 GParamSpec   *pspec)
168 {
169   ShellRecorderSrc *src = SHELL_RECORDER_SRC (object);
170 
171   switch (prop_id)
172     {
173     case PROP_CAPS:
174       shell_recorder_src_set_caps (src, gst_value_get_caps (value));
175       break;
176     default:
177       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
178       break;
179     }
180 }
181 
182 static void
183 shell_recorder_src_get_property (GObject         *object,
184 				 guint            prop_id,
185 				 GValue          *value,
186 				 GParamSpec      *pspec)
187 {
188   ShellRecorderSrc *src = SHELL_RECORDER_SRC (object);
189 
190   switch (prop_id)
191     {
192     case PROP_CAPS:
193       gst_value_set_caps (value, src->caps);
194       break;
195     case PROP_MEMORY_USED:
196       g_mutex_lock (src->mutex);
197       g_value_set_uint (value, src->memory_used);
198       g_mutex_unlock (src->mutex);
199       break;
200     default:
201       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
202       break;
203     }
204 }
205 
206 static void
207 shell_recorder_src_class_init (ShellRecorderSrcClass *klass)
208 {
209   GObjectClass *object_class = G_OBJECT_CLASS (klass);
210   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
211   GstPushSrcClass *push_src_class = GST_PUSH_SRC_CLASS (klass);
212 
213   static GstStaticPadTemplate src_template =
214     GST_STATIC_PAD_TEMPLATE ("src",
215 			     GST_PAD_SRC,
216 			     GST_PAD_ALWAYS,
217 			     GST_STATIC_CAPS_ANY);
218 
219   object_class->finalize = shell_recorder_src_finalize;
220   object_class->set_property = shell_recorder_src_set_property;
221   object_class->get_property = shell_recorder_src_get_property;
222 
223   push_src_class->create = shell_recorder_src_create;
224 
225   g_object_class_install_property (object_class,
226                                    PROP_CAPS,
227                                    g_param_spec_boxed ("caps",
228 						       "Caps",
229 						       "Fixed GstCaps for the source",
230 						       GST_TYPE_CAPS,
231 						       G_PARAM_READWRITE));
232   g_object_class_install_property (object_class,
233                                    PROP_MEMORY_USED,
234                                    g_param_spec_uint ("memory-used",
235 						     "Memory Used",
236 						     "Memory currently used by the queue (in kB)",
237 						      0, G_MAXUINT, 0,
238 						      G_PARAM_READABLE));
239   gst_element_class_add_pad_template (element_class,
240 				      gst_static_pad_template_get (&src_template));
241 
242   gst_element_class_set_details_simple (element_class,
243 					"ShellRecorderSrc",
244 					"Generic/Src",
245 					"Feed screen capture data to a pipeline",
246 					"Owen Taylor <otaylor@redhat.com>");
247 }
248 
249 /**
250  * shell_recorder_src_add_buffer:
251  *
252  * Adds a buffer to the internal queue to be pushed out at the next opportunity.
253  * There is no flow control, so arbitrary amounts of memory may be used by
254  * the buffers on the queue. The buffer contents must match the #GstCaps
255  * set in the :caps property.
256  */
257 void
258 shell_recorder_src_add_buffer (ShellRecorderSrc *src,
259 			       GstBuffer        *buffer)
260 {
261   g_return_if_fail (SHELL_IS_RECORDER_SRC (src));
262   g_return_if_fail (src->caps != NULL);
263 
264   gst_base_src_set_caps (GST_BASE_SRC (src), src->caps);
265   shell_recorder_src_update_memory_used (src,
266 					 (int)(gst_buffer_get_size(buffer) / 1024));
267 
268   g_async_queue_push (src->queue, gst_buffer_ref (buffer));
269 }
270 
271 /**
272  * shell_recorder_src_close:
273  *
274  * Indicates the end of the input stream. Once all previously added buffers have
275  * been pushed out an end-of-stream message will be sent.
276  */
277 void
278 shell_recorder_src_close (ShellRecorderSrc *src)
279 {
280   /* We can't send a message to the source immediately or buffers that haven't
281    * been pushed yet will be discarded. Instead stick a marker onto our own
282    * queue to send an event once everything has been pushed.
283    */
284   g_async_queue_push (src->queue, RECORDER_QUEUE_END);
285 }
286 
287 static gboolean
288 plugin_init (GstPlugin *plugin)
289 {
290   gst_element_register(plugin, "shellrecordersrc", GST_RANK_NONE,
291 		       SHELL_TYPE_RECORDER_SRC);
292 
293   return TRUE;
294 }
295 
296 /**
297  * shell_recorder_src_register:
298  *
299  * Registers a plugin holding our single element to use privately in
300  * this application. Can safely be called multiple times.
301  */
302 void
303 shell_recorder_src_register (void)
304 {
305   static gboolean registered = FALSE;
306   if (registered)
307     return;
308 
309   gst_plugin_register_static (GST_VERSION_MAJOR, GST_VERSION_MINOR,
310 			      "shellrecorder",
311 			      "Plugin for ShellRecorder",
312 			      plugin_init,
313 			      "0.1",
314 			      "LGPL",
315 			      "gnome-shell", "gnome-shell", "http://live.gnome.org/GnomeShell");
316 
317   registered = TRUE;
318 }