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 }