diff options
Diffstat (limited to 'linden/indra/llmedia/llmediaimplgstreamer.cpp')
-rw-r--r-- | linden/indra/llmedia/llmediaimplgstreamer.cpp | 847 |
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 | ||
38 | extern "C" { | 45 | extern "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 |
50 | static LLMediaImplRegister sLLMediaImplGStreamerReg( "LLMediaImplGStreamer", new LLMediaImplGStreamerMaker() ); | 68 | static LLMediaImplRegister sLLMediaImplGStreamerReg( "LLMediaImplGStreamer", new LLMediaImplGStreamerMaker() ); |
@@ -64,21 +82,20 @@ LLMediaImplGStreamerMaker::LLMediaImplGStreamerMaker() | |||
64 | // | 82 | // |
65 | LLMediaImplGStreamer:: | 83 | LLMediaImplGStreamer:: |
66 | LLMediaImplGStreamer () : | 84 | LLMediaImplGStreamer () : |
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 | |||
145 | LLMediaImplGStreamer:: | 156 | LLMediaImplGStreamer:: |
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 |
154 | std::string LLMediaImplGStreamer::getVersion() | 165 | std::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, µ, &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 | 177 | bool LLMediaImplGStreamer::startup (LLMediaManagerData* init_data) | |
164 | //static | ||
165 | bool LLMediaImplGStreamer::mDoneInit = false; | ||
166 | |||
167 | //static | ||
168 | bool | ||
169 | LLMediaImplGStreamer:: | ||
170 | startup ( 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 | 228 | void 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 | |||
338 | void 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 | ||
378 | bool LLMediaImplGStreamer::closedown() | ||
379 | { | ||
255 | return true; | 380 | return true; |
256 | } | 381 | } |
257 | 382 | ||
258 | 383 | ||
259 | bool LLMediaImplGStreamer:: | 384 | bool LLMediaImplGStreamer::setDebugLevel( LLMediaBase::EDebugLevel level ) |
260 | closedown() | ||
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 |
275 | static char* get_gst_state_name(GstState state) | 401 | static 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 | ||
288 | extern "C" { | 415 | //static |
289 | gboolean | 416 | gboolean LLMediaImplGStreamer::bus_callback(GstBus *bus, GstMessage *message, gpointer data) |
290 | llmediaimplgstreamer_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 |
452 | bool | 568 | bool LLMediaImplGStreamer::navigateTo (const std::string urlIn) |
453 | LLMediaImplGStreamer:: | ||
454 | navigateTo ( 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 | // |
481 | bool | 599 | bool LLMediaImplGStreamer::unload() |
482 | LLMediaImplGStreamer:: | ||
483 | unload () | ||
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 |
519 | bool | 631 | bool LLMediaImplGStreamer::updateMedia() |
520 | LLMediaImplGStreamer:: | ||
521 | updateMedia () | ||
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 | // |
644 | bool | 757 | bool LLMediaImplGStreamer::stop() |
645 | LLMediaImplGStreamer:: | ||
646 | stop () | ||
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 | // |
656 | bool | 785 | bool LLMediaImplGStreamer::play() |
657 | LLMediaImplGStreamer:: | ||
658 | play () | ||
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 | |||
818 | void 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 | // |
668 | bool | 841 | bool LLMediaImplGStreamer::pause() |
669 | LLMediaImplGStreamer:: | ||
670 | pause () | ||
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 |
681 | unsigned char* | 869 | unsigned char* LLMediaImplGStreamer::getMediaData() |
682 | LLMediaImplGStreamer:: | ||
683 | getMediaData () | ||
684 | { | 870 | { |
685 | return mediaData; | 871 | return mediaData; |
686 | } | 872 | } |
@@ -688,73 +874,26 @@ getMediaData () | |||
688 | 874 | ||
689 | /////////////////////////////////////////////////////////////////////////////// | 875 | /////////////////////////////////////////////////////////////////////////////// |
690 | // virtual | 876 | // virtual |
691 | bool | 877 | bool LLMediaImplGStreamer::seek(double time) |
692 | LLMediaImplGStreamer:: | ||
693 | seek( 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 | ||
709 | bool | ||
710 | LLMediaImplGStreamer:: | ||
711 | getTimePos(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 |
755 | bool | 896 | bool LLMediaImplGStreamer::setVolume(float volume) |
756 | LLMediaImplGStreamer:: | ||
757 | setVolume(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 | ||