aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llmedia/llmediaimplgstreamer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'linden/indra/llmedia/llmediaimplgstreamer.cpp')
-rw-r--r--linden/indra/llmedia/llmediaimplgstreamer.cpp847
1 files changed, 494 insertions, 353 deletions
diff --git a/linden/indra/llmedia/llmediaimplgstreamer.cpp b/linden/indra/llmedia/llmediaimplgstreamer.cpp
index a4fe930..d1bab29 100644
--- a/linden/indra/llmedia/llmediaimplgstreamer.cpp
+++ b/linden/indra/llmedia/llmediaimplgstreamer.cpp
@@ -18,8 +18,7 @@
18 * There are special exceptions to the terms and conditions of the GPL as 18 * There are special exceptions to the terms and conditions of the GPL as
19 * it is applied to this Source Code. View the full text of the exception 19 * it is applied to this Source Code. View the full text of the exception
20 * in the file doc/FLOSS-exception.txt in this software distribution, or 20 * in the file doc/FLOSS-exception.txt in this software distribution, or
21 * online at 21 * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception
22 * http://secondlifegrid.net/programs/open_source/licensing/flossexception
23 * 22 *
24 * By copying, modifying or distributing this software, you acknowledge 23 * By copying, modifying or distributing this software, you acknowledge
25 * that you have read and understood your obligations described above, 24 * that you have read and understood your obligations described above,
@@ -31,20 +30,39 @@
31 * $/LicenseInfo$ 30 * $/LicenseInfo$
32 */ 31 */
33 32
34#include "llmediaimplgstreamer.h" 33///#if LL_GSTREAMER_ENABLED
34
35#if LL_WINDOWS
36 // GStreamer 0.10.22 - gstutils.h - conversion from 'guint64' to 'guint8'.
37 // This was an intentional change to make GStreamer more threadsafe, and
38 // is okay. Delete this bit if GStreamer ever gets more VS-friendly -- McCabe
39 #pragma warning(disable : 4244)
40#endif
35 41
36#if LL_GSTREAMER_ENABLED 42#include "linden_common.h"
43#include "llmediaimplgstreamer.h"
37 44
38extern "C" { 45extern "C" {
39#include <gst/gst.h> 46#include <gst/gst.h>
47#include <gst/gstelement.h>
40} 48}
41 49
50#if LL_WINDOWS
51 #pragma warning(default : 4244)
52#include <direct.h>
53#include <stdlib.h>
54#endif
55
42#include "llmediamanager.h" 56#include "llmediamanager.h"
43#include "llmediaimplregister.h" 57#include "llmediaimplregister.h"
44 58
45#include "llmediaimplgstreamervidplug.h" 59#include "llmediaimplgstreamervidplug.h"
60#include "llgstplaythread.h"
46 61
47#include "llmediaimplgstreamer_syms.h" 62
63#if LL_DARWIN
64#include <CoreFoundation/CoreFoundation.h> // For CF functions used in set_gst_plugin_path
65#endif
48 66
49// register this impl with media manager factory 67// register this impl with media manager factory
50static LLMediaImplRegister sLLMediaImplGStreamerReg( "LLMediaImplGStreamer", new LLMediaImplGStreamerMaker() ); 68static LLMediaImplRegister sLLMediaImplGStreamerReg( "LLMediaImplGStreamer", new LLMediaImplGStreamerMaker() );
@@ -64,21 +82,20 @@ LLMediaImplGStreamerMaker::LLMediaImplGStreamerMaker()
64// 82//
65LLMediaImplGStreamer:: 83LLMediaImplGStreamer::
66LLMediaImplGStreamer () : 84LLMediaImplGStreamer () :
67 mBusWatchID ( 0 ),
68 mediaData ( NULL ), 85 mediaData ( NULL ),
69 mMediaRowbytes ( 1 ), 86 mMediaRowbytes ( 1 ),
70 mTextureFormatPrimary ( LL_MEDIA_BGRA ), 87 mTextureFormatPrimary ( LL_MEDIA_BGRA ),
71 mTextureFormatType ( LL_MEDIA_UNSIGNED_INT_8_8_8_8_REV ), 88 mTextureFormatType ( LL_MEDIA_UNSIGNED_INT_8_8_8_8_REV ),
72 mPump ( NULL ), 89 mPump ( NULL ),
73 mPlaybin ( NULL ), 90 mPlaybin ( NULL ),
74 mVideoSink ( NULL ) 91 mVideoSink ( NULL ),
92 mState( GST_STATE_NULL ),
93 mPlayThread ( NULL )
75{ 94{
76 if (!mDoneInit) 95 startup( NULL ); // Startup gstreamer if it hasn't been already.
77 return; // error
78 96
79 DEBUGMSG("constructing media..."); 97 LL_DEBUGS("MediaManager") << "constructing media..." << LL_ENDL;
80 98 mVolume = -1.0; // XXX Hack to make the vould change happend first time
81 mVolume = 0.1234567; // minor hack to force an initial volume update
82 99
83 setMediaDepth(4); 100 setMediaDepth(4);
84 101
@@ -90,30 +107,24 @@ LLMediaImplGStreamer () :
90 } 107 }
91 108
92 // instantiate a playbin element to do the hard work 109 // instantiate a playbin element to do the hard work
93 mPlaybin = llgst_element_factory_make ("playbin", "play"); 110 mPlaybin = gst_element_factory_make ("playbin", "play");
94 if (!mPlaybin) 111 if (!mPlaybin)
95 { 112 {
113 // todo: cleanup pump
96 return; // error 114 return; // error
97 } 115 }
98 116
99 // get playbin's bus 117 if (NULL == getenv("LL_GSTREAMER_EXTERNAL"))
100 GstBus *bus = llgst_pipeline_get_bus (GST_PIPELINE (mPlaybin));
101 if (!bus)
102 { 118 {
103 return; // error
104 }
105 mBusWatchID = llgst_bus_add_watch (bus,
106 llmediaimplgstreamer_bus_callback,
107 this);
108 llgst_object_unref (bus);
109
110 if (NULL == getenv("LL_GSTREAMER_EXTERNAL")) {
111 // instantiate and connect a custom video sink 119 // instantiate and connect a custom video sink
120 LL_DEBUGS("MediaManager") << "extrenal video sink..." << LL_ENDL;
121
122 // Plays inworld instead of in external player
112 mVideoSink = 123 mVideoSink =
113 GST_SLVIDEO(llgst_element_factory_make ("private-slvideo", "slvideo")); 124 GST_SLVIDEO(gst_element_factory_make ("private-slvideo", "slvideo"));
114 if (!mVideoSink) 125 if (!mVideoSink)
115 { 126 {
116 WARNMSG("Could not instantiate private-slvideo element."); 127 LL_WARNS("MediaImpl") << "Could not instantiate private-slvideo element." << LL_ENDL;
117 // todo: cleanup. 128 // todo: cleanup.
118 return; // error 129 return; // error
119 } 130 }
@@ -145,7 +156,7 @@ int LLMediaImplGStreamer::getTextureFormatInternal() const
145LLMediaImplGStreamer:: 156LLMediaImplGStreamer::
146~LLMediaImplGStreamer () 157~LLMediaImplGStreamer ()
147{ 158{
148 DEBUGMSG("dtor of media..."); 159 LL_DEBUGS("MediaImpl") << ("dtor of media...") << LL_ENDL;
149 unload(); 160 unload();
150} 161}
151 162
@@ -153,116 +164,230 @@ LLMediaImplGStreamer::
153// virtual 164// virtual
154std::string LLMediaImplGStreamer::getVersion() 165std::string LLMediaImplGStreamer::getVersion()
155{ 166{
156 std::string rtn; 167 guint major, minor, micro, nano;
157 rtn = "[" + sLLMediaImplGStreamerReg.getImplName() + "] - GStreamer 0.10.x"; 168 gst_version(&major, &minor, &micro, &nano);
158 return rtn; 169 std::string version = llformat("%d.%d.%d.%d",major,minor,micro,nano);
170 return version;
159} 171}
160 172
173//
174// STARTUP
161/////////////////////////////////////////////////////////////////////////////// 175///////////////////////////////////////////////////////////////////////////////
162// (static) super-initialization - called once at application startup 176// (static) super-initialization - called once at application startup
163 177bool LLMediaImplGStreamer::startup (LLMediaManagerData* init_data)
164//static
165bool LLMediaImplGStreamer::mDoneInit = false;
166
167//static
168bool
169LLMediaImplGStreamer::
170startup ( LLMediaManagerData* init_data )
171{ 178{
172 // first - check if GStreamer is explicitly disabled 179 static bool done_init = false;
173 if (NULL != getenv("LL_DISABLE_GSTREAMER")) 180 if (!done_init)
174 return false;
175
176 // only do global GStreamer initialization once.
177 if (!mDoneInit)
178 { 181 {
179 // Init the glib type system - we need it. 182 // Init the glib type system - we need it.
180 g_type_init(); 183 g_type_init();
181 184
182 // Get symbols! 185 set_gst_plugin_path();
183 if (! grab_gst_syms("libgstreamer-0.10.so.0", 186
184 "libgstvideo-0.10.so.0") ) 187 // Protect against GStreamer resetting the locale, yuck.
188 static std::string saved_locale;
189 saved_locale = setlocale(LC_ALL, NULL);
190 if (0 == gst_init_check(NULL, NULL, NULL))
185 { 191 {
186 WARNMSG("Couldn't find suitable GStreamer 0.10 support on this system - video playback disabled."); 192 LL_WARNS("MediaImpl") << "GStreamer library failed to initialize and load standard plugins." << LL_ENDL;
193 setlocale(LC_ALL, saved_locale.c_str() );
187 return false; 194 return false;
188 } 195 }
196 setlocale(LC_ALL, saved_locale.c_str() );
189 197
190 if (llgst_segtrap_set_enabled) 198 // Set up logging facilities
191 { 199 gst_debug_remove_log_function( gst_debug_log_default );
192 llgst_segtrap_set_enabled(FALSE); 200 gst_debug_add_log_function( gstreamer_log, NULL );
193 }
194 else
195 {
196 WARNMSG("gst_segtrap_set_enabled() is not available; Automated crash-reporter may cease to function until next restart.");
197 }
198 201
199#if LL_LINUX 202 // Init our custom plugins - only really need do this once.
200 // Gstreamer tries a fork during init, waitpid-ing on it, 203 gst_slvideo_init_class();
201 // which conflicts with any installed SIGCHLD handler... 204
202 struct sigaction tmpact, oldact; 205
203 if (llgst_registry_fork_set_enabled) { 206 // List the plugins GStreamer can find
204 // if we can disable SIGCHLD-using forking behaviour, 207 LL_DEBUGS("MediaImpl") << "Found GStreamer plugins:" << LL_ENDL;
205 // do it. 208 GList *list;
206 llgst_registry_fork_set_enabled(false); 209 GstRegistry *registry = gst_registry_get_default();
207 } 210 std::string loaded = "";
208 else { 211 for (list = gst_registry_get_plugin_list(registry);
209 // else temporarily install default SIGCHLD handler 212 list != NULL;
210 // while GStreamer initialises 213 list = g_list_next(list))
211 tmpact.sa_handler = SIG_DFL; 214 {
212 sigemptyset( &tmpact.sa_mask ); 215 GstPlugin *list_plugin = (GstPlugin *)list->data;
213 tmpact.sa_flags = SA_SIGINFO; 216 (bool)gst_plugin_is_loaded(list_plugin) ? loaded = "Yes" : loaded = "No";
214 sigaction(SIGCHLD, &tmpact, &oldact); 217 LL_DEBUGS("MediaImpl") << gst_plugin_get_name(list_plugin) << ", loaded? " << loaded << LL_ENDL;
215 } 218 }
216#endif // LL_LINUX 219 gst_plugin_list_free(list);
217 220
218 // Protect against GStreamer resetting the locale, yuck.
219 static std::string saved_locale;
220 saved_locale = setlocale(LC_ALL, NULL);
221 221
222 // finally, try to initialize GStreamer! 222 done_init = true;
223 GError *err = NULL; 223 }
224 gboolean init_gst_success = llgst_init_check(NULL, NULL, &err); 224 return true;
225}
226
225 227
226 // restore old locale 228void LLMediaImplGStreamer::set_gst_plugin_path()
227 setlocale(LC_ALL, saved_locale.c_str() ); 229{
230 // Linux sets GST_PLUGIN_PATH in wrapper.sh, not here.
231#if LL_WINDOWS || LL_DARWIN
228 232
229#if LL_LINUX 233 std::string imp_dir = "";
230 // restore old SIGCHLD handler
231 if (!llgst_registry_fork_set_enabled)
232 sigaction(SIGCHLD, &oldact, NULL);
233#endif // LL_LINUX
234 234
235 if (!init_gst_success) // fail 235 // Get the current working directory:
236#if LL_WINDOWS
237 char* raw_dir;
238 raw_dir = _getcwd(NULL,0);
239 if( raw_dir != NULL )
240 {
241 imp_dir = std::string( raw_dir );
242 }
243#elif LL_DARWIN
244 CFBundleRef main_bundle = CFBundleGetMainBundle();
245 if( main_bundle != NULL )
246 {
247 CFURLRef bundle_url = CFBundleCopyBundleURL( main_bundle );
248 if( bundle_url != NULL )
236 { 249 {
237 if (err) 250 #ifndef MAXPATHLEN
238 { 251 #define MAXPATHLEN 1024
239 WARNMSG("GST init failed: %s", err->message); 252 #endif
240 g_error_free(err); 253 char raw_dir[MAXPATHLEN];
241 } 254 if( CFURLGetFileSystemRepresentation( bundle_url, true, (UInt8 *)raw_dir, MAXPATHLEN) )
242 else
243 { 255 {
244 WARNMSG("GST init failed for unspecified reason."); 256 imp_dir = std::string( raw_dir ) + "/Contents/MacOS/";
245 } 257 }
246 return false; 258 CFRelease(bundle_url);
247 } 259 }
260 }
261#endif
262
263 if( imp_dir == "" )
264 {
265 LL_WARNS("MediaImpl") << "Could not get application directory, not setting GST_PLUGIN_PATH."
266 << LL_ENDL;
267 return;
268 }
269
270 LL_DEBUGS("MediaImpl") << "Imprudence is installed at "
271 << imp_dir << LL_ENDL;
272
273 // ":" on Mac and 'Nix, ";" on Windows
274 std::string separator = G_SEARCHPATH_SEPARATOR_S;
275
276 // Grab the current path, if it's set.
277 std::string old_plugin_path = "";
278 char *old_path = getenv("GST_PLUGIN_PATH");
279 if(old_path == NULL)
280 {
281 LL_DEBUGS("MediaImpl") << "Did not find user-set GST_PLUGIN_PATH."
282 << LL_ENDL;
283 }
284 else
285 {
286 old_plugin_path = separator + std::string( old_path );
287 }
288
289
290 // Search both Imprudence and Imprudence\lib\gstreamer-plugins.
291 // But we also want to search the path the user has set, if any.
292 std::string plugin_path =
293 "GST_PLUGIN_PATH=" +
294#if LL_WINDOWS
295 imp_dir + "\\lib\\gstreamer-plugins" +
296#elif LL_DARWIN
297 imp_dir + separator +
298 imp_dir + "/../Resources/lib/gstreamer-plugins" +
299#endif
300 old_plugin_path;
301
302 int put_result;
303
304 // Place GST_PLUGIN_PATH in the environment settings
305#if LL_WINDOWS
306 put_result = _putenv( (char*)plugin_path.c_str() );
307#elif LL_DARWIN
308 put_result = putenv( (char*)plugin_path.c_str() );
309#endif
310
311 if( put_result == -1 )
312 {
313 LL_WARNS("MediaImpl") << "Setting GST_PLUGIN_PATH failed!" << LL_ENDL;
314 }
315 else
316 {
317 LL_DEBUGS("MediaImpl") << "GST_PLUGIN_PATH set to "
318 << getenv("GST_PLUGIN_PATH") << LL_ENDL;
319 }
248 320
249 // Init our custom plugins - only really need do this once. 321 // Don't load system plugins. We only want to use ours, to avoid conflicts.
250 gst_slvideo_init_class(); 322#if LL_WINDOWS
323 put_result = _putenv( "GST_PLUGIN_SYSTEM_PATH=\"\"" );
324#elif LL_DARWIN
325 put_result = putenv( "GST_PLUGIN_SYSTEM_PATH=\"\"" );
326#endif
327
328 if( put_result == -1 )
329 {
330 LL_WARNS("MediaImpl") << "Setting GST_PLUGIN_SYSTEM_PATH=\"\" failed!"
331 << LL_ENDL;
332 }
333
334#endif // LL_WINDOWS || LL_DARWIN
335}
336
337
338void LLMediaImplGStreamer::gstreamer_log(GstDebugCategory *category,
339 GstDebugLevel level,
340 const gchar *file,
341 const gchar *function,
342 gint line,
343 GObject *object,
344 GstDebugMessage *message,
345 gpointer data)
346{
347 std::stringstream log(std::stringstream::out);
348
349 // Log format example:
350 //
351 // GST_ELEMENT_PADS: removing pad 'sink' (in gstelement.c:757:gst_element_remove_pad)
352 //
353 log << gst_debug_category_get_name( category ) << ": "
354 << gst_debug_message_get(message) << " "
355 << "(in " << file << ":" << line << ":" << function << ")";
251 356
252 mDoneInit = true; 357 switch( level )
358 {
359 case GST_LEVEL_ERROR:
360 LL_WARNS("MediaImpl") << "(ERROR) " << log.str() << LL_ENDL;
361 break;
362 case GST_LEVEL_WARNING:
363 LL_WARNS("MediaImpl") << log.str() << LL_ENDL;
364 break;
365 case GST_LEVEL_DEBUG:
366 LL_DEBUGS("MediaImpl") << log.str() << LL_ENDL;
367 break;
368 case GST_LEVEL_INFO:
369 LL_INFOS("MediaImpl") << log.str() << LL_ENDL;
370 break;
371 default:
372 // Do nothing.
373 break;
253 } 374 }
375}
376
254 377
378bool LLMediaImplGStreamer::closedown()
379{
255 return true; 380 return true;
256} 381}
257 382
258 383
259bool LLMediaImplGStreamer:: 384bool LLMediaImplGStreamer::setDebugLevel( LLMediaBase::EDebugLevel level )
260closedown()
261{ 385{
262 if (!mDoneInit) 386 // Do parent class stuff.
263 return false; // error 387 LLMediaImplCommon::setDebugLevel(level);
264 388
265 ungrab_gst_syms(); 389 // Set GStreamer verbosity.
390 gst_debug_set_default_threshold( (GstDebugLevel)level );
266 391
267 return true; 392 return true;
268} 393}
@@ -270,230 +395,215 @@ closedown()
270 395
271/////////////////////////////////////////////////////////////////////////////// 396///////////////////////////////////////////////////////////////////////////////
272// 397//
398// Uncomment the line below to enable spammy debug data.
273//#define LL_GST_REPORT_STATE_CHANGES 399//#define LL_GST_REPORT_STATE_CHANGES
274#ifdef LL_GST_REPORT_STATE_CHANGES 400#ifdef LL_GST_REPORT_STATE_CHANGES
275static char* get_gst_state_name(GstState state) 401static const char* get_gst_state_name(GstState state)
276{ 402{
277 switch (state) { 403 switch (state)
278 case GST_STATE_VOID_PENDING: return "VOID_PENDING"; 404 {
279 case GST_STATE_NULL: return "NULL"; 405 case GST_STATE_VOID_PENDING: return "VOID_PENDING";
280 case GST_STATE_READY: return "READY"; 406 case GST_STATE_NULL: return "NULL";
281 case GST_STATE_PAUSED: return "PAUSED"; 407 case GST_STATE_READY: return "READY";
282 case GST_STATE_PLAYING: return "PLAYING"; 408 case GST_STATE_PAUSED: return "PAUSED";
409 case GST_STATE_PLAYING: return "PLAYING";
283 } 410 }
284 return "(unknown)"; 411 return "(unknown)";
285} 412}
286#endif // LL_GST_REPORT_STATE_CHANGES 413#endif // LL_GST_REPORT_STATE_CHANGES
287 414
288extern "C" { 415//static
289gboolean 416gboolean LLMediaImplGStreamer::bus_callback(GstBus *bus, GstMessage *message, gpointer data)
290llmediaimplgstreamer_bus_callback (GstBus *bus,
291 GstMessage *message,
292 gpointer data)
293{ 417{
294 if (!message) 418#ifdef LL_GST_REPORT_STATE_CHANGES
295 return TRUE; // shield against GStreamer bug 419 LL_DEBUGS("MediaCallback") << "Got GST message type: " << GST_MESSAGE_TYPE_NAME (message) << LL_ENDL;
296 420#endif
297 if (GST_MESSAGE_TYPE(message) != GST_MESSAGE_STATE_CHANGED &&
298 GST_MESSAGE_TYPE(message) != GST_MESSAGE_BUFFERING)
299 {
300 DEBUGMSG("Got GST message type: %s",
301 LLGST_MESSAGE_TYPE_NAME (message));
302 }
303 else
304 {
305 DEBUGMSG("Got GST message type: %s",
306 LLGST_MESSAGE_TYPE_NAME (message));
307 }
308 421
309 LLMediaImplGStreamer *impl = (LLMediaImplGStreamer*)data; 422 LLMediaImplGStreamer *impl = (LLMediaImplGStreamer*)data;
310 423
311 switch (GST_MESSAGE_TYPE (message)) { 424 switch (GST_MESSAGE_TYPE (message))
312 case GST_MESSAGE_BUFFERING: { 425 {
313 // NEEDS GST 0.10.11+ 426 case GST_MESSAGE_BUFFERING:
314 if (llgst_message_parse_buffering)
315 { 427 {
316 gint percent = 0; 428 gint percent = 0;
317 llgst_message_parse_buffering(message, &percent); 429 gst_message_parse_buffering(message, &percent);
318 DEBUGMSG("GST buffering: %d%%", percent); 430#ifdef LL_GST_REPORT_STATE_CHANGES
431 LL_DEBUGS("MediaBuffering") << "GST buffering: " << percent << "%%" << LL_ENDL;
432#endif
319 LLMediaEvent event( impl, percent ); 433 LLMediaEvent event( impl, percent );
320 impl->getEventEmitter().update( &LLMediaObserver::onUpdateProgress, event ); 434 impl->getEventEmitter().update( &LLMediaObserver::onUpdateProgress, event );
321
322 } 435 }
323 break; 436 break;
324 } 437 case GST_MESSAGE_STATE_CHANGED:
325 case GST_MESSAGE_STATE_CHANGED: { 438 {
326 GstState old_state; 439 GstState old_state;
327 GstState new_state; 440 GstState new_state;
328 GstState pending_state; 441 GstState pending_state;
329 llgst_message_parse_state_changed(message, 442 gst_message_parse_state_changed(message,
330 &old_state, 443 &old_state,
331 &new_state, 444 &new_state,
332 &pending_state); 445 &pending_state);
333#ifdef LL_GST_REPORT_STATE_CHANGES 446#ifdef LL_GST_REPORT_STATE_CHANGES
334 // not generally very useful, and rather spammy. 447 // not generally very useful, and rather spammy.
335 DEBUGMSG("state change (old,<new>,pending): %s,<%s>,%s", 448 LL_DEBUGS("MediaState") << "GST state change (old,<new>,pending): "<< get_gst_state_name(old_state) << ",<" << get_gst_state_name(new_state) << ">," << get_gst_state_name(pending_state) << LL_ENDL;
336 get_gst_state_name(old_state),
337 get_gst_state_name(new_state),
338 get_gst_state_name(pending_state));
339#endif // LL_GST_REPORT_STATE_CHANGES 449#endif // LL_GST_REPORT_STATE_CHANGES
340 450
341 switch (new_state) { 451 switch (new_state)
342 case GST_STATE_VOID_PENDING: 452 {
343 break; 453 case GST_STATE_VOID_PENDING:
344 case GST_STATE_NULL: 454 break;
345 break; 455 case GST_STATE_NULL:
346 case GST_STATE_READY: 456#ifdef LL_GST_REPORT_STATE_CHANGES
347 break; 457 LL_DEBUGS("MediaImpl") << "State changed to NULL" << LL_ENDL;
348 case GST_STATE_PAUSED: 458#endif
349 break; 459 if (impl->getState() == GST_STATE_PLAYING)
350 case GST_STATE_PLAYING: 460 {
351 LLMediaEvent event( impl, 100 ); 461 // Stream was probably dropped, trying to restart
352 impl->getEventEmitter().update( &LLMediaObserver::onUpdateProgress, event ); 462 impl->play();
353 // emit an event to say that a media source was loaded 463#ifdef LL_GST_REPORT_STATE_CHANGES
354 LLMediaEvent event2( impl ); 464 LL_DEBUGS("MediaImpl") << "Trying to restart." << LL_ENDL;
355 impl->getEventEmitter().update( &LLMediaObserver::onMediaLoaded, event2 ); 465#endif
466 }
467 break;
468 case GST_STATE_READY:
469 break;
470 case GST_STATE_PAUSED:
471 break;
472 case GST_STATE_PLAYING:
473 LLMediaEvent event( impl, 100 );
474 impl->getEventEmitter().update( &LLMediaObserver::onUpdateProgress, event );
475 // emit an event to say that a media source was loaded
476 LLMediaEvent event2( impl );
477 impl->getEventEmitter().update( &LLMediaObserver::onMediaLoaded, event2 );
478 break;
479 }
356 break; 480 break;
357 } 481 }
358 break; 482 case GST_MESSAGE_ERROR:
359 } 483 {
360 case GST_MESSAGE_ERROR: { 484 GError *err = NULL;
361 GError *err = NULL; 485 gchar *debug = NULL;
362 gchar *debug = NULL;
363 486
364 llgst_message_parse_error (message, &err, &debug); 487 gst_message_parse_error (message, &err, &debug);
365 WARNMSG("GST error: %s", err->message); 488 LL_WARNS("MediaImpl") << "GST Error: " << err->message << LL_ENDL;
366 g_error_free (err); 489 g_error_free (err);
367 g_free (debug); 490 g_free (debug);
368 491
369 impl->addCommand(LLMediaBase::COMMAND_STOP); 492 impl->addCommand(LLMediaBase::COMMAND_STOP);
493 //impl->addCommand(LLMediaBase::COMMAND_START);
370 494
371 break; 495 break;
372 } 496 }
373 case GST_MESSAGE_INFO: { 497 case GST_MESSAGE_INFO:
374 if (llgst_message_parse_info)
375 { 498 {
376 GError *err = NULL; 499 GError *err = NULL;
377 gchar *debug = NULL; 500 gchar *debug = NULL;
378 501
379 llgst_message_parse_info (message, &err, &debug); 502 gst_message_parse_info (message, &err, &debug);
380 INFOMSG("GST info: %s", err->message); 503 LL_INFOS("MediaImpl") << "GST info: " << err->message
504 << LL_ENDL;
381 g_error_free (err); 505 g_error_free (err);
382 g_free (debug); 506 g_free (debug);
507 break;
383 } 508 }
384 break; 509 case GST_MESSAGE_WARNING:
385 } 510 {
386 case GST_MESSAGE_WARNING: { 511 GError *err = NULL;
387 GError *err = NULL; 512 gchar *debug = NULL;
388 gchar *debug = NULL;
389 513
390 llgst_message_parse_warning (message, &err, &debug); 514 gst_message_parse_warning (message, &err, &debug);
391 WARNMSG("GST warning: %s", err->message); 515 LL_WARNS("MediaImpl") << "GST warning: " << err->message
392 g_error_free (err); 516 << LL_ENDL;
393 g_free (debug); 517 g_error_free (err);
518 g_free (debug);
394 519
395 break; 520 break;
396 } 521 }
397 case GST_MESSAGE_EOS: 522 case GST_MESSAGE_TAG:
398 /* end-of-stream */
399 DEBUGMSG("GST end-of-stream.");
400 if (impl->isLooping())
401 { 523 {
402 DEBUGMSG("looping media..."); 524 GstTagList *tag_list;
403 double eos_pos_sec = 0.0F; 525 gchar *title;
404 bool got_eos_position = impl->getTimePos(eos_pos_sec); 526 gchar *artist;
405 527 gst_message_parse_tag(message, &tag_list);
406 if (got_eos_position && eos_pos_sec < impl->MIN_LOOP_SEC) 528 gboolean hazTitle = gst_tag_list_get_string(tag_list,
529 GST_TAG_TITLE, &title);
530 gboolean hazArtist = gst_tag_list_get_string(tag_list,
531 GST_TAG_ARTIST, &artist);
532 if(hazTitle)
533 LL_INFOS("MediaInfo") << "Title: " << title << LL_ENDL;
534 if(hazArtist)
535 LL_INFOS("MediaInfo") << "Artist: " << artist << LL_ENDL;
536 break;
537 }
538 case GST_MESSAGE_EOS:
539 {
540 /* end-of-stream */
541 LL_DEBUGS("MediaImpl") << "GST end-of-stream." << LL_ENDL;
542 if (impl->isLooping())
407 { 543 {
408 // if we know that the movie is really short, don't 544 LL_DEBUGS("MediaImpl") << "looping media..." << LL_ENDL;
409 // loop it else it can easily become a time-hog 545 impl->stop();
410 // because of GStreamer spin-up overhead 546 impl->play();
411 DEBUGMSG("really short movie (%0.3fsec) - not gonna loop this, pausing instead.", eos_pos_sec);
412 // inject a COMMAND_PAUSE
413 impl->addCommand(LLMediaBase::COMMAND_PAUSE);
414 } 547 }
415 else 548 else
416 { 549 {
417 // first try looping by an explicit rewind 550 // inject a COMMAND_STOP
418 bool seeksuccess = impl->seek(0.0); 551 impl->addCommand(LLMediaBase::COMMAND_STOP);
419 if (seeksuccess)
420 {
421 impl->play();
422 }
423 else // use clumsy stop-start to loop
424 {
425 DEBUGMSG("couldn't loop by rewinding - stopping and starting instead...");
426 impl->stop();
427 impl->play();
428 }
429 } 552 }
553 break;
554 default:
555 /* unhandled message */
556 break;
430 } 557 }
431 else // not a looping media
432 {
433 // inject a COMMAND_STOP
434 impl->addCommand(LLMediaBase::COMMAND_STOP);
435 }
436 break;
437 default:
438 /* unhandled message */
439 break;
440 } 558 }
441
442 /* we want to be notified again the next time there is a message 559 /* we want to be notified again the next time there is a message
443 * on the bus, so return true (false means we want to stop watching 560 * on the bus, so return true (false means we want to stop watching
444 * for messages on the bus and our callback should not be called again) 561 * for messages on the bus and our callback should not be called again)
445 */ 562 */
446 return TRUE; 563 return TRUE;
447} 564}
448} // extern "C"
449 565
450/////////////////////////////////////////////////////////// 566///////////////////////////////////////////////////////////
451// virtual 567// virtual
452bool 568bool LLMediaImplGStreamer::navigateTo (const std::string urlIn)
453LLMediaImplGStreamer::
454navigateTo ( const std::string urlIn )
455{ 569{
456 if (!mDoneInit) 570 LL_DEBUGS("MediaImpl") << "Setting media URI: " << urlIn.c_str()
457 return false; // error 571 << LL_ENDL;
458
459 DEBUGMSG("Setting media URI: %s", urlIn.c_str());
460 572
461 if (NULL == mPump || 573 if (mPump == NULL || mPlaybin == NULL)
462 NULL == mPlaybin)
463 { 574 {
464 return false; // error 575 return false;
465 } 576 }
466 577
467 setStatus( LLMediaBase::STATUS_NAVIGATING ); 578 setStatus( LLMediaBase::STATUS_NAVIGATING );
468 579
469 // set URI 580 // set URI
470 g_object_set (G_OBJECT (mPlaybin), "uri", urlIn.c_str(), NULL); 581 g_object_set (G_OBJECT (mPlaybin), "uri", urlIn.c_str(), NULL);
471 //g_object_set (G_OBJECT (mPlaybin), "uri", "file:///tmp/movie", NULL);
472 582
473 // navigateTo implicitly plays, too. 583 // get playbin's bus - perhaps this can/should be done in ctor
474 play(); 584 GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (mPlaybin));
585 if (!bus)
586 {
587 return false;
588 }
589 gst_bus_add_watch (bus, bus_callback, this);
590 gst_object_unref (bus);
591
592 mState = GST_STATE_READY;
475 593
476 return true; 594 return true;
477} 595}
478 596
479/////////////////////////////////////////////////////////////////////////////// 597///////////////////////////////////////////////////////////////////////////////
480// 598//
481bool 599bool LLMediaImplGStreamer::unload()
482LLMediaImplGStreamer::
483unload ()
484{ 600{
485 if (!mDoneInit) 601 LL_DEBUGS("MediaImpl") << "unloading media..." << LL_ENDL;
486 return false; // error
487
488 DEBUGMSG("unloading media...");
489
490 // stop getting callbacks for this bus
491 g_source_remove(mBusWatchID);
492
493 if (mPlaybin) 602 if (mPlaybin)
494 { 603 {
495 llgst_element_set_state (mPlaybin, GST_STATE_NULL); 604 gst_element_set_state (mPlaybin, GST_STATE_NULL);
496 llgst_object_unref (GST_OBJECT (mPlaybin)); 605 mState = GST_STATE_NULL;
606 gst_object_unref (GST_OBJECT (mPlaybin));
497 mPlaybin = NULL; 607 mPlaybin = NULL;
498 } 608 }
499 609
@@ -510,63 +620,63 @@ unload ()
510 } 620 }
511 621
512 mVideoSink = NULL; 622 mVideoSink = NULL;
623 mState = GST_STATE_NULL;
624 setStatus(LLMediaBase::STATUS_UNKNOWN);
513 625
514 return true; 626 return true;
515} 627}
516 628
517/////////////////////////////////////////////////////////////////////////////// 629///////////////////////////////////////////////////////////////////////////////
518// virtual 630// virtual
519bool 631bool LLMediaImplGStreamer::updateMedia()
520LLMediaImplGStreamer::
521updateMedia ()
522{ 632{
523 if (!mDoneInit) 633 //LL_DEBUGS("MediaImpl") << "updating media..." << LL_ENDL;
524 return false; // error
525
526 DEBUGMSG("updating media...");
527 634
528 // sanity check 635 // sanity check
529 if (NULL == mPump || 636 if (mPump == NULL || mPlaybin == NULL)
530 NULL == mPlaybin)
531 { 637 {
532 DEBUGMSG("dead media..."); 638#ifdef LL_GST_REPORT_STATE_CHANGES
639 LL_DEBUGS("MediaImpl") << "dead media..." << LL_ENDL;
640#endif
641 mState = GST_STATE_NULL;
642 setStatus(LLMediaBase::STATUS_DEAD);
533 return false; 643 return false;
534 } 644 }
535 645
646 if (mState == GST_STATE_VOID_PENDING || mState == GST_STATE_NULL)
647 return false;
648
536 // process next outstanding command 649 // process next outstanding command
537 switch (nextCommand()) 650 switch (nextCommand())
538 { 651 {
539 case LLMediaBase::COMMAND_START: 652 case LLMediaBase::COMMAND_START:
540 DEBUGMSG("COMMAND_START"); 653 LL_DEBUGS("MediaImpl") << "COMMAND_START" << LL_ENDL;
541 if (getStatus() == LLMediaBase::STATUS_PAUSED || 654 if (getStatus() == LLMediaBase::STATUS_PAUSED ||
542 getStatus() == LLMediaBase::STATUS_NAVIGATING || 655 getStatus() == LLMediaBase::STATUS_NAVIGATING ||
543 getStatus() == LLMediaBase::STATUS_STOPPED) 656 getStatus() == LLMediaBase::STATUS_STOPPED)
544 { 657 {
545 DEBUGMSG("doing COMMAND_START");
546 play(); 658 play();
547 setStatus(LLMediaBase::STATUS_STARTED); 659 setStatus(LLMediaBase::STATUS_STARTED);
548 clearCommand(); 660 clearCommand();
549 } 661 }
550 break; 662 break;
551 case LLMediaBase::COMMAND_STOP: 663 case LLMediaBase::COMMAND_STOP:
552 DEBUGMSG("COMMAND_STOP"); 664 LL_DEBUGS("MediaImpl") << "COMMAND_STOP" << LL_ENDL;
553 DEBUGMSG("doing COMMAND_STOP");
554 stop(); 665 stop();
555 setStatus(LLMediaBase::STATUS_STOPPED); 666 setStatus(LLMediaBase::STATUS_STOPPED);
556 clearCommand(); 667 clearCommand();
557 break; 668 break;
558 case LLMediaBase::COMMAND_PAUSE: 669 case LLMediaBase::COMMAND_PAUSE:
559 DEBUGMSG("COMMAND_PAUSE"); 670 LL_DEBUGS("MediaImpl") << "COMMAND_PAUSE" << LL_ENDL;
560 if (getStatus() == LLMediaBase::STATUS_STARTED) 671 if (getStatus() == LLMediaBase::STATUS_STARTED)
561 { 672 {
562 DEBUGMSG("doing COMMAND_PAUSE");
563 pause(); 673 pause();
564 setStatus(LLMediaBase::STATUS_PAUSED); 674 setStatus(LLMediaBase::STATUS_PAUSED);
565 clearCommand(); 675 clearCommand();
566 } 676 }
567 break; 677 break;
568 default: 678 default:
569 DEBUGMSG("COMMAND_?"); 679 LL_INFOS("MediaImpl") << "Unknown command" << LL_ENDL;
570 clearCommand(); 680 clearCommand();
571 break; 681 break;
572 case LLMediaBase::COMMAND_NONE: 682 case LLMediaBase::COMMAND_NONE:
@@ -584,12 +694,14 @@ updateMedia ()
584 GST_OBJECT_LOCK(mVideoSink); 694 GST_OBJECT_LOCK(mVideoSink);
585 if (mVideoSink->retained_frame_ready) 695 if (mVideoSink->retained_frame_ready)
586 { 696 {
587 DEBUGMSG("NEW FRAME "); 697#ifdef LL_GST_REPORT_STATE_CHANGES
698 LL_DEBUGS("MediaImpl") <<"NEW FRAME " << LL_ENDL;
699#endif
588 if (mVideoSink->retained_frame_width != getMediaWidth() || 700 if (mVideoSink->retained_frame_width != getMediaWidth() ||
589 mVideoSink->retained_frame_height != getMediaHeight()) 701 mVideoSink->retained_frame_height != getMediaHeight())
590 // *TODO: also check for change in format 702 // *TODO: also check for change in format
591 { 703 {
592 // just resize container 704 // just resize containe
593 int neww = mVideoSink->retained_frame_width; 705 int neww = mVideoSink->retained_frame_width;
594 int newh = mVideoSink->retained_frame_height; 706 int newh = mVideoSink->retained_frame_height;
595 int newd = SLVPixelFormatBytes[mVideoSink->retained_frame_format]; 707 int newd = SLVPixelFormatBytes[mVideoSink->retained_frame_format];
@@ -604,8 +716,9 @@ updateMedia ()
604 mTextureFormatType = LL_MEDIA_UNSIGNED_INT_8_8_8_8_REV; 716 mTextureFormatType = LL_MEDIA_UNSIGNED_INT_8_8_8_8_REV;
605 } 717 }
606 mMediaRowbytes = neww * newd; 718 mMediaRowbytes = neww * newd;
607 DEBUGMSG("video container resized to %dx%d", 719 LL_DEBUGS("MediaImpl")
608 neww, newh); 720 << "video container resized to " <<
721 neww <<"x"<< newh << LL_ENDL;
609 722
610 delete[] mediaData; 723 delete[] mediaData;
611 mediaData = new unsigned char[mMediaRowbytes * 724 mediaData = new unsigned char[mMediaRowbytes *
@@ -641,46 +754,119 @@ updateMedia ()
641 754
642/////////////////////////////////////////////////////////////////////////////// 755///////////////////////////////////////////////////////////////////////////////
643// 756//
644bool 757bool LLMediaImplGStreamer::stop()
645LLMediaImplGStreamer::
646stop ()
647{ 758{
648 DEBUGMSG("stopping media..."); 759 LL_DEBUGS("MediaImpl") << "attempting to stop..." << LL_ENDL;
649 // todo: error-check this? 760
650 llgst_element_set_state(mPlaybin, GST_STATE_READY); 761 if (!mPlaybin || mState == GST_STATE_NULL)
651 return true; 762 return true;
763
764 GstStateChangeReturn state_change;
765
766 state_change = gst_element_set_state(mPlaybin, GST_STATE_READY);
767
768 LL_DEBUGS("MediaImpl") << gst_element_state_change_return_get_name(state_change) << LL_ENDL;
769
770 if (state_change == GST_STATE_CHANGE_FAILURE)
771 {
772 LL_WARNS("MediaImpl") << "could not stop stream!" << LL_ENDL;
773 return false;
774 }
775 else
776 {
777 // Going into pending after play keeps dead streams from looping
778 (mState == GST_STATE_PLAYING) ? (mState = GST_STATE_VOID_PENDING) : (mState = GST_STATE_READY);
779 return true;
780 }
652} 781}
653 782
654/////////////////////////////////////////////////////////////////////////////// 783///////////////////////////////////////////////////////////////////////////////
655// 784//
656bool 785bool LLMediaImplGStreamer::play()
657LLMediaImplGStreamer::
658play ()
659{ 786{
660 DEBUGMSG("playing media..."); 787 LL_DEBUGS("MediaImpl") << "attempting to play..." << LL_ENDL;
661 // todo: error-check this? 788
662 llgst_element_set_state(mPlaybin, GST_STATE_PLAYING); 789 if (!mPlaybin || mState == GST_STATE_NULL)
790 return true;
791
792
793 if( getState() == GST_STATE_PLAYING )
794 {
795 LL_DEBUGS("MediaImpl") << "... but already playing." << LL_ENDL;
796 return true;
797 }
798
799 // Clean up the existing thread, if any.
800 if( mPlayThread != NULL && mPlayThread->isStopped())
801 {
802 delete mPlayThread;
803 mPlayThread = NULL;
804 }
805
806 if( mPlayThread == NULL )
807 {
808 // Make a new thread to start playing. This keeps the viewer
809 // responsive while the stream is resolved and buffered.
810 mPlayThread = new LLGstPlayThread( (LLMediaImplCommon *)this, "GstPlayThread", NULL);
811 mPlayThread->start();
812 }
813
663 return true; 814 return true;
664} 815}
665 816
817
818void LLMediaImplGStreamer::startPlay()
819{
820 GstStateChangeReturn state_change;
821
822 state_change = gst_element_set_state(mPlaybin, GST_STATE_PLAYING);
823 mState = GST_STATE_PLAYING;
824
825 LL_DEBUGS("MediaImpl") << gst_element_state_change_return_get_name(state_change) << LL_ENDL;
826
827 // Check to make sure playing was successful. If not, stop.
828 // NOTE: state_change is almost always GST_STATE_CHANGE_ASYNC
829 if (state_change == GST_STATE_CHANGE_FAILURE)
830 {
831 // If failing from a bad stream, go into an unknown
832 // state to stop bus_callback from looping back.
833 // We also force a stop in case the operations don't sync
834 setStatus(LLMediaBase::STATUS_UNKNOWN);
835 stop();
836 }
837}
838
666/////////////////////////////////////////////////////////////////////////////// 839///////////////////////////////////////////////////////////////////////////////
667// 840//
668bool 841bool LLMediaImplGStreamer::pause()
669LLMediaImplGStreamer::
670pause ()
671{ 842{
672 DEBUGMSG("pausing media..."); 843 LL_DEBUGS("MediaImpl") << "attempting to pause..." << LL_ENDL;
673 // todo: error-check this? 844
674 llgst_element_set_state(mPlaybin, GST_STATE_PAUSED); 845 if (!mPlaybin || mState == GST_STATE_NULL)
675 return true; 846 return true;
847
848 GstStateChangeReturn state_change;
849
850 state_change = gst_element_set_state(mPlaybin, GST_STATE_PAUSED);
851
852 LL_DEBUGS("MediaImpl") << gst_element_state_change_return_get_name(state_change) << LL_ENDL;
853
854 if (state_change == GST_STATE_CHANGE_FAILURE)
855 {
856 LL_WARNS("MediaImpl") << "could not pause stream!" << LL_ENDL;
857 return false;
858 }
859 else
860 {
861 mState = GST_STATE_PAUSED;
862 return true;
863 }
676}; 864};
677 865
678 866
679/////////////////////////////////////////////////////////////////////////////// 867///////////////////////////////////////////////////////////////////////////////
680// virtual 868// virtual
681unsigned char* 869unsigned char* LLMediaImplGStreamer::getMediaData()
682LLMediaImplGStreamer::
683getMediaData ()
684{ 870{
685 return mediaData; 871 return mediaData;
686} 872}
@@ -688,73 +874,26 @@ getMediaData ()
688 874
689/////////////////////////////////////////////////////////////////////////////// 875///////////////////////////////////////////////////////////////////////////////
690// virtual 876// virtual
691bool 877bool LLMediaImplGStreamer::seek(double time)
692LLMediaImplGStreamer::
693seek( double time )
694{ 878{
695 bool success = false; 879 bool success = false;
696 if (mDoneInit && mPlaybin) 880 if (mPlaybin)
697 { 881 {
698 success = llgst_element_seek(mPlaybin, 1.0F, GST_FORMAT_TIME, 882 success = gst_element_seek(mPlaybin, 1.0F, GST_FORMAT_TIME,
699 GstSeekFlags(GST_SEEK_FLAG_FLUSH | 883 GstSeekFlags(GST_SEEK_FLAG_FLUSH |
700 GST_SEEK_FLAG_KEY_UNIT), 884 GST_SEEK_FLAG_KEY_UNIT),
701 GST_SEEK_TYPE_SET, gint64(time*GST_SECOND), 885 GST_SEEK_TYPE_SET, gint64(time*1000000000.0F),
702 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE); 886 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
703 } 887 }
704 DEBUGMSG("MEDIA SEEK REQUEST to %fsec result was %d", 888 LL_DEBUGS("MediaImpl") << "MEDIA SEEK REQUEST to " << float(time)
705 float(time), int(success)); 889 << "sec result was " << int(success) << LL_ENDL;
706 return success; 890 return success;
707} 891}
708 892
709bool
710LLMediaImplGStreamer::
711getTimePos(double &sec_out)
712{
713 bool got_position = false;
714 if (mPlaybin)
715 {
716 gint64 pos;
717 GstFormat timefmt = GST_FORMAT_TIME;
718 got_position =
719 llgst_element_query_position &&
720 llgst_element_query_position(mPlaybin,
721 &timefmt,
722 &pos);
723 got_position = got_position
724 && (timefmt == GST_FORMAT_TIME);
725 // GStreamer may have other ideas, but we consider the current position
726 // undefined if not PLAYING or PAUSED
727 got_position = got_position &&
728 (GST_STATE(mPlaybin) == GST_STATE_PLAYING ||
729 GST_STATE(mPlaybin) == GST_STATE_PAUSED);
730 if (got_position && !GST_CLOCK_TIME_IS_VALID(pos))
731 {
732 if (GST_STATE(mPlaybin) == GST_STATE_PLAYING)
733 {
734 // if we're playing then we treat an invalid clock time
735 // as 0, for complicated reasons (insert reason here)
736 pos = 0;
737 }
738 else
739 {
740 got_position = false;
741 }
742
743 }
744 // If all the preconditions succeeded... we can trust the result.
745 if (got_position)
746 {
747 sec_out = double(pos) / double(GST_SECOND); // gst to sec
748 }
749 }
750 return got_position;
751}
752 893
753/////////////////////////////////////////////////////////////////////////////// 894///////////////////////////////////////////////////////////////////////////////
754// virtual 895// virtual
755bool 896bool LLMediaImplGStreamer::setVolume(float volume)
756LLMediaImplGStreamer::
757setVolume(float volume)
758{ 897{
759 // we try to only update volume as conservatively as 898 // we try to only update volume as conservatively as
760 // possible, as many gst-plugins-base versions up to at least 899 // possible, as many gst-plugins-base versions up to at least
@@ -763,7 +902,7 @@ setVolume(float volume)
763 return true; // nothing to do, everything's fine 902 return true; // nothing to do, everything's fine
764 903
765 mVolume = volume; 904 mVolume = volume;
766 if (mDoneInit && mPlaybin) 905 if (mPlaybin)
767 { 906 {
768 g_object_set(mPlaybin, "volume", mVolume, NULL); 907 g_object_set(mPlaybin, "volume", mVolume, NULL);
769 return true; 908 return true;
@@ -772,4 +911,6 @@ setVolume(float volume)
772 return false; 911 return false;
773} 912}
774 913
775#endif // LL_GSTREAMER_ENABLED 914
915
916///#endif // LL_GSTREAMER_ENABLED