PageRenderTime 120ms CodeModel.GetById 30ms app.highlight 42ms RepoModel.GetById 38ms app.codeStats 0ms

/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 527 lines | 345 code | 90 blank | 92 comment | 25 complexity | 074322b5dab5028ecd9cacdcd8e7356a MD5 | raw file
  1/**
  2 * @file llmediaimplgstreamervidplug.h
  3 * @brief Video-consuming static GStreamer plugin for gst-to-LLMediaImpl
  4 *
  5 * @cond
  6 * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  7 * Second Life Viewer Source Code
  8 * Copyright (C) 2010, Linden Research, Inc.
  9 * 
 10 * This library is free software; you can redistribute it and/or
 11 * modify it under the terms of the GNU Lesser General Public
 12 * License as published by the Free Software Foundation;
 13 * version 2.1 of the License only.
 14 * 
 15 * This library is distributed in the hope that it will be useful,
 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 18 * Lesser General Public License for more details.
 19 * 
 20 * You should have received a copy of the GNU Lesser General Public
 21 * License along with this library; if not, write to the Free Software
 22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 23 * 
 24 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 25 * $/LicenseInfo$
 26 * @endcond
 27 */
 28
 29#if LL_GSTREAMER010_ENABLED
 30
 31#include "linden_common.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#include "llmediaimplgstreamertriviallogging.h"
 39
 40#include "llmediaimplgstreamervidplug.h"
 41
 42
 43GST_DEBUG_CATEGORY_STATIC (gst_slvideo_debug);
 44#define GST_CAT_DEFAULT gst_slvideo_debug
 45
 46
 47#define SLV_SIZECAPS ", width=(int)[1,2048], height=(int)[1,2048] "
 48#define SLV_ALLCAPS GST_VIDEO_CAPS_RGBx SLV_SIZECAPS
 49
 50static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE (
 51    (gchar*)"sink",
 52    GST_PAD_SINK,
 53    GST_PAD_ALWAYS,
 54    GST_STATIC_CAPS (SLV_ALLCAPS)
 55    );
 56
 57GST_BOILERPLATE (GstSLVideo, gst_slvideo, GstVideoSink,
 58    GST_TYPE_VIDEO_SINK);
 59
 60static void gst_slvideo_set_property (GObject * object, guint prop_id,
 61				      const GValue * value,
 62				      GParamSpec * pspec);
 63static void gst_slvideo_get_property (GObject * object, guint prop_id,
 64				      GValue * value, GParamSpec * pspec);
 65
 66static void
 67gst_slvideo_base_init (gpointer gclass)
 68{
 69	static GstElementDetails element_details = {
 70		(gchar*)"PluginTemplate",
 71		(gchar*)"Generic/PluginTemplate",
 72		(gchar*)"Generic Template Element",
 73		(gchar*)"Linden Lab"
 74	};
 75	GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
 76	
 77	llgst_element_class_add_pad_template (element_class,
 78			      llgst_static_pad_template_get (&sink_factory));
 79	llgst_element_class_set_details (element_class, &element_details);
 80}
 81
 82
 83static void
 84gst_slvideo_finalize (GObject * object)
 85{
 86	GstSLVideo *slvideo;
 87	slvideo = GST_SLVIDEO (object);
 88	if (slvideo->caps)
 89	{
 90		llgst_caps_unref(slvideo->caps);
 91	}
 92
 93	G_OBJECT_CLASS(parent_class)->finalize (object);
 94}
 95
 96
 97static GstFlowReturn
 98gst_slvideo_show_frame (GstBaseSink * bsink, GstBuffer * buf)
 99{
100	GstSLVideo *slvideo;
101	llg_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
102	
103	slvideo = GST_SLVIDEO(bsink);
104	
105	DEBUGMSG("transferring a frame of %dx%d <- %p (%d)",
106		 slvideo->width, slvideo->height, GST_BUFFER_DATA(buf),
107		 slvideo->format);
108
109	if (GST_BUFFER_DATA(buf))
110	{
111		// copy frame and frame info into neutral territory
112		GST_OBJECT_LOCK(slvideo);
113		slvideo->retained_frame_ready = TRUE;
114		slvideo->retained_frame_width = slvideo->width;
115		slvideo->retained_frame_height = slvideo->height;
116		slvideo->retained_frame_format = slvideo->format;
117		int rowbytes = 
118			SLVPixelFormatBytes[slvideo->retained_frame_format] *
119			slvideo->retained_frame_width;
120		int needbytes = rowbytes * slvideo->retained_frame_width;
121		// resize retained frame hunk only if necessary
122		if (needbytes != slvideo->retained_frame_allocbytes)
123		{
124			delete[] slvideo->retained_frame_data;
125			slvideo->retained_frame_data = new unsigned char[needbytes];
126			slvideo->retained_frame_allocbytes = needbytes;
127			
128		}
129		// copy the actual frame data to neutral territory -
130		// flipped, for GL reasons
131		for (int ypos=0; ypos<slvideo->height; ++ypos)
132		{
133			memcpy(&slvideo->retained_frame_data[(slvideo->height-1-ypos)*rowbytes],
134			       &(((unsigned char*)GST_BUFFER_DATA(buf))[ypos*rowbytes]),
135			       rowbytes);
136		}
137		// done with the shared data
138		GST_OBJECT_UNLOCK(slvideo);
139	}
140
141	return GST_FLOW_OK;
142}
143
144
145static GstStateChangeReturn
146gst_slvideo_change_state(GstElement * element, GstStateChange transition)
147{
148	GstSLVideo *slvideo;
149	GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
150	
151	slvideo = GST_SLVIDEO (element);
152
153	switch (transition) {
154	case GST_STATE_CHANGE_NULL_TO_READY:
155		break;
156	case GST_STATE_CHANGE_READY_TO_PAUSED:
157		break;
158	case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
159		break;
160	default:
161		break;
162	}
163
164	ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
165	if (ret == GST_STATE_CHANGE_FAILURE)
166		return ret;
167
168	switch (transition) {
169	case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
170		break;
171	case GST_STATE_CHANGE_PAUSED_TO_READY:
172		slvideo->fps_n = 0;
173		slvideo->fps_d = 1;
174		GST_VIDEO_SINK_WIDTH(slvideo) = 0;
175		GST_VIDEO_SINK_HEIGHT(slvideo) = 0;
176		break;
177	case GST_STATE_CHANGE_READY_TO_NULL:
178		break;
179	default:
180		break;
181	}
182
183	return ret;
184}
185
186
187static GstCaps *
188gst_slvideo_get_caps (GstBaseSink * bsink)
189{
190	GstSLVideo *slvideo;
191	slvideo = GST_SLVIDEO(bsink);
192	
193	return llgst_caps_ref (slvideo->caps);
194}
195
196
197/* this function handles the link with other elements */
198static gboolean
199gst_slvideo_set_caps (GstBaseSink * bsink, GstCaps * caps)
200{
201	GstSLVideo *filter;
202	GstStructure *structure;
203	
204	GST_DEBUG ("set caps with %" GST_PTR_FORMAT, caps);
205	
206	filter = GST_SLVIDEO(bsink);
207
208	int width, height;
209	gboolean ret;
210	const GValue *fps;
211	const GValue *par;
212	structure = llgst_caps_get_structure (caps, 0);
213	ret = llgst_structure_get_int (structure, "width", &width);
214	ret = ret && llgst_structure_get_int (structure, "height", &height);
215	fps = llgst_structure_get_value (structure, "framerate");
216	ret = ret && (fps != NULL);
217	par = llgst_structure_get_value (structure, "pixel-aspect-ratio");
218	if (!ret)
219		return FALSE;
220
221	INFOMSG("** filter caps set with width=%d, height=%d", width, height);
222
223	GST_OBJECT_LOCK(filter);
224
225	filter->width = width;
226	filter->height = height;
227
228	filter->fps_n = llgst_value_get_fraction_numerator(fps);
229	filter->fps_d = llgst_value_get_fraction_denominator(fps);
230	if (par)
231	{
232		filter->par_n = llgst_value_get_fraction_numerator(par);
233		filter->par_d = llgst_value_get_fraction_denominator(par);
234	}
235	else
236	{
237		filter->par_n = 1;
238		filter->par_d = 1;
239	}
240	GST_VIDEO_SINK_WIDTH(filter) = width;
241	GST_VIDEO_SINK_HEIGHT(filter) = height;
242	
243	// crufty lump - we *always* accept *only* RGBX now.
244	/*
245	filter->format = SLV_PF_UNKNOWN;
246	if (0 == strcmp(llgst_structure_get_name(structure),
247			"video/x-raw-rgb"))
248	{
249		int red_mask;
250		int green_mask;
251		int blue_mask;
252		llgst_structure_get_int(structure, "red_mask", &red_mask);
253		llgst_structure_get_int(structure, "green_mask", &green_mask);
254		llgst_structure_get_int(structure, "blue_mask", &blue_mask);
255		if ((unsigned int)red_mask   == 0xFF000000 &&
256		    (unsigned int)green_mask == 0x00FF0000 &&
257		    (unsigned int)blue_mask  == 0x0000FF00)
258		{
259			filter->format = SLV_PF_RGBX;
260			//fprintf(stderr, "\n\nPIXEL FORMAT RGB\n\n");
261		} else if ((unsigned int)red_mask   == 0x0000FF00 &&
262			   (unsigned int)green_mask == 0x00FF0000 &&
263			   (unsigned int)blue_mask  == 0xFF000000)
264		{
265			filter->format = SLV_PF_BGRX;
266			//fprintf(stderr, "\n\nPIXEL FORMAT BGR\n\n");
267		}
268		}*/
269	
270	filter->format = SLV_PF_RGBX;
271
272	GST_OBJECT_UNLOCK(filter);
273
274	return TRUE;
275}
276
277
278static gboolean
279gst_slvideo_start (GstBaseSink * bsink)
280{
281	GstSLVideo *slvideo;
282	gboolean ret = TRUE;
283	
284	slvideo = GST_SLVIDEO(bsink);
285
286	return ret;
287}
288
289static gboolean
290gst_slvideo_stop (GstBaseSink * bsink)
291{
292	GstSLVideo *slvideo;
293	slvideo = GST_SLVIDEO(bsink);
294
295	// free-up retained frame buffer
296	GST_OBJECT_LOCK(slvideo);
297	slvideo->retained_frame_ready = FALSE;
298	delete[] slvideo->retained_frame_data;
299	slvideo->retained_frame_data = NULL;
300	slvideo->retained_frame_allocbytes = 0;
301	GST_OBJECT_UNLOCK(slvideo);
302
303	return TRUE;
304}
305
306
307static GstFlowReturn
308gst_slvideo_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
309			  GstCaps * caps, GstBuffer ** buf)
310{
311	gint width, height;
312	GstStructure *structure = NULL;
313	GstSLVideo *slvideo;
314	slvideo = GST_SLVIDEO(bsink);
315
316	// caps == requested caps
317	// we can ignore these and reverse-negotiate our preferred dimensions with
318	// the peer if we like - we need to do this to obey dynamic resize requests
319	// flowing in from the app.
320	structure = llgst_caps_get_structure (caps, 0);
321	if (!llgst_structure_get_int(structure, "width", &width) ||
322	    !llgst_structure_get_int(structure, "height", &height))
323	{
324		GST_WARNING_OBJECT (slvideo, "no width/height in caps %" GST_PTR_FORMAT, caps);
325		return GST_FLOW_NOT_NEGOTIATED;
326	}
327
328	GstBuffer *newbuf = llgst_buffer_new();
329	bool made_bufferdata_ptr = false;
330#define MAXDEPTHHACK 4
331	
332	GST_OBJECT_LOCK(slvideo);
333	if (slvideo->resize_forced_always) // app is giving us a fixed size to work with
334	{
335		gint slwantwidth, slwantheight;
336		slwantwidth = slvideo->resize_try_width;
337		slwantheight = slvideo->resize_try_height;
338	
339		if (slwantwidth != width ||
340		    slwantheight != height)
341		{
342			// don't like requested caps, we will issue our own suggestion - copy
343			// the requested caps but substitute our own width and height and see
344			// if our peer is happy with that.
345		
346			GstCaps *desired_caps;
347			GstStructure *desired_struct;
348			desired_caps = llgst_caps_copy (caps);
349			desired_struct = llgst_caps_get_structure (desired_caps, 0);
350			
351			GValue value = {0};
352			g_value_init(&value, G_TYPE_INT);
353			g_value_set_int(&value, slwantwidth);
354			llgst_structure_set_value (desired_struct, "width", &value);
355			g_value_unset(&value);
356			g_value_init(&value, G_TYPE_INT);
357			g_value_set_int(&value, slwantheight);
358			llgst_structure_set_value (desired_struct, "height", &value);
359			
360			if (llgst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (slvideo),
361							desired_caps))
362			{
363				// todo: re-use buffers from a pool?
364				// todo: set MALLOCDATA to null, set DATA to point straight to shm?
365				
366				// peer likes our cap suggestion
367				DEBUGMSG("peer loves us :)");
368				GST_BUFFER_SIZE(newbuf) = slwantwidth * slwantheight * MAXDEPTHHACK;
369				GST_BUFFER_MALLOCDATA(newbuf) = (guint8*)g_malloc(GST_BUFFER_SIZE(newbuf));
370				GST_BUFFER_DATA(newbuf) = GST_BUFFER_MALLOCDATA(newbuf);
371				llgst_buffer_set_caps (GST_BUFFER_CAST(newbuf), desired_caps);
372
373				made_bufferdata_ptr = true;
374			} else {
375				// peer hates our cap suggestion
376				INFOMSG("peer hates us :(");
377				llgst_caps_unref(desired_caps);
378			}
379		}
380	}
381
382	GST_OBJECT_UNLOCK(slvideo);
383
384	if (!made_bufferdata_ptr) // need to fallback to malloc at original size
385	{
386		GST_BUFFER_SIZE(newbuf) = width * height * MAXDEPTHHACK;
387		GST_BUFFER_MALLOCDATA(newbuf) = (guint8*)g_malloc(GST_BUFFER_SIZE(newbuf));
388		GST_BUFFER_DATA(newbuf) = GST_BUFFER_MALLOCDATA(newbuf);
389		llgst_buffer_set_caps (GST_BUFFER_CAST(newbuf), caps);
390	}
391
392	*buf = GST_BUFFER_CAST(newbuf);
393
394	return GST_FLOW_OK;
395}
396
397
398/* initialize the plugin's class */
399static void
400gst_slvideo_class_init (GstSLVideoClass * klass)
401{
402	GObjectClass *gobject_class;
403	GstElementClass *gstelement_class;
404	GstBaseSinkClass *gstbasesink_class;
405	
406	gobject_class = (GObjectClass *) klass;
407	gstelement_class = (GstElementClass *) klass;
408	gstbasesink_class = (GstBaseSinkClass *) klass;
409	
410	gobject_class->finalize = gst_slvideo_finalize;
411	gobject_class->set_property = gst_slvideo_set_property;
412	gobject_class->get_property = gst_slvideo_get_property;
413	
414	gstelement_class->change_state = gst_slvideo_change_state;
415	
416#define LLGST_DEBUG_FUNCPTR(p) (p)
417	gstbasesink_class->get_caps = LLGST_DEBUG_FUNCPTR (gst_slvideo_get_caps);
418	gstbasesink_class->set_caps = LLGST_DEBUG_FUNCPTR( gst_slvideo_set_caps);
419	gstbasesink_class->buffer_alloc=LLGST_DEBUG_FUNCPTR(gst_slvideo_buffer_alloc);
420	//gstbasesink_class->get_times = LLGST_DEBUG_FUNCPTR (gst_slvideo_get_times);
421	gstbasesink_class->preroll = LLGST_DEBUG_FUNCPTR (gst_slvideo_show_frame);
422	gstbasesink_class->render = LLGST_DEBUG_FUNCPTR (gst_slvideo_show_frame);
423	
424	gstbasesink_class->start = LLGST_DEBUG_FUNCPTR (gst_slvideo_start);
425	gstbasesink_class->stop = LLGST_DEBUG_FUNCPTR (gst_slvideo_stop);
426	
427	//	gstbasesink_class->unlock = LLGST_DEBUG_FUNCPTR (gst_slvideo_unlock);
428#undef LLGST_DEBUG_FUNCPTR
429}
430
431
432/* initialize the new element
433 * instantiate pads and add them to element
434 * set functions
435 * initialize structure
436 */
437static void
438gst_slvideo_init (GstSLVideo * filter,
439		  GstSLVideoClass * gclass)
440{
441	filter->caps = NULL;
442	filter->width = -1;
443	filter->height = -1;
444
445	// this is the info we share with the client app
446	GST_OBJECT_LOCK(filter);
447	filter->retained_frame_ready = FALSE;
448	filter->retained_frame_data = NULL;
449	filter->retained_frame_allocbytes = 0;
450	filter->retained_frame_width = filter->width;
451	filter->retained_frame_height = filter->height;
452	filter->retained_frame_format = SLV_PF_UNKNOWN;
453	GstCaps *caps = llgst_caps_from_string (SLV_ALLCAPS);
454	llgst_caps_replace (&filter->caps, caps);
455	filter->resize_forced_always = false;
456	filter->resize_try_width = -1;
457	filter->resize_try_height = -1;
458	GST_OBJECT_UNLOCK(filter);
459}
460
461static void
462gst_slvideo_set_property (GObject * object, guint prop_id,
463			  const GValue * value, GParamSpec * pspec)
464{
465	llg_return_if_fail (GST_IS_SLVIDEO (object));
466	
467	switch (prop_id) {
468	default:
469		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
470		break;
471	}
472}
473
474static void
475gst_slvideo_get_property (GObject * object, guint prop_id,
476			  GValue * value, GParamSpec * pspec)
477{
478	llg_return_if_fail (GST_IS_SLVIDEO (object));
479
480	switch (prop_id) {
481	default:
482		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
483		break;
484	}
485}
486
487
488/* entry point to initialize the plug-in
489 * initialize the plug-in itself
490 * register the element factories and pad templates
491 * register the features
492 */
493static gboolean
494plugin_init (GstPlugin * plugin)
495{
496	DEBUGMSG("PLUGIN INIT");
497
498	GST_DEBUG_CATEGORY_INIT (gst_slvideo_debug, (gchar*)"private-slvideo-plugin",
499				 0, (gchar*)"Second Life Video Sink");
500
501	return llgst_element_register (plugin, "private-slvideo",
502				       GST_RANK_NONE, GST_TYPE_SLVIDEO);
503}
504
505/* this is the structure that gstreamer looks for to register plugins
506 */
507/* NOTE: Can't rely upon GST_PLUGIN_DEFINE_STATIC to self-register, since
508   some g++ versions buggily avoid __attribute__((constructor)) functions -
509   so we provide an explicit plugin init function.
510 */
511#define PACKAGE (gchar*)"packagehack"
512// this macro quietly refers to PACKAGE internally
513GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
514		   GST_VERSION_MINOR,
515		   (gchar*)"private-slvideoplugin", 
516		   (gchar*)"SL Video sink plugin",
517		   plugin_init, (gchar*)"1.0", (gchar*)"LGPL",
518		   (gchar*)"Second Life",
519		   (gchar*)"http://www.secondlife.com/");
520#undef PACKAGE
521void gst_slvideo_init_class (void)
522{
523	ll_gst_plugin_register_static (&gst_plugin_desc);
524	DEBUGMSG("CLASS INIT");
525}
526
527#endif // LL_GSTREAMER010_ENABLED