1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/unix/mediactrl.cpp
3 // Purpose: GStreamer backend for Unix
4 // Author: Ryan Norton <wxprojects@comcast.net>
8 // Copyright: (c) 2004-2005 Ryan Norton
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
15 #include "wx/mediactrl.h"
21 #include <gst/gst.h> // main gstreamer header
23 // xoverlay/video stuff, gst-gconf for 0.8
24 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
25 # include <gst/interfaces/xoverlay.h>
27 # include <gst/xoverlay/xoverlay.h>
28 # include <gst/gconf/gconf.h> // gstreamer glib configuration
31 #include "wx/log.h" // wxLogDebug/wxLogSysError/wxLogTrace
32 #include "wx/app.h" // wxTheApp->argc, wxTheApp->argv
33 #include "wx/thread.h" // wxMutex/wxMutexLocker
34 #include "wx/timer.h" // wxTimer
37 # include "wx/gtk/win_gtk.h" // for <gdk/gdkx.h>/GDK_WINDOW_XWINDOW
40 //-----------------------------------------------------------------------------
41 // Discussion of internals
42 //-----------------------------------------------------------------------------
45 This is the GStreamer backend for unix. Currently we require 0.8 or
46 0.10. Here we use the "playbin" GstElement for ease of use.
48 Note that now we compare state change functions to GST_STATE_FAILURE
49 now rather than GST_STATE_SUCCESS as newer gstreamer versions return
50 non-success values for returns that are otherwise successful but not
53 Also this probably doesn't work with anything other than wxGTK at the
54 moment but with a tad bit of work it could theorectically work in
57 One last note is that resuming from pausing/seeking can result
58 in erratic video playback (GStreamer-based bug, happens in totem as well)
59 - this is better in 0.10, however. One thing that might make it worse
60 here is that we don't preserve the aspect ratio of the video and stretch
61 it to the whole window.
63 Note that there are some things used here that could be undocumented -
64 for reference see the media player Kiss and Totem as well as some
65 other sources. There was a backend for a kde media player as well
66 that attempted thread-safety...
68 Then there is the issue of m_asynclock. This serves several purposes:
69 1) It prevents the C callbacks from sending wx state change events
70 so that we don't get duplicate ones in 0.8
71 2) It makes the sync and async handlers in 0.10 not drop any
72 messages so that while we are polling it we get the messages in
73 SyncStateChange instead of the queue.
74 3) Keeps the pausing in Stop() synchronous
76 RN: Note that I've tried to follow the wxGTK conventions here as close
77 as possible. In the implementation the C Callbacks come first, then
78 the internal functions, then the public ones. Set your vi to 80
82 //=============================================================================
84 //=============================================================================
86 //-----------------------------------------------------------------------------
87 // GStreamer (most version compatability) macros
88 //-----------------------------------------------------------------------------
90 // In 0.9 there was a HUGE change to GstQuery and the
91 // gst_element_query function changed dramatically and split off
92 // into two seperate ones
93 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR <= 8
94 # define wxGst_element_query_duration(e, f, p) \
95 gst_element_query(e, GST_QUERY_TOTAL, f, p)
96 # define wxGst_element_query_position(e, f, p) \
97 gst_element_query(e, GST_QUERY_POSITION, f, p)
98 #elif GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR == 9
99 // However, the actual 0.9 version has a slightly different definition
100 // and instead of gst_element_query_duration it has two parameters to
101 // gst_element_query_position instead
102 # define wxGst_element_query_duration(e, f, p) \
103 gst_element_query_position(e, f, 0, p)
104 # define wxGst_element_query_position(e, f, p) \
105 gst_element_query_position(e, f, p, 0)
107 # define wxGst_element_query_duration \
108 gst_element_query_duration
109 # define wxGst_element_query_position \
110 gst_element_query_position
114 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
115 # define GST_STATE_FAILURE GST_STATE_CHANGE_FAILURE
116 # define GST_STATE_SUCCESS GST_STATE_CHANGE_SUCCESS
117 # define GstElementState GstState
118 # define gst_gconf_get_default_video_sink() \
119 gst_element_factory_make ("gconfvideosink", "video-sink");
120 # define gst_gconf_get_default_audio_sink() \
121 gst_element_factory_make ("gconfaudiosink", "audio-sink");
124 // Max wait time for element state waiting - GST_CLOCK_TIME_NONE for inf
125 #define wxGSTREAMER_TIMEOUT (100 * GST_MSECOND) // Max 100 milliseconds
127 //-----------------------------------------------------------------------------
128 // wxGTK Debugging and idle stuff
129 //-----------------------------------------------------------------------------
134 # define DEBUG_MAIN_THREAD \
135 if (wxThread::IsMain() && g_mainThreadLocked) \
136 wxPrintf(wxT("gui reentrance"));
138 # define DEBUG_MAIN_THREAD
141 # define DEBUG_MAIN_THREAD
144 extern void wxapp_install_idle_handler();
145 extern bool g_isIdle
;
146 extern bool g_mainThreadLocked
;
149 //-----------------------------------------------------------------------------
150 // wxLogTrace mask string
151 //-----------------------------------------------------------------------------
152 #define wxTRACE_GStreamer wxT("GStreamer")
154 //-----------------------------------------------------------------------------
156 // wxGStreamerMediaBackend
158 //-----------------------------------------------------------------------------
159 class WXDLLIMPEXP_MEDIA
160 wxGStreamerMediaBackend
: public wxMediaBackendCommonBase
164 wxGStreamerMediaBackend();
165 ~wxGStreamerMediaBackend();
167 virtual bool CreateControl(wxControl
* ctrl
, wxWindow
* parent
,
172 const wxValidator
& validator
,
173 const wxString
& name
);
176 virtual bool Pause();
179 virtual bool Load(const wxString
& fileName
);
180 virtual bool Load(const wxURI
& location
);
182 virtual wxMediaState
GetState();
184 virtual bool SetPosition(wxLongLong where
);
185 virtual wxLongLong
GetPosition();
186 virtual wxLongLong
GetDuration();
188 virtual void Move(int x
, int y
, int w
, int h
);
189 wxSize
GetVideoSize() const;
191 virtual double GetPlaybackRate();
192 virtual bool SetPlaybackRate(double dRate
);
194 virtual wxLongLong
GetDownloadProgress();
195 virtual wxLongLong
GetDownloadTotal();
197 virtual bool SetVolume(double dVolume
);
198 virtual double GetVolume();
200 //------------implementation from now on-----------------------------------
201 wxMediaCtrl
* GetControl() { return m_ctrl
; } // for C Callbacks
202 void HandleStateChange(GstElementState oldstate
, GstElementState newstate
);
203 bool QueryVideoSizeFromElement(GstElement
* element
);
204 bool QueryVideoSizeFromPad(GstPad
* caps
);
205 void SetupXOverlay();
206 bool SyncStateChange(GstElement
* element
, GstElementState state
,
207 gint64 llTimeout
= wxGSTREAMER_TIMEOUT
);
208 bool TryAudioSink(GstElement
* audiosink
);
209 bool TryVideoSink(GstElement
* videosink
);
211 GstElement
* m_playbin
; // GStreamer media element
212 wxSize m_videoSize
; // Cached actual video size
213 double m_dRate
; // Current playback rate -
214 // see GetPlaybackRate for notes
215 wxLongLong m_llPausedPos
; // Paused position - see Pause()
216 GstXOverlay
* m_xoverlay
; // X Overlay that contains the GST video
217 wxMutex m_asynclock
; // See "discussion of internals"
218 class wxGStreamerMediaEventHandler
* m_eventHandler
; // see below
220 friend class wxGStreamerMediaEventHandler
;
221 friend class wxGStreamerLoadWaitTimer
;
222 DECLARE_DYNAMIC_CLASS(wxGStreamerMediaBackend
);
225 //-----------------------------------------------------------------------------
226 // wxGStreamerMediaEventHandler
228 // OK, this will take an explanation - basically gstreamer callbacks
229 // are issued in a seperate thread, and in this thread we may not set
230 // the state of the playbin, so we need to send a wx event in that
231 // callback so that we set the state of the media and other stuff
233 //-----------------------------------------------------------------------------
234 class wxGStreamerMediaEventHandler
: public wxEvtHandler
237 wxGStreamerMediaEventHandler(wxGStreamerMediaBackend
* be
) : m_be(be
)
239 this->Connect(wxID_ANY
, wxEVT_MEDIA_FINISHED
,
240 wxMediaEventHandler(wxGStreamerMediaEventHandler::OnMediaFinish
));
243 void OnMediaFinish(wxMediaEvent
& event
);
245 wxGStreamerMediaBackend
* m_be
;
248 //=============================================================================
250 //=============================================================================
252 IMPLEMENT_DYNAMIC_CLASS(wxGStreamerMediaBackend
, wxMediaBackend
)
254 //-----------------------------------------------------------------------------
258 //-----------------------------------------------------------------------------
260 //-----------------------------------------------------------------------------
261 // "expose_event" from m_ctrl->m_wxwindow
263 // Handle GTK expose event from our window - here we hopefully
264 // redraw the video in the case of pausing and other instances...
265 // (Returns TRUE to pass to other handlers, FALSE if not)
267 // TODO: Do a DEBUG_MAIN_THREAD/install_idle_handler here?
268 //-----------------------------------------------------------------------------
271 static gboolean
gtk_window_expose_callback(GtkWidget
*widget
,
272 GdkEventExpose
*event
,
273 wxGStreamerMediaBackend
*be
)
278 GdkWindow
*window
= GTK_PIZZA(be
->GetControl()->m_wxwindow
)->bin_window
;
280 // I've seen this reccommended somewhere...
281 // TODO: Is this needed? Maybe it is just cruft...
282 // gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(be->m_xoverlay),
283 // GDK_WINDOW_XWINDOW( window ) );
285 // If we have actual video.....
286 if(!(be
->m_videoSize
.x
==0&&be
->m_videoSize
.y
==0) &&
287 GST_STATE(be
->m_playbin
) >= GST_STATE_PAUSED
)
289 // GST Doesn't redraw automatically while paused
290 // Plus, the video sometimes doesn't redraw when it looses focus
291 // or is painted over so we just tell it to redraw...
292 gst_x_overlay_expose(be
->m_xoverlay
);
296 // draw a black background like some other backends do....
297 gdk_draw_rectangle (window
, widget
->style
->black_gc
, TRUE
, 0, 0,
298 widget
->allocation
.width
,
299 widget
->allocation
.height
);
307 //-----------------------------------------------------------------------------
308 // "realize" from m_ctrl->m_wxwindow
310 // If the window wasn't realized when Load was called, this is the
311 // callback for when it is - the purpose of which is to tell
312 // GStreamer to play the video in our control
313 //-----------------------------------------------------------------------------
316 static gint
gtk_window_realize_callback(GtkWidget
* theWidget
,
317 wxGStreamerMediaBackend
* be
)
319 DEBUG_MAIN_THREAD
// TODO: Is this neccessary?
321 if (g_isIdle
) // FIXME: Why is needed? For wxYield? ??
322 wxapp_install_idle_handler();
324 wxYield(); // FIXME: RN: X Server gets an error/crash if I don't do
325 // this or a messagebox beforehand?!?!??
327 GdkWindow
*window
= GTK_PIZZA(theWidget
)->bin_window
;
330 gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(be
->m_xoverlay
),
331 GDK_WINDOW_XWINDOW( window
)
333 g_signal_connect (be
->GetControl()->m_wxwindow
,
335 G_CALLBACK(gtk_window_expose_callback
), be
);
341 //-----------------------------------------------------------------------------
342 // "state-change" from m_playbin/GST_MESSAGE_STATE_CHANGE
344 // Called by gstreamer when the state changes - here we
345 // send the appropriate corresponding wx event.
347 // 0.8 only as HandleStateChange does this in both versions
348 //-----------------------------------------------------------------------------
349 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10
351 static void gst_state_change_callback(GstElement
*play
,
352 GstElementState oldstate
,
353 GstElementState newstate
,
354 wxGStreamerMediaBackend
* be
)
356 if(be
->m_asynclock
.TryLock() == wxMUTEX_NO_ERROR
)
358 be
->HandleStateChange(oldstate
, newstate
);
359 be
->m_asynclock
.Unlock();
365 //-----------------------------------------------------------------------------
366 // "eos" from m_playbin/GST_MESSAGE_EOS
368 // Called by gstreamer when the media is done playing ("end of stream")
369 //-----------------------------------------------------------------------------
371 static void gst_finish_callback(GstElement
*play
,
372 wxGStreamerMediaBackend
* be
)
374 wxLogTrace(wxTRACE_GStreamer
, wxT("gst_finish_callback"));
375 wxMediaEvent
event(wxEVT_MEDIA_FINISHED
);
376 be
->m_eventHandler
->AddPendingEvent(event
);
380 //-----------------------------------------------------------------------------
381 // "error" from m_playbin/GST_MESSAGE_ERROR
383 // Called by gstreamer when an error is encountered playing the media -
384 // We call wxLogTrace in addition wxLogSysError so that we can get it
385 // on the command line as well for those who want extra traces.
386 //-----------------------------------------------------------------------------
388 static void gst_error_callback(GstElement
*play
,
392 wxGStreamerMediaBackend
* be
)
395 sError
.Printf(wxT("gst_error_callback\n")
396 wxT("Error Message:%s\nDebug:%s\n"),
397 (const wxChar
*)wxConvUTF8
.cMB2WX(err
->message
),
398 (const wxChar
*)wxConvUTF8
.cMB2WX(debug
));
399 wxLogTrace(wxTRACE_GStreamer
, sError
);
400 wxLogSysError(sError
);
404 //-----------------------------------------------------------------------------
405 // "notify::caps" from the videopad inside "stream-info" of m_playbin
407 // Called by gstreamer when the video caps for the media is ready - currently
408 // we use the caps to get the natural size of the video
411 //-----------------------------------------------------------------------------
413 static void gst_notify_caps_callback(GstPad
* pad
,
415 wxGStreamerMediaBackend
* be
)
417 wxLogTrace(wxTRACE_GStreamer
, wxT("gst_notify_caps_callback"));
418 be
->QueryVideoSizeFromPad(pad
);
422 //-----------------------------------------------------------------------------
423 // "notify::stream-info" from m_playbin
425 // Run through the stuff in "stream-info" of m_playbin for a valid
426 // video pad, and then attempt to query the video size from it - if not
427 // set up an event to do so when ready.
429 // Currently unused - now we just query it directly using
430 // QueryVideoSizeFromElement.
433 //-----------------------------------------------------------------------------
434 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
436 static void gst_notify_stream_info_callback(GstElement
* element
,
438 wxGStreamerMediaBackend
* be
)
440 wxLogTrace(wxTRACE_GStreamer
, wxT("gst_notify_stream_info_callback"));
441 be
->QueryVideoSizeFromElement(be
->m_playbin
);
446 //-----------------------------------------------------------------------------
447 // "desired-size-changed" from m_xoverlay
449 // 0.8-specific this provides us with the video size when it changes -
450 // even though we get the caps as well this seems to come before the
451 // caps notification does...
453 // Note it will return 16,16 for an early-bird value or for audio
454 //-----------------------------------------------------------------------------
455 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10
457 static void gst_desired_size_changed_callback(GstElement
* play
,
458 guint width
, guint height
,
459 wxGStreamerMediaBackend
* be
)
461 if(!(width
== 16 && height
== 16))
463 be
->m_videoSize
.x
= width
;
464 be
->m_videoSize
.y
= height
;
467 be
->QueryVideoSizeFromElement(be
->m_playbin
);
472 //-----------------------------------------------------------------------------
473 // gst_bus_async_callback [static]
474 // gst_bus_sync_callback [static]
476 // Called by m_playbin for notifications such as end-of-stream in 0.10 -
477 // in previous versions g_signal notifications were used. Because everything
478 // in centered in one switch statement though it reminds one of old WinAPI
481 // gst_bus_sync_callback is that sync version that is called on the main GUI
482 // thread before the async version that we use to set the xwindow id of the
483 // XOverlay (NB: This isn't currently used - see CreateControl()).
484 //-----------------------------------------------------------------------------
485 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
487 static gboolean
gst_bus_async_callback(GstBus
* bus
,
489 wxGStreamerMediaBackend
* be
)
491 if(((GstElement
*)GST_MESSAGE_SRC(message
)) != be
->m_playbin
)
493 if(be
->m_asynclock
.TryLock() != wxMUTEX_NO_ERROR
)
496 switch(GST_MESSAGE_TYPE(message
))
498 case GST_MESSAGE_STATE_CHANGED
:
500 GstState oldstate
, newstate
, pendingstate
;
501 gst_message_parse_state_changed(message
, &oldstate
,
502 &newstate
, &pendingstate
);
503 be
->HandleStateChange(oldstate
, newstate
);
506 case GST_MESSAGE_EOS
:
508 gst_finish_callback(NULL
, be
);
511 case GST_MESSAGE_ERROR
:
515 gst_message_parse_error(message
, &error
, &debug
);
516 gst_error_callback(NULL
, NULL
, error
, debug
, be
);
523 be
->m_asynclock
.Unlock();
524 return FALSE
; // remove the message from Z queue
527 static GstBusSyncReply
gst_bus_sync_callback(GstBus
* bus
,
529 wxGStreamerMediaBackend
* be
)
531 // Pass a non-xwindowid-setting event on to the async handler where it
533 if (GST_MESSAGE_TYPE (message
) != GST_MESSAGE_ELEMENT
||
534 !gst_structure_has_name (message
->structure
, "prepare-xwindow-id"))
537 // NB: Unfortunately, the async callback can be quite
538 // buggy at times and often doesn't get called at all,
539 // so here we are processing it right here in the calling
540 // thread instead of the GUI one...
542 if(gst_bus_async_callback(bus
, message
, be
))
548 wxLogTrace(wxTRACE_GStreamer
, wxT("Got prepare-xwindow-id"));
550 return GST_BUS_DROP
; // We handled this message - drop from the queue
555 //-----------------------------------------------------------------------------
557 // Private (although not in the C++ sense) methods
559 //-----------------------------------------------------------------------------
561 //-----------------------------------------------------------------------------
562 // wxGStreamerMediaBackend::HandleStateChange
564 // Handles a state change event from our C Callback for "state-change" or
565 // the async queue in 0.10. (Mostly this is here to avoid locking the
566 // the mutex twice...)
567 //-----------------------------------------------------------------------------
568 void wxGStreamerMediaBackend::HandleStateChange(GstElementState oldstate
,
569 GstElementState newstate
)
573 case GST_STATE_PLAYING
:
574 wxLogTrace(wxTRACE_GStreamer
, wxT("Play event"));
577 case GST_STATE_PAUSED
:
578 // For some reason .10 sends a lot of oldstate == newstate
579 // messages - most likely for pending ones - also
580 // !<GST_STATE_PAUSED as we are only concerned
581 if(oldstate
< GST_STATE_PAUSED
|| oldstate
== newstate
)
583 if(wxGStreamerMediaBackend::GetPosition() != 0)
585 wxLogTrace(wxTRACE_GStreamer
, wxT("Pause event"));
590 wxLogTrace(wxTRACE_GStreamer
, wxT("Stop event"));
594 default: // GST_STATE_NULL etc.
599 //-----------------------------------------------------------------------------
600 // wxGStreamerMediaBackend::QueryVideoSizeFromElement
602 // Run through the stuff in "stream-info" of element for a valid
603 // video pad, and then attempt to query the video size from it - if not
604 // set up an event to do so when ready. Return true
605 // if we got a valid video pad.
606 //-----------------------------------------------------------------------------
607 bool wxGStreamerMediaBackend::QueryVideoSizeFromElement(GstElement
* element
)
609 const GList
*list
= NULL
;
610 g_object_get (G_OBJECT (element
), "stream-info", &list
, NULL
);
612 for ( ; list
!= NULL
; list
= list
->next
)
614 GObject
*info
= (GObject
*) list
->data
;
620 g_object_get (info
, "type", &type
, NULL
);
621 pspec
= g_object_class_find_property (
622 G_OBJECT_GET_CLASS (info
), "type");
623 val
= g_enum_get_value (G_PARAM_SPEC_ENUM (pspec
)->enum_class
, type
);
625 if (!strncasecmp(val
->value_name
, "video", 5) ||
626 !strncmp(val
->value_name
, "GST_STREAM_TYPE_VIDEO", 21))
628 // Newer gstreamer 0.8+ plugins are SUPPOSED to have "object"...
629 // but a lot of old plugins still use "pad" :)
630 pspec
= g_object_class_find_property (
631 G_OBJECT_GET_CLASS (info
), "object");
634 g_object_get (info
, "pad", &pad
, NULL
);
636 g_object_get (info
, "object", &pad
, NULL
);
638 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR <= 8
639 // Killed in 0.9, presumely because events and such
640 // should be pushed on pads regardless of whether they
641 // are currently linked
642 pad
= (GstPad
*) GST_PAD_REALIZE (pad
);
646 if(!QueryVideoSizeFromPad(pad
))
648 // wait for those caps to get ready
652 G_CALLBACK(gst_notify_caps_callback
),
657 }// end searching through info list
659 // no video (or extremely delayed stream-info)
662 m_videoSize
= wxSize(0,0);
669 //-----------------------------------------------------------------------------
670 // wxGStreamerMediaBackend::QueryVideoSizeFromPad
672 // Gets the size of our video (in wxSize) from a GstPad
673 //-----------------------------------------------------------------------------
674 bool wxGStreamerMediaBackend::QueryVideoSizeFromPad(GstPad
* pad
)
676 const GstCaps
* caps
= GST_PAD_CAPS(pad
);
679 const GstStructure
*s
= gst_caps_get_structure (caps
, 0);
682 gst_structure_get_int (s
, "width", &m_videoSize
.x
);
683 gst_structure_get_int (s
, "height", &m_videoSize
.y
);
686 par
= gst_structure_get_value (s
, "pixel-aspect-ratio");
690 wxLogTrace(wxTRACE_GStreamer
,
691 wxT("pixel-aspect-ratio found in pad"));
692 int num
= par
->data
[0].v_int
,
693 den
= par
->data
[1].v_int
;
695 // TODO: maybe better fraction normalization...
697 m_videoSize
.x
= (int) ((float) num
* m_videoSize
.x
/ den
);
699 m_videoSize
.y
= (int) ((float) den
* m_videoSize
.y
/ num
);
702 wxLogTrace(wxTRACE_GStreamer
, wxT("Adjusted video size: [%i,%i]"),
703 m_videoSize
.x
, m_videoSize
.y
);
707 return false; // not ready/massive failure
710 //-----------------------------------------------------------------------------
711 // wxGStreamerMediaBackend::SetupXOverlay
713 // Attempts to set the XWindow id of our GstXOverlay to tell it which
714 // window to play video in.
715 //-----------------------------------------------------------------------------
716 void wxGStreamerMediaBackend::SetupXOverlay()
718 // Use the xoverlay extension to tell gstreamer to play in our window
720 if(!GTK_WIDGET_REALIZED(m_ctrl
->m_wxwindow
))
722 // Not realized yet - set to connect at realization time
723 g_signal_connect (m_ctrl
->m_wxwindow
,
725 G_CALLBACK (gtk_window_realize_callback
),
730 wxYield(); // see realize callback...
731 GdkWindow
*window
= GTK_PIZZA(m_ctrl
->m_wxwindow
)->bin_window
;
735 gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(m_xoverlay
),
737 GDK_WINDOW_XWINDOW( window
)
744 g_signal_connect (m_ctrl
->m_wxwindow
,
745 // m_ctrl->m_wxwindow/*m_ctrl->m_widget*/,
747 G_CALLBACK(gtk_window_expose_callback
), this);
748 } // end if GtkPizza realized
752 //-----------------------------------------------------------------------------
753 // wxGStreamerMediaBackend::SyncStateChange
755 // This function is rather complex - basically the idea is that we
756 // poll the GstBus of m_playbin until it has reached desiredstate, an error
757 // is reached, or there are no more messages left in the GstBus queue.
759 // Returns true if there are no messages left in the queue or
760 // the current state reaches the disired state.
762 // PRECONDITION: Assumes m_asynclock is Lock()ed
763 //-----------------------------------------------------------------------------
764 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
765 bool wxGStreamerMediaBackend::SyncStateChange(GstElement
* element
,
766 GstElementState desiredstate
,
769 GstBus
* bus
= gst_element_get_bus(element
);
773 gint64 llTimeWaited
= 0;
778 // NB: The GStreamer gst_bus_poll is unfortunately broken and
779 // throws silly critical internal errors (for instance
780 // "message != NULL" when the whole point of it is to
781 // poll for the message in the first place!) so we implement
782 // our own "waiting mechinism"
783 if(gst_bus_have_pending(bus
) == FALSE
)
785 if(llTimeWaited
>= llTimeout
)
786 return true; // Reached timeout... assume success
787 llTimeWaited
+= 10*GST_MSECOND
;
792 message
= gst_bus_pop(bus
);
794 message
= gst_bus_poll(bus
, (GstMessageType
)
795 (GST_MESSAGE_STATE_CHANGED
|
797 GST_MESSAGE_EOS
), llTimeout
);
801 if(((GstElement
*)GST_MESSAGE_SRC(message
)) == element
)
803 switch(GST_MESSAGE_TYPE(message
))
805 case GST_MESSAGE_STATE_CHANGED
:
807 GstState oldstate
, newstate
, pendingstate
;
808 gst_message_parse_state_changed(message
, &oldstate
,
809 &newstate
, &pendingstate
);
810 if(newstate
== desiredstate
)
812 bSuccess
= bBreak
= true;
816 case GST_MESSAGE_ERROR
:
820 gst_message_parse_error(message
, &error
, &debug
);
821 gst_error_callback(NULL
, NULL
, error
, debug
, this);
825 case GST_MESSAGE_EOS
:
826 wxLogSysError(wxT("Reached end of stream prematurely"));
830 break; // not handled
834 gst_message_unref(message
);
839 #else // 0.8 implementation
840 bool wxGStreamerMediaBackend::SyncStateChange(GstElement
* element
,
841 GstElementState desiredstate
,
844 gint64 llTimeWaited
= 0;
845 while(GST_STATE(element
) != desiredstate
)
847 if(llTimeWaited
>= llTimeout
)
849 llTimeWaited
+= 10*GST_MSECOND
;
853 return llTimeWaited
!= llTimeout
;
857 //-----------------------------------------------------------------------------
858 // wxGStreamerMediaBackend::TryAudioSink
859 // wxGStreamerMediaBackend::TryVideoSink
861 // Uses various means to determine whether a passed in video/audio sink
862 // if suitable for us - if it is not we return false and unref the
863 // inappropriate sink.
864 //-----------------------------------------------------------------------------
865 bool wxGStreamerMediaBackend::TryAudioSink(GstElement
* audiosink
)
867 if( !GST_IS_ELEMENT(audiosink
) )
869 if(G_IS_OBJECT(audiosink
))
870 g_object_unref(audiosink
);
877 bool wxGStreamerMediaBackend::TryVideoSink(GstElement
* videosink
)
879 // Check if the video sink either is an xoverlay or might contain one...
880 if( !GST_IS_BIN(videosink
) && !GST_IS_X_OVERLAY(videosink
) )
882 if(G_IS_OBJECT(videosink
))
883 g_object_unref(videosink
);
887 // Make our video sink and make sure it supports the x overlay interface
888 // the x overlay enables us to put the video in our control window
889 // (i.e. we NEED it!) - also connect to the natural video size change event
890 if( GST_IS_BIN(videosink
) )
891 m_xoverlay
= (GstXOverlay
*)
892 gst_bin_get_by_interface (GST_BIN (videosink
),
895 m_xoverlay
= (GstXOverlay
*) videosink
;
897 if ( !GST_IS_X_OVERLAY(m_xoverlay
) )
899 g_object_unref(videosink
);
906 //-----------------------------------------------------------------------------
907 // wxGStreamerMediaEventHandler::OnMediaFinish
909 // Called when the media is about to stop
910 //-----------------------------------------------------------------------------
911 void wxGStreamerMediaEventHandler::OnMediaFinish(wxMediaEvent
& event
)
913 // (RN - I have no idea why I thought this was good behaviour....
914 // maybe it made sense for streaming/nonseeking data but
915 // generally it seems like a really bad idea) -
916 if(m_be
->SendStopEvent())
918 // Stop the media (we need to set it back to paused
919 // so that people can get the duration et al.
920 // and send the finish event (luckily we can "Sync" it out... LOL!)
921 // (We don't check return values here because we can't really do
923 wxMutexLocker
lock(m_be
->m_asynclock
);
925 // Set element to ready+sync it
926 gst_element_set_state (m_be
->m_playbin
, GST_STATE_READY
);
927 m_be
->SyncStateChange(m_be
->m_playbin
, GST_STATE_READY
);
929 // Now set it to paused + update pause pos to 0 and
930 // Sync that as well (note that we don't call Stop() here
931 // due to mutex issues)
932 gst_element_set_state (m_be
->m_playbin
, GST_STATE_PAUSED
);
933 m_be
->SyncStateChange(m_be
->m_playbin
, GST_STATE_PAUSED
);
934 m_be
->m_llPausedPos
= 0;
936 // Finally, queue the finish event
937 m_be
->QueueFinishEvent();
941 //-----------------------------------------------------------------------------
945 //-----------------------------------------------------------------------------
947 //-----------------------------------------------------------------------------
948 // wxGStreamerMediaBackend Constructor
950 // Sets m_playbin to NULL signifying we havn't loaded anything yet
951 //-----------------------------------------------------------------------------
952 wxGStreamerMediaBackend::wxGStreamerMediaBackend()
957 //-----------------------------------------------------------------------------
958 // wxGStreamerMediaBackend Destructor
960 // Stops/cleans up memory
962 // NB: This could trigger a critical warning but doing a SyncStateChange
963 // here is just going to slow down quitting of the app, which is bad.
964 //-----------------------------------------------------------------------------
965 wxGStreamerMediaBackend::~wxGStreamerMediaBackend()
967 // Dispose of the main player and related objects
970 wxASSERT( GST_IS_OBJECT(m_playbin
) );
971 gst_element_set_state (m_playbin
, GST_STATE_NULL
);
972 gst_object_unref (GST_OBJECT (m_playbin
));
973 delete m_eventHandler
;
977 //-----------------------------------------------------------------------------
978 // wxGStreamerMediaBackend::CreateControl
980 // Initializes GStreamer and creates the wx side of our media control
981 //-----------------------------------------------------------------------------
982 bool wxGStreamerMediaBackend::CreateControl(wxControl
* ctrl
, wxWindow
* parent
,
987 const wxValidator
& validator
,
988 const wxString
& name
)
993 gst_init(&wxTheApp
->argc
, &wxTheApp
->argv
);
996 // wxControl creation
998 m_ctrl
= wxStaticCast(ctrl
, wxMediaCtrl
);
1001 // We handle our own GTK expose events
1002 m_ctrl
->m_noExpose
= TRUE
;
1005 if( !m_ctrl
->wxControl::Create(parent
, id
, pos
, size
,
1006 style
, // TODO: remove borders???
1009 wxFAIL_MSG(wxT("Could not create wxControl!!!"));
1014 // Turn off double-buffering so that
1015 // so it doesn't draw over the video and cause sporadic
1016 // disappearances of the video
1017 gtk_widget_set_double_buffered(m_ctrl
->m_wxwindow
, FALSE
);
1019 // Tell GtkPizza not to clear the background
1020 gtk_pizza_set_clear(GTK_PIZZA(m_ctrl
->m_wxwindow
), FALSE
);
1023 // don't erase the background of our control window
1024 // so that resizing is a bit smoother
1025 m_ctrl
->SetBackgroundStyle(wxBG_STYLE_CUSTOM
);
1027 // Create our playbin object
1028 m_playbin
= gst_element_factory_make ("playbin", "play");
1029 if (!GST_IS_ELEMENT(m_playbin
))
1031 if(G_IS_OBJECT(m_playbin
))
1032 g_object_unref(m_playbin
);
1033 wxLogSysError(wxT("Got an invalid playbin"));
1037 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10
1038 // Connect the glib events/callbacks we want to our playbin
1039 g_signal_connect(m_playbin
, "eos",
1040 G_CALLBACK(gst_finish_callback
), this);
1041 g_signal_connect(m_playbin
, "error",
1042 G_CALLBACK(gst_error_callback
), this);
1043 g_signal_connect(m_playbin
, "state-change",
1044 G_CALLBACK(gst_state_change_callback
), this);
1046 // GStreamer 0.10+ uses GstBus for this now, connect to the sync
1047 // handler as well so we can set the X window id of our xoverlay
1048 gst_bus_add_watch (gst_element_get_bus(m_playbin
),
1049 (GstBusFunc
) gst_bus_async_callback
, this);
1050 gst_bus_set_sync_handler(gst_element_get_bus(m_playbin
),
1051 (GstBusSyncHandler
) gst_bus_sync_callback
, this);
1052 g_signal_connect(m_playbin
, "notify::stream-info",
1053 G_CALLBACK(gst_notify_stream_info_callback
), this);
1056 // Get the audio sink
1057 GstElement
* audiosink
= gst_gconf_get_default_audio_sink();
1058 if( !TryAudioSink(audiosink
) )
1060 // fallback to autodetection, then alsa, then oss as a stopgap
1061 audiosink
= gst_element_factory_make ("autoaudiosink", "audio-sink");
1062 if( !TryAudioSink(audiosink
) )
1064 audiosink
= gst_element_factory_make ("alsasink", "alsa-output");
1065 if( !TryAudioSink(audiosink
) )
1067 audiosink
= gst_element_factory_make ("osssink", "play_audio");
1068 if( !TryAudioSink(audiosink
) )
1070 wxLogSysError(wxT("Could not find a valid audiosink"));
1077 // Setup video sink - first try gconf, then auto, then xvimage and
1078 // then finally plain ximage
1079 GstElement
* videosink
= gst_gconf_get_default_video_sink();
1080 if( !TryVideoSink(videosink
) )
1082 videosink
= gst_element_factory_make ("autovideosink", "video-sink");
1083 if( !TryVideoSink(videosink
) )
1085 videosink
= gst_element_factory_make ("xvimagesink", "video-sink");
1086 if( !TryVideoSink(videosink
) )
1088 // finally, do a final fallback to ximagesink
1090 gst_element_factory_make ("ximagesink", "video-sink");
1091 if( !TryVideoSink(videosink
) )
1093 g_object_unref(audiosink
);
1094 wxLogSysError(wxT("Could not find a suitable video sink"));
1101 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10
1102 // Not on 0.10... called when video size changes
1103 g_signal_connect(m_xoverlay
, "desired-size-changed",
1104 G_CALLBACK(gst_desired_size_changed_callback
), this);
1106 // Tell GStreamer which window to draw to in 0.8 - 0.10
1107 // sometimes needs this too...
1110 // Now that we know (or, rather think) our video and audio sink
1111 // are valid set our playbin to use them
1112 g_object_set (G_OBJECT (m_playbin
),
1113 "video-sink", videosink
,
1114 "audio-sink", audiosink
,
1117 m_eventHandler
= new wxGStreamerMediaEventHandler(this);
1121 //-----------------------------------------------------------------------------
1122 // wxGStreamerMediaBackend::Load (File version)
1124 // Just calls the URI version
1125 //-----------------------------------------------------------------------------
1126 bool wxGStreamerMediaBackend::Load(const wxString
& fileName
)
1130 wxString( wxT("file://") ) + fileName
1135 //-----------------------------------------------------------------------------
1136 // wxGStreamerMediaBackend::Load (URI version)
1139 // 1) Reset member variables and set playbin back to ready state
1140 // 2) Check URI for validity and then tell the playbin to load it
1141 // 3) Set the playbin to the pause state
1143 // NB: Even after this function is over with we probably don't have the
1144 // video size or duration - no amount of clever hacking is going to get
1145 // around that, unfortunately.
1146 //-----------------------------------------------------------------------------
1147 bool wxGStreamerMediaBackend::Load(const wxURI
& location
)
1149 wxMutexLocker
lock(m_asynclock
); // lock state events and async callbacks
1151 // Reset positions & rate
1154 m_videoSize
= wxSize(0,0);
1156 // Set playbin to ready to stop the current media...
1157 if( gst_element_set_state (m_playbin
,
1158 GST_STATE_READY
) == GST_STATE_FAILURE
||
1159 !SyncStateChange(m_playbin
, GST_STATE_READY
))
1161 wxLogSysError(wxT("wxGStreamerMediaBackend::Load - ")
1162 wxT("Could not set initial state to ready"));
1166 // Make sure the passed URI is valid and tell playbin to load it
1167 // non-file uris are encoded
1169 if(location
.GetScheme().CmpNoCase(wxT("file")))
1170 locstring
= location
.BuildUnescapedURI();
1172 locstring
= location
.BuildURI();
1174 wxASSERT(gst_uri_protocol_is_valid("file"));
1175 wxASSERT(gst_uri_is_valid(locstring
.mb_str()));
1177 g_object_set (G_OBJECT (m_playbin
), "uri",
1178 (const char*)locstring
.mb_str(), NULL
);
1180 // Try to pause media as gstreamer won't let us query attributes
1181 // such as video size unless it is paused or playing
1182 if( gst_element_set_state (m_playbin
,
1183 GST_STATE_PAUSED
) == GST_STATE_FAILURE
||
1184 !SyncStateChange(m_playbin
, GST_STATE_PAUSED
))
1186 return false; // no real error message needed here as this is
1187 // generic failure 99% of the time (i.e. no
1188 // source etc.) and has an error message
1192 NotifyMovieLoaded(); // Notify the user - all we can do for now
1197 //-----------------------------------------------------------------------------
1198 // wxGStreamerMediaBackend::Play
1200 // Sets the stream to a playing state
1202 // THREAD-UNSAFE in 0.8, maybe in 0.10 as well
1203 //-----------------------------------------------------------------------------
1204 bool wxGStreamerMediaBackend::Play()
1206 if (gst_element_set_state (m_playbin
,
1207 GST_STATE_PLAYING
) == GST_STATE_FAILURE
)
1212 //-----------------------------------------------------------------------------
1213 // wxGStreamerMediaBackend::Pause
1215 // Marks where we paused and pauses the stream
1217 // THREAD-UNSAFE in 0.8, maybe in 0.10 as well
1218 //-----------------------------------------------------------------------------
1219 bool wxGStreamerMediaBackend::Pause()
1221 m_llPausedPos
= wxGStreamerMediaBackend::GetPosition();
1222 if (gst_element_set_state (m_playbin
,
1223 GST_STATE_PAUSED
) == GST_STATE_FAILURE
)
1228 //-----------------------------------------------------------------------------
1229 // wxGStreamerMediaBackend::Stop
1231 // Pauses the stream and sets the position to 0. Note that this is
1232 // synchronous (!) pausing.
1234 // Due to the mutex locking this is probably thread-safe actually.
1235 //-----------------------------------------------------------------------------
1236 bool wxGStreamerMediaBackend::Stop()
1238 { // begin state lock
1239 wxMutexLocker
lock(m_asynclock
);
1240 if(gst_element_set_state (m_playbin
,
1241 GST_STATE_PAUSED
) == GST_STATE_FAILURE
||
1242 !SyncStateChange(m_playbin
, GST_STATE_PAUSED
))
1244 wxLogSysError(wxT("Could not set state to paused for Stop()"));
1249 bool bSeekedOK
= wxGStreamerMediaBackend::SetPosition(0);
1253 wxLogSysError(wxT("Could not seek to initial position in Stop()"));
1257 QueueStopEvent(); // Success
1261 //-----------------------------------------------------------------------------
1262 // wxGStreamerMediaBackend::GetState
1264 // Gets the state of the media
1265 //-----------------------------------------------------------------------------
1266 wxMediaState
wxGStreamerMediaBackend::GetState()
1268 switch(GST_STATE(m_playbin
))
1270 case GST_STATE_PLAYING
:
1271 return wxMEDIASTATE_PLAYING
;
1272 case GST_STATE_PAUSED
:
1273 if (m_llPausedPos
== 0)
1274 return wxMEDIASTATE_STOPPED
;
1276 return wxMEDIASTATE_PAUSED
;
1277 default://case GST_STATE_READY:
1278 return wxMEDIASTATE_STOPPED
;
1282 //-----------------------------------------------------------------------------
1283 // wxGStreamerMediaBackend::GetPosition
1285 // If paused, returns our marked position - otherwise it queries the
1286 // GStreamer playbin for the position and returns that
1289 // NB: At least in 0.8, when you pause and seek gstreamer
1290 // NB: doesn't update the position sometimes, so we need to keep track of
1291 // NB: whether we have paused or not and keep track of the time after the
1292 // NB: pause and whenever the user seeks while paused
1295 // THREAD-UNSAFE, at least if not paused. Requires media to be at least paused.
1296 //-----------------------------------------------------------------------------
1297 wxLongLong
wxGStreamerMediaBackend::GetPosition()
1299 if(GetState() != wxMEDIASTATE_PLAYING
)
1300 return m_llPausedPos
;
1304 GstFormat fmtTime
= GST_FORMAT_TIME
;
1306 if (!wxGst_element_query_position(m_playbin
, &fmtTime
, &pos
) ||
1307 fmtTime
!= GST_FORMAT_TIME
|| pos
== -1)
1309 return pos
/ GST_MSECOND
;
1313 //-----------------------------------------------------------------------------
1314 // wxGStreamerMediaBackend::SetPosition
1316 // Sets the position of the stream
1317 // Note that GST_MSECOND is 1000000 (GStreamer uses nanoseconds - so
1318 // there is 1000000 nanoseconds in a millisecond)
1320 // If we are paused we update the cached pause position.
1322 // This is also an exceedingly ugly function due to the three implementations
1323 // (or, rather two plus one implementation without a seek function).
1325 // This is asynchronous and thread-safe on both 0.8 and 0.10.
1327 // NB: This fires both a stop and play event if the media was previously
1328 // playing... which in some ways makes sense. And yes, this makes the video
1329 // go all haywire at times - a gstreamer bug...
1330 //-----------------------------------------------------------------------------
1331 bool wxGStreamerMediaBackend::SetPosition(wxLongLong where
)
1333 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR == 8 \
1334 && GST_VERSION_MICRO == 0
1335 // 0.8.0 has no gst_element_seek according to official docs!!!
1336 wxLogSysError(wxT("GStreamer 0.8.0 does not have gst_element_seek")
1337 wxT(" according to official docs"));
1341 # if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
1342 gst_element_seek (m_playbin
, m_dRate
, GST_FORMAT_TIME
,
1343 (GstSeekFlags
)(GST_SEEK_FLAG_FLUSH
| GST_SEEK_FLAG_KEY_UNIT
),
1344 GST_SEEK_TYPE_SET
, where
.GetValue() * GST_MSECOND
,
1345 GST_SEEK_TYPE_NONE
, GST_CLOCK_TIME_NONE
);
1347 // NB: Some gstreamer versions return false basically all the time
1348 // here - even totem doesn't bother to check the return value here
1349 // so I guess we'll just assume it worked -
1350 // TODO: maybe check the gst error callback???
1351 gst_element_seek (m_playbin
, (GstSeekType
) (GST_SEEK_METHOD_SET
|
1352 GST_FORMAT_TIME
| GST_SEEK_FLAG_FLUSH
),
1353 where
.GetValue() * GST_MSECOND
);
1355 # endif // GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
1358 m_llPausedPos
= where
;
1365 //-----------------------------------------------------------------------------
1366 // wxGStreamerMediaBackend::GetDuration
1368 // Obtains the total time of our stream
1369 // THREAD-UNSAFE, requires media to be paused or playing
1370 //-----------------------------------------------------------------------------
1371 wxLongLong
wxGStreamerMediaBackend::GetDuration()
1374 GstFormat fmtTime
= GST_FORMAT_TIME
;
1376 if(!wxGst_element_query_duration(m_playbin
, &fmtTime
, &length
) ||
1377 fmtTime
!= GST_FORMAT_TIME
|| length
== -1)
1379 return length
/ GST_MSECOND
;
1382 //-----------------------------------------------------------------------------
1383 // wxGStreamerMediaBackend::Move
1385 // Called when the window is moved - GStreamer takes care of this
1386 // for us so nothing is needed
1387 //-----------------------------------------------------------------------------
1388 void wxGStreamerMediaBackend::Move(int x
, int y
, int w
, int h
)
1392 //-----------------------------------------------------------------------------
1393 // wxGStreamerMediaBackend::GetVideoSize
1395 // Returns our cached video size from Load/gst_notify_caps_callback
1396 // gst_x_overlay_get_desired_size also does this in 0.8...
1397 //-----------------------------------------------------------------------------
1398 wxSize
wxGStreamerMediaBackend::GetVideoSize() const
1403 //-----------------------------------------------------------------------------
1404 // wxGStreamerMediaBackend::GetPlaybackRate
1405 // wxGStreamerMediaBackend::SetPlaybackRate
1407 // Obtains/Sets the playback rate of the stream
1409 //TODO: PlaybackRate not currently supported via playbin directly -
1410 //TODO: Ronald S. Bultje noted on gstreamer-devel:
1412 //TODO: Like "play at twice normal speed"? Or "play at 25 fps and 44,1 kHz"? As
1413 //TODO: for the first, yes, we have elements for that, btu they"re not part of
1414 //TODO: playbin. You can create a bin (with a ghost pad) containing the actual
1415 //TODO: video/audiosink and the speed-changing element for this, and set that
1416 //TODO: element as video-sink or audio-sink property in playbin. The
1417 //TODO: audio-element is called "speed", the video-element is called "videodrop"
1418 //TODO: (although that appears to be deprecated in favour of "videorate", which
1419 //TODO: again cannot do this, so this may not work at all in the end). For
1420 //TODO: forcing frame/samplerates, see audioscale and videorate. Audioscale is
1421 //TODO: part of playbin.
1423 // In 0.10 GStreamer has new gst_element_seek API that might
1424 // support this - and I've got an attempt to do so but it is untested
1425 // but it would appear to work...
1426 //-----------------------------------------------------------------------------
1427 double wxGStreamerMediaBackend::GetPlaybackRate()
1429 return m_dRate
; // Could use GST_QUERY_RATE but the API doesn't seem
1430 // final on that yet and there may not be any actual
1431 // plugins that support it...
1434 bool wxGStreamerMediaBackend::SetPlaybackRate(double dRate
)
1436 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
1437 #if 0 // not tested enough
1438 if( gst_element_seek (m_playbin
, dRate
, GST_FORMAT_TIME
,
1439 (GstSeekFlags
)(GST_SEEK_FLAG_FLUSH
| GST_SEEK_FLAG_KEY_UNIT
),
1440 GST_SEEK_TYPE_CUR
, 0,
1441 GST_SEEK_TYPE_NONE
, GST_CLOCK_TIME_NONE
) )
1453 //-----------------------------------------------------------------------------
1454 // wxGStreamerMediaBackend::GetDownloadProgress
1456 // Not really outwardly possible - have been suggested that one could
1457 // get the information from the component that "downloads"
1458 //-----------------------------------------------------------------------------
1459 wxLongLong
wxGStreamerMediaBackend::GetDownloadProgress()
1464 //-----------------------------------------------------------------------------
1465 // wxGStreamerMediaBackend::GetDownloadTotal
1467 // TODO: Cache this?
1468 // NB: The length changes every call for some reason due to
1469 // GStreamer implementation issues
1470 // THREAD-UNSAFE, requires media to be paused or playing
1471 //-----------------------------------------------------------------------------
1472 wxLongLong
wxGStreamerMediaBackend::GetDownloadTotal()
1475 GstFormat fmtBytes
= GST_FORMAT_BYTES
;
1477 if (!wxGst_element_query_duration(m_playbin
, &fmtBytes
, &length
) ||
1478 fmtBytes
!= GST_FORMAT_BYTES
|| length
== -1)
1483 //-----------------------------------------------------------------------------
1484 // wxGStreamerMediaBackend::SetVolume
1485 // wxGStreamerMediaBackend::GetVolume
1487 // Sets/Gets the volume through the playbin object.
1488 // Note that this requires a relatively recent gst-plugins so we
1489 // check at runtime to see whether it is available or not otherwise
1490 // GST spits out an error on the command line
1491 //-----------------------------------------------------------------------------
1492 bool wxGStreamerMediaBackend::SetVolume(double dVolume
)
1494 if(g_object_class_find_property(
1495 G_OBJECT_GET_CLASS(G_OBJECT(m_playbin
)),
1498 g_object_set(G_OBJECT(m_playbin
), "volume", dVolume
, NULL
);
1503 wxLogTrace(wxTRACE_GStreamer
,
1504 wxT("SetVolume: volume prop not found - 0.8.5 of ")
1505 wxT("gst-plugins probably needed"));
1510 double wxGStreamerMediaBackend::GetVolume()
1512 double dVolume
= 1.0;
1514 if(g_object_class_find_property(
1515 G_OBJECT_GET_CLASS(G_OBJECT(m_playbin
)),
1518 g_object_get(G_OBJECT(m_playbin
), "volume", &dVolume
, NULL
);
1522 wxLogTrace(wxTRACE_GStreamer
,
1523 wxT("GetVolume: volume prop not found - 0.8.5 of ")
1524 wxT("gst-plugins probably needed"));
1530 #endif //wxUSE_GSTREAMER
1532 // Force link into main library so this backend can be loaded
1533 #include "wx/html/forcelnk.h"
1534 FORCE_LINK_ME(basewxmediabackends
)
1536 #endif //wxUSE_MEDIACTRL