diff options
Diffstat (limited to 'linden/indra/llmedia/llmediaimplgstreamervidplug.cpp')
-rw-r--r-- | linden/indra/llmedia/llmediaimplgstreamervidplug.cpp | 461 |
1 files changed, 461 insertions, 0 deletions
diff --git a/linden/indra/llmedia/llmediaimplgstreamervidplug.cpp b/linden/indra/llmedia/llmediaimplgstreamervidplug.cpp new file mode 100644 index 0000000..dade946 --- /dev/null +++ b/linden/indra/llmedia/llmediaimplgstreamervidplug.cpp | |||
@@ -0,0 +1,461 @@ | |||
1 | /** | ||
2 | * @file llmediaimplgstreamervidplug.h | ||
3 | * @brief Video-consuming static GStreamer plugin for gst-to-LLMediaImpl | ||
4 | * | ||
5 | * Copyright (c) 2007-2007, Linden Research, Inc. | ||
6 | * | ||
7 | * Second Life Viewer Source Code | ||
8 | * The source code in this file ("Source Code") is provided by Linden Lab | ||
9 | * to you under the terms of the GNU General Public License, version 2.0 | ||
10 | * ("GPL"), unless you have obtained a separate licensing agreement | ||
11 | * ("Other License"), formally executed by you and Linden Lab. Terms of | ||
12 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
13 | * online at http://secondlife.com/developers/opensource/gplv2 | ||
14 | * | ||
15 | * There are special exceptions to the terms and conditions of the GPL as | ||
16 | * it is applied to this Source Code. View the full text of the exception | ||
17 | * in the file doc/FLOSS-exception.txt in this software distribution, or | ||
18 | * online at http://secondlife.com/developers/opensource/flossexception | ||
19 | * | ||
20 | * By copying, modifying or distributing this software, you acknowledge | ||
21 | * that you have read and understood your obligations described above, | ||
22 | * and agree to abide by those obligations. | ||
23 | * | ||
24 | * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
25 | * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
26 | * COMPLETENESS OR PERFORMANCE. | ||
27 | */ | ||
28 | |||
29 | #if LL_GSTREAMER_ENABLED | ||
30 | |||
31 | #include <string.h> | ||
32 | |||
33 | #include <gst/gst.h> | ||
34 | #include <gst/video/video.h> | ||
35 | #include <gst/video/gstvideosink.h> | ||
36 | |||
37 | #include "llmediaimplgstreamer_syms.h" | ||
38 | |||
39 | #include "llthread.h" | ||
40 | |||
41 | #include "llmediaimplgstreamervidplug.h" | ||
42 | |||
43 | GST_DEBUG_CATEGORY_STATIC (gst_slvideo_debug); | ||
44 | #define GST_CAT_DEFAULT gst_slvideo_debug | ||
45 | |||
46 | /* Filter signals and args */ | ||
47 | enum | ||
48 | { | ||
49 | /* FILL ME */ | ||
50 | LAST_SIGNAL | ||
51 | }; | ||
52 | |||
53 | enum | ||
54 | { | ||
55 | ARG_0 | ||
56 | }; | ||
57 | |||
58 | #define SLV_SIZECAPS ", width=(int){1,2,4,8,16,32,64,128,256,512,1024}, height=(int){1,2,4,8,16,32,64,128,256,512,1024} " | ||
59 | #define SLV_ALLCAPS GST_VIDEO_CAPS_RGBx SLV_SIZECAPS ";" GST_VIDEO_CAPS_BGRx SLV_SIZECAPS | ||
60 | |||
61 | static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ( | ||
62 | "sink", | ||
63 | GST_PAD_SINK, | ||
64 | GST_PAD_ALWAYS, | ||
65 | GST_STATIC_CAPS (SLV_ALLCAPS) | ||
66 | ); | ||
67 | |||
68 | GST_BOILERPLATE (GstSLVideo, gst_slvideo, GstVideoSink, | ||
69 | GST_TYPE_VIDEO_SINK); | ||
70 | |||
71 | static void gst_slvideo_set_property (GObject * object, guint prop_id, | ||
72 | const GValue * value, | ||
73 | GParamSpec * pspec); | ||
74 | static void gst_slvideo_get_property (GObject * object, guint prop_id, | ||
75 | GValue * value, GParamSpec * pspec); | ||
76 | |||
77 | static void | ||
78 | gst_slvideo_base_init (gpointer gclass) | ||
79 | { | ||
80 | static GstElementDetails element_details = { | ||
81 | "PluginTemplate", | ||
82 | "Generic/PluginTemplate", | ||
83 | "Generic Template Element", | ||
84 | "Linden Lab" | ||
85 | }; | ||
86 | GstElementClass *element_class = GST_ELEMENT_CLASS (gclass); | ||
87 | |||
88 | llgst_element_class_add_pad_template (element_class, | ||
89 | llgst_static_pad_template_get (&sink_factory)); | ||
90 | llgst_element_class_set_details (element_class, &element_details); | ||
91 | } | ||
92 | |||
93 | |||
94 | static void | ||
95 | gst_slvideo_finalize (GObject * object) | ||
96 | { | ||
97 | GstSLVideo *slvideo; | ||
98 | slvideo = GST_SLVIDEO (object); | ||
99 | if (slvideo->caps) | ||
100 | { | ||
101 | llgst_caps_unref(slvideo->caps); | ||
102 | } | ||
103 | |||
104 | G_OBJECT_CLASS(parent_class)->finalize (object); | ||
105 | } | ||
106 | |||
107 | |||
108 | static GstFlowReturn | ||
109 | gst_slvideo_show_frame (GstBaseSink * bsink, GstBuffer * buf) | ||
110 | { | ||
111 | GstSLVideo *slvideo; | ||
112 | llg_return_val_if_fail (buf != NULL, GST_FLOW_ERROR); | ||
113 | |||
114 | slvideo = GST_SLVIDEO(bsink); | ||
115 | |||
116 | #if 0 | ||
117 | fprintf(stderr, "\n\ntransferring a frame of %dx%d <- %p (%d)\n\n", | ||
118 | slvideo->width, slvideo->height, GST_BUFFER_DATA(buf), | ||
119 | slvideo->format); | ||
120 | #endif | ||
121 | if (GST_BUFFER_DATA(buf)) | ||
122 | { | ||
123 | // copy frame and frame info into neutral territory | ||
124 | GST_OBJECT_LOCK(slvideo); | ||
125 | slvideo->retained_frame_ready = TRUE; | ||
126 | slvideo->retained_frame_width = slvideo->width; | ||
127 | slvideo->retained_frame_height = slvideo->height; | ||
128 | slvideo->retained_frame_format = slvideo->format; | ||
129 | int rowbytes = | ||
130 | SLVPixelFormatBytes[slvideo->retained_frame_format] * | ||
131 | slvideo->retained_frame_width; | ||
132 | int needbytes = rowbytes * slvideo->retained_frame_width; | ||
133 | // resize retained frame hunk only if necessary | ||
134 | if (needbytes != slvideo->retained_frame_allocbytes) | ||
135 | { | ||
136 | delete[] slvideo->retained_frame_data; | ||
137 | slvideo->retained_frame_data = new U8[needbytes]; | ||
138 | slvideo->retained_frame_allocbytes = needbytes; | ||
139 | |||
140 | } | ||
141 | // copy the actual frame data to neutral territory - | ||
142 | // flipped, for GL reasons | ||
143 | for (int ypos=0; ypos<slvideo->height; ++ypos) | ||
144 | { | ||
145 | memcpy(&slvideo->retained_frame_data[(slvideo->height-1-ypos)*rowbytes], | ||
146 | &(((U8*)GST_BUFFER_DATA(buf))[ypos*rowbytes]), | ||
147 | rowbytes); | ||
148 | } | ||
149 | // done with the shared data | ||
150 | GST_OBJECT_UNLOCK(slvideo); | ||
151 | } | ||
152 | |||
153 | return GST_FLOW_OK; | ||
154 | } | ||
155 | |||
156 | |||
157 | static GstStateChangeReturn | ||
158 | gst_slvideo_change_state(GstElement * element, GstStateChange transition) | ||
159 | { | ||
160 | GstSLVideo *slvideo; | ||
161 | GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; | ||
162 | |||
163 | slvideo = GST_SLVIDEO (element); | ||
164 | |||
165 | switch (transition) { | ||
166 | case GST_STATE_CHANGE_NULL_TO_READY: | ||
167 | break; | ||
168 | case GST_STATE_CHANGE_READY_TO_PAUSED: | ||
169 | break; | ||
170 | case GST_STATE_CHANGE_PAUSED_TO_PLAYING: | ||
171 | break; | ||
172 | default: | ||
173 | break; | ||
174 | } | ||
175 | |||
176 | ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); | ||
177 | if (ret == GST_STATE_CHANGE_FAILURE) | ||
178 | return ret; | ||
179 | |||
180 | switch (transition) { | ||
181 | case GST_STATE_CHANGE_PLAYING_TO_PAUSED: | ||
182 | break; | ||
183 | case GST_STATE_CHANGE_PAUSED_TO_READY: | ||
184 | slvideo->fps_n = 0; | ||
185 | slvideo->fps_d = 1; | ||
186 | GST_VIDEO_SINK_WIDTH(slvideo) = 0; | ||
187 | GST_VIDEO_SINK_HEIGHT(slvideo) = 0; | ||
188 | break; | ||
189 | case GST_STATE_CHANGE_READY_TO_NULL: | ||
190 | break; | ||
191 | default: | ||
192 | break; | ||
193 | } | ||
194 | |||
195 | return ret; | ||
196 | } | ||
197 | |||
198 | |||
199 | static GstCaps * | ||
200 | gst_slvideo_get_caps (GstBaseSink * bsink) | ||
201 | { | ||
202 | GstSLVideo *slvideo; | ||
203 | slvideo = GST_SLVIDEO(bsink); | ||
204 | |||
205 | return llgst_caps_ref (slvideo->caps); | ||
206 | } | ||
207 | |||
208 | |||
209 | /* this function handles the link with other elements */ | ||
210 | static gboolean | ||
211 | gst_slvideo_set_caps (GstBaseSink * bsink, GstCaps * caps) | ||
212 | { | ||
213 | GstSLVideo *filter; | ||
214 | GstStructure *structure; | ||
215 | GstCaps *intersection; | ||
216 | |||
217 | GST_DEBUG ("set caps with %" GST_PTR_FORMAT, caps); | ||
218 | |||
219 | filter = GST_SLVIDEO(bsink); | ||
220 | |||
221 | intersection = llgst_caps_intersect (filter->caps, caps); | ||
222 | if (llgst_caps_is_empty (intersection)) | ||
223 | { | ||
224 | // no overlap between our caps and requested caps | ||
225 | return FALSE; | ||
226 | } | ||
227 | llgst_caps_unref(intersection); | ||
228 | |||
229 | int width, height; | ||
230 | gboolean ret; | ||
231 | const GValue *fps; | ||
232 | const GValue *par; | ||
233 | structure = llgst_caps_get_structure (caps, 0); | ||
234 | ret = llgst_structure_get_int (structure, "width", &width); | ||
235 | ret = ret && llgst_structure_get_int (structure, "height", &height); | ||
236 | fps = llgst_structure_get_value (structure, "framerate"); | ||
237 | ret = ret && (fps != NULL); | ||
238 | par = llgst_structure_get_value (structure, "pixel-aspect-ratio"); | ||
239 | if (!ret) | ||
240 | return FALSE; | ||
241 | |||
242 | filter->width = width; | ||
243 | filter->height = height; | ||
244 | filter->fps_n = llgst_value_get_fraction_numerator(fps); | ||
245 | filter->fps_d = llgst_value_get_fraction_denominator(fps); | ||
246 | if (par) | ||
247 | { | ||
248 | filter->par_n = llgst_value_get_fraction_numerator(par); | ||
249 | filter->par_d = llgst_value_get_fraction_denominator(par); | ||
250 | } | ||
251 | else | ||
252 | { | ||
253 | filter->par_n = 1; | ||
254 | filter->par_d = 1; | ||
255 | } | ||
256 | GST_VIDEO_SINK_WIDTH(filter) = width; | ||
257 | GST_VIDEO_SINK_HEIGHT(filter) = height; | ||
258 | |||
259 | filter->format = SLV_PF_UNKNOWN; | ||
260 | if (0 == strcmp(llgst_structure_get_name(structure), | ||
261 | "video/x-raw-rgb")) | ||
262 | { | ||
263 | int red_mask; | ||
264 | int green_mask; | ||
265 | int blue_mask; | ||
266 | llgst_structure_get_int(structure, "red_mask", &red_mask); | ||
267 | llgst_structure_get_int(structure, "green_mask", &green_mask); | ||
268 | llgst_structure_get_int(structure, "blue_mask", &blue_mask); | ||
269 | if ((unsigned int)red_mask == 0xFF000000 && | ||
270 | (unsigned int)green_mask == 0x00FF0000 && | ||
271 | (unsigned int)blue_mask == 0x0000FF00) | ||
272 | { | ||
273 | filter->format = SLV_PF_RGBX; | ||
274 | //fprintf(stderr, "\n\nPIXEL FORMAT RGB\n\n"); | ||
275 | } else if ((unsigned int)red_mask == 0x0000FF00 && | ||
276 | (unsigned int)green_mask == 0x00FF0000 && | ||
277 | (unsigned int)blue_mask == 0xFF000000) | ||
278 | { | ||
279 | filter->format = SLV_PF_BGRX; | ||
280 | //fprintf(stderr, "\n\nPIXEL FORMAT BGR\n\n"); | ||
281 | } | ||
282 | } | ||
283 | |||
284 | return TRUE; | ||
285 | } | ||
286 | |||
287 | |||
288 | static gboolean | ||
289 | gst_slvideo_start (GstBaseSink * bsink) | ||
290 | { | ||
291 | GstSLVideo *slvideo; | ||
292 | gboolean ret = TRUE; | ||
293 | |||
294 | slvideo = GST_SLVIDEO(bsink); | ||
295 | |||
296 | return ret; | ||
297 | } | ||
298 | |||
299 | static gboolean | ||
300 | gst_slvideo_stop (GstBaseSink * bsink) | ||
301 | { | ||
302 | GstSLVideo *slvideo; | ||
303 | slvideo = GST_SLVIDEO(bsink); | ||
304 | |||
305 | // free-up retained frame buffer | ||
306 | GST_OBJECT_LOCK(slvideo); | ||
307 | slvideo->retained_frame_ready = FALSE; | ||
308 | delete[] slvideo->retained_frame_data; | ||
309 | slvideo->retained_frame_data = NULL; | ||
310 | slvideo->retained_frame_allocbytes = 0; | ||
311 | GST_OBJECT_UNLOCK(slvideo); | ||
312 | |||
313 | return TRUE; | ||
314 | } | ||
315 | |||
316 | |||
317 | static gboolean | ||
318 | gst_slvideo_unlock (GstBaseSink * bsink) | ||
319 | { | ||
320 | // nothing really to do here. | ||
321 | return TRUE; | ||
322 | } | ||
323 | |||
324 | |||
325 | /* initialize the plugin's class */ | ||
326 | static void | ||
327 | gst_slvideo_class_init (GstSLVideoClass * klass) | ||
328 | { | ||
329 | GObjectClass *gobject_class; | ||
330 | GstElementClass *gstelement_class; | ||
331 | GstBaseSinkClass *gstbasesink_class; | ||
332 | |||
333 | gobject_class = (GObjectClass *) klass; | ||
334 | gstelement_class = (GstElementClass *) klass; | ||
335 | gstbasesink_class = (GstBaseSinkClass *) klass; | ||
336 | |||
337 | gobject_class->finalize = gst_slvideo_finalize; | ||
338 | gobject_class->set_property = gst_slvideo_set_property; | ||
339 | gobject_class->get_property = gst_slvideo_get_property; | ||
340 | |||
341 | gstelement_class->change_state = gst_slvideo_change_state; | ||
342 | |||
343 | gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_slvideo_get_caps); | ||
344 | gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR( gst_slvideo_set_caps); | ||
345 | //gstbasesink_class->buffer_alloc=GST_DEBUG_FUNCPTR(gst_slvideo_buffer_alloc); | ||
346 | //gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_slvideo_get_times); | ||
347 | gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_slvideo_show_frame); | ||
348 | gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_slvideo_show_frame); | ||
349 | |||
350 | gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_slvideo_start); | ||
351 | gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_slvideo_stop); | ||
352 | |||
353 | gstbasesink_class->unlock = GST_DEBUG_FUNCPTR (gst_slvideo_unlock); | ||
354 | } | ||
355 | |||
356 | |||
357 | static void | ||
358 | gst_slvideo_update_caps (GstSLVideo * slvideo) | ||
359 | { | ||
360 | GstCaps *caps; | ||
361 | |||
362 | // GStreamer will automatically convert colourspace if necessary. | ||
363 | // GStreamer will automatically resize media to one of these enumerated | ||
364 | // powers-of-two that we ask for (yay GStreamer!) | ||
365 | caps = llgst_caps_from_string (SLV_ALLCAPS); | ||
366 | |||
367 | llgst_caps_replace (&slvideo->caps, caps); | ||
368 | } | ||
369 | |||
370 | |||
371 | /* initialize the new element | ||
372 | * instantiate pads and add them to element | ||
373 | * set functions | ||
374 | * initialize structure | ||
375 | */ | ||
376 | static void | ||
377 | gst_slvideo_init (GstSLVideo * filter, | ||
378 | GstSLVideoClass * gclass) | ||
379 | { | ||
380 | filter->width = -1; | ||
381 | filter->height = -1; | ||
382 | |||
383 | // this is the info we share with the client app | ||
384 | GST_OBJECT_LOCK(filter); | ||
385 | filter->retained_frame_ready = FALSE; | ||
386 | filter->retained_frame_data = NULL; | ||
387 | filter->retained_frame_allocbytes = 0; | ||
388 | filter->retained_frame_width = filter->width; | ||
389 | filter->retained_frame_height = filter->height; | ||
390 | filter->retained_frame_format = SLV_PF_UNKNOWN; | ||
391 | GST_OBJECT_UNLOCK(filter); | ||
392 | |||
393 | gst_slvideo_update_caps(filter); | ||
394 | } | ||
395 | |||
396 | static void | ||
397 | gst_slvideo_set_property (GObject * object, guint prop_id, | ||
398 | const GValue * value, GParamSpec * pspec) | ||
399 | { | ||
400 | llg_return_if_fail (GST_IS_SLVIDEO (object)); | ||
401 | |||
402 | switch (prop_id) { | ||
403 | default: | ||
404 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); | ||
405 | break; | ||
406 | } | ||
407 | } | ||
408 | |||
409 | static void | ||
410 | gst_slvideo_get_property (GObject * object, guint prop_id, | ||
411 | GValue * value, GParamSpec * pspec) | ||
412 | { | ||
413 | llg_return_if_fail (GST_IS_SLVIDEO (object)); | ||
414 | |||
415 | switch (prop_id) { | ||
416 | default: | ||
417 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); | ||
418 | break; | ||
419 | } | ||
420 | } | ||
421 | |||
422 | |||
423 | /* entry point to initialize the plug-in | ||
424 | * initialize the plug-in itself | ||
425 | * register the element factories and pad templates | ||
426 | * register the features | ||
427 | */ | ||
428 | static gboolean | ||
429 | plugin_init (GstPlugin * plugin) | ||
430 | { | ||
431 | //fprintf(stderr, "\n\n\nPLUGIN INIT\n\n\n"); | ||
432 | |||
433 | GST_DEBUG_CATEGORY_INIT (gst_slvideo_debug, "private-slvideo-plugin", | ||
434 | 0, "Second Life Video Sink"); | ||
435 | |||
436 | return llgst_element_register (plugin, "private-slvideo", | ||
437 | GST_RANK_NONE, GST_TYPE_SLVIDEO); | ||
438 | } | ||
439 | |||
440 | /* this is the structure that gstreamer looks for to register plugins | ||
441 | */ | ||
442 | /* NOTE: Can't rely upon GST_PLUGIN_DEFINE_STATIC to self-register, since | ||
443 | some g++ versions buggily avoid __attribute__((constructor)) functions - | ||
444 | so we provide an explicit plugin init function. | ||
445 | */ | ||
446 | void gst_slvideo_init_class (void) | ||
447 | { | ||
448 | #define PACKAGE "packagehack" | ||
449 | static GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, | ||
450 | GST_VERSION_MINOR, | ||
451 | "private-slvideoplugin", | ||
452 | "SL Video sink plugin", | ||
453 | plugin_init, "0.1", GST_LICENSE_UNKNOWN, | ||
454 | "Second Life", | ||
455 | "http://www.secondlife.com/"); | ||
456 | #undef PACKAGE | ||
457 | ll_gst_plugin_register_static (&gst_plugin_desc); | ||
458 | //fprintf(stderr, "\n\n\nCLASS INIT\n\n\n"); | ||
459 | } | ||
460 | |||
461 | #endif // LL_GSTREAMER_ENABLED | ||