00001
00032 #include "linden_common.h"
00033
00034 #if LL_GSTREAMER_ENABLED
00035
00036 extern "C" {
00037 #include <gst/gst.h>
00038 }
00039
00040 #include "llmediaimplgstreamer.h"
00041
00042 #include "llmediaimplgstreamervidplug.h"
00043
00044 #ifdef LL_GST_SOUNDSINK
00045 #include "llmediaimplgstreamersndplug.h"
00046 #endif // LL_GST_SOUNDSINK
00047
00048 #include "llmediaimplgstreamer_syms.h"
00049
00050 #include "llgl.h"
00051 #include "llglheaders.h"
00052
00054
00055 LLMediaImplGStreamer::
00056 LLMediaImplGStreamer () :
00057 mediaData ( NULL ),
00058 ownBuffer ( TRUE ),
00059 mVolume ( 1.0f ),
00060 currentMode ( ModeIdle ),
00061 mPump ( NULL ),
00062 mPlaybin ( NULL ),
00063 mVideoSink ( NULL )
00064 #ifdef LL_GST_SOUNDSINK
00065 ,mAudioSink ( NULL )
00066 #endif
00067 {
00068 mMediaDepthBytes = 4;
00069 mTextureDepth = 4;
00070 mTextureFormatInternal = GL_RGB8;
00071 mTextureFormatPrimary = GL_BGRA;
00072 mTextureFormatType = GL_UNSIGNED_INT_8_8_8_8_REV;
00073 }
00074
00076
00077 LLMediaImplGStreamer::
00078 ~LLMediaImplGStreamer ()
00079 {
00080 unload();
00081 }
00082
00083 void UnloadGStreamer()
00084 {
00085 ungrab_gst_syms();
00086 }
00087
00088
00090
00091 BOOL
00092 LLMediaImplGStreamer::
00093 setBuffer ( U8* bufferIn )
00094 {
00095
00096
00097 U8* oldMediaData = mediaData;
00098 BOOL ownedMediaData = ownBuffer;
00099
00100 if(bufferIn == NULL)
00101 {
00102
00103
00104 mediaData = new unsigned char[ mMediaHeight * mMediaRowbytes ];
00105 ownBuffer = TRUE;
00106 }
00107 else
00108 {
00109
00110 mediaData = bufferIn;
00111 ownBuffer = FALSE;
00112 }
00113
00114 if(mediaData == NULL)
00115 {
00116
00117 llerrs << "LLMediaImplGStreamer::setBuffer: mediaData is NULL" << llendl;
00118
00119
00120 return FALSE;
00121 }
00122
00123
00124
00125
00126 if ( ownedMediaData )
00127 {
00128 if ( oldMediaData )
00129 {
00130 delete [] oldMediaData;
00131 }
00132 }
00133
00134 return TRUE;
00135 }
00136
00138
00139 BOOL
00140 LLMediaImplGStreamer::
00141 init ()
00142 {
00143 static bool done_init = false;
00144 if (!done_init)
00145 {
00146
00147 if (! grab_gst_syms("libgstreamer-0.10.so.0",
00148 "libgstvideo-0.10.so.0",
00149 "libgstaudio-0.10.so.0") )
00150 {
00151 llwarns << "Couldn't find suitable GStreamer 0.10 support on this system - video playback disabled." << llendl;
00152 return FALSE;
00153 }
00154
00155 if (llgst_segtrap_set_enabled)
00156 llgst_segtrap_set_enabled(FALSE);
00157 else
00158 llwarns << "gst_segtrap_set_enabled() is not available; Second Life automated crash-reporter may cease to function until next restart." << llendl;
00159
00160 if (0 == llgst_init_check(NULL, NULL, NULL))
00161 {
00162 llwarns << "GST init failed for unspecified reason." << llendl;
00163 return FALSE;
00164 }
00165
00166
00167 gst_slvideo_init_class();
00168 #if 0
00169 gst_slsound_init_class();
00170 #endif
00171
00172 done_init = true;
00173 }
00174
00175
00176 mPump = g_main_loop_new (NULL, FALSE);
00177 if (!mPump)
00178 {
00179 return FALSE;
00180 }
00181
00182
00183 mPlaybin = llgst_element_factory_make ("playbin", "play");
00184 if (!mPlaybin)
00185 {
00186
00187 return FALSE;
00188 }
00189
00190 if (NULL == getenv("LL_GSTREAMER_EXTERNAL")) {
00191
00192 mVideoSink =
00193 GST_SLVIDEO(llgst_element_factory_make ("private-slvideo", "slvideo"));
00194 if (!mVideoSink)
00195 {
00196 llwarns << "Could not instantiate private-slvideo element."
00197 << llendl;
00198
00199 return FALSE;
00200 }
00201
00202 g_object_set(mPlaybin, "video-sink", mVideoSink, NULL);
00203
00204 #ifdef LL_GST_SOUNDSINK
00205
00206 mAudioSink =
00207 GST_SLSOUND(llgst_element_factory_make ("private-slsound", "slsound"));
00208 if (!mAudioSink)
00209 {
00210 llwarns << "Could not instantiate private-slsound element."
00211 << llendl;
00212
00213 return FALSE;
00214 }
00215
00216 g_object_set(mPlaybin, "audio-sink", mAudioSink, NULL);
00217 #endif
00218 }
00219
00220 return LLMediaMovieBase::init();
00221 }
00222
00223
00225
00226
00227 #ifdef LL_GST_REPORT_STATE_CHANGES
00228 static char* get_gst_state_name(GstState state)
00229 {
00230 switch (state) {
00231 case GST_STATE_VOID_PENDING: return "VOID_PENDING";
00232 case GST_STATE_NULL: return "NULL";
00233 case GST_STATE_READY: return "READY";
00234 case GST_STATE_PAUSED: return "PAUSED";
00235 case GST_STATE_PLAYING: return "PLAYING";
00236 }
00237 return "(unknown)";
00238 }
00239 #endif // LL_GST_REPORT_STATE_CHANGES
00240
00241 static gboolean
00242 my_bus_callback (GstBus *bus,
00243 GstMessage *message,
00244 gpointer data)
00245 {
00246 if (GST_MESSAGE_TYPE(message) != GST_MESSAGE_STATE_CHANGED &&
00247 GST_MESSAGE_TYPE(message) != GST_MESSAGE_BUFFERING)
00248 {
00249 llinfos << "Got GST message type: "
00250 << LLGST_MESSAGE_TYPE_NAME (message)
00251 << llendl;
00252 }
00253 else
00254 {
00255 lldebugs << "Got GST message type: "
00256 << LLGST_MESSAGE_TYPE_NAME (message)
00257 << llendl;
00258 }
00259
00260 LLMediaImplGStreamer *impl = (LLMediaImplGStreamer*)data;
00261
00262 switch (GST_MESSAGE_TYPE (message)) {
00263 case GST_MESSAGE_BUFFERING: {
00264
00265 if (llgst_message_parse_buffering)
00266 {
00267 gint percent = 0;
00268 llgst_message_parse_buffering(message, &percent);
00269 llinfos << "GST buffering: " << percent
00270 << "%" << llendl;
00271
00272
00273
00274 }
00275 break;
00276 }
00277 case GST_MESSAGE_STATE_CHANGED: {
00278 GstState old_state;
00279 GstState new_state;
00280 GstState pending_state;
00281 llgst_message_parse_state_changed(message,
00282 &old_state,
00283 &new_state,
00284 &pending_state);
00285 #ifdef LL_GST_REPORT_STATE_CHANGES
00286
00287 llinfos << "state change (old,<new>,pending): "
00288 << get_gst_state_name(old_state) << ", <"
00289 << get_gst_state_name(new_state) << ">, "
00290 << get_gst_state_name(pending_state) <<
00291 llendl;
00292 #endif // LL_GST_REPORT_STATE_CHANGES
00293
00294 switch (new_state) {
00295 case GST_STATE_VOID_PENDING:
00296 impl->setCurrentMode(LLMediaImplGStreamer::ModeNone);
00297 break;
00298 case GST_STATE_NULL:
00299 impl->setCurrentMode(LLMediaImplGStreamer::ModeNone);
00300 break;
00301 case GST_STATE_READY:
00302 impl->setCurrentMode(LLMediaImplGStreamer::ModeStopped);
00303 break;
00304 case GST_STATE_PAUSED:
00305 impl->setCurrentMode(LLMediaImplGStreamer::ModePaused);
00306 break;
00307 case GST_STATE_PLAYING:
00308 impl->setCurrentMode(LLMediaImplGStreamer::ModePlaying);
00309 break;
00310 }
00311 break;
00312 }
00313 case GST_MESSAGE_ERROR: {
00314 GError *err;
00315 gchar *debug;
00316
00317 llgst_message_parse_error (message, &err, &debug);
00318 llinfos << "GST error: " << err->message << llendl;
00319 g_error_free (err);
00320 g_free (debug);
00321
00322 impl->setCurrentMode(LLMediaImplGStreamer::ModeError);
00323
00324 impl->stop();
00325
00326 break;
00327 }
00328 case GST_MESSAGE_INFO: {
00329 if (llgst_message_parse_info)
00330 {
00331 GError *err;
00332 gchar *debug;
00333
00334 llgst_message_parse_info (message, &err, &debug);
00335 llinfos << "GST info: " << err->message << llendl;
00336 g_error_free (err);
00337 g_free (debug);
00338 }
00339 break;
00340 }
00341 case GST_MESSAGE_WARNING: {
00342 GError *err;
00343 gchar *debug;
00344
00345 llgst_message_parse_warning (message, &err, &debug);
00346 llinfos << "GST warning: " << err->message << llendl;
00347 g_error_free (err);
00348 g_free (debug);
00349
00350 break;
00351 }
00352 case GST_MESSAGE_EOS:
00353
00354 llinfos << "GST EOS." << llendl;
00355 impl->setCurrentMode(LLMediaImplGStreamer::ModeStopped);
00356 impl->stop();
00357 break;
00358 default:
00359
00360 break;
00361 }
00362
00363
00364
00365
00366
00367 return TRUE;
00368 }
00369
00370 BOOL
00371 LLMediaImplGStreamer::
00372 load ( const LLString& urlIn )
00373 {
00374 llinfos << "Setting media URI: " << urlIn << llendl;
00375
00376
00377 g_object_set (G_OBJECT (mPlaybin), "uri", urlIn.c_str(), NULL);
00378
00379
00380
00381 GstBus *bus = llgst_pipeline_get_bus (GST_PIPELINE (mPlaybin));
00382 if (!bus)
00383 {
00384 return FALSE;
00385 }
00386 llgst_bus_add_watch (bus, my_bus_callback, this);
00387 llgst_object_unref (bus);
00388
00389 if (true)
00390 {
00391 const int fixedsize = 2;
00392 mMediaRowbytes = mMediaDepthBytes * fixedsize;
00393 mMediaWidth = fixedsize;
00394 mMediaHeight = fixedsize;
00395 mTextureWidth = fixedsize;
00396 mTextureHeight = fixedsize;
00397 }
00398
00399 BOOL rtn = LLMediaMovieBase::load(urlIn);
00400 llinfos << "load returns " << int(rtn) << llendl;
00401 return rtn;
00402 }
00403
00405
00406 BOOL
00407 LLMediaImplGStreamer::
00408 unload ()
00409 {
00410 if (mPlaybin)
00411 {
00412 llgst_element_set_state (mPlaybin, GST_STATE_NULL);
00413 llgst_object_unref (GST_OBJECT (mPlaybin));
00414 mPlaybin = NULL;
00415 }
00416
00417 if (mPump)
00418 {
00419 g_main_loop_quit(mPump);
00420 mPump = NULL;
00421 }
00422
00423 if (mediaData)
00424 {
00425 if (ownBuffer)
00426 {
00427 delete mediaData;
00428 mediaData = NULL;
00429 }
00430 }
00431
00432 mVideoSink = NULL;
00433
00434 return TRUE;
00435 }
00436
00438
00439 S32
00440 LLMediaImplGStreamer::
00441 updateMedia ()
00442 {
00443
00444 if (g_main_context_pending(g_main_loop_get_context(mPump)))
00445 {
00446 g_main_context_iteration(g_main_loop_get_context(mPump), FALSE);
00447 }
00448
00449 if (mVideoSink)
00450 {
00451 GST_OBJECT_LOCK(mVideoSink);
00452 if (mVideoSink->retained_frame_ready)
00453 {
00454
00455 if (mVideoSink->retained_frame_width != mMediaWidth ||
00456 mVideoSink->retained_frame_height != mMediaHeight)
00457
00458 {
00459
00460 mMediaWidth = mVideoSink->retained_frame_width;
00461 mMediaHeight = mVideoSink->retained_frame_height;
00462 mTextureWidth = mMediaWidth;
00463 mTextureHeight = mMediaHeight;
00464 mMediaDepthBytes = mTextureDepth =
00465 SLVPixelFormatBytes[mVideoSink->retained_frame_format];
00466 if (SLV_PF_RGBX == mVideoSink->retained_frame_format)
00467 {
00468 mTextureFormatPrimary = GL_RGBA;
00469 mTextureFormatType=GL_UNSIGNED_INT_8_8_8_8_REV;
00470 }
00471 else
00472 {
00473 mTextureFormatPrimary = GL_BGRA;
00474 mTextureFormatType=GL_UNSIGNED_INT_8_8_8_8_REV;
00475 }
00476 mMediaRowbytes = mMediaWidth * mMediaDepthBytes;
00477 llinfos << "video container resized to " <<
00478 mMediaWidth << "x" << mMediaHeight << llendl;
00479
00480 if (ownBuffer)
00481 {
00482
00483 delete[] mediaData;
00484 mediaData = new U8[mMediaRowbytes *
00485 mMediaHeight];
00486 }
00487
00488 GST_OBJECT_UNLOCK(mVideoSink);
00489 return updateMediaNeedsSizeChange;
00490 }
00491
00492
00493 mVideoSink->retained_frame_ready = FALSE;
00494 memcpy(mediaData, mVideoSink->retained_frame_data,
00495 mMediaRowbytes * mMediaHeight);
00496
00497 GST_OBJECT_UNLOCK(mVideoSink);
00498 return updateMediaNeedsUpdate;
00499 }
00500 else
00501 {
00502
00503 GST_OBJECT_UNLOCK(mVideoSink);
00504 return updateMediaNoChanges;
00505 }
00506 }
00507
00508 return updateMediaNoChanges;
00509 }
00510
00512
00513 void
00514 LLMediaImplGStreamer::
00515 setAutoScaled ( BOOL autoScaledIn )
00516 {
00517 autoScaled = autoScaledIn;
00518 }
00519
00521
00522 BOOL
00523 LLMediaImplGStreamer::
00524 stop ()
00525 {
00526 llinfos << "stopping media..." << llendl;
00527
00528 llgst_element_set_state(mPlaybin, GST_STATE_READY);
00529
00530 BOOL rtn = LLMediaMovieBase::stop();
00531 setCurrentMode(LLMediaImplGStreamer::ModeStopped);
00532 return rtn;
00533 }
00534
00536
00537 BOOL
00538 LLMediaImplGStreamer::
00539 play ()
00540 {
00541 llinfos << "playing media..." << llendl;
00542
00543 llgst_element_set_state(mPlaybin, GST_STATE_PLAYING);
00544
00545 return LLMediaMovieBase::play();
00546 }
00547
00549
00550 BOOL
00551 LLMediaImplGStreamer::
00552 loop ( S32 howMany )
00553 {
00554 llinfos << "looping media... " << howMany << llendl;
00555
00556 if (!play())
00557 return FALSE;
00558
00559 return LLMediaMovieBase::loop(howMany);
00560 };
00561
00563
00564 BOOL
00565 LLMediaImplGStreamer::
00566 pause ()
00567 {
00568 llinfos << "pausing media..." << llendl;
00569
00570 llgst_element_set_state(mPlaybin, GST_STATE_PAUSED);
00571
00572 return LLMediaMovieBase::pause();
00573 };
00574
00576
00577 BOOL
00578 LLMediaImplGStreamer::
00579 setVolume ( F32 volumeIn )
00580 {
00581 mVolume = volumeIn;
00582 g_object_set(mPlaybin, "volume", mVolume, NULL);
00583 return TRUE;
00584 }
00585
00587
00588 F32
00589 LLMediaImplGStreamer::
00590 getVolume ()
00591 {
00592 return mVolume;
00593 }
00594
00596
00597 BOOL
00598 LLMediaImplGStreamer::
00599 isIdle () const
00600 {
00601
00602 return currentMode == ModeIdle;
00603 }
00604
00606
00607 BOOL
00608 LLMediaImplGStreamer::
00609 isError () const
00610 {
00611
00612 return currentMode == ModeError;
00613 }
00614
00616
00617 BOOL
00618 LLMediaImplGStreamer::
00619 isBuffering () const
00620 {
00621
00622 return currentMode == ModeBuffering;
00623 }
00624
00626
00627 BOOL
00628 LLMediaImplGStreamer::
00629 isLoaded () const
00630 {
00631
00632
00633 return (mPump != NULL);
00634 }
00635
00637
00638 BOOL
00639 LLMediaImplGStreamer::
00640 isPlaying () const
00641 {
00642
00643 return currentMode == ModePlaying;
00644 }
00645
00647
00648 BOOL
00649 LLMediaImplGStreamer::
00650 isLooping () const
00651 {
00652
00653 return currentMode == ModeLooping;
00654 }
00655
00657
00658 BOOL
00659 LLMediaImplGStreamer::
00660 isPaused () const
00661 {
00662
00663 return currentMode == ModePaused;
00664 }
00665
00667
00668 BOOL
00669 LLMediaImplGStreamer::
00670 isStopped () const
00671 {
00672
00673 return currentMode == ModeStopped;
00674 }
00675
00677
00678 U8*
00679 LLMediaImplGStreamer::
00680 getMediaData ()
00681 {
00682 return mediaData;
00683 }
00684
00686
00687 BOOL
00688 LLMediaImplGStreamer::
00689 seek ( F64 time )
00690 {
00691
00692 llinfos << "Tried to seek to time " << time
00693 << " - faking it" << llendl;
00694 return TRUE;
00695 }
00696
00698
00699 F64
00700 LLMediaImplGStreamer::
00701 getTime () const
00702 {
00703
00704 F64 result = 0;
00705 return result;
00706 }
00707
00709
00710 F64
00711 LLMediaImplGStreamer::
00712 getMediaDuration () const
00713 {
00714
00715 F64 result = 0;
00716 return result;
00717 }
00718
00719 #endif // LL_GSTREAMER_ENABLED