aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llmedia/llmediaimplgstreamer.cpp
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:45:04 -0500
committerJacek Antonelli2008-08-15 23:45:04 -0500
commit117e22047c5752352342d64e3fb7ce00a4eb8113 (patch)
treee32de2cfba0dda8705ae528fcd1fbe23ba075685 /linden/indra/llmedia/llmediaimplgstreamer.cpp
parentSecond Life viewer sources 1.18.0.6 (diff)
downloadmeta-impy-117e22047c5752352342d64e3fb7ce00a4eb8113.zip
meta-impy-117e22047c5752352342d64e3fb7ce00a4eb8113.tar.gz
meta-impy-117e22047c5752352342d64e3fb7ce00a4eb8113.tar.bz2
meta-impy-117e22047c5752352342d64e3fb7ce00a4eb8113.tar.xz
Second Life viewer sources 1.18.1.2
Diffstat (limited to 'linden/indra/llmedia/llmediaimplgstreamer.cpp')
-rw-r--r--linden/indra/llmedia/llmediaimplgstreamer.cpp715
1 files changed, 715 insertions, 0 deletions
diff --git a/linden/indra/llmedia/llmediaimplgstreamer.cpp b/linden/indra/llmedia/llmediaimplgstreamer.cpp
new file mode 100644
index 0000000..f8b4c74
--- /dev/null
+++ b/linden/indra/llmedia/llmediaimplgstreamer.cpp
@@ -0,0 +1,715 @@
1/**
2 * @file llmediaimplgstreamer.cpp
3 * @brief implementation that supports various media through GStreamer.
4 *
5 * Copyright (c) 2007-2007, Linden Research, Inc.
6 *
7 * Second Life Viewer Source Code
8 * The source code in this file ("Source Code") is provided by Linden Lab
9 * to you under the terms of the GNU General Public License, version 2.0
10 * ("GPL"), unless you have obtained a separate licensing agreement
11 * ("Other License"), formally executed by you and Linden Lab. Terms of
12 * the GPL can be found in doc/GPL-license.txt in this distribution, or
13 * online at http://secondlife.com/developers/opensource/gplv2
14 *
15 * There are special exceptions to the terms and conditions of the GPL as
16 * it is applied to this Source Code. View the full text of the exception
17 * in the file doc/FLOSS-exception.txt in this software distribution, or
18 * online at http://secondlife.com/developers/opensource/flossexception
19 *
20 * By copying, modifying or distributing this software, you acknowledge
21 * that you have read and understood your obligations described above,
22 * and agree to abide by those obligations.
23 *
24 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
25 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
26 * COMPLETENESS OR PERFORMANCE.
27 */
28
29#include "linden_common.h"
30
31#if LL_GSTREAMER_ENABLED
32
33extern "C" {
34#include <gst/gst.h>
35}
36
37#include "llmediaimplgstreamer.h"
38
39#include "llmediaimplgstreamervidplug.h"
40
41#ifdef LL_GST_SOUNDSINK
42#include "llmediaimplgstreamersndplug.h"
43#endif // LL_GST_SOUNDSINK
44
45#include "llmediaimplgstreamer_syms.h"
46
47#include "llgl.h"
48#include "llglheaders.h" // For gl texture modes
49
50///////////////////////////////////////////////////////////////////////////////
51//
52LLMediaImplGStreamer::
53LLMediaImplGStreamer () :
54 mediaData ( NULL ),
55 ownBuffer ( TRUE ),
56 mVolume ( 1.0f ),
57 currentMode ( ModeIdle ),
58 mPump ( NULL ),
59 mPlaybin ( NULL ),
60 mVideoSink ( NULL )
61#ifdef LL_GST_SOUNDSINK
62 ,mAudioSink ( NULL )
63#endif // LL_GST_SOUNDSINK
64{
65 mMediaDepthBytes = 4;
66 mTextureDepth = 4;
67 mTextureFormatInternal = GL_RGB8;
68 mTextureFormatPrimary = GL_BGRA;
69 mTextureFormatType = GL_UNSIGNED_INT_8_8_8_8_REV;
70}
71
72///////////////////////////////////////////////////////////////////////////////
73//
74LLMediaImplGStreamer::
75~LLMediaImplGStreamer ()
76{
77 unload();
78}
79
80void UnloadGStreamer()
81{
82 ungrab_gst_syms();
83}
84
85
86///////////////////////////////////////////////////////////////////////////////
87//
88BOOL
89LLMediaImplGStreamer::
90setBuffer ( U8* bufferIn )
91{
92 // Since we've pointed GStreamer at the old media data buffer
93 // directly, we need to be somewhat careful deleting it...
94 U8* oldMediaData = mediaData;
95 BOOL ownedMediaData = ownBuffer;
96
97 if(bufferIn == NULL)
98 {
99 // Passing NULL to this function requests that the object
100 // allocate its own buffer.
101 mediaData = new unsigned char[ mMediaHeight * mMediaRowbytes ];
102 ownBuffer = TRUE;
103 }
104 else
105 {
106 // Use the supplied buffer.
107 mediaData = bufferIn;
108 ownBuffer = FALSE;
109 }
110
111 if(mediaData == NULL)
112 {
113 // This is bad - probably out of memory.
114 llerrs << "LLMediaImplGStreamer::setBuffer: mediaData is NULL" << llendl;
115 // NOTE: This case doesn't clean up properly. This assert is fatal, so this isn't a huge problem,
116 // but if this assert is ever removed the code should be fixed to clean up correctly.
117 return FALSE;
118 }
119
120 // [..]
121
122 // Delete the old media data buffer iff we owned it.
123 if ( ownedMediaData )
124 {
125 if ( oldMediaData )
126 {
127 delete [] oldMediaData;
128 }
129 }
130
131 return TRUE;
132}
133
134///////////////////////////////////////////////////////////////////////////////
135//
136BOOL
137LLMediaImplGStreamer::
138init ()
139{
140 static bool done_init = false;
141 if (!done_init)
142 {
143 // Get symbols!
144 if (! grab_gst_syms("libgstreamer-0.10.so.0",
145 "libgstvideo-0.10.so.0",
146 "libgstaudio-0.10.so.0") )
147 {
148 llwarns << "Couldn't find suitable GStreamer 0.10 support on this system - video playback disabled." << llendl;
149 return FALSE;
150 }
151
152 if (llgst_segtrap_set_enabled)
153 llgst_segtrap_set_enabled(FALSE);
154 else
155 llwarns << "gst_segtrap_set_enabled() is not available; Second Life automated crash-reporter may cease to function until next restart." << llendl;
156
157 if (0 == llgst_init_check(NULL, NULL, NULL))
158 {
159 return FALSE;
160 }
161
162 // Init our custom plugins - only really need do this once.
163 gst_slvideo_init_class();
164#if 0
165 gst_slsound_init_class();
166#endif
167
168 done_init = true;
169 }
170
171 // Create a pumpable main-loop for this media
172 mPump = g_main_loop_new (NULL, FALSE);
173 if (!mPump)
174 {
175 return FALSE;
176 }
177
178 // instantiate a playbin element to do the hard work
179 mPlaybin = llgst_element_factory_make ("playbin", "play");
180 if (!mPlaybin)
181 {
182 // todo: cleanup pump
183 return FALSE;
184 }
185
186 if (NULL == getenv("LL_GSTREAMER_EXTERNAL")) {
187 // instantiate and connect a custom video sink
188 mVideoSink =
189 GST_SLVIDEO(llgst_element_factory_make ("private-slvideo", "slvideo"));
190 if (!mVideoSink)
191 {
192 llwarns << "Could not instantiate private-slvideo element."
193 << llendl;
194 // todo: cleanup.
195 return FALSE;
196 }
197
198 g_object_set(mPlaybin, "video-sink", mVideoSink, NULL);
199
200#ifdef LL_GST_SOUNDSINK
201 // instantiate and connect a custom audio sink
202 mAudioSink =
203 GST_SLSOUND(llgst_element_factory_make ("private-slsound", "slsound"));
204 if (!mAudioSink)
205 {
206 llwarns << "Could not instantiate private-slsound element."
207 << llendl;
208 // todo: cleanup.
209 return FALSE;
210 }
211
212 g_object_set(mPlaybin, "audio-sink", mAudioSink, NULL);
213#endif
214 }
215
216 return LLMediaMovieBase::init();
217}
218
219
220///////////////////////////////////////////////////////////////////////////////
221//
222//#define LL_GST_REPORT_STATE_CHANGES
223#ifdef LL_GST_REPORT_STATE_CHANGES
224static char* get_gst_state_name(GstState state)
225{
226 switch (state) {
227 case GST_STATE_VOID_PENDING: return "VOID_PENDING";
228 case GST_STATE_NULL: return "NULL";
229 case GST_STATE_READY: return "READY";
230 case GST_STATE_PAUSED: return "PAUSED";
231 case GST_STATE_PLAYING: return "PLAYING";
232 }
233 return "(unknown)";
234}
235#endif // LL_GST_REPORT_STATE_CHANGES
236
237static gboolean
238my_bus_callback (GstBus *bus,
239 GstMessage *message,
240 gpointer data)
241{
242 if (GST_MESSAGE_TYPE(message) != GST_MESSAGE_STATE_CHANGED &&
243 GST_MESSAGE_TYPE(message) != GST_MESSAGE_BUFFERING)
244 {
245 llinfos << "Got GST message type: "
246 << LLGST_MESSAGE_TYPE_NAME (message)
247 << llendl;
248 }
249 else
250 {
251 lldebugs << "Got GST message type: "
252 << LLGST_MESSAGE_TYPE_NAME (message)
253 << llendl;
254 }
255
256 LLMediaImplGStreamer *impl = (LLMediaImplGStreamer*)data;
257
258 switch (GST_MESSAGE_TYPE (message)) {
259 case GST_MESSAGE_BUFFERING: {
260 // NEEDS GST 0.10.11+
261 if (llgst_message_parse_buffering)
262 {
263 gint percent = 0;
264 llgst_message_parse_buffering(message, &percent);
265 llinfos << "GST buffering: " << percent
266 << "%" << llendl;
267 // ModeBuffering seems to do nothing except make
268 // the UI worse
269 /*if (percent < 100) impl->setCurrentMode(LLMediaImplGStreamer::ModeBuffering);*/
270 }
271 break;
272 }
273 case GST_MESSAGE_STATE_CHANGED: {
274 GstState old_state;
275 GstState new_state;
276 GstState pending_state;
277 llgst_message_parse_state_changed(message,
278 &old_state,
279 &new_state,
280 &pending_state);
281#ifdef LL_GST_REPORT_STATE_CHANGES
282 // not generally very useful, and rather spammy.
283 llinfos << "state change (old,<new>,pending): "
284 << get_gst_state_name(old_state) << ", <"
285 << get_gst_state_name(new_state) << ">, "
286 << get_gst_state_name(pending_state) <<
287 llendl;
288#endif // LL_GST_REPORT_STATE_CHANGES
289
290 switch (new_state) {
291 case GST_STATE_VOID_PENDING:
292 impl->setCurrentMode(LLMediaImplGStreamer::ModeNone);
293 break;
294 case GST_STATE_NULL:
295 impl->setCurrentMode(LLMediaImplGStreamer::ModeNone);
296 break;
297 case GST_STATE_READY:
298 impl->setCurrentMode(LLMediaImplGStreamer::ModeStopped);
299 break;
300 case GST_STATE_PAUSED:
301 impl->setCurrentMode(LLMediaImplGStreamer::ModePaused);
302 break;
303 case GST_STATE_PLAYING:
304 impl->setCurrentMode(LLMediaImplGStreamer::ModePlaying);
305 break;
306 }
307 break;
308 }
309 case GST_MESSAGE_ERROR: {
310 GError *err;
311 gchar *debug;
312
313 llgst_message_parse_error (message, &err, &debug);
314 llinfos << "GST error: " << err->message << llendl;
315 g_error_free (err);
316 g_free (debug);
317
318 impl->setCurrentMode(LLMediaImplGStreamer::ModeError);
319
320 impl->stop();
321
322 break;
323 }
324 case GST_MESSAGE_INFO: {
325 if (llgst_message_parse_info)
326 {
327 GError *err;
328 gchar *debug;
329
330 llgst_message_parse_info (message, &err, &debug);
331 llinfos << "GST info: " << err->message << llendl;
332 g_error_free (err);
333 g_free (debug);
334 }
335 break;
336 }
337 case GST_MESSAGE_WARNING: {
338 GError *err;
339 gchar *debug;
340
341 llgst_message_parse_warning (message, &err, &debug);
342 llinfos << "GST warning: " << err->message << llendl;
343 g_error_free (err);
344 g_free (debug);
345
346 break;
347 }
348 case GST_MESSAGE_EOS:
349 /* end-of-stream */
350 llinfos << "GST EOS." << llendl;
351 impl->setCurrentMode(LLMediaImplGStreamer::ModeStopped);//?
352 impl->stop();
353 break;
354 default:
355 /* unhandled message */
356 break;
357 }
358
359 /* we want to be notified again the next time there is a message
360 * on the bus, so returning TRUE (FALSE means we want to stop watching
361 * for messages on the bus and our callback should not be called again)
362 */
363 return TRUE;
364}
365
366BOOL
367LLMediaImplGStreamer::
368load ( const LLString& urlIn )
369{
370 llinfos << "Setting media URI: " << urlIn << llendl;
371
372 // set URI
373 g_object_set (G_OBJECT (mPlaybin), "uri", urlIn.c_str(), NULL);
374 //g_object_set (G_OBJECT (mPlaybin), "uri", "file:///tmp/movie", NULL);
375
376 // get playbin's bus - perhaps this can/should be done at init()
377 GstBus *bus = llgst_pipeline_get_bus (GST_PIPELINE (mPlaybin));
378 if (!bus)
379 {
380 return FALSE;
381 }
382 llgst_bus_add_watch (bus, my_bus_callback, this);
383 llgst_object_unref (bus);
384
385 if (true) // dummy values
386 {
387 const int fixedsize = 2;
388 mMediaRowbytes = mMediaDepthBytes * fixedsize;
389 mMediaWidth = fixedsize;
390 mMediaHeight = fixedsize;
391 mTextureWidth = fixedsize;
392 mTextureHeight = fixedsize;
393 }
394
395 BOOL rtn = LLMediaMovieBase::load(urlIn);
396 llinfos << "load returns " << int(rtn) << llendl;
397 return rtn;
398}
399
400///////////////////////////////////////////////////////////////////////////////
401//
402BOOL
403LLMediaImplGStreamer::
404unload ()
405{
406 if (mPlaybin)
407 {
408 llgst_element_set_state (mPlaybin, GST_STATE_NULL);
409 llgst_object_unref (GST_OBJECT (mPlaybin));
410 mPlaybin = NULL;
411 }
412
413 if (mPump)
414 {
415 g_main_loop_quit(mPump);
416 mPump = NULL;
417 }
418
419 if (mediaData)
420 {
421 if (ownBuffer)
422 {
423 delete mediaData;
424 mediaData = NULL;
425 }
426 }
427
428 mVideoSink = NULL;
429
430 return TRUE;
431}
432
433///////////////////////////////////////////////////////////////////////////////
434//
435S32
436LLMediaImplGStreamer::
437updateMedia ()
438{
439 //llinfos << "updating media..." << llendl;
440 if (g_main_context_pending(g_main_loop_get_context(mPump)))
441 {
442 g_main_context_iteration(g_main_loop_get_context(mPump), FALSE);
443 }
444
445 if (mVideoSink)
446 {
447 GST_OBJECT_LOCK(mVideoSink);
448 if (mVideoSink->retained_frame_ready)
449 {
450 //llinfos << "NEW FRAME " << llendl;
451 if (mVideoSink->retained_frame_width != mMediaWidth ||
452 mVideoSink->retained_frame_height != mMediaHeight)
453 // *TODO: also check for change in format
454 {
455 // just resize container
456 mMediaWidth = mVideoSink->retained_frame_width;
457 mMediaHeight = mVideoSink->retained_frame_height;
458 mTextureWidth = mMediaWidth;
459 mTextureHeight = mMediaHeight;
460 mMediaDepthBytes = mTextureDepth =
461 SLVPixelFormatBytes[mVideoSink->retained_frame_format];
462 if (SLV_PF_RGBX == mVideoSink->retained_frame_format)
463 {
464 mTextureFormatPrimary = GL_RGBA;
465 mTextureFormatType=GL_UNSIGNED_INT_8_8_8_8_REV;
466 }
467 else
468 {
469 mTextureFormatPrimary = GL_BGRA;
470 mTextureFormatType=GL_UNSIGNED_INT_8_8_8_8_REV;
471 }
472 mMediaRowbytes = mMediaWidth * mMediaDepthBytes;
473 llinfos << "video container resized to " <<
474 mMediaWidth << "x" << mMediaHeight << llendl;
475
476 if (ownBuffer)
477 {
478 // we manage the buffer, so we need to realloc
479 delete[] mediaData;
480 mediaData = new U8[mMediaRowbytes *
481 mMediaHeight];
482 }
483
484 GST_OBJECT_UNLOCK(mVideoSink);
485 return updateMediaNeedsSizeChange;
486 }
487
488 // we're gonna totally consume this frame - reset 'ready' flag
489 mVideoSink->retained_frame_ready = FALSE;
490 memcpy(mediaData, mVideoSink->retained_frame_data,
491 mMediaRowbytes * mMediaHeight);
492
493 GST_OBJECT_UNLOCK(mVideoSink);
494 return updateMediaNeedsUpdate;
495 }
496 else
497 {
498 // nothing to do yet.
499 GST_OBJECT_UNLOCK(mVideoSink);
500 return updateMediaNoChanges;
501 }
502 }
503
504 return updateMediaNoChanges;
505}
506
507///////////////////////////////////////////////////////////////////////////////
508//
509void
510LLMediaImplGStreamer::
511setAutoScaled ( BOOL autoScaledIn )
512{
513 autoScaled = autoScaledIn;
514}
515
516///////////////////////////////////////////////////////////////////////////////
517//
518BOOL
519LLMediaImplGStreamer::
520stop ()
521{
522 llinfos << "stopping media..." << llendl;
523 // todo: error-check this?
524 llgst_element_set_state(mPlaybin, GST_STATE_READY);
525
526 BOOL rtn = LLMediaMovieBase::stop();
527 setCurrentMode(LLMediaImplGStreamer::ModeStopped);//?
528 return rtn;
529}
530
531///////////////////////////////////////////////////////////////////////////////
532//
533BOOL
534LLMediaImplGStreamer::
535play ()
536{
537 llinfos << "playing media..." << llendl;
538 // todo: error-check this?
539 llgst_element_set_state(mPlaybin, GST_STATE_PLAYING);
540
541 return LLMediaMovieBase::play();
542}
543
544///////////////////////////////////////////////////////////////////////////////
545//
546BOOL
547LLMediaImplGStreamer::
548loop ( S32 howMany )
549{
550 llinfos << "looping media... " << howMany << llendl;
551 // todo: implement this
552 if (!play())
553 return FALSE;
554
555 return LLMediaMovieBase::loop(howMany);
556};
557
558///////////////////////////////////////////////////////////////////////////////
559//
560BOOL
561LLMediaImplGStreamer::
562pause ()
563{
564 llinfos << "pausing media..." << llendl;
565 // todo: error-check this?
566 llgst_element_set_state(mPlaybin, GST_STATE_PAUSED);
567
568 return LLMediaMovieBase::pause();
569};
570
571///////////////////////////////////////////////////////////////////////////////
572//
573BOOL
574LLMediaImplGStreamer::
575setVolume ( F32 volumeIn )
576{
577 mVolume = volumeIn;
578 g_object_set(mPlaybin, "volume", mVolume, NULL);
579 return TRUE;
580}
581
582///////////////////////////////////////////////////////////////////////////////
583//
584F32
585LLMediaImplGStreamer::
586getVolume ()
587{
588 return mVolume;
589}
590
591///////////////////////////////////////////////////////////////////////////////
592//
593BOOL
594LLMediaImplGStreamer::
595isIdle () const
596{
597 // todo: probably semantically decouple from currentMode
598 return currentMode == ModeIdle;
599}
600
601///////////////////////////////////////////////////////////////////////////////
602//
603BOOL
604LLMediaImplGStreamer::
605isError () const
606{
607 // todo: probably semantically decouple from currentMode
608 return currentMode == ModeError;
609}
610
611///////////////////////////////////////////////////////////////////////////////
612//
613BOOL
614LLMediaImplGStreamer::
615isBuffering () const
616{
617 // todo: probably semantically decouple from currentMode
618 return currentMode == ModeBuffering;
619}
620
621///////////////////////////////////////////////////////////////////////////////
622//
623BOOL
624LLMediaImplGStreamer::
625isLoaded () const
626{
627 // todo: probably semantically decouple from currentMode
628 //return currentMode == ModeLoaded;
629 return (mPump != NULL);
630}
631
632///////////////////////////////////////////////////////////////////////////////
633//
634BOOL
635LLMediaImplGStreamer::
636isPlaying () const
637{
638 // todo: probably semantically decouple from currentMode
639 return currentMode == ModePlaying;
640}
641
642///////////////////////////////////////////////////////////////////////////////
643//
644BOOL
645LLMediaImplGStreamer::
646isLooping () const
647{
648 // todo: probably semantically decouple from currentMode
649 return currentMode == ModeLooping;
650}
651
652///////////////////////////////////////////////////////////////////////////////
653//
654BOOL
655LLMediaImplGStreamer::
656isPaused () const
657{
658 // todo: probably semantically decouple from currentMode
659 return currentMode == ModePaused;
660}
661
662///////////////////////////////////////////////////////////////////////////////
663//
664BOOL
665LLMediaImplGStreamer::
666isStopped () const
667{
668 // todo: probably semantically decouple from currentMode
669 return currentMode == ModeStopped;
670}
671
672///////////////////////////////////////////////////////////////////////////////
673//
674U8*
675LLMediaImplGStreamer::
676getMediaData ()
677{
678 return mediaData;
679}
680
681///////////////////////////////////////////////////////////////////////////////
682//
683BOOL
684LLMediaImplGStreamer::
685seek ( F64 time )
686{
687 // todo: implement this
688 llinfos << "Tried to seek to time " << time
689 << " - faking it" << llendl;
690 return TRUE;
691}
692
693///////////////////////////////////////////////////////////////////////////////
694//
695F64
696LLMediaImplGStreamer::
697getTime () const
698{
699 // todo: implement this
700 F64 result = 0;
701 return result;
702}
703
704///////////////////////////////////////////////////////////////////////////////
705//
706F64
707LLMediaImplGStreamer::
708getMediaDuration () const
709{
710 // todo: implement this
711 F64 result = 0;
712 return result;
713}
714
715#endif // LL_GSTREAMER_ENABLED