llmediaimplgstreamervidplug.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:15k
源码类别:

游戏引擎

开发平台:

C++ Builder

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