llmediaimplgstreamer.cpp

Go to the documentation of this file.
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"        // For gl texture modes
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 // LL_GST_SOUNDSINK
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         // Since we've pointed GStreamer at the old media data buffer
00096         // directly, we need to be somewhat careful deleting it...
00097         U8* oldMediaData = mediaData;
00098         BOOL ownedMediaData = ownBuffer;
00099 
00100         if(bufferIn == NULL)
00101         {
00102                 // Passing NULL to this function requests that the object
00103                 // allocate its own buffer.
00104                 mediaData = new unsigned char[ mMediaHeight * mMediaRowbytes ];
00105                 ownBuffer = TRUE;
00106         }
00107         else
00108         {
00109                 // Use the supplied buffer.
00110                 mediaData = bufferIn;
00111                 ownBuffer = FALSE;
00112         }
00113         
00114         if(mediaData == NULL)
00115         {
00116                 // This is bad - probably out of memory.
00117                 llerrs << "LLMediaImplGStreamer::setBuffer: mediaData is NULL" << llendl;
00118                 // NOTE: This case doesn't clean up properly.  This assert is fatal, so this isn't a huge problem,
00119                 // but if this assert is ever removed the code should be fixed to clean up correctly.
00120                 return FALSE;
00121         }
00122         
00123         // [..]
00124 
00125         // Delete the old media data buffer iff we owned it.
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                 // Get symbols!
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                 // Init our custom plugins - only really need do this once.
00167                 gst_slvideo_init_class();
00168 #if 0
00169                 gst_slsound_init_class();
00170 #endif
00171 
00172                 done_init = true;
00173         }
00174 
00175         // Create a pumpable main-loop for this media
00176         mPump = g_main_loop_new (NULL, FALSE);
00177         if (!mPump)
00178         {
00179                 return FALSE;
00180         }
00181 
00182         // instantiate a playbin element to do the hard work
00183         mPlaybin = llgst_element_factory_make ("playbin", "play");
00184         if (!mPlaybin)
00185         {
00186                 // todo: cleanup pump
00187                 return FALSE;
00188         }
00189 
00190         if (NULL == getenv("LL_GSTREAMER_EXTERNAL")) {
00191                 // instantiate and connect a custom video sink
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                         // todo: cleanup.
00199                         return FALSE;
00200                 }
00201 
00202                 g_object_set(mPlaybin, "video-sink", mVideoSink, NULL);
00203 
00204 #ifdef LL_GST_SOUNDSINK
00205                 // instantiate and connect a custom audio sink
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                         // todo: cleanup.
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 //#define LL_GST_REPORT_STATE_CHANGES
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                 // NEEDS GST 0.10.11+
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                         // ModeBuffering seems to do nothing except make
00272                         // the UI worse
00273                         /*if (percent < 100) impl->setCurrentMode(LLMediaImplGStreamer::ModeBuffering);*/
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                 // not generally very useful, and rather spammy.
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                 /* end-of-stream */
00354                 llinfos << "GST EOS." << llendl;
00355                 impl->setCurrentMode(LLMediaImplGStreamer::ModeStopped);//?
00356                 impl->stop();
00357                 break;
00358         default:
00359                 /* unhandled message */
00360                 break;
00361         }
00362 
00363         /* we want to be notified again the next time there is a message
00364          * on the bus, so returning TRUE (FALSE means we want to stop watching
00365          * for messages on the bus and our callback should not be called again)
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         // set URI
00377         g_object_set (G_OBJECT (mPlaybin), "uri", urlIn.c_str(), NULL);
00378         //g_object_set (G_OBJECT (mPlaybin), "uri", "file:///tmp/movie", NULL);
00379 
00380         // get playbin's bus - perhaps this can/should be done at init()
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) // dummy values
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         //llinfos << "updating media..." << llendl;
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                         //llinfos << "NEW FRAME " << llendl;
00455                         if (mVideoSink->retained_frame_width != mMediaWidth ||
00456                             mVideoSink->retained_frame_height != mMediaHeight)
00457                                 // *TODO: also check for change in format
00458                         {
00459                                 // just resize container
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                                         // we manage the buffer, so we need to realloc
00483                                         delete[] mediaData;
00484                                         mediaData = new U8[mMediaRowbytes *
00485                                                            mMediaHeight];
00486                                 }
00487                                 
00488                                 GST_OBJECT_UNLOCK(mVideoSink);
00489                                 return updateMediaNeedsSizeChange;
00490                         }
00491 
00492                         // we're gonna totally consume this frame - reset 'ready' flag
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                         // nothing to do yet.
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         // todo: error-check this?
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         // todo: error-check this?
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         // todo: implement this
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         // todo: error-check this?
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         // todo: probably semantically decouple from currentMode
00602         return currentMode == ModeIdle;
00603 }
00604 
00606 //
00607 BOOL
00608 LLMediaImplGStreamer::
00609 isError () const
00610 {
00611         // todo: probably semantically decouple from currentMode
00612         return currentMode == ModeError;
00613 }
00614 
00616 //
00617 BOOL
00618 LLMediaImplGStreamer::
00619 isBuffering () const
00620 {
00621         // todo: probably semantically decouple from currentMode
00622         return currentMode == ModeBuffering;
00623 }
00624 
00626 //
00627 BOOL
00628 LLMediaImplGStreamer::
00629 isLoaded () const
00630 {
00631         // todo: probably semantically decouple from currentMode
00632         //return currentMode == ModeLoaded;
00633         return (mPump != NULL);
00634 }
00635 
00637 //
00638 BOOL
00639 LLMediaImplGStreamer::
00640 isPlaying () const
00641 {
00642         // todo: probably semantically decouple from currentMode
00643         return currentMode == ModePlaying;
00644 }
00645 
00647 //
00648 BOOL
00649 LLMediaImplGStreamer::
00650 isLooping () const
00651 {
00652         // todo: probably semantically decouple from currentMode
00653         return currentMode == ModeLooping;
00654 }
00655 
00657 //
00658 BOOL
00659 LLMediaImplGStreamer::
00660 isPaused () const
00661 {
00662         // todo: probably semantically decouple from currentMode
00663         return currentMode == ModePaused;
00664 }
00665 
00667 //
00668 BOOL
00669 LLMediaImplGStreamer::
00670 isStopped () const
00671 {
00672         // todo: probably semantically decouple from currentMode
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         // todo: implement this
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         // todo: implement this
00704         F64 result = 0; 
00705         return result;
00706 }
00707 
00709 //
00710 F64 
00711 LLMediaImplGStreamer::
00712 getMediaDuration () const
00713 {
00714         // todo: implement this
00715         F64 result = 0;
00716         return result;
00717 }
00718 
00719 #endif // LL_GSTREAMER_ENABLED

Generated on Thu Jul 1 06:08:51 2010 for Second Life Viewer by  doxygen 1.4.7