PageRenderTime 51ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1266 lines | 940 code | 175 blank | 151 comment | 168 complexity | cdb98e3ccfa05ac3cb26c84b93afd6c9 MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file media_plugin_gstreamer010.cpp
  3. * @brief GStreamer-0.10 plugin for LLMedia API plugin system
  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. #include "linden_common.h"
  29. #include "llgl.h"
  30. #include "llplugininstance.h"
  31. #include "llpluginmessage.h"
  32. #include "llpluginmessageclasses.h"
  33. #include "media_plugin_base.h"
  34. #if LL_GSTREAMER010_ENABLED
  35. extern "C" {
  36. #include <gst/gst.h>
  37. }
  38. #include "llmediaimplgstreamer.h"
  39. #include "llmediaimplgstreamertriviallogging.h"
  40. #include "llmediaimplgstreamervidplug.h"
  41. #include "llmediaimplgstreamer_syms.h"
  42. //////////////////////////////////////////////////////////////////////////////
  43. //
  44. class MediaPluginGStreamer010 : public MediaPluginBase
  45. {
  46. public:
  47. MediaPluginGStreamer010(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data);
  48. ~MediaPluginGStreamer010();
  49. /* virtual */ void receiveMessage(const char *message_string);
  50. static bool startup();
  51. static bool closedown();
  52. gboolean processGSTEvents(GstBus *bus,
  53. GstMessage *message);
  54. private:
  55. std::string getVersion();
  56. bool navigateTo( const std::string urlIn );
  57. bool seek( double time_sec );
  58. bool setVolume( float volume );
  59. // misc
  60. bool pause();
  61. bool stop();
  62. bool play(double rate);
  63. bool getTimePos(double &sec_out);
  64. static const double MIN_LOOP_SEC = 1.0F;
  65. bool mIsLooping;
  66. enum ECommand {
  67. COMMAND_NONE,
  68. COMMAND_STOP,
  69. COMMAND_PLAY,
  70. COMMAND_FAST_FORWARD,
  71. COMMAND_FAST_REWIND,
  72. COMMAND_PAUSE,
  73. COMMAND_SEEK,
  74. };
  75. ECommand mCommand;
  76. private:
  77. bool unload();
  78. bool load();
  79. bool update(int milliseconds);
  80. void mouseDown( int x, int y );
  81. void mouseUp( int x, int y );
  82. void mouseMove( int x, int y );
  83. void sizeChanged();
  84. static bool mDoneInit;
  85. guint mBusWatchID;
  86. float mVolume;
  87. int mDepth;
  88. // media NATURAL size
  89. int mNaturalWidth;
  90. int mNaturalHeight;
  91. // media current size
  92. int mCurrentWidth;
  93. int mCurrentHeight;
  94. int mCurrentRowbytes;
  95. // previous media size so we can detect changes
  96. int mPreviousWidth;
  97. int mPreviousHeight;
  98. // desired render size from host
  99. int mWidth;
  100. int mHeight;
  101. // padded texture size we need to write into
  102. int mTextureWidth;
  103. int mTextureHeight;
  104. int mTextureFormatPrimary;
  105. int mTextureFormatType;
  106. bool mSeekWanted;
  107. double mSeekDestination;
  108. // Very GStreamer-specific
  109. GMainLoop *mPump; // event pump for this media
  110. GstElement *mPlaybin;
  111. GstElement *mVisualizer;
  112. GstSLVideo *mVideoSink;
  113. };
  114. //static
  115. bool MediaPluginGStreamer010::mDoneInit = false;
  116. MediaPluginGStreamer010::MediaPluginGStreamer010(
  117. LLPluginInstance::sendMessageFunction host_send_func,
  118. void *host_user_data ) :
  119. MediaPluginBase(host_send_func, host_user_data),
  120. mBusWatchID ( 0 ),
  121. mCurrentRowbytes ( 4 ),
  122. mTextureFormatPrimary ( GL_RGBA ),
  123. mTextureFormatType ( GL_UNSIGNED_INT_8_8_8_8_REV ),
  124. mSeekWanted(false),
  125. mSeekDestination(0.0),
  126. mPump ( NULL ),
  127. mPlaybin ( NULL ),
  128. mVisualizer ( NULL ),
  129. mVideoSink ( NULL ),
  130. mCommand ( COMMAND_NONE )
  131. {
  132. std::ostringstream str;
  133. INFOMSG("MediaPluginGStreamer010 constructor - my PID=%u", U32(getpid()));
  134. }
  135. ///////////////////////////////////////////////////////////////////////////////
  136. //
  137. //#define LL_GST_REPORT_STATE_CHANGES
  138. #ifdef LL_GST_REPORT_STATE_CHANGES
  139. static char* get_gst_state_name(GstState state)
  140. {
  141. switch (state) {
  142. case GST_STATE_VOID_PENDING: return "VOID_PENDING";
  143. case GST_STATE_NULL: return "NULL";
  144. case GST_STATE_READY: return "READY";
  145. case GST_STATE_PAUSED: return "PAUSED";
  146. case GST_STATE_PLAYING: return "PLAYING";
  147. }
  148. return "(unknown)";
  149. }
  150. #endif // LL_GST_REPORT_STATE_CHANGES
  151. gboolean
  152. MediaPluginGStreamer010::processGSTEvents(GstBus *bus,
  153. GstMessage *message)
  154. {
  155. if (!message)
  156. return TRUE; // shield against GStreamer bug
  157. if (GST_MESSAGE_TYPE(message) != GST_MESSAGE_STATE_CHANGED &&
  158. GST_MESSAGE_TYPE(message) != GST_MESSAGE_BUFFERING)
  159. {
  160. DEBUGMSG("Got GST message type: %s",
  161. LLGST_MESSAGE_TYPE_NAME (message));
  162. }
  163. else
  164. {
  165. // TODO: grok 'duration' message type
  166. DEBUGMSG("Got GST message type: %s",
  167. LLGST_MESSAGE_TYPE_NAME (message));
  168. }
  169. switch (GST_MESSAGE_TYPE (message)) {
  170. case GST_MESSAGE_BUFFERING: {
  171. // NEEDS GST 0.10.11+
  172. if (llgst_message_parse_buffering)
  173. {
  174. gint percent = 0;
  175. llgst_message_parse_buffering(message, &percent);
  176. DEBUGMSG("GST buffering: %d%%", percent);
  177. }
  178. break;
  179. }
  180. case GST_MESSAGE_STATE_CHANGED: {
  181. GstState old_state;
  182. GstState new_state;
  183. GstState pending_state;
  184. llgst_message_parse_state_changed(message,
  185. &old_state,
  186. &new_state,
  187. &pending_state);
  188. #ifdef LL_GST_REPORT_STATE_CHANGES
  189. // not generally very useful, and rather spammy.
  190. DEBUGMSG("state change (old,<new>,pending): %s,<%s>,%s",
  191. get_gst_state_name(old_state),
  192. get_gst_state_name(new_state),
  193. get_gst_state_name(pending_state));
  194. #endif // LL_GST_REPORT_STATE_CHANGES
  195. switch (new_state) {
  196. case GST_STATE_VOID_PENDING:
  197. break;
  198. case GST_STATE_NULL:
  199. break;
  200. case GST_STATE_READY:
  201. setStatus(STATUS_LOADED);
  202. break;
  203. case GST_STATE_PAUSED:
  204. setStatus(STATUS_PAUSED);
  205. break;
  206. case GST_STATE_PLAYING:
  207. setStatus(STATUS_PLAYING);
  208. break;
  209. }
  210. break;
  211. }
  212. case GST_MESSAGE_ERROR: {
  213. GError *err = NULL;
  214. gchar *debug = NULL;
  215. llgst_message_parse_error (message, &err, &debug);
  216. WARNMSG("GST error: %s", err?err->message:"(unknown)");
  217. if (err)
  218. g_error_free (err);
  219. g_free (debug);
  220. mCommand = COMMAND_STOP;
  221. setStatus(STATUS_ERROR);
  222. break;
  223. }
  224. case GST_MESSAGE_INFO: {
  225. if (llgst_message_parse_info)
  226. {
  227. GError *err = NULL;
  228. gchar *debug = NULL;
  229. llgst_message_parse_info (message, &err, &debug);
  230. INFOMSG("GST info: %s", err?err->message:"(unknown)");
  231. if (err)
  232. g_error_free (err);
  233. g_free (debug);
  234. }
  235. break;
  236. }
  237. case GST_MESSAGE_WARNING: {
  238. GError *err = NULL;
  239. gchar *debug = NULL;
  240. llgst_message_parse_warning (message, &err, &debug);
  241. WARNMSG("GST warning: %s", err?err->message:"(unknown)");
  242. if (err)
  243. g_error_free (err);
  244. g_free (debug);
  245. break;
  246. }
  247. case GST_MESSAGE_EOS:
  248. /* end-of-stream */
  249. DEBUGMSG("GST end-of-stream.");
  250. if (mIsLooping)
  251. {
  252. DEBUGMSG("looping media...");
  253. double eos_pos_sec = 0.0F;
  254. bool got_eos_position = getTimePos(eos_pos_sec);
  255. if (got_eos_position && eos_pos_sec < MIN_LOOP_SEC)
  256. {
  257. // if we know that the movie is really short, don't
  258. // loop it else it can easily become a time-hog
  259. // because of GStreamer spin-up overhead
  260. DEBUGMSG("really short movie (%0.3fsec) - not gonna loop this, pausing instead.", eos_pos_sec);
  261. // inject a COMMAND_PAUSE
  262. mCommand = COMMAND_PAUSE;
  263. }
  264. else
  265. {
  266. #undef LLGST_LOOP_BY_SEEKING
  267. // loop with a stop-start instead of a seek, because it actually seems rather
  268. // faster than seeking on remote streams.
  269. #ifdef LLGST_LOOP_BY_SEEKING
  270. // first, try looping by an explicit rewind
  271. bool seeksuccess = seek(0.0);
  272. if (seeksuccess)
  273. {
  274. play(1.0);
  275. }
  276. else
  277. #endif // LLGST_LOOP_BY_SEEKING
  278. { // use clumsy stop-start to loop
  279. DEBUGMSG("didn't loop by rewinding - stopping and starting instead...");
  280. stop();
  281. play(1.0);
  282. }
  283. }
  284. }
  285. else // not a looping media
  286. {
  287. // inject a COMMAND_STOP
  288. mCommand = COMMAND_STOP;
  289. }
  290. break;
  291. default:
  292. /* unhandled message */
  293. break;
  294. }
  295. /* we want to be notified again the next time there is a message
  296. * on the bus, so return true (false means we want to stop watching
  297. * for messages on the bus and our callback should not be called again)
  298. */
  299. return TRUE;
  300. }
  301. extern "C" {
  302. gboolean
  303. llmediaimplgstreamer_bus_callback (GstBus *bus,
  304. GstMessage *message,
  305. gpointer data)
  306. {
  307. MediaPluginGStreamer010 *impl = (MediaPluginGStreamer010*)data;
  308. return impl->processGSTEvents(bus, message);
  309. }
  310. } // extern "C"
  311. bool
  312. MediaPluginGStreamer010::navigateTo ( const std::string urlIn )
  313. {
  314. if (!mDoneInit)
  315. return false; // error
  316. setStatus(STATUS_LOADING);
  317. DEBUGMSG("Setting media URI: %s", urlIn.c_str());
  318. mSeekWanted = false;
  319. if (NULL == mPump ||
  320. NULL == mPlaybin)
  321. {
  322. setStatus(STATUS_ERROR);
  323. return false; // error
  324. }
  325. // set URI
  326. g_object_set (G_OBJECT (mPlaybin), "uri", urlIn.c_str(), NULL);
  327. //g_object_set (G_OBJECT (mPlaybin), "uri", "file:///tmp/movie", NULL);
  328. // navigateTo implicitly plays, too.
  329. play(1.0);
  330. return true;
  331. }
  332. bool
  333. MediaPluginGStreamer010::update(int milliseconds)
  334. {
  335. if (!mDoneInit)
  336. return false; // error
  337. DEBUGMSG("updating media...");
  338. // sanity check
  339. if (NULL == mPump ||
  340. NULL == mPlaybin)
  341. {
  342. DEBUGMSG("dead media...");
  343. return false;
  344. }
  345. // see if there's an outstanding seek wanted
  346. if (mSeekWanted &&
  347. // bleh, GST has to be happy that the movie is really truly playing
  348. // or it may quietly ignore the seek (with rtsp:// at least).
  349. (GST_STATE(mPlaybin) == GST_STATE_PLAYING))
  350. {
  351. seek(mSeekDestination);
  352. mSeekWanted = false;
  353. }
  354. // *TODO: time-limit - but there isn't a lot we can do here, most
  355. // time is spent in gstreamer's own opaque worker-threads. maybe
  356. // we can do something sneaky like only unlock the video object
  357. // for 'milliseconds' and otherwise hold the lock.
  358. while (g_main_context_pending(g_main_loop_get_context(mPump)))
  359. {
  360. g_main_context_iteration(g_main_loop_get_context(mPump), FALSE);
  361. }
  362. // check for availability of a new frame
  363. if (mVideoSink)
  364. {
  365. GST_OBJECT_LOCK(mVideoSink);
  366. if (mVideoSink->retained_frame_ready)
  367. {
  368. DEBUGMSG("NEW FRAME READY");
  369. if (mVideoSink->retained_frame_width != mCurrentWidth ||
  370. mVideoSink->retained_frame_height != mCurrentHeight)
  371. // *TODO: also check for change in format
  372. {
  373. // just resize container, don't consume frame
  374. int neww = mVideoSink->retained_frame_width;
  375. int newh = mVideoSink->retained_frame_height;
  376. int newd = 4;
  377. mTextureFormatPrimary = GL_RGBA;
  378. mTextureFormatType = GL_UNSIGNED_INT_8_8_8_8_REV;
  379. /*
  380. int newd = SLVPixelFormatBytes[mVideoSink->retained_frame_format];
  381. if (SLV_PF_BGRX == mVideoSink->retained_frame_format)
  382. {
  383. mTextureFormatPrimary = GL_BGRA;
  384. mTextureFormatType = GL_UNSIGNED_INT_8_8_8_8_REV;
  385. }
  386. else
  387. {
  388. mTextureFormatPrimary = GL_RGBA;
  389. mTextureFormatType = GL_UNSIGNED_INT_8_8_8_8_REV;
  390. }
  391. */
  392. GST_OBJECT_UNLOCK(mVideoSink);
  393. mCurrentRowbytes = neww * newd;
  394. DEBUGMSG("video container resized to %dx%d",
  395. neww, newh);
  396. mDepth = newd;
  397. mCurrentWidth = neww;
  398. mCurrentHeight = newh;
  399. sizeChanged();
  400. return true;
  401. }
  402. if (mPixels &&
  403. mCurrentHeight <= mHeight &&
  404. mCurrentWidth <= mWidth &&
  405. !mTextureSegmentName.empty())
  406. {
  407. // we're gonna totally consume this frame - reset 'ready' flag
  408. mVideoSink->retained_frame_ready = FALSE;
  409. int destination_rowbytes = mWidth * mDepth;
  410. for (int row=0; row<mCurrentHeight; ++row)
  411. {
  412. memcpy(&mPixels
  413. [destination_rowbytes * row],
  414. &mVideoSink->retained_frame_data
  415. [mCurrentRowbytes * row],
  416. mCurrentRowbytes);
  417. }
  418. GST_OBJECT_UNLOCK(mVideoSink);
  419. DEBUGMSG("NEW FRAME REALLY TRULY CONSUMED, TELLING HOST");
  420. setDirty(0,0,mCurrentWidth,mCurrentHeight);
  421. }
  422. else
  423. {
  424. // new frame ready, but we're not ready to
  425. // consume it.
  426. GST_OBJECT_UNLOCK(mVideoSink);
  427. DEBUGMSG("NEW FRAME not consumed, still waiting for a shm segment and/or shm resize");
  428. }
  429. return true;
  430. }
  431. else
  432. {
  433. // nothing to do yet.
  434. GST_OBJECT_UNLOCK(mVideoSink);
  435. return true;
  436. }
  437. }
  438. return true;
  439. }
  440. void
  441. MediaPluginGStreamer010::mouseDown( int x, int y )
  442. {
  443. // do nothing
  444. }
  445. void
  446. MediaPluginGStreamer010::mouseUp( int x, int y )
  447. {
  448. // do nothing
  449. }
  450. void
  451. MediaPluginGStreamer010::mouseMove( int x, int y )
  452. {
  453. // do nothing
  454. }
  455. bool
  456. MediaPluginGStreamer010::pause()
  457. {
  458. DEBUGMSG("pausing media...");
  459. // todo: error-check this?
  460. if (mDoneInit && mPlaybin)
  461. {
  462. llgst_element_set_state(mPlaybin, GST_STATE_PAUSED);
  463. return true;
  464. }
  465. return false;
  466. }
  467. bool
  468. MediaPluginGStreamer010::stop()
  469. {
  470. DEBUGMSG("stopping media...");
  471. // todo: error-check this?
  472. if (mDoneInit && mPlaybin)
  473. {
  474. llgst_element_set_state(mPlaybin, GST_STATE_READY);
  475. return true;
  476. }
  477. return false;
  478. }
  479. bool
  480. MediaPluginGStreamer010::play(double rate)
  481. {
  482. // NOTE: we don't actually support non-natural rate.
  483. DEBUGMSG("playing media... rate=%f", rate);
  484. // todo: error-check this?
  485. if (mDoneInit && mPlaybin)
  486. {
  487. llgst_element_set_state(mPlaybin, GST_STATE_PLAYING);
  488. return true;
  489. }
  490. return false;
  491. }
  492. bool
  493. MediaPluginGStreamer010::setVolume( float volume )
  494. {
  495. // we try to only update volume as conservatively as
  496. // possible, as many gst-plugins-base versions up to at least
  497. // November 2008 have critical race-conditions in setting volume - sigh
  498. if (mVolume == volume)
  499. return true; // nothing to do, everything's fine
  500. mVolume = volume;
  501. if (mDoneInit && mPlaybin)
  502. {
  503. g_object_set(mPlaybin, "volume", mVolume, NULL);
  504. return true;
  505. }
  506. return false;
  507. }
  508. bool
  509. MediaPluginGStreamer010::seek(double time_sec)
  510. {
  511. bool success = false;
  512. if (mDoneInit && mPlaybin)
  513. {
  514. success = llgst_element_seek(mPlaybin, 1.0F, GST_FORMAT_TIME,
  515. GstSeekFlags(GST_SEEK_FLAG_FLUSH |
  516. GST_SEEK_FLAG_KEY_UNIT),
  517. GST_SEEK_TYPE_SET, gint64(time_sec*GST_SECOND),
  518. GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
  519. }
  520. DEBUGMSG("MEDIA SEEK REQUEST to %fsec result was %d",
  521. float(time_sec), int(success));
  522. return success;
  523. }
  524. bool
  525. MediaPluginGStreamer010::getTimePos(double &sec_out)
  526. {
  527. bool got_position = false;
  528. if (mDoneInit && mPlaybin)
  529. {
  530. gint64 pos;
  531. GstFormat timefmt = GST_FORMAT_TIME;
  532. got_position =
  533. llgst_element_query_position &&
  534. llgst_element_query_position(mPlaybin,
  535. &timefmt,
  536. &pos);
  537. got_position = got_position
  538. && (timefmt == GST_FORMAT_TIME);
  539. // GStreamer may have other ideas, but we consider the current position
  540. // undefined if not PLAYING or PAUSED
  541. got_position = got_position &&
  542. (GST_STATE(mPlaybin) == GST_STATE_PLAYING ||
  543. GST_STATE(mPlaybin) == GST_STATE_PAUSED);
  544. if (got_position && !GST_CLOCK_TIME_IS_VALID(pos))
  545. {
  546. if (GST_STATE(mPlaybin) == GST_STATE_PLAYING)
  547. {
  548. // if we're playing then we treat an invalid clock time
  549. // as 0, for complicated reasons (insert reason here)
  550. pos = 0;
  551. }
  552. else
  553. {
  554. got_position = false;
  555. }
  556. }
  557. // If all the preconditions succeeded... we can trust the result.
  558. if (got_position)
  559. {
  560. sec_out = double(pos) / double(GST_SECOND); // gst to sec
  561. }
  562. }
  563. return got_position;
  564. }
  565. bool
  566. MediaPluginGStreamer010::load()
  567. {
  568. if (!mDoneInit)
  569. return false; // error
  570. setStatus(STATUS_LOADING);
  571. DEBUGMSG("setting up media...");
  572. mIsLooping = false;
  573. mVolume = 0.1234567; // minor hack to force an initial volume update
  574. // Create a pumpable main-loop for this media
  575. mPump = g_main_loop_new (NULL, FALSE);
  576. if (!mPump)
  577. {
  578. setStatus(STATUS_ERROR);
  579. return false; // error
  580. }
  581. // instantiate a playbin element to do the hard work
  582. mPlaybin = llgst_element_factory_make ("playbin", "play");
  583. if (!mPlaybin)
  584. {
  585. setStatus(STATUS_ERROR);
  586. return false; // error
  587. }
  588. // get playbin's bus
  589. GstBus *bus = llgst_pipeline_get_bus (GST_PIPELINE (mPlaybin));
  590. if (!bus)
  591. {
  592. setStatus(STATUS_ERROR);
  593. return false; // error
  594. }
  595. mBusWatchID = llgst_bus_add_watch (bus,
  596. llmediaimplgstreamer_bus_callback,
  597. this);
  598. llgst_object_unref (bus);
  599. #if 0 // not quite stable/correct yet
  600. // get a visualizer element (bonus feature!)
  601. char* vis_name = getenv("LL_GST_VIS_NAME");
  602. if (!vis_name ||
  603. (vis_name && std::string(vis_name)!="none"))
  604. {
  605. if (vis_name)
  606. {
  607. mVisualizer = llgst_element_factory_make (vis_name, "vis");
  608. }
  609. if (!mVisualizer)
  610. {
  611. mVisualizer = llgst_element_factory_make ("libvisual_jess", "vis");
  612. if (!mVisualizer)
  613. {
  614. mVisualizer = llgst_element_factory_make ("goom", "vis");
  615. if (!mVisualizer)
  616. {
  617. mVisualizer = llgst_element_factory_make ("libvisual_lv_scope", "vis");
  618. if (!mVisualizer)
  619. {
  620. // That's okay, we don't NEED this.
  621. }
  622. }
  623. }
  624. }
  625. }
  626. #endif
  627. if (NULL == getenv("LL_GSTREAMER_EXTERNAL")) {
  628. // instantiate a custom video sink
  629. mVideoSink =
  630. GST_SLVIDEO(llgst_element_factory_make ("private-slvideo", "slvideo"));
  631. if (!mVideoSink)
  632. {
  633. WARNMSG("Could not instantiate private-slvideo element.");
  634. // todo: cleanup.
  635. setStatus(STATUS_ERROR);
  636. return false; // error
  637. }
  638. // connect the pieces
  639. g_object_set(mPlaybin, "video-sink", mVideoSink, NULL);
  640. }
  641. if (mVisualizer)
  642. {
  643. g_object_set(mPlaybin, "vis-plugin", mVisualizer, NULL);
  644. }
  645. return true;
  646. }
  647. bool
  648. MediaPluginGStreamer010::unload ()
  649. {
  650. if (!mDoneInit)
  651. return false; // error
  652. DEBUGMSG("unloading media...");
  653. // stop getting callbacks for this bus
  654. g_source_remove(mBusWatchID);
  655. mBusWatchID = 0;
  656. if (mPlaybin)
  657. {
  658. llgst_element_set_state (mPlaybin, GST_STATE_NULL);
  659. llgst_object_unref (GST_OBJECT (mPlaybin));
  660. mPlaybin = NULL;
  661. }
  662. if (mVisualizer)
  663. {
  664. llgst_object_unref (GST_OBJECT (mVisualizer));
  665. mVisualizer = NULL;
  666. }
  667. if (mPump)
  668. {
  669. g_main_loop_quit(mPump);
  670. mPump = NULL;
  671. }
  672. mVideoSink = NULL;
  673. setStatus(STATUS_NONE);
  674. return true;
  675. }
  676. //static
  677. bool
  678. MediaPluginGStreamer010::startup()
  679. {
  680. // first - check if GStreamer is explicitly disabled
  681. if (NULL != getenv("LL_DISABLE_GSTREAMER"))
  682. return false;
  683. // only do global GStreamer initialization once.
  684. if (!mDoneInit)
  685. {
  686. g_thread_init(NULL);
  687. // Init the glib type system - we need it.
  688. g_type_init();
  689. // Get symbols!
  690. #if LL_DARWIN
  691. if (! grab_gst_syms("libgstreamer-0.10.dylib",
  692. "libgstvideo-0.10.dylib") )
  693. #elseif LL_WINDOWS
  694. if (! grab_gst_syms("libgstreamer-0.10.dll",
  695. "libgstvideo-0.10.dll") )
  696. #else // linux or other ELFy unixoid
  697. if (! grab_gst_syms("libgstreamer-0.10.so.0",
  698. "libgstvideo-0.10.so.0") )
  699. #endif
  700. {
  701. WARNMSG("Couldn't find suitable GStreamer 0.10 support on this system - video playback disabled.");
  702. return false;
  703. }
  704. if (llgst_segtrap_set_enabled)
  705. {
  706. llgst_segtrap_set_enabled(FALSE);
  707. }
  708. else
  709. {
  710. WARNMSG("gst_segtrap_set_enabled() is not available; plugin crashes won't be caught.");
  711. }
  712. #if LL_LINUX
  713. // Gstreamer tries a fork during init, waitpid-ing on it,
  714. // which conflicts with any installed SIGCHLD handler...
  715. struct sigaction tmpact, oldact;
  716. if (llgst_registry_fork_set_enabled) {
  717. // if we can disable SIGCHLD-using forking behaviour,
  718. // do it.
  719. llgst_registry_fork_set_enabled(false);
  720. }
  721. else {
  722. // else temporarily install default SIGCHLD handler
  723. // while GStreamer initialises
  724. tmpact.sa_handler = SIG_DFL;
  725. sigemptyset( &tmpact.sa_mask );
  726. tmpact.sa_flags = SA_SIGINFO;
  727. sigaction(SIGCHLD, &tmpact, &oldact);
  728. }
  729. #endif // LL_LINUX
  730. // Protect against GStreamer resetting the locale, yuck.
  731. static std::string saved_locale;
  732. saved_locale = setlocale(LC_ALL, NULL);
  733. // finally, try to initialize GStreamer!
  734. GError *err = NULL;
  735. gboolean init_gst_success = llgst_init_check(NULL, NULL, &err);
  736. // restore old locale
  737. setlocale(LC_ALL, saved_locale.c_str() );
  738. #if LL_LINUX
  739. // restore old SIGCHLD handler
  740. if (!llgst_registry_fork_set_enabled)
  741. sigaction(SIGCHLD, &oldact, NULL);
  742. #endif // LL_LINUX
  743. if (!init_gst_success) // fail
  744. {
  745. if (err)
  746. {
  747. WARNMSG("GST init failed: %s", err->message);
  748. g_error_free(err);
  749. }
  750. else
  751. {
  752. WARNMSG("GST init failed for unspecified reason.");
  753. }
  754. return false;
  755. }
  756. // Init our custom plugins - only really need do this once.
  757. gst_slvideo_init_class();
  758. mDoneInit = true;
  759. }
  760. return true;
  761. }
  762. void
  763. MediaPluginGStreamer010::sizeChanged()
  764. {
  765. // the shared writing space has possibly changed size/location/whatever
  766. // Check to see whether the movie's NATURAL size has been set yet
  767. if (1 == mNaturalWidth &&
  768. 1 == mNaturalHeight)
  769. {
  770. mNaturalWidth = mCurrentWidth;
  771. mNaturalHeight = mCurrentHeight;
  772. DEBUGMSG("Media NATURAL size better detected as %dx%d",
  773. mNaturalWidth, mNaturalHeight);
  774. }
  775. // if the size has changed then the shm has changed and the app needs telling
  776. if (mCurrentWidth != mPreviousWidth ||
  777. mCurrentHeight != mPreviousHeight)
  778. {
  779. mPreviousWidth = mCurrentWidth;
  780. mPreviousHeight = mCurrentHeight;
  781. LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_request");
  782. message.setValue("name", mTextureSegmentName);
  783. message.setValueS32("width", mNaturalWidth);
  784. message.setValueS32("height", mNaturalHeight);
  785. DEBUGMSG("<--- Sending size change request to application with name: '%s' - natural size is %d x %d", mTextureSegmentName.c_str(), mNaturalWidth, mNaturalHeight);
  786. sendMessage(message);
  787. }
  788. }
  789. //static
  790. bool
  791. MediaPluginGStreamer010::closedown()
  792. {
  793. if (!mDoneInit)
  794. return false; // error
  795. ungrab_gst_syms();
  796. mDoneInit = false;
  797. return true;
  798. }
  799. MediaPluginGStreamer010::~MediaPluginGStreamer010()
  800. {
  801. DEBUGMSG("MediaPluginGStreamer010 destructor");
  802. closedown();
  803. DEBUGMSG("GStreamer010 closing down");
  804. }
  805. std::string
  806. MediaPluginGStreamer010::getVersion()
  807. {
  808. std::string plugin_version = "GStreamer010 media plugin, GStreamer version ";
  809. if (mDoneInit &&
  810. llgst_version)
  811. {
  812. guint major, minor, micro, nano;
  813. llgst_version(&major, &minor, &micro, &nano);
  814. plugin_version += llformat("%u.%u.%u.%u (runtime), %u.%u.%u.%u (headers)", (unsigned int)major, (unsigned int)minor, (unsigned int)micro, (unsigned int)nano, (unsigned int)GST_VERSION_MAJOR, (unsigned int)GST_VERSION_MINOR, (unsigned int)GST_VERSION_MICRO, (unsigned int)GST_VERSION_NANO);
  815. }
  816. else
  817. {
  818. plugin_version += "(unknown)";
  819. }
  820. return plugin_version;
  821. }
  822. void MediaPluginGStreamer010::receiveMessage(const char *message_string)
  823. {
  824. //std::cerr << "MediaPluginGStreamer010::receiveMessage: received message: \"" << message_string << "\"" << std::endl;
  825. LLPluginMessage message_in;
  826. if(message_in.parse(message_string) >= 0)
  827. {
  828. std::string message_class = message_in.getClass();
  829. std::string message_name = message_in.getName();
  830. if(message_class == LLPLUGIN_MESSAGE_CLASS_BASE)
  831. {
  832. if(message_name == "init")
  833. {
  834. LLPluginMessage message("base", "init_response");
  835. LLSD versions = LLSD::emptyMap();
  836. versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION;
  837. versions[LLPLUGIN_MESSAGE_CLASS_MEDIA] = LLPLUGIN_MESSAGE_CLASS_MEDIA_VERSION;
  838. versions[LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME] = LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME_VERSION;
  839. message.setValueLLSD("versions", versions);
  840. if ( load() )
  841. {
  842. DEBUGMSG("GStreamer010 media instance set up");
  843. }
  844. else
  845. {
  846. WARNMSG("GStreamer010 media instance failed to set up");
  847. }
  848. message.setValue("plugin_version", getVersion());
  849. sendMessage(message);
  850. }
  851. else if(message_name == "idle")
  852. {
  853. // no response is necessary here.
  854. double time = message_in.getValueReal("time");
  855. // Convert time to milliseconds for update()
  856. update((int)(time * 1000.0f));
  857. }
  858. else if(message_name == "cleanup")
  859. {
  860. unload();
  861. closedown();
  862. }
  863. else if(message_name == "shm_added")
  864. {
  865. SharedSegmentInfo info;
  866. info.mAddress = message_in.getValuePointer("address");
  867. info.mSize = (size_t)message_in.getValueS32("size");
  868. std::string name = message_in.getValue("name");
  869. std::ostringstream str;
  870. INFOMSG("MediaPluginGStreamer010::receiveMessage: shared memory added, name: %s, size: %d, address: %p", name.c_str(), int(info.mSize), info.mAddress);
  871. mSharedSegments.insert(SharedSegmentMap::value_type(name, info));
  872. }
  873. else if(message_name == "shm_remove")
  874. {
  875. std::string name = message_in.getValue("name");
  876. DEBUGMSG("MediaPluginGStreamer010::receiveMessage: shared memory remove, name = %s", name.c_str());
  877. SharedSegmentMap::iterator iter = mSharedSegments.find(name);
  878. if(iter != mSharedSegments.end())
  879. {
  880. if(mPixels == iter->second.mAddress)
  881. {
  882. // This is the currently active pixel buffer. Make sure we stop drawing to it.
  883. mPixels = NULL;
  884. mTextureSegmentName.clear();
  885. // Make sure the movie decoder is no longer pointed at the shared segment.
  886. sizeChanged();
  887. }
  888. mSharedSegments.erase(iter);
  889. }
  890. else
  891. {
  892. WARNMSG("MediaPluginGStreamer010::receiveMessage: unknown shared memory region!");
  893. }
  894. // Send the response so it can be cleaned up.
  895. LLPluginMessage message("base", "shm_remove_response");
  896. message.setValue("name", name);
  897. sendMessage(message);
  898. }
  899. else
  900. {
  901. std::ostringstream str;
  902. INFOMSG("MediaPluginGStreamer010::receiveMessage: unknown base message: %s", message_name.c_str());
  903. }
  904. }
  905. else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA)
  906. {
  907. if(message_name == "init")
  908. {
  909. // Plugin gets to decide the texture parameters to use.
  910. LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params");
  911. // lame to have to decide this now, it depends on the movie. Oh well.
  912. mDepth = 4;
  913. mCurrentWidth = 1;
  914. mCurrentHeight = 1;
  915. mPreviousWidth = 1;
  916. mPreviousHeight = 1;
  917. mNaturalWidth = 1;
  918. mNaturalHeight = 1;
  919. mWidth = 1;
  920. mHeight = 1;
  921. mTextureWidth = 1;
  922. mTextureHeight = 1;
  923. message.setValueU32("format", GL_RGBA);
  924. message.setValueU32("type", GL_UNSIGNED_INT_8_8_8_8_REV);
  925. message.setValueS32("depth", mDepth);
  926. message.setValueS32("default_width", mWidth);
  927. message.setValueS32("default_height", mHeight);
  928. message.setValueU32("internalformat", GL_RGBA8);
  929. message.setValueBoolean("coords_opengl", true); // true == use OpenGL-style coordinates, false == (0,0) is upper left.
  930. message.setValueBoolean("allow_downsample", true); // we respond with grace and performance if asked to downscale
  931. sendMessage(message);
  932. }
  933. else if(message_name == "size_change")
  934. {
  935. std::string name = message_in.getValue("name");
  936. S32 width = message_in.getValueS32("width");
  937. S32 height = message_in.getValueS32("height");
  938. S32 texture_width = message_in.getValueS32("texture_width");
  939. S32 texture_height = message_in.getValueS32("texture_height");
  940. std::ostringstream str;
  941. INFOMSG("---->Got size change instruction from application with shm name: %s - size is %d x %d", name.c_str(), width, height);
  942. LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_response");
  943. message.setValue("name", name);
  944. message.setValueS32("width", width);
  945. message.setValueS32("height", height);
  946. message.setValueS32("texture_width", texture_width);
  947. message.setValueS32("texture_height", texture_height);
  948. sendMessage(message);
  949. if(!name.empty())
  950. {
  951. // Find the shared memory region with this name
  952. SharedSegmentMap::iterator iter = mSharedSegments.find(name);
  953. if(iter != mSharedSegments.end())
  954. {
  955. INFOMSG("*** Got size change with matching shm, new size is %d x %d", width, height);
  956. INFOMSG("*** Got size change with matching shm, texture size size is %d x %d", texture_width, texture_height);
  957. mPixels = (unsigned char*)iter->second.mAddress;
  958. mTextureSegmentName = name;
  959. mWidth = width;
  960. mHeight = height;
  961. if (texture_width > 1 ||
  962. texture_height > 1) // not a dummy size from the app, a real explicit forced size
  963. {
  964. INFOMSG("**** = REAL RESIZE REQUEST FROM APP");
  965. GST_OBJECT_LOCK(mVideoSink);
  966. mVideoSink->resize_forced_always = true;
  967. mVideoSink->resize_try_width = texture_width;
  968. mVideoSink->resize_try_height = texture_height;
  969. GST_OBJECT_UNLOCK(mVideoSink);
  970. }
  971. mTextureWidth = texture_width;
  972. mTextureHeight = texture_height;
  973. }
  974. }
  975. }
  976. else if(message_name == "load_uri")
  977. {
  978. std::string uri = message_in.getValue("uri");
  979. navigateTo( uri );
  980. sendStatus();
  981. }
  982. else if(message_name == "mouse_event")
  983. {
  984. std::string event = message_in.getValue("event");
  985. S32 x = message_in.getValueS32("x");
  986. S32 y = message_in.getValueS32("y");
  987. if(event == "down")
  988. {
  989. mouseDown(x, y);
  990. }
  991. else if(event == "up")
  992. {
  993. mouseUp(x, y);
  994. }
  995. else if(event == "move")
  996. {
  997. mouseMove(x, y);
  998. };
  999. };
  1000. }
  1001. else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME)
  1002. {
  1003. if(message_name == "stop")
  1004. {
  1005. stop();
  1006. }
  1007. else if(message_name == "start")
  1008. {
  1009. double rate = 0.0;
  1010. if(message_in.hasValue("rate"))
  1011. {
  1012. rate = message_in.getValueReal("rate");
  1013. }
  1014. // NOTE: we don't actually support rate.
  1015. play(rate);
  1016. }
  1017. else if(message_name == "pause")
  1018. {
  1019. pause();
  1020. }
  1021. else if(message_name == "seek")
  1022. {
  1023. double time = message_in.getValueReal("time");
  1024. // defer the actual seek in case we haven't
  1025. // really truly started yet in which case there
  1026. // is nothing to seek upon
  1027. mSeekWanted = true;
  1028. mSeekDestination = time;
  1029. }
  1030. else if(message_name == "set_loop")
  1031. {
  1032. bool loop = message_in.getValueBoolean("loop");
  1033. mIsLooping = loop;
  1034. }
  1035. else if(message_name == "set_volume")
  1036. {
  1037. double volume = message_in.getValueReal("volume");
  1038. setVolume(volume);
  1039. }
  1040. }
  1041. else
  1042. {
  1043. INFOMSG("MediaPluginGStreamer010::receiveMessage: unknown message class: %s", message_class.c_str());
  1044. }
  1045. }
  1046. }
  1047. int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data)
  1048. {
  1049. if (MediaPluginGStreamer010::startup())
  1050. {
  1051. MediaPluginGStreamer010 *self = new MediaPluginGStreamer010(host_send_func, host_user_data);
  1052. *plugin_send_func = MediaPluginGStreamer010::staticReceiveMessage;
  1053. *plugin_user_data = (void*)self;
  1054. return 0; // okay
  1055. }
  1056. else
  1057. {
  1058. return -1; // failed to init
  1059. }
  1060. }
  1061. #else // LL_GSTREAMER010_ENABLED
  1062. // Stubbed-out class with constructor/destructor (necessary or windows linker
  1063. // will just think its dead code and optimize it all out)
  1064. class MediaPluginGStreamer010 : public MediaPluginBase
  1065. {
  1066. public:
  1067. MediaPluginGStreamer010(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data);
  1068. ~MediaPluginGStreamer010();
  1069. /* virtual */ void receiveMessage(const char *message_string);
  1070. };
  1071. MediaPluginGStreamer010::MediaPluginGStreamer010(
  1072. LLPluginInstance::sendMessageFunction host_send_func,
  1073. void *host_user_data ) :
  1074. MediaPluginBase(host_send_func, host_user_data)
  1075. {
  1076. // no-op
  1077. }
  1078. MediaPluginGStreamer010::~MediaPluginGStreamer010()
  1079. {
  1080. // no-op
  1081. }
  1082. void MediaPluginGStreamer010::receiveMessage(const char *message_string)
  1083. {
  1084. // no-op
  1085. }
  1086. // We're building without GStreamer enabled. Just refuse to initialize.
  1087. int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data)
  1088. {
  1089. return -1;
  1090. }
  1091. #endif // LL_GSTREAMER010_ENABLED