aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llmedia/llmediaimplgstreamervidplug.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'linden/indra/llmedia/llmediaimplgstreamervidplug.cpp')
-rw-r--r--linden/indra/llmedia/llmediaimplgstreamervidplug.cpp461
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
43GST_DEBUG_CATEGORY_STATIC (gst_slvideo_debug);
44#define GST_CAT_DEFAULT gst_slvideo_debug
45
46/* Filter signals and args */
47enum
48{
49 /* FILL ME */
50 LAST_SIGNAL
51};
52
53enum
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
61static 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
68GST_BOILERPLATE (GstSLVideo, gst_slvideo, GstVideoSink,
69 GST_TYPE_VIDEO_SINK);
70
71static void gst_slvideo_set_property (GObject * object, guint prop_id,
72 const GValue * value,
73 GParamSpec * pspec);
74static void gst_slvideo_get_property (GObject * object, guint prop_id,
75 GValue * value, GParamSpec * pspec);
76
77static void
78gst_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
94static void
95gst_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
108static GstFlowReturn
109gst_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
157static GstStateChangeReturn
158gst_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
199static GstCaps *
200gst_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 */
210static gboolean
211gst_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
288static gboolean
289gst_slvideo_start (GstBaseSink * bsink)
290{
291 GstSLVideo *slvideo;
292 gboolean ret = TRUE;
293
294 slvideo = GST_SLVIDEO(bsink);
295
296 return ret;
297}
298
299static gboolean
300gst_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
317static gboolean
318gst_slvideo_unlock (GstBaseSink * bsink)
319{
320 // nothing really to do here.
321 return TRUE;
322}
323
324
325/* initialize the plugin's class */
326static void
327gst_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
357static void
358gst_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 */
376static void
377gst_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
396static void
397gst_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
409static void
410gst_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 */
428static gboolean
429plugin_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 */
446void 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