/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. #if LL_GSTREAMER010_ENABLED
  29. #include "linden_common.h"
  30. #include <gst/gst.h>
  31. #include <gst/video/video.h>
  32. #include <gst/video/gstvideosink.h>
  33. #include "llmediaimplgstreamer_syms.h"
  34. #include "llmediaimplgstreamertriviallogging.h"
  35. #include "llmediaimplgstreamervidplug.h"
  36. GST_DEBUG_CATEGORY_STATIC (gst_slvideo_debug);
  37. #define GST_CAT_DEFAULT gst_slvideo_debug
  38. #define SLV_SIZECAPS ", width=(int)[1,2048], height=(int)[1,2048] "
  39. #define SLV_ALLCAPS GST_VIDEO_CAPS_RGBx SLV_SIZECAPS
  40. static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE (
  41. (gchar*)"sink",
  42. GST_PAD_SINK,
  43. GST_PAD_ALWAYS,
  44. GST_STATIC_CAPS (SLV_ALLCAPS)
  45. );
  46. GST_BOILERPLATE (GstSLVideo, gst_slvideo, GstVideoSink,
  47. GST_TYPE_VIDEO_SINK);
  48. static void gst_slvideo_set_property (GObject * object, guint prop_id,
  49. const GValue * value,
  50. GParamSpec * pspec);
  51. static void gst_slvideo_get_property (GObject * object, guint prop_id,
  52. GValue * value, GParamSpec * pspec);
  53. static void
  54. gst_slvideo_base_init (gpointer gclass)
  55. {
  56. static GstElementDetails element_details = {
  57. (gchar*)"PluginTemplate",
  58. (gchar*)"Generic/PluginTemplate",
  59. (gchar*)"Generic Template Element",
  60. (gchar*)"Linden Lab"
  61. };
  62. GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
  63. llgst_element_class_add_pad_template (element_class,
  64. llgst_static_pad_template_get (&sink_factory));
  65. llgst_element_class_set_details (element_class, &element_details);
  66. }
  67. static void
  68. gst_slvideo_finalize (GObject * object)
  69. {
  70. GstSLVideo *slvideo;
  71. slvideo = GST_SLVIDEO (object);
  72. if (slvideo->caps)
  73. {
  74. llgst_caps_unref(slvideo->caps);
  75. }
  76. G_OBJECT_CLASS(parent_class)->finalize (object);
  77. }
  78. static GstFlowReturn
  79. gst_slvideo_show_frame (GstBaseSink * bsink, GstBuffer * buf)
  80. {
  81. GstSLVideo *slvideo;
  82. llg_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
  83. slvideo = GST_SLVIDEO(bsink);
  84. DEBUGMSG("transferring a frame of %dx%d <- %p (%d)",
  85. slvideo->width, slvideo->height, GST_BUFFER_DATA(buf),
  86. slvideo->format);
  87. if (GST_BUFFER_DATA(buf))
  88. {
  89. // copy frame and frame info into neutral territory
  90. GST_OBJECT_LOCK(slvideo);
  91. slvideo->retained_frame_ready = TRUE;
  92. slvideo->retained_frame_width = slvideo->width;
  93. slvideo->retained_frame_height = slvideo->height;
  94. slvideo->retained_frame_format = slvideo->format;
  95. int rowbytes =
  96. SLVPixelFormatBytes[slvideo->retained_frame_format] *
  97. slvideo->retained_frame_width;
  98. int needbytes = rowbytes * slvideo->retained_frame_width;
  99. // resize retained frame hunk only if necessary
  100. if (needbytes != slvideo->retained_frame_allocbytes)
  101. {
  102. delete[] slvideo->retained_frame_data;
  103. slvideo->retained_frame_data = new unsigned char[needbytes];
  104. slvideo->retained_frame_allocbytes = needbytes;
  105. }
  106. // copy the actual frame data to neutral territory -
  107. // flipped, for GL reasons
  108. for (int ypos=0; ypos<slvideo->height; ++ypos)
  109. {
  110. memcpy(&slvideo->retained_frame_data[(slvideo->height-1-ypos)*rowbytes],
  111. &(((unsigned char*)GST_BUFFER_DATA(buf))[ypos*rowbytes]),
  112. rowbytes);
  113. }
  114. // done with the shared data
  115. GST_OBJECT_UNLOCK(slvideo);
  116. }
  117. return GST_FLOW_OK;
  118. }
  119. static GstStateChangeReturn
  120. gst_slvideo_change_state(GstElement * element, GstStateChange transition)
  121. {
  122. GstSLVideo *slvideo;
  123. GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
  124. slvideo = GST_SLVIDEO (element);
  125. switch (transition) {
  126. case GST_STATE_CHANGE_NULL_TO_READY:
  127. break;
  128. case GST_STATE_CHANGE_READY_TO_PAUSED:
  129. break;
  130. case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
  131. break;
  132. default:
  133. break;
  134. }
  135. ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
  136. if (ret == GST_STATE_CHANGE_FAILURE)
  137. return ret;
  138. switch (transition) {
  139. case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
  140. break;
  141. case GST_STATE_CHANGE_PAUSED_TO_READY:
  142. slvideo->fps_n = 0;
  143. slvideo->fps_d = 1;
  144. GST_VIDEO_SINK_WIDTH(slvideo) = 0;
  145. GST_VIDEO_SINK_HEIGHT(slvideo) = 0;
  146. break;
  147. case GST_STATE_CHANGE_READY_TO_NULL:
  148. break;
  149. default:
  150. break;
  151. }
  152. return ret;
  153. }
  154. static GstCaps *
  155. gst_slvideo_get_caps (GstBaseSink * bsink)
  156. {
  157. GstSLVideo *slvideo;
  158. slvideo = GST_SLVIDEO(bsink);
  159. return llgst_caps_ref (slvideo->caps);
  160. }
  161. /* this function handles the link with other elements */
  162. static gboolean
  163. gst_slvideo_set_caps (GstBaseSink * bsink, GstCaps * caps)
  164. {
  165. GstSLVideo *filter;
  166. GstStructure *structure;
  167. GST_DEBUG ("set caps with %" GST_PTR_FORMAT, caps);
  168. filter = GST_SLVIDEO(bsink);
  169. int width, height;
  170. gboolean ret;
  171. const GValue *fps;
  172. const GValue *par;
  173. structure = llgst_caps_get_structure (caps, 0);
  174. ret = llgst_structure_get_int (structure, "width", &width);
  175. ret = ret && llgst_structure_get_int (structure, "height", &height);
  176. fps = llgst_structure_get_value (structure, "framerate");
  177. ret = ret && (fps != NULL);
  178. par = llgst_structure_get_value (structure, "pixel-aspect-ratio");
  179. if (!ret)
  180. return FALSE;
  181. INFOMSG("** filter caps set with width=%d, height=%d", width, height);
  182. GST_OBJECT_LOCK(filter);
  183. filter->width = width;
  184. filter->height = height;
  185. filter->fps_n = llgst_value_get_fraction_numerator(fps);
  186. filter->fps_d = llgst_value_get_fraction_denominator(fps);
  187. if (par)
  188. {
  189. filter->par_n = llgst_value_get_fraction_numerator(par);
  190. filter->par_d = llgst_value_get_fraction_denominator(par);
  191. }
  192. else
  193. {
  194. filter->par_n = 1;
  195. filter->par_d = 1;
  196. }
  197. GST_VIDEO_SINK_WIDTH(filter) = width;
  198. GST_VIDEO_SINK_HEIGHT(filter) = height;
  199. // crufty lump - we *always* accept *only* RGBX now.
  200. /*
  201. filter->format = SLV_PF_UNKNOWN;
  202. if (0 == strcmp(llgst_structure_get_name(structure),
  203. "video/x-raw-rgb"))
  204. {
  205. int red_mask;
  206. int green_mask;
  207. int blue_mask;
  208. llgst_structure_get_int(structure, "red_mask", &red_mask);
  209. llgst_structure_get_int(structure, "green_mask", &green_mask);
  210. llgst_structure_get_int(structure, "blue_mask", &blue_mask);
  211. if ((unsigned int)red_mask == 0xFF000000 &&
  212. (unsigned int)green_mask == 0x00FF0000 &&
  213. (unsigned int)blue_mask == 0x0000FF00)
  214. {
  215. filter->format = SLV_PF_RGBX;
  216. //fprintf(stderr, "\n\nPIXEL FORMAT RGB\n\n");
  217. } else if ((unsigned int)red_mask == 0x0000FF00 &&
  218. (unsigned int)green_mask == 0x00FF0000 &&
  219. (unsigned int)blue_mask == 0xFF000000)
  220. {
  221. filter->format = SLV_PF_BGRX;
  222. //fprintf(stderr, "\n\nPIXEL FORMAT BGR\n\n");
  223. }
  224. }*/
  225. filter->format = SLV_PF_RGBX;
  226. GST_OBJECT_UNLOCK(filter);
  227. return TRUE;
  228. }
  229. static gboolean
  230. gst_slvideo_start (GstBaseSink * bsink)
  231. {
  232. GstSLVideo *slvideo;
  233. gboolean ret = TRUE;
  234. slvideo = GST_SLVIDEO(bsink);
  235. return ret;
  236. }
  237. static gboolean
  238. gst_slvideo_stop (GstBaseSink * bsink)
  239. {
  240. GstSLVideo *slvideo;
  241. slvideo = GST_SLVIDEO(bsink);
  242. // free-up retained frame buffer
  243. GST_OBJECT_LOCK(slvideo);
  244. slvideo->retained_frame_ready = FALSE;
  245. delete[] slvideo->retained_frame_data;
  246. slvideo->retained_frame_data = NULL;
  247. slvideo->retained_frame_allocbytes = 0;
  248. GST_OBJECT_UNLOCK(slvideo);
  249. return TRUE;
  250. }
  251. static GstFlowReturn
  252. gst_slvideo_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
  253. GstCaps * caps, GstBuffer ** buf)
  254. {
  255. gint width, height;
  256. GstStructure *structure = NULL;
  257. GstSLVideo *slvideo;
  258. slvideo = GST_SLVIDEO(bsink);
  259. // caps == requested caps
  260. // we can ignore these and reverse-negotiate our preferred dimensions with
  261. // the peer if we like - we need to do this to obey dynamic resize requests
  262. // flowing in from the app.
  263. structure = llgst_caps_get_structure (caps, 0);
  264. if (!llgst_structure_get_int(structure, "width", &width) ||
  265. !llgst_structure_get_int(structure, "height", &height))
  266. {
  267. GST_WARNING_OBJECT (slvideo, "no width/height in caps %" GST_PTR_FORMAT, caps);
  268. return GST_FLOW_NOT_NEGOTIATED;
  269. }
  270. GstBuffer *newbuf = llgst_buffer_new();
  271. bool made_bufferdata_ptr = false;
  272. #define MAXDEPTHHACK 4
  273. GST_OBJECT_LOCK(slvideo);
  274. if (slvideo->resize_forced_always) // app is giving us a fixed size to work with
  275. {
  276. gint slwantwidth, slwantheight;
  277. slwantwidth = slvideo->resize_try_width;
  278. slwantheight = slvideo->resize_try_height;
  279. if (slwantwidth != width ||
  280. slwantheight != height)
  281. {
  282. // don't like requested caps, we will issue our own suggestion - copy
  283. // the requested caps but substitute our own width and height and see
  284. // if our peer is happy with that.
  285. GstCaps *desired_caps;
  286. GstStructure *desired_struct;
  287. desired_caps = llgst_caps_copy (caps);
  288. desired_struct = llgst_caps_get_structure (desired_caps, 0);
  289. GValue value = {0};
  290. g_value_init(&value, G_TYPE_INT);
  291. g_value_set_int(&value, slwantwidth);
  292. llgst_structure_set_value (desired_struct, "width", &value);
  293. g_value_unset(&value);
  294. g_value_init(&value, G_TYPE_INT);
  295. g_value_set_int(&value, slwantheight);
  296. llgst_structure_set_value (desired_struct, "height", &value);
  297. if (llgst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (slvideo),
  298. desired_caps))
  299. {
  300. // todo: re-use buffers from a pool?
  301. // todo: set MALLOCDATA to null, set DATA to point straight to shm?
  302. // peer likes our cap suggestion
  303. DEBUGMSG("peer loves us :)");
  304. GST_BUFFER_SIZE(newbuf) = slwantwidth * slwantheight * MAXDEPTHHACK;
  305. GST_BUFFER_MALLOCDATA(newbuf) = (guint8*)g_malloc(GST_BUFFER_SIZE(newbuf));
  306. GST_BUFFER_DATA(newbuf) = GST_BUFFER_MALLOCDATA(newbuf);
  307. llgst_buffer_set_caps (GST_BUFFER_CAST(newbuf), desired_caps);
  308. made_bufferdata_ptr = true;
  309. } else {
  310. // peer hates our cap suggestion
  311. INFOMSG("peer hates us :(");
  312. llgst_caps_unref(desired_caps);
  313. }
  314. }
  315. }
  316. GST_OBJECT_UNLOCK(slvideo);
  317. if (!made_bufferdata_ptr) // need to fallback to malloc at original size
  318. {
  319. GST_BUFFER_SIZE(newbuf) = width * height * MAXDEPTHHACK;
  320. GST_BUFFER_MALLOCDATA(newbuf) = (guint8*)g_malloc(GST_BUFFER_SIZE(newbuf));
  321. GST_BUFFER_DATA(newbuf) = GST_BUFFER_MALLOCDATA(newbuf);
  322. llgst_buffer_set_caps (GST_BUFFER_CAST(newbuf), caps);
  323. }
  324. *buf = GST_BUFFER_CAST(newbuf);
  325. return GST_FLOW_OK;
  326. }
  327. /* initialize the plugin's class */
  328. static void
  329. gst_slvideo_class_init (GstSLVideoClass * klass)
  330. {
  331. GObjectClass *gobject_class;
  332. GstElementClass *gstelement_class;
  333. GstBaseSinkClass *gstbasesink_class;
  334. gobject_class = (GObjectClass *) klass;
  335. gstelement_class = (GstElementClass *) klass;
  336. gstbasesink_class = (GstBaseSinkClass *) klass;
  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. gstelement_class->change_state = gst_slvideo_change_state;
  341. #define LLGST_DEBUG_FUNCPTR(p) (p)
  342. gstbasesink_class->get_caps = LLGST_DEBUG_FUNCPTR (gst_slvideo_get_caps);
  343. gstbasesink_class->set_caps = LLGST_DEBUG_FUNCPTR( gst_slvideo_set_caps);
  344. gstbasesink_class->buffer_alloc=LLGST_DEBUG_FUNCPTR(gst_slvideo_buffer_alloc);
  345. //gstbasesink_class->get_times = LLGST_DEBUG_FUNCPTR (gst_slvideo_get_times);
  346. gstbasesink_class->preroll = LLGST_DEBUG_FUNCPTR (gst_slvideo_show_frame);
  347. gstbasesink_class->render = LLGST_DEBUG_FUNCPTR (gst_slvideo_show_frame);
  348. gstbasesink_class->start = LLGST_DEBUG_FUNCPTR (gst_slvideo_start);
  349. gstbasesink_class->stop = LLGST_DEBUG_FUNCPTR (gst_slvideo_stop);
  350. // gstbasesink_class->unlock = LLGST_DEBUG_FUNCPTR (gst_slvideo_unlock);
  351. #undef LLGST_DEBUG_FUNCPTR
  352. }
  353. /* initialize the new element
  354. * instantiate pads and add them to element
  355. * set functions
  356. * initialize structure
  357. */
  358. static void
  359. gst_slvideo_init (GstSLVideo * filter,
  360. GstSLVideoClass * gclass)
  361. {
  362. filter->caps = NULL;
  363. filter->width = -1;
  364. filter->height = -1;
  365. // this is the info we share with the client app
  366. GST_OBJECT_LOCK(filter);
  367. filter->retained_frame_ready = FALSE;
  368. filter->retained_frame_data = NULL;
  369. filter->retained_frame_allocbytes = 0;
  370. filter->retained_frame_width = filter->width;
  371. filter->retained_frame_height = filter->height;
  372. filter->retained_frame_format = SLV_PF_UNKNOWN;
  373. GstCaps *caps = llgst_caps_from_string (SLV_ALLCAPS);
  374. llgst_caps_replace (&filter->caps, caps);
  375. filter->resize_forced_always = false;
  376. filter->resize_try_width = -1;
  377. filter->resize_try_height = -1;
  378. GST_OBJECT_UNLOCK(filter);
  379. }
  380. static void
  381. gst_slvideo_set_property (GObject * object, guint prop_id,
  382. const GValue * value, GParamSpec * pspec)
  383. {
  384. llg_return_if_fail (GST_IS_SLVIDEO (object));
  385. switch (prop_id) {
  386. default:
  387. G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  388. break;
  389. }
  390. }
  391. static void
  392. gst_slvideo_get_property (GObject * object, guint prop_id,
  393. GValue * value, GParamSpec * pspec)
  394. {
  395. llg_return_if_fail (GST_IS_SLVIDEO (object));
  396. switch (prop_id) {
  397. default:
  398. G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  399. break;
  400. }
  401. }
  402. /* entry point to initialize the plug-in
  403. * initialize the plug-in itself
  404. * register the element factories and pad templates
  405. * register the features
  406. */
  407. static gboolean
  408. plugin_init (GstPlugin * plugin)
  409. {
  410. DEBUGMSG("PLUGIN INIT");
  411. GST_DEBUG_CATEGORY_INIT (gst_slvideo_debug, (gchar*)"private-slvideo-plugin",
  412. 0, (gchar*)"Second Life Video Sink");
  413. return llgst_element_register (plugin, "private-slvideo",
  414. GST_RANK_NONE, GST_TYPE_SLVIDEO);
  415. }
  416. /* this is the structure that gstreamer looks for to register plugins
  417. */
  418. /* NOTE: Can't rely upon GST_PLUGIN_DEFINE_STATIC to self-register, since
  419. some g++ versions buggily avoid __attribute__((constructor)) functions -
  420. so we provide an explicit plugin init function.
  421. */
  422. #define PACKAGE (gchar*)"packagehack"
  423. // this macro quietly refers to PACKAGE internally
  424. GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
  425. GST_VERSION_MINOR,
  426. (gchar*)"private-slvideoplugin",
  427. (gchar*)"SL Video sink plugin",
  428. plugin_init, (gchar*)"1.0", (gchar*)"LGPL",
  429. (gchar*)"Second Life",
  430. (gchar*)"http://www.secondlife.com/");
  431. #undef PACKAGE
  432. void gst_slvideo_init_class (void)
  433. {
  434. ll_gst_plugin_register_static (&gst_plugin_desc);
  435. DEBUGMSG("CLASS INIT");
  436. }
  437. #endif // LL_GSTREAMER010_ENABLED