aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'linden/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp')
-rwxr-xr-xlinden/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp572
1 files changed, 572 insertions, 0 deletions
diff --git a/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp
new file mode 100755
index 0000000..1fee545
--- /dev/null
+++ b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp
@@ -0,0 +1,572 @@
1/**
2 * @file llmediaimplgstreamervidplug.h
3 * @brief Video-consuming static GStreamer plugin for gst-to-LLMediaImpl
4 *
5 * @cond
6 * $LicenseInfo:firstyear=2007&license=viewergpl$
7 *
8 * Copyright (c) 2007-2010, Linden Research, Inc.
9 *
10 * Second Life Viewer Source Code
11 * The source code in this file ("Source Code") is provided by Linden Lab
12 * to you under the terms of the GNU General Public License, version 2.0
13 * ("GPL"), unless you have obtained a separate licensing agreement
14 * ("Other License"), formally executed by you and Linden Lab. Terms of
15 * the GPL can be found in doc/GPL-license.txt in this distribution, or
16 * online at http://secondlife.com/developers/opensource/gplv2
17 *
18 * There are special exceptions to the terms and conditions of the GPL as
19 * it is applied to this Source Code. View the full text of the exception
20 * in the file doc/FLOSS-exception.txt in this software distribution, or
21 * online at
22 * http://secondlife.com/developers/opensource/flossexception
23 *
24 * By copying, modifying or distributing this software, you acknowledge
25 * that you have read and understood your obligations described above,
26 * and agree to abide by those obligations.
27 *
28 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
29 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
30 * COMPLETENESS OR PERFORMANCE.
31 * $/LicenseInfo$
32 *
33 * @endcond
34 */
35
36///#if LL_GSTREAMER010_ENABLED
37
38#include "linden_common.h"
39
40#include <gst/gst.h>
41#include <gst/video/video.h>
42#include <gst/video/gstvideosink.h>
43
44#include "llmediaimplgstreamertriviallogging.h"
45// #include "llthread.h"
46
47#include "llmediaimplgstreamervidplug.h"
48
49GST_DEBUG_CATEGORY_STATIC (gst_slvideo_debug);
50#define GST_CAT_DEFAULT gst_slvideo_debug
51
52/* Filter signals and args *//*
53enum
54{
55 *//* FILL ME *//*
56 LAST_SIGNAL
57};
58
59enum
60{
61 ARG_0
62};
63
64#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} "
65#define SLV_ALLCAPS GST_VIDEO_CAPS_RGBx SLV_SIZECAPS ";" GST_VIDEO_CAPS_BGRx SLV_SIZECAPS
66*/
67
68#define SLV_SIZECAPS ", width=(int)[1,2048], height=(int)[1,2048] "
69#define SLV_ALLCAPS GST_VIDEO_CAPS_RGBx SLV_SIZECAPS
70
71static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE (
72 (gchar*)"sink",
73 GST_PAD_SINK,
74 GST_PAD_ALWAYS,
75 GST_STATIC_CAPS (SLV_ALLCAPS)
76 );
77
78GST_BOILERPLATE (GstSLVideo, gst_slvideo, GstVideoSink,
79 GST_TYPE_VIDEO_SINK);
80
81static void gst_slvideo_set_property (GObject * object, guint prop_id,
82 const GValue * value,
83 GParamSpec * pspec);
84static void gst_slvideo_get_property (GObject * object, guint prop_id,
85 GValue * value, GParamSpec * pspec);
86
87static void
88gst_slvideo_base_init (gpointer gclass)
89{
90 static GstElementDetails element_details = {
91 (gchar*)"PluginTemplate",
92 (gchar*)"Generic/PluginTemplate",
93 (gchar*)"Generic Template Element",
94 (gchar*)"Linden Lab"
95 };
96 GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
97
98 gst_element_class_add_pad_template (element_class,
99 gst_static_pad_template_get (&sink_factory));
100 gst_element_class_set_details (element_class, &element_details);
101}
102
103
104static void
105gst_slvideo_finalize (GObject * object)
106{
107 GstSLVideo *slvideo;
108 slvideo = GST_SLVIDEO (object);
109 if (slvideo->caps)
110 {
111 gst_caps_unref(slvideo->caps);
112 }
113
114 G_OBJECT_CLASS(parent_class)->finalize (object);
115}
116
117
118static GstFlowReturn
119gst_slvideo_show_frame (GstBaseSink * bsink, GstBuffer * buf)
120{
121 GstSLVideo *slvideo;
122 g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
123
124 slvideo = GST_SLVIDEO(bsink);
125
126 DEBUGMSG("transferring a frame of %dx%d <- %p (%d)",
127 slvideo->width, slvideo->height, GST_BUFFER_DATA(buf),
128 slvideo->format);
129
130 if (GST_BUFFER_DATA(buf))
131 {
132 // copy frame and frame info into neutral territory
133 GST_OBJECT_LOCK(slvideo);
134 slvideo->retained_frame_ready = TRUE;
135 slvideo->retained_frame_width = slvideo->width;
136 slvideo->retained_frame_height = slvideo->height;
137 slvideo->retained_frame_format = slvideo->format;
138 int rowbytes =
139 SLVPixelFormatBytes[slvideo->retained_frame_format] *
140 slvideo->retained_frame_width;
141 int needbytes = rowbytes * slvideo->retained_frame_width;
142 // resize retained frame hunk only if necessary
143 if (needbytes != slvideo->retained_frame_allocbytes)
144 {
145 delete[] slvideo->retained_frame_data;
146 slvideo->retained_frame_data = new unsigned char[needbytes];
147 slvideo->retained_frame_allocbytes = needbytes;
148
149 }
150 // copy the actual frame data to neutral territory -
151 // flipped, for GL reasons
152 for (int ypos=0; ypos<slvideo->height; ++ypos)
153 {
154 memcpy(&slvideo->retained_frame_data[(slvideo->height-1-ypos)*rowbytes],
155 &(((unsigned char*)GST_BUFFER_DATA(buf))[ypos*rowbytes]),
156 rowbytes);
157 }
158 // done with the shared data
159 GST_OBJECT_UNLOCK(slvideo);
160 }
161
162 return GST_FLOW_OK;
163}
164
165
166static GstStateChangeReturn
167gst_slvideo_change_state(GstElement * element, GstStateChange transition)
168{
169 GstSLVideo *slvideo;
170 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
171
172 slvideo = GST_SLVIDEO (element);
173
174 switch (transition) {
175 case GST_STATE_CHANGE_NULL_TO_READY:
176 break;
177 case GST_STATE_CHANGE_READY_TO_PAUSED:
178 break;
179 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
180 break;
181 default:
182 break;
183 }
184
185 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
186 if (ret == GST_STATE_CHANGE_FAILURE)
187 return ret;
188
189 switch (transition) {
190 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
191 break;
192 case GST_STATE_CHANGE_PAUSED_TO_READY:
193 slvideo->fps_n = 0;
194 slvideo->fps_d = 1;
195 GST_VIDEO_SINK_WIDTH(slvideo) = 0;
196 GST_VIDEO_SINK_HEIGHT(slvideo) = 0;
197 break;
198 case GST_STATE_CHANGE_READY_TO_NULL:
199 break;
200 default:
201 break;
202 }
203
204 return ret;
205}
206
207
208static GstCaps *
209gst_slvideo_get_caps (GstBaseSink * bsink)
210{
211 GstSLVideo *slvideo;
212 slvideo = GST_SLVIDEO(bsink);
213
214 return gst_caps_ref (slvideo->caps);
215}
216
217
218/* this function handles the link with other elements */
219static gboolean
220gst_slvideo_set_caps (GstBaseSink * bsink, GstCaps * caps)
221{
222 GstSLVideo *filter;
223 GstStructure *structure;
224// GstCaps *intersection;
225
226 GST_DEBUG ("set caps with %" GST_PTR_FORMAT, caps);
227
228 filter = GST_SLVIDEO(bsink);
229
230/*
231 intersection = gst_caps_intersect (filter->caps, caps);
232 if (gst_caps_is_empty (intersection))
233 {
234 // no overlap between our caps and requested caps
235 return FALSE;
236 }
237 gst_caps_unref(intersection);
238*/
239 int width = 0;
240 int height = 0;
241 gboolean ret;
242 const GValue *fps;
243 const GValue *par;
244 structure = gst_caps_get_structure (caps, 0);
245 ret = gst_structure_get_int (structure, "width", &width);
246 ret = ret && gst_structure_get_int (structure, "height", &height);
247 fps = gst_structure_get_value (structure, "framerate");
248 ret = ret && (fps != NULL);
249 par = gst_structure_get_value (structure, "pixel-aspect-ratio");
250 if (!ret)
251 return FALSE;
252
253 INFOMSG("** filter caps set with width=%d, height=%d", width, height);
254
255 GST_OBJECT_LOCK(filter);
256
257 filter->width = width;
258 filter->height = height;
259 filter->fps_n = gst_value_get_fraction_numerator(fps);
260 filter->fps_d = gst_value_get_fraction_denominator(fps);
261 if (par)
262 {
263 filter->par_n = gst_value_get_fraction_numerator(par);
264 filter->par_d = gst_value_get_fraction_denominator(par);
265 }
266 else
267 {
268 filter->par_n = 1;
269 filter->par_d = 1;
270 }
271
272 GST_VIDEO_SINK_WIDTH(filter) = width;
273 GST_VIDEO_SINK_HEIGHT(filter) = height;
274
275 // crufty lump - we *always* accept *only* RGBX now.
276 /*
277
278 filter->format = SLV_PF_UNKNOWN;
279 if (0 == strcmp(gst_structure_get_name(structure),
280 "video/x-raw-rgb"))
281 {
282 int red_mask;
283 int green_mask;
284 int blue_mask;
285 gst_structure_get_int(structure, "red_mask", &red_mask);
286 gst_structure_get_int(structure, "green_mask", &green_mask);
287 gst_structure_get_int(structure, "blue_mask", &blue_mask);
288 if ((unsigned int)red_mask == 0xFF000000 &&
289 (unsigned int)green_mask == 0x00FF0000 &&
290 (unsigned int)blue_mask == 0x0000FF00)
291 {
292 filter->format = SLV_PF_RGBX;
293 //fprintf(stderr, "\n\nPIXEL FORMAT RGB\n\n");
294 } else if ((unsigned int)red_mask == 0x0000FF00 &&
295 (unsigned int)green_mask == 0x00FF0000 &&
296 (unsigned int)blue_mask == 0xFF000000)
297 {
298 filter->format = SLV_PF_BGRX;
299 //fprintf(stderr, "\n\nPIXEL FORMAT BGR\n\n");
300 }
301
302 }*/
303
304 filter->format = SLV_PF_RGBX;
305
306 GST_OBJECT_UNLOCK(filter);
307
308 return TRUE;
309}
310
311
312static gboolean
313gst_slvideo_start (GstBaseSink * bsink)
314{
315 GstSLVideo *slvideo;
316 gboolean ret = TRUE;
317
318 slvideo = GST_SLVIDEO(bsink);
319
320 return ret;
321}
322
323static gboolean
324gst_slvideo_stop (GstBaseSink * bsink)
325{
326 GstSLVideo *slvideo;
327 slvideo = GST_SLVIDEO(bsink);
328
329 // free-up retained frame buffer
330 GST_OBJECT_LOCK(slvideo);
331 slvideo->retained_frame_ready = FALSE;
332 delete[] slvideo->retained_frame_data;
333 slvideo->retained_frame_data = NULL;
334 slvideo->retained_frame_allocbytes = 0;
335 GST_OBJECT_UNLOCK(slvideo);
336
337 return TRUE;
338}
339
340
341static GstFlowReturn
342gst_slvideo_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
343 GstCaps * caps, GstBuffer ** buf)
344{
345 gint width, height;
346 GstStructure *structure = NULL;
347 GstSLVideo *slvideo;
348 slvideo = GST_SLVIDEO(bsink);
349
350 // caps == requested caps
351 // we can ignore these and reverse-negotiate our preferred dimensions with
352 // the peer if we like - we need to do this to obey dynamic resize requests
353 // flowing in from the app.
354 structure = gst_caps_get_structure (caps, 0);
355 if (!gst_structure_get_int(structure, "width", &width) ||
356 !gst_structure_get_int(structure, "height", &height))
357 {
358 GST_WARNING_OBJECT (slvideo, "no width/height in caps %" GST_PTR_FORMAT, caps);
359 return GST_FLOW_NOT_NEGOTIATED;
360 }
361
362 GstBuffer *newbuf = gst_buffer_new();
363 bool made_bufferdata_ptr = false;
364#define MAXDEPTHHACK 4
365
366 GST_OBJECT_LOCK(slvideo);
367 if (slvideo->resize_forced_always) // app is giving us a fixed size to work with
368 {
369 gint slwantwidth, slwantheight;
370 slwantwidth = slvideo->resize_try_width;
371 slwantheight = slvideo->resize_try_height;
372
373 if (slwantwidth != width ||
374 slwantheight != height)
375 {
376 // don't like requested caps, we will issue our own suggestion - copy
377 // the requested caps but substitute our own width and height and see
378 // if our peer is happy with that.
379
380 GstCaps *desired_caps;
381 GstStructure *desired_struct;
382 desired_caps = gst_caps_copy (caps);
383 desired_struct = gst_caps_get_structure (desired_caps, 0);
384
385 GValue value = {0};
386 g_value_init(&value, G_TYPE_INT);
387 g_value_set_int(&value, slwantwidth);
388 gst_structure_set_value (desired_struct, "width", &value);
389 g_value_unset(&value);
390 g_value_init(&value, G_TYPE_INT);
391 g_value_set_int(&value, slwantheight);
392 gst_structure_set_value (desired_struct, "height", &value);
393
394 if (gst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (slvideo),
395 desired_caps))
396 {
397 // todo: re-use buffers from a pool?
398 // todo: set MALLOCDATA to null, set DATA to point straight to shm?
399
400 // peer likes our cap suggestion
401 DEBUGMSG("peer loves us :)");
402 GST_BUFFER_SIZE(newbuf) = slwantwidth * slwantheight * MAXDEPTHHACK;
403 GST_BUFFER_MALLOCDATA(newbuf) = (guint8*)g_malloc(GST_BUFFER_SIZE(newbuf));
404 GST_BUFFER_DATA(newbuf) = GST_BUFFER_MALLOCDATA(newbuf);
405 gst_buffer_set_caps (GST_BUFFER_CAST(newbuf), desired_caps);
406
407 made_bufferdata_ptr = true;
408 } else {
409 // peer hates our cap suggestion
410 INFOMSG("peer hates us :(");
411 gst_caps_unref(desired_caps);
412 }
413 }
414 }
415
416 GST_OBJECT_UNLOCK(slvideo);
417
418 if (!made_bufferdata_ptr) // need to fallback to malloc at original size
419 {
420 GST_BUFFER_SIZE(newbuf) = width * height * MAXDEPTHHACK;
421 GST_BUFFER_MALLOCDATA(newbuf) = (guint8*)g_malloc(GST_BUFFER_SIZE(newbuf));
422 GST_BUFFER_DATA(newbuf) = GST_BUFFER_MALLOCDATA(newbuf);
423 gst_buffer_set_caps (GST_BUFFER_CAST(newbuf), caps);
424 }
425
426 *buf = GST_BUFFER_CAST(newbuf);
427
428 return GST_FLOW_OK;
429}
430
431
432/* initialize the plugin's class */
433static void
434gst_slvideo_class_init (GstSLVideoClass * klass)
435{
436 GObjectClass *gobject_class;
437 GstElementClass *gstelement_class;
438 GstBaseSinkClass *gstbasesink_class;
439
440 gobject_class = (GObjectClass *) klass;
441 gstelement_class = (GstElementClass *) klass;
442 gstbasesink_class = (GstBaseSinkClass *) klass;
443
444 gobject_class->finalize = gst_slvideo_finalize;
445 gobject_class->set_property = gst_slvideo_set_property;
446 gobject_class->get_property = gst_slvideo_get_property;
447
448 gstelement_class->change_state = gst_slvideo_change_state;
449
450#define LLGST_DEBUG_FUNCPTR(p) (p)
451 gstbasesink_class->get_caps = LLGST_DEBUG_FUNCPTR (gst_slvideo_get_caps);
452 gstbasesink_class->set_caps = LLGST_DEBUG_FUNCPTR( gst_slvideo_set_caps);
453 gstbasesink_class->buffer_alloc=LLGST_DEBUG_FUNCPTR(gst_slvideo_buffer_alloc);
454 //gstbasesink_class->get_times = LLGST_DEBUG_FUNCPTR (gst_slvideo_get_times);
455 gstbasesink_class->preroll = LLGST_DEBUG_FUNCPTR (gst_slvideo_show_frame);
456 gstbasesink_class->render = LLGST_DEBUG_FUNCPTR (gst_slvideo_show_frame);
457
458 gstbasesink_class->start = LLGST_DEBUG_FUNCPTR (gst_slvideo_start);
459 gstbasesink_class->stop = LLGST_DEBUG_FUNCPTR (gst_slvideo_stop);
460
461 // gstbasesink_class->unlock = LLGST_DEBUG_FUNCPTR (gst_slvideo_unlock);
462#undef LLGST_DEBUG_FUNCPTR
463}
464
465/*
466static void
467gst_slvideo_update_caps (GstSLVideo * slvideo)
468{
469 GstCaps *caps;
470
471 // GStreamer will automatically convert colourspace if necessary.
472 // GStreamer will automatically resize media to one of these enumerated
473 // powers-of-two that we ask for (yay GStreamer!)
474 caps = gst_caps_from_string (SLV_ALLCAPS);
475
476 gst_caps_replace (&slvideo->caps, caps);
477}
478*/
479
480/* initialize the new element
481 * instantiate pads and add them to element
482 * set functions
483 * initialize structure
484 */
485static void
486gst_slvideo_init (GstSLVideo * filter,
487 GstSLVideoClass * gclass)
488{
489 filter->caps = NULL;
490 filter->width = -1;
491 filter->height = -1;
492
493 // this is the info we share with the client app
494 GST_OBJECT_LOCK(filter);
495 filter->retained_frame_ready = FALSE;
496 filter->retained_frame_data = NULL;
497 filter->retained_frame_allocbytes = 0;
498 filter->retained_frame_width = filter->width;
499 filter->retained_frame_height = filter->height;
500 filter->retained_frame_format = SLV_PF_UNKNOWN;
501 GstCaps *caps = gst_caps_from_string (SLV_ALLCAPS);
502 gst_caps_replace (&filter->caps, caps);
503 filter->resize_forced_always = false;
504 filter->resize_try_width = -1;
505 filter->resize_try_height = -1;
506 GST_OBJECT_UNLOCK(filter);
507
508 //gst_slvideo_update_caps(filter);
509}
510
511static void
512gst_slvideo_set_property (GObject * object, guint prop_id,
513 const GValue * value, GParamSpec * pspec)
514{
515 g_return_if_fail (GST_IS_SLVIDEO (object));
516
517 if (prop_id) {
518 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
519 }
520}
521
522static void
523gst_slvideo_get_property (GObject * object, guint prop_id,
524 GValue * value, GParamSpec * pspec)
525{
526 g_return_if_fail (GST_IS_SLVIDEO (object));
527
528 if (prop_id) {
529 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
530 }
531}
532
533
534/* entry point to initialize the plug-in
535 * initialize the plug-in itself
536 * register the element factories and pad templates
537 * register the features
538 */
539static gboolean
540plugin_init (GstPlugin * plugin)
541{
542 DEBUGMSG("PLUGIN INIT");
543
544 GST_DEBUG_CATEGORY_INIT (gst_slvideo_debug, (gchar*)"private-slvideo-plugin",
545 0, (gchar*)"Second Life Video Sink");
546
547 return gst_element_register (plugin, "private-slvideo",
548 GST_RANK_NONE, GST_TYPE_SLVIDEO);
549}
550
551/* this is the structure that gstreamer looks for to register plugins
552 */
553/* NOTE: Can't rely upon GST_PLUGIN_DEFINE_STATIC to self-register, since
554 some g++ versions buggily avoid __attribute__((constructor)) functions -
555 so we provide an explicit plugin init function.
556 */
557
558void gst_slvideo_init_class (void)
559{
560 gst_plugin_register_static( GST_VERSION_MAJOR,
561 GST_VERSION_MINOR,
562 (const gchar *)"private-slvideoplugin",
563 (gchar *)"SL Video sink plugin",
564 plugin_init,
565 (const gchar *)"0.1",
566 GST_LICENSE_UNKNOWN,
567 (const gchar *)"Second Life",
568 (const gchar *)"Second Life",
569 (const gchar *)"http://www.secondlife.com/" );
570}
571
572///#endif // LL_GSTREAMER010_ENABLED