llmediaimplgstreamer.cpp

Go to the documentation of this file.
00001 
00033 #include "llmediaimplgstreamer.h"
00034 
00035 #if LL_GSTREAMER_ENABLED
00036 
00037 extern "C" {
00038 #include <gst/gst.h>
00039 }
00040 
00041 #include "llmediamanager.h"
00042 #include "llmediaimplregister.h"
00043 
00044 #include "llmediaimplgstreamervidplug.h"
00045 
00046 #ifdef LL_GST_SOUNDSINK
00047 #include "llmediaimplgstreamersndplug.h"
00048 #endif // LL_GST_SOUNDSINK
00049 
00050 #include "llmediaimplgstreamer_syms.h"
00051 
00052 // register this impl with media manager factory
00053 static LLMediaImplRegister sLLMediaImplGStreamerReg( "LLMediaImplGStreamer", new LLMediaImplGStreamerMaker() );
00054 
00055 LLMediaImplGStreamerMaker::LLMediaImplGStreamerMaker()
00056 {
00057         // Register to handle the scheme
00058         mSchema.push_back( "rtsp" );
00059         mSchema.push_back( "rtmp" );
00060         
00061         // Register to handle the category
00062         mMimeTypeCategories.push_back( "video" );
00063         mMimeTypeCategories.push_back( "audio" );
00064 }
00065 
00067 //
00068 LLMediaImplGStreamer::
00069 LLMediaImplGStreamer () :
00070         mediaData ( NULL ),
00071         mMediaRowbytes ( 1 ),
00072         mTextureFormatPrimary ( LL_MEDIA_BGRA ),
00073         mTextureFormatType ( LL_MEDIA_UNSIGNED_INT_8_8_8_8_REV ),
00074         mPump ( NULL ),
00075         mPlaybin ( NULL ),
00076         mVideoSink ( NULL )
00077 #ifdef LL_GST_SOUNDSINK
00078         ,mAudioSink ( NULL )
00079 #endif // LL_GST_SOUNDSINK
00080 {
00081         DEBUGMSG("constructing media...");
00082 
00083         setMediaDepth(4);
00084 
00085         // Create a pumpable main-loop for this media
00086         mPump = g_main_loop_new (NULL, FALSE);
00087         if (!mPump)
00088         {
00089                 return; // error
00090         }
00091 
00092         // instantiate a playbin element to do the hard work
00093         mPlaybin = llgst_element_factory_make ("playbin", "play");
00094         if (!mPlaybin)
00095         {
00096                 // todo: cleanup pump
00097                 return; // error
00098         }
00099 
00100         if (NULL == getenv("LL_GSTREAMER_EXTERNAL")) {
00101                 // instantiate and connect a custom video sink
00102                 mVideoSink =
00103                         GST_SLVIDEO(llgst_element_factory_make ("private-slvideo", "slvideo"));
00104                 if (!mVideoSink)
00105                 {
00106                         WARNMSG("Could not instantiate private-slvideo element.");
00107                         // todo: cleanup.
00108                         return; // error
00109                 }
00110 
00111                 g_object_set(mPlaybin, "video-sink", mVideoSink, NULL);
00112 
00113 #ifdef LL_GST_SOUNDSINK
00114                 // instantiate and connect a custom audio sink
00115                 mAudioSink =
00116                         GST_SLSOUND(llgst_element_factory_make ("private-slsound", "slsound"));
00117                 if (!mAudioSink)
00118                 {
00119                         WARNMSG("Could not instantiate private-slsound element.");
00120                         // todo: cleanup.
00121                         return; // error
00122                 }
00123 
00124                 g_object_set(mPlaybin, "audio-sink", mAudioSink, NULL);
00125 #endif
00126         }
00127 }
00128 
00129 // virtual
00130 int LLMediaImplGStreamer::getTextureFormatPrimary() const
00131 {
00132         return mTextureFormatPrimary;
00133 }
00134 
00135 // virtual
00136 int LLMediaImplGStreamer::getTextureFormatType() const
00137 {
00138         return mTextureFormatType;
00139 }
00140 
00141 // virtual
00142 int LLMediaImplGStreamer::getTextureFormatInternal() const
00143 {
00144         return LL_MEDIA_RGB8;
00145 }
00146 
00148 //
00149 LLMediaImplGStreamer::
00150 ~LLMediaImplGStreamer ()
00151 {
00152         DEBUGMSG("dtor of media...");
00153         unload();
00154 }
00155 
00157 // virtual
00158 std::string LLMediaImplGStreamer::getVersion()
00159 {
00160         std::string rtn;
00161         rtn = "[" + sLLMediaImplGStreamerReg.getImplName() + "] - GStreamer 0.10.x";
00162         return rtn;
00163 }
00164 
00166 // (static) super-initialization - called once at application startup
00167 bool
00168 LLMediaImplGStreamer::
00169 startup ( LLMediaManagerData* init_data )
00170 {
00171         static bool done_init = false;
00172         if (!done_init)
00173         {
00174                 // Get symbols!
00175                 if (! grab_gst_syms("libgstreamer-0.10.so.0",
00176                                     "libgstvideo-0.10.so.0",
00177                                     "libgstaudio-0.10.so.0") )
00178                 {
00179                         WARNMSG("Couldn't find suitable GStreamer 0.10 support on this system - video playback disabled.");
00180                         return false;
00181                 }
00182 
00183                 if (llgst_segtrap_set_enabled)
00184                         llgst_segtrap_set_enabled(FALSE);
00185                 else
00186                         WARNMSG("gst_segtrap_set_enabled() is not available; Automated crash-reporter may cease to function until next restart.");
00187 
00188                 // Protect against GStreamer resetting the locale, yuck.
00189                 static std::string saved_locale;
00190                 saved_locale = setlocale(LC_ALL, NULL);
00191                 if (0 == llgst_init_check(NULL, NULL, NULL))
00192                 {
00193                         WARNMSG("GST init failed for unspecified reason.");
00194                         setlocale(LC_ALL, saved_locale.c_str() );
00195                         return false;
00196                 }
00197                 setlocale(LC_ALL, saved_locale.c_str() );
00198                 
00199                 // Init our custom plugins - only really need do this once.
00200                 gst_slvideo_init_class();
00201 #if 0
00202                 gst_slsound_init_class();
00203 #endif
00204 
00205                 done_init = true;
00206         }
00207 
00208         return true;
00209 }
00210 
00211 
00212 bool LLMediaImplGStreamer::
00213 closedown()
00214 {
00215         ungrab_gst_syms();
00216 
00217         return true;
00218 }
00219 
00220 
00222 //
00223 //#define LL_GST_REPORT_STATE_CHANGES
00224 #ifdef LL_GST_REPORT_STATE_CHANGES
00225 static char* get_gst_state_name(GstState state)
00226 {
00227         switch (state) {
00228         case GST_STATE_VOID_PENDING: return "VOID_PENDING";
00229         case GST_STATE_NULL: return "NULL";
00230         case GST_STATE_READY: return "READY";
00231         case GST_STATE_PAUSED: return "PAUSED";
00232         case GST_STATE_PLAYING: return "PLAYING";
00233         }
00234         return "(unknown)";
00235 }
00236 #endif // LL_GST_REPORT_STATE_CHANGES
00237 
00238 static gboolean
00239 bus_callback (GstBus     *bus,
00240               GstMessage *message,
00241               gpointer    data)
00242 {
00243         if (GST_MESSAGE_TYPE(message) != GST_MESSAGE_STATE_CHANGED &&
00244             GST_MESSAGE_TYPE(message) != GST_MESSAGE_BUFFERING)
00245         {
00246                 DEBUGMSG("Got GST message type: %s",
00247                         LLGST_MESSAGE_TYPE_NAME (message));
00248         }
00249         else
00250         {
00251                 DEBUGMSG("Got GST message type: %s",
00252                          LLGST_MESSAGE_TYPE_NAME (message));
00253         }
00254 
00255         LLMediaImplGStreamer *impl = (LLMediaImplGStreamer*)data;
00256 
00257         switch (GST_MESSAGE_TYPE (message)) {
00258         case GST_MESSAGE_BUFFERING: {
00259                 // NEEDS GST 0.10.11+
00260                 if (llgst_message_parse_buffering)
00261                 {
00262                         gint percent = 0;
00263                         llgst_message_parse_buffering(message, &percent);
00264                         DEBUGMSG("GST buffering: %d%%", percent);
00265                         LLMediaEvent event( impl, percent );
00266                         impl->getEventEmitter().update( &LLMediaObserver::onUpdateProgress, event );
00267 
00268                 }
00269                 break;
00270         }
00271         case GST_MESSAGE_STATE_CHANGED: {
00272                 GstState old_state;
00273                 GstState new_state;
00274                 GstState pending_state;
00275                 llgst_message_parse_state_changed(message,
00276                                                 &old_state,
00277                                                 &new_state,
00278                                                 &pending_state);
00279 #ifdef LL_GST_REPORT_STATE_CHANGES
00280                 // not generally very useful, and rather spammy.
00281                 DEBUGMSG("state change (old,<new>,pending): %s,<%s>,%s",
00282                          get_gst_state_name(old_state),
00283                          get_gst_state_name(new_state),
00284                          get_gst_state_name(pending_state));
00285 #endif // LL_GST_REPORT_STATE_CHANGES
00286 
00287                 switch (new_state) {
00288                 case GST_STATE_VOID_PENDING:
00289                         break;
00290                 case GST_STATE_NULL:
00291                         break;
00292                 case GST_STATE_READY:
00293                         break;
00294                 case GST_STATE_PAUSED:
00295                         break;
00296                 case GST_STATE_PLAYING:
00297                         LLMediaEvent event( impl, 100 );
00298                         impl->getEventEmitter().update( &LLMediaObserver::onUpdateProgress, event );
00299                         // emit an event to say that a media source was loaded
00300                         LLMediaEvent event2( impl );
00301                         impl->getEventEmitter().update( &LLMediaObserver::onMediaLoaded, event2 );
00302                         break;
00303                 }
00304                 break;
00305         }
00306         case GST_MESSAGE_ERROR: {
00307                 GError *err = NULL;
00308                 gchar *debug = NULL;
00309 
00310                 llgst_message_parse_error (message, &err, &debug);
00311                 WARNMSG("GST error: %s", err->message);
00312                 g_error_free (err);
00313                 g_free (debug);
00314 
00315                 impl->addCommand(LLMediaBase::COMMAND_STOP);
00316 
00317                 break;
00318         }
00319         case GST_MESSAGE_INFO: {
00320                 if (llgst_message_parse_info)
00321                 {
00322                         GError *err = NULL;
00323                         gchar *debug = NULL;
00324                         
00325                         llgst_message_parse_info (message, &err, &debug);
00326                         INFOMSG("GST info: %s", err->message);
00327                         g_error_free (err);
00328                         g_free (debug);
00329                 }
00330                 break;
00331         }
00332         case GST_MESSAGE_WARNING: {
00333                 GError *err = NULL;
00334                 gchar *debug = NULL;
00335 
00336                 llgst_message_parse_warning (message, &err, &debug);
00337                 WARNMSG("GST warning: %s", err->message);
00338                 g_error_free (err);
00339                 g_free (debug);
00340 
00341                 break;
00342         }
00343         case GST_MESSAGE_EOS:
00344                 /* end-of-stream */
00345                 DEBUGMSG("GST end-of-stream.");
00346                 if (impl->isLooping())
00347                 {
00348                         DEBUGMSG("looping media...");
00349                         impl->stop();
00350                         impl->play();
00351                 }
00352                 else
00353                 {
00354                         // inject a COMMAND_STOP
00355                         impl->addCommand(LLMediaBase::COMMAND_STOP);
00356                 }
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 return 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 
00371 // virtual
00372 bool
00373 LLMediaImplGStreamer::
00374 navigateTo ( const std::string urlIn )
00375 {
00376         DEBUGMSG("Setting media URI: %s", urlIn.c_str());
00377 
00378         if (NULL == mPump
00379 #ifdef LL_GST_SOUNDSINK
00380             || NULL == mAudioSink
00381 #endif
00382             || NULL == mPlaybin)
00383         {
00384                 return false;
00385         }
00386 
00387         setStatus( LLMediaBase::STATUS_NAVIGATING );
00388 
00389         // set URI
00390         g_object_set (G_OBJECT (mPlaybin), "uri", urlIn.c_str(), NULL);
00391         //g_object_set (G_OBJECT (mPlaybin), "uri", "file:///tmp/movie", NULL);
00392 
00393         // get playbin's bus - perhaps this can/should be done in ctor
00394         GstBus *bus = llgst_pipeline_get_bus (GST_PIPELINE (mPlaybin));
00395         if (!bus)
00396         {
00397                 return false;
00398         }
00399         llgst_bus_add_watch (bus, bus_callback, this);
00400         llgst_object_unref (bus);
00401 
00402         // navigateTo implicitly plays, too.
00403         play();
00404 
00405         return true;
00406 }
00407 
00409 //
00410 bool
00411 LLMediaImplGStreamer::
00412 unload ()
00413 {
00414         DEBUGMSG("unloading media...");
00415         if (mPlaybin)
00416         {
00417                 llgst_element_set_state (mPlaybin, GST_STATE_NULL);
00418                 llgst_object_unref (GST_OBJECT (mPlaybin));
00419                 mPlaybin = NULL;
00420         }
00421 
00422         if (mPump)
00423         {
00424                 g_main_loop_quit(mPump);
00425                 mPump = NULL;
00426         }
00427 
00428         if (mediaData)
00429         {
00430                 delete mediaData;
00431                 mediaData = NULL;
00432         }
00433 
00434         mVideoSink = NULL;
00435 
00436         return true;
00437 }
00438 
00440 // virtual
00441 bool
00442 LLMediaImplGStreamer::
00443 updateMedia ()
00444 {
00445         DEBUGMSG("updating media...");
00446         
00447         // sanity check
00448         if (NULL == mPump
00449 #ifdef LL_GST_SOUNDSINK
00450             || NULL == mAudioSink
00451 #endif
00452             || NULL == mPlaybin)
00453         {
00454                 DEBUGMSG("dead media...");
00455                 return false;
00456         }
00457 
00458         // process next outstanding command
00459         switch (nextCommand())
00460         {
00461         case LLMediaBase::COMMAND_START:
00462                 DEBUGMSG("COMMAND_START");
00463                 if (getStatus() == LLMediaBase::STATUS_PAUSED ||
00464                     getStatus() == LLMediaBase::STATUS_NAVIGATING ||
00465                     getStatus() == LLMediaBase::STATUS_STOPPED)
00466                 {
00467                         DEBUGMSG("doing COMMAND_START");
00468                         play();
00469                         setStatus(LLMediaBase::STATUS_STARTED);
00470                         clearCommand();
00471                 }
00472                 break;
00473         case LLMediaBase::COMMAND_STOP:
00474                 DEBUGMSG("COMMAND_STOP");
00475                 DEBUGMSG("doing COMMAND_STOP");
00476                 stop();
00477                 setStatus(LLMediaBase::STATUS_STOPPED);
00478                 clearCommand();
00479                 break;
00480         case LLMediaBase::COMMAND_PAUSE:
00481                 DEBUGMSG("COMMAND_PAUSE");
00482                 if (getStatus() == LLMediaBase::STATUS_STARTED)
00483                 {
00484                         DEBUGMSG("doing COMMAND_PAUSE");
00485                         pause();
00486                         setStatus(LLMediaBase::STATUS_PAUSED);
00487                         clearCommand();
00488                 }
00489                 break;
00490         default:
00491                 DEBUGMSG("COMMAND_?");
00492                 clearCommand();
00493                 break;
00494         case LLMediaBase::COMMAND_NONE:
00495                 break;
00496         }
00497 
00498         // deal with results
00499         if (g_main_context_pending(g_main_loop_get_context(mPump)))
00500         {
00501                g_main_context_iteration(g_main_loop_get_context(mPump), FALSE);
00502         }
00503 
00504         if (mVideoSink)
00505         {
00506                 GST_OBJECT_LOCK(mVideoSink);
00507                 if (mVideoSink->retained_frame_ready)
00508                 {
00509                         DEBUGMSG("NEW FRAME ");
00510                         if (mVideoSink->retained_frame_width != getMediaWidth() ||
00511                             mVideoSink->retained_frame_height != getMediaHeight())
00512                                 // *TODO: also check for change in format
00513                         {
00514                                 // just resize containe
00515                                 int neww = mVideoSink->retained_frame_width;
00516                                 int newh = mVideoSink->retained_frame_height;
00517                                 int newd = SLVPixelFormatBytes[mVideoSink->retained_frame_format];
00518                                 if (SLV_PF_RGBX == mVideoSink->retained_frame_format)
00519                                 {
00520                                         mTextureFormatPrimary = LL_MEDIA_RGBA;
00521                                         mTextureFormatType = LL_MEDIA_UNSIGNED_INT_8_8_8_8_REV;
00522                                 }
00523                                 else
00524                                 {
00525                                         mTextureFormatPrimary = LL_MEDIA_BGRA;
00526                                         mTextureFormatType = LL_MEDIA_UNSIGNED_INT_8_8_8_8_REV;
00527                                 }
00528                                 mMediaRowbytes = neww * newd;
00529                                 DEBUGMSG("video container resized to %dx%d",
00530                                          neww, newh);
00531                                 
00532                                 delete[] mediaData;
00533                                 mediaData = new unsigned char[mMediaRowbytes *
00534                                                               newh];
00535                                 
00536                                 GST_OBJECT_UNLOCK(mVideoSink);
00537 
00538                                 setMediaDepth(newd);
00539                                 setMediaSize(neww, newh);
00540                                 return true;
00541                         }
00542 
00543                         // we're gonna totally consume this frame - reset 'ready' flag
00544                         mVideoSink->retained_frame_ready = FALSE;
00545                         memcpy(mediaData, mVideoSink->retained_frame_data,
00546                                mMediaRowbytes * getMediaHeight());
00547                         
00548                         GST_OBJECT_UNLOCK(mVideoSink);
00549                         LLMediaEvent event( this );
00550                         mEventEmitter.update( &LLMediaObserver::onMediaContentsChange, event );
00551                         return true;
00552                 }
00553                 else
00554                 {
00555                         // nothing to do yet.
00556                         GST_OBJECT_UNLOCK(mVideoSink);
00557                         return true;
00558                 }
00559         }
00560 
00561         return true;
00562 }
00563 
00565 //
00566 bool
00567 LLMediaImplGStreamer::
00568 stop ()
00569 {
00570         DEBUGMSG("stopping media...");
00571         // todo: error-check this?
00572         llgst_element_set_state(mPlaybin, GST_STATE_READY);
00573         return true;
00574 }
00575 
00577 //
00578 bool
00579 LLMediaImplGStreamer::
00580 play ()
00581 {
00582         DEBUGMSG("playing media...");
00583         // todo: error-check this?
00584         llgst_element_set_state(mPlaybin, GST_STATE_PLAYING);
00585         return true;
00586 }
00587 
00589 //
00590 bool
00591 LLMediaImplGStreamer::
00592 pause ()
00593 {
00594         DEBUGMSG("pausing media...");
00595         // todo: error-check this?
00596         llgst_element_set_state(mPlaybin, GST_STATE_PAUSED);
00597         return true;
00598 };
00599 
00600 
00602 // virtual
00603 unsigned char*
00604 LLMediaImplGStreamer::
00605 getMediaData ()
00606 {
00607         return mediaData;
00608 }
00609 
00610 
00612 // virtual
00613 bool
00614 LLMediaImplGStreamer::
00615 setVolume(float volume)
00616 {
00617         mVolume = volume;
00618         if (mPlaybin)
00619         {
00620                 g_object_set(mPlaybin, "volume", mVolume, NULL);
00621                 return true;
00622         }
00623         return false;
00624 }
00625 
00626 #endif // LL_GSTREAMER_ENABLED

Generated on Fri May 16 08:32:21 2008 for SecondLife by  doxygen 1.5.5