00001
00032 #if LL_GSTREAMER_ENABLED
00033
00034 #include "linden_common.h"
00035
00036 #include <gst/gst.h>
00037 #include <gst/video/video.h>
00038 #include <gst/video/gstvideosink.h>
00039
00040 #include "llmediaimplgstreamer_syms.h"
00041
00042 #include "llthread.h"
00043
00044 #include "llmediaimplgstreamervidplug.h"
00045
00046 GST_DEBUG_CATEGORY_STATIC (gst_slvideo_debug);
00047 #define GST_CAT_DEFAULT gst_slvideo_debug
00048
00049
00050 enum
00051 {
00052
00053 LAST_SIGNAL
00054 };
00055
00056 enum
00057 {
00058 ARG_0
00059 };
00060
00061 #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} "
00062 #define SLV_ALLCAPS GST_VIDEO_CAPS_RGBx SLV_SIZECAPS ";" GST_VIDEO_CAPS_BGRx SLV_SIZECAPS
00063
00064 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE (
00065 "sink",
00066 GST_PAD_SINK,
00067 GST_PAD_ALWAYS,
00068 GST_STATIC_CAPS (SLV_ALLCAPS)
00069 );
00070
00071 GST_BOILERPLATE (GstSLVideo, gst_slvideo, GstVideoSink,
00072 GST_TYPE_VIDEO_SINK);
00073
00074 static void gst_slvideo_set_property (GObject * object, guint prop_id,
00075 const GValue * value,
00076 GParamSpec * pspec);
00077 static void gst_slvideo_get_property (GObject * object, guint prop_id,
00078 GValue * value, GParamSpec * pspec);
00079
00080 static void
00081 gst_slvideo_base_init (gpointer gclass)
00082 {
00083 static GstElementDetails element_details = {
00084 "PluginTemplate",
00085 "Generic/PluginTemplate",
00086 "Generic Template Element",
00087 "Linden Lab"
00088 };
00089 GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
00090
00091 llgst_element_class_add_pad_template (element_class,
00092 llgst_static_pad_template_get (&sink_factory));
00093 llgst_element_class_set_details (element_class, &element_details);
00094 }
00095
00096
00097 static void
00098 gst_slvideo_finalize (GObject * object)
00099 {
00100 GstSLVideo *slvideo;
00101 slvideo = GST_SLVIDEO (object);
00102 if (slvideo->caps)
00103 {
00104 llgst_caps_unref(slvideo->caps);
00105 }
00106
00107 G_OBJECT_CLASS(parent_class)->finalize (object);
00108 }
00109
00110
00111 static GstFlowReturn
00112 gst_slvideo_show_frame (GstBaseSink * bsink, GstBuffer * buf)
00113 {
00114 GstSLVideo *slvideo;
00115 llg_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
00116
00117 slvideo = GST_SLVIDEO(bsink);
00118
00119 #if 0
00120 fprintf(stderr, "\n\ntransferring a frame of %dx%d <- %p (%d)\n\n",
00121 slvideo->width, slvideo->height, GST_BUFFER_DATA(buf),
00122 slvideo->format);
00123 #endif
00124 if (GST_BUFFER_DATA(buf))
00125 {
00126
00127 GST_OBJECT_LOCK(slvideo);
00128 slvideo->retained_frame_ready = TRUE;
00129 slvideo->retained_frame_width = slvideo->width;
00130 slvideo->retained_frame_height = slvideo->height;
00131 slvideo->retained_frame_format = slvideo->format;
00132 int rowbytes =
00133 SLVPixelFormatBytes[slvideo->retained_frame_format] *
00134 slvideo->retained_frame_width;
00135 int needbytes = rowbytes * slvideo->retained_frame_width;
00136
00137 if (needbytes != slvideo->retained_frame_allocbytes)
00138 {
00139 delete[] slvideo->retained_frame_data;
00140 slvideo->retained_frame_data = new U8[needbytes];
00141 slvideo->retained_frame_allocbytes = needbytes;
00142
00143 }
00144
00145
00146 for (int ypos=0; ypos<slvideo->height; ++ypos)
00147 {
00148 memcpy(&slvideo->retained_frame_data[(slvideo->height-1-ypos)*rowbytes],
00149 &(((U8*)GST_BUFFER_DATA(buf))[ypos*rowbytes]),
00150 rowbytes);
00151 }
00152
00153 GST_OBJECT_UNLOCK(slvideo);
00154 }
00155
00156 return GST_FLOW_OK;
00157 }
00158
00159
00160 static GstStateChangeReturn
00161 gst_slvideo_change_state(GstElement * element, GstStateChange transition)
00162 {
00163 GstSLVideo *slvideo;
00164 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
00165
00166 slvideo = GST_SLVIDEO (element);
00167
00168 switch (transition) {
00169 case GST_STATE_CHANGE_NULL_TO_READY:
00170 break;
00171 case GST_STATE_CHANGE_READY_TO_PAUSED:
00172 break;
00173 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
00174 break;
00175 default:
00176 break;
00177 }
00178
00179 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
00180 if (ret == GST_STATE_CHANGE_FAILURE)
00181 return ret;
00182
00183 switch (transition) {
00184 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
00185 break;
00186 case GST_STATE_CHANGE_PAUSED_TO_READY:
00187 slvideo->fps_n = 0;
00188 slvideo->fps_d = 1;
00189 GST_VIDEO_SINK_WIDTH(slvideo) = 0;
00190 GST_VIDEO_SINK_HEIGHT(slvideo) = 0;
00191 break;
00192 case GST_STATE_CHANGE_READY_TO_NULL:
00193 break;
00194 default:
00195 break;
00196 }
00197
00198 return ret;
00199 }
00200
00201
00202 static GstCaps *
00203 gst_slvideo_get_caps (GstBaseSink * bsink)
00204 {
00205 GstSLVideo *slvideo;
00206 slvideo = GST_SLVIDEO(bsink);
00207
00208 return llgst_caps_ref (slvideo->caps);
00209 }
00210
00211
00212
00213 static gboolean
00214 gst_slvideo_set_caps (GstBaseSink * bsink, GstCaps * caps)
00215 {
00216 GstSLVideo *filter;
00217 GstStructure *structure;
00218 GstCaps *intersection;
00219
00220 GST_DEBUG ("set caps with %" GST_PTR_FORMAT, caps);
00221
00222 filter = GST_SLVIDEO(bsink);
00223
00224 intersection = llgst_caps_intersect (filter->caps, caps);
00225 if (llgst_caps_is_empty (intersection))
00226 {
00227
00228 return FALSE;
00229 }
00230 llgst_caps_unref(intersection);
00231
00232 int width, height;
00233 gboolean ret;
00234 const GValue *fps;
00235 const GValue *par;
00236 structure = llgst_caps_get_structure (caps, 0);
00237 ret = llgst_structure_get_int (structure, "width", &width);
00238 ret = ret && llgst_structure_get_int (structure, "height", &height);
00239 fps = llgst_structure_get_value (structure, "framerate");
00240 ret = ret && (fps != NULL);
00241 par = llgst_structure_get_value (structure, "pixel-aspect-ratio");
00242 if (!ret)
00243 return FALSE;
00244
00245 filter->width = width;
00246 filter->height = height;
00247 filter->fps_n = llgst_value_get_fraction_numerator(fps);
00248 filter->fps_d = llgst_value_get_fraction_denominator(fps);
00249 if (par)
00250 {
00251 filter->par_n = llgst_value_get_fraction_numerator(par);
00252 filter->par_d = llgst_value_get_fraction_denominator(par);
00253 }
00254 else
00255 {
00256 filter->par_n = 1;
00257 filter->par_d = 1;
00258 }
00259 GST_VIDEO_SINK_WIDTH(filter) = width;
00260 GST_VIDEO_SINK_HEIGHT(filter) = height;
00261
00262 filter->format = SLV_PF_UNKNOWN;
00263 if (0 == strcmp(llgst_structure_get_name(structure),
00264 "video/x-raw-rgb"))
00265 {
00266 int red_mask;
00267 int green_mask;
00268 int blue_mask;
00269 llgst_structure_get_int(structure, "red_mask", &red_mask);
00270 llgst_structure_get_int(structure, "green_mask", &green_mask);
00271 llgst_structure_get_int(structure, "blue_mask", &blue_mask);
00272 if ((unsigned int)red_mask == 0xFF000000 &&
00273 (unsigned int)green_mask == 0x00FF0000 &&
00274 (unsigned int)blue_mask == 0x0000FF00)
00275 {
00276 filter->format = SLV_PF_RGBX;
00277
00278 } else if ((unsigned int)red_mask == 0x0000FF00 &&
00279 (unsigned int)green_mask == 0x00FF0000 &&
00280 (unsigned int)blue_mask == 0xFF000000)
00281 {
00282 filter->format = SLV_PF_BGRX;
00283
00284 }
00285 }
00286
00287 return TRUE;
00288 }
00289
00290
00291 static gboolean
00292 gst_slvideo_start (GstBaseSink * bsink)
00293 {
00294 GstSLVideo *slvideo;
00295 gboolean ret = TRUE;
00296
00297 slvideo = GST_SLVIDEO(bsink);
00298
00299 return ret;
00300 }
00301
00302 static gboolean
00303 gst_slvideo_stop (GstBaseSink * bsink)
00304 {
00305 GstSLVideo *slvideo;
00306 slvideo = GST_SLVIDEO(bsink);
00307
00308
00309 GST_OBJECT_LOCK(slvideo);
00310 slvideo->retained_frame_ready = FALSE;
00311 delete[] slvideo->retained_frame_data;
00312 slvideo->retained_frame_data = NULL;
00313 slvideo->retained_frame_allocbytes = 0;
00314 GST_OBJECT_UNLOCK(slvideo);
00315
00316 return TRUE;
00317 }
00318
00319
00320 static gboolean
00321 gst_slvideo_unlock (GstBaseSink * bsink)
00322 {
00323
00324 return TRUE;
00325 }
00326
00327
00328
00329 static void
00330 gst_slvideo_class_init (GstSLVideoClass * klass)
00331 {
00332 GObjectClass *gobject_class;
00333 GstElementClass *gstelement_class;
00334 GstBaseSinkClass *gstbasesink_class;
00335
00336 gobject_class = (GObjectClass *) klass;
00337 gstelement_class = (GstElementClass *) klass;
00338 gstbasesink_class = (GstBaseSinkClass *) klass;
00339
00340 gobject_class->finalize = gst_slvideo_finalize;
00341 gobject_class->set_property = gst_slvideo_set_property;
00342 gobject_class->get_property = gst_slvideo_get_property;
00343
00344 gstelement_class->change_state = gst_slvideo_change_state;
00345
00346 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_slvideo_get_caps);
00347 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR( gst_slvideo_set_caps);
00348
00349
00350 gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_slvideo_show_frame);
00351 gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_slvideo_show_frame);
00352
00353 gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_slvideo_start);
00354 gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_slvideo_stop);
00355
00356 gstbasesink_class->unlock = GST_DEBUG_FUNCPTR (gst_slvideo_unlock);
00357 }
00358
00359
00360 static void
00361 gst_slvideo_update_caps (GstSLVideo * slvideo)
00362 {
00363 GstCaps *caps;
00364
00365
00366
00367
00368 caps = llgst_caps_from_string (SLV_ALLCAPS);
00369
00370 llgst_caps_replace (&slvideo->caps, caps);
00371 }
00372
00373
00374
00375
00376
00377
00378
00379 static void
00380 gst_slvideo_init (GstSLVideo * filter,
00381 GstSLVideoClass * gclass)
00382 {
00383 filter->width = -1;
00384 filter->height = -1;
00385
00386
00387 GST_OBJECT_LOCK(filter);
00388 filter->retained_frame_ready = FALSE;
00389 filter->retained_frame_data = NULL;
00390 filter->retained_frame_allocbytes = 0;
00391 filter->retained_frame_width = filter->width;
00392 filter->retained_frame_height = filter->height;
00393 filter->retained_frame_format = SLV_PF_UNKNOWN;
00394 GST_OBJECT_UNLOCK(filter);
00395
00396 gst_slvideo_update_caps(filter);
00397 }
00398
00399 static void
00400 gst_slvideo_set_property (GObject * object, guint prop_id,
00401 const GValue * value, GParamSpec * pspec)
00402 {
00403 llg_return_if_fail (GST_IS_SLVIDEO (object));
00404
00405 switch (prop_id) {
00406 default:
00407 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
00408 break;
00409 }
00410 }
00411
00412 static void
00413 gst_slvideo_get_property (GObject * object, guint prop_id,
00414 GValue * value, GParamSpec * pspec)
00415 {
00416 llg_return_if_fail (GST_IS_SLVIDEO (object));
00417
00418 switch (prop_id) {
00419 default:
00420 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
00421 break;
00422 }
00423 }
00424
00425
00426
00427
00428
00429
00430
00431 static gboolean
00432 plugin_init (GstPlugin * plugin)
00433 {
00434
00435
00436 GST_DEBUG_CATEGORY_INIT (gst_slvideo_debug, "private-slvideo-plugin",
00437 0, "Second Life Video Sink");
00438
00439 return llgst_element_register (plugin, "private-slvideo",
00440 GST_RANK_NONE, GST_TYPE_SLVIDEO);
00441 }
00442
00443
00444
00445
00446
00447
00448
00449 void gst_slvideo_init_class (void)
00450 {
00451 #define PACKAGE "packagehack"
00452 static GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
00453 GST_VERSION_MINOR,
00454 "private-slvideoplugin",
00455 "SL Video sink plugin",
00456 plugin_init, "0.1", GST_LICENSE_UNKNOWN,
00457 "Second Life",
00458 "http://www.secondlife.com/");
00459 #undef PACKAGE
00460 ll_gst_plugin_register_static (&gst_plugin_desc);
00461
00462 }
00463
00464 #endif // LL_GSTREAMER_ENABLED