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"
17 #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
32 #include "wx/log.h" // wxLogDebug/wxLogSysError/wxLogTrace
33 #include "wx/app.h" // wxTheApp->argc, wxTheApp->argv
34 #include "wx/timer.h" // wxTimer
37 #include "wx/thread.h" // wxMutex/wxMutexLocker
41 # include <gdk/gdkx.h> // for GDK_WINDOW_XWINDOW
44 //-----------------------------------------------------------------------------
45 // Discussion of internals
46 //-----------------------------------------------------------------------------
49 This is the GStreamer backend for unix. Currently we require 0.8 or
50 0.10. Here we use the "playbin" GstElement for ease of use.
52 Note that now we compare state change functions to GST_STATE_FAILURE
53 now rather than GST_STATE_SUCCESS as newer gstreamer versions return
54 non-success values for returns that are otherwise successful but not
57 Also this probably doesn't work with anything other than wxGTK at the
58 moment but with a tad bit of work it could theorectically work in
61 One last note is that resuming from pausing/seeking can result
62 in erratic video playback (GStreamer-based bug, happens in totem as well)
63 - this is better in 0.10, however. One thing that might make it worse
64 here is that we don't preserve the aspect ratio of the video and stretch
65 it to the whole window.
67 Note that there are some things used here that could be undocumented -
68 for reference see the media player Kiss and Totem as well as some
69 other sources. There was a backend for a kde media player as well
70 that attempted thread-safety...
72 Then there is the issue of m_asynclock. This serves several purposes:
73 1) It prevents the C callbacks from sending wx state change events
74 so that we don't get duplicate ones in 0.8
75 2) It makes the sync and async handlers in 0.10 not drop any
76 messages so that while we are polling it we get the messages in
77 SyncStateChange instead of the queue.
78 3) Keeps the pausing in Stop() synchronous
80 RN: Note that I've tried to follow the wxGTK conventions here as close
81 as possible. In the implementation the C Callbacks come first, then
82 the internal functions, then the public ones. Set your vi to 80
86 //=============================================================================
88 //=============================================================================
90 //-----------------------------------------------------------------------------
91 // GStreamer (most version compatability) macros
92 //-----------------------------------------------------------------------------
94 // In 0.9 there was a HUGE change to GstQuery and the
95 // gst_element_query function changed dramatically and split off
96 // into two seperate ones
97 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR <= 8
98 # define wxGst_element_query_duration(e, f, p) \
99 gst_element_query(e, GST_QUERY_TOTAL, f, p)
100 # define wxGst_element_query_position(e, f, p) \
101 gst_element_query(e, GST_QUERY_POSITION, f, p)
102 #elif GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR == 9
103 // However, the actual 0.9 version has a slightly different definition
104 // and instead of gst_element_query_duration it has two parameters to
105 // gst_element_query_position instead
106 # define wxGst_element_query_duration(e, f, p) \
107 gst_element_query_position(e, f, 0, p)
108 # define wxGst_element_query_position(e, f, p) \
109 gst_element_query_position(e, f, p, 0)
111 # define wxGst_element_query_duration \
112 gst_element_query_duration
113 # define wxGst_element_query_position \
114 gst_element_query_position
118 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
119 # define GST_STATE_FAILURE GST_STATE_CHANGE_FAILURE
120 # define GST_STATE_SUCCESS GST_STATE_CHANGE_SUCCESS
121 # define GstElementState GstState
122 # define gst_gconf_get_default_video_sink() \
123 gst_element_factory_make ("gconfvideosink", "video-sink");
124 # define gst_gconf_get_default_audio_sink() \
125 gst_element_factory_make ("gconfaudiosink", "audio-sink");
128 // Max wait time for element state waiting - GST_CLOCK_TIME_NONE for inf
129 #define wxGSTREAMER_TIMEOUT (100 * GST_MSECOND) // Max 100 milliseconds
131 //-----------------------------------------------------------------------------
132 // wxLogTrace mask string
133 //-----------------------------------------------------------------------------
134 #define wxTRACE_GStreamer wxT("GStreamer")
136 //-----------------------------------------------------------------------------
138 // wxGStreamerMediaBackend
140 //-----------------------------------------------------------------------------
141 class WXDLLIMPEXP_MEDIA
142 wxGStreamerMediaBackend
: public wxMediaBackendCommonBase
146 wxGStreamerMediaBackend();
147 virtual ~wxGStreamerMediaBackend();
149 virtual bool CreateControl(wxControl
* ctrl
, wxWindow
* parent
,
154 const wxValidator
& validator
,
155 const wxString
& name
);
158 virtual bool Pause();
161 virtual bool Load(const wxString
& fileName
);
162 virtual bool Load(const wxURI
& location
);
164 virtual wxMediaState
GetState();
166 virtual bool SetPosition(wxLongLong where
);
167 virtual wxLongLong
GetPosition();
168 virtual wxLongLong
GetDuration();
170 virtual void Move(int x
, int y
, int w
, int h
);
171 wxSize
GetVideoSize() const;
173 virtual double GetPlaybackRate();
174 virtual bool SetPlaybackRate(double dRate
);
176 virtual wxLongLong
GetDownloadProgress();
177 virtual wxLongLong
GetDownloadTotal();
179 virtual bool SetVolume(double dVolume
);
180 virtual double GetVolume();
182 //------------implementation from now on-----------------------------------
183 bool DoLoad(const wxString
& locstring
);
184 wxMediaCtrl
* GetControl() { return m_ctrl
; } // for C Callbacks
185 void HandleStateChange(GstElementState oldstate
, GstElementState newstate
);
186 bool QueryVideoSizeFromElement(GstElement
* element
);
187 bool QueryVideoSizeFromPad(GstPad
* caps
);
188 void SetupXOverlay();
189 bool SyncStateChange(GstElement
* element
, GstElementState state
,
190 gint64 llTimeout
= wxGSTREAMER_TIMEOUT
);
191 bool TryAudioSink(GstElement
* audiosink
);
192 bool TryVideoSink(GstElement
* videosink
);
194 GstElement
* m_playbin
; // GStreamer media element
195 wxSize m_videoSize
; // Cached actual video size
196 double m_dRate
; // Current playback rate -
197 // see GetPlaybackRate for notes
198 wxLongLong m_llPausedPos
; // Paused position - see Pause()
199 GstXOverlay
* m_xoverlay
; // X Overlay that contains the GST video
200 wxMutex m_asynclock
; // See "discussion of internals"
201 class wxGStreamerMediaEventHandler
* m_eventHandler
; // see below
203 friend class wxGStreamerMediaEventHandler
;
204 friend class wxGStreamerLoadWaitTimer
;
205 DECLARE_DYNAMIC_CLASS(wxGStreamerMediaBackend
);
208 //-----------------------------------------------------------------------------
209 // wxGStreamerMediaEventHandler
211 // OK, this will take an explanation - basically gstreamer callbacks
212 // are issued in a seperate thread, and in this thread we may not set
213 // the state of the playbin, so we need to send a wx event in that
214 // callback so that we set the state of the media and other stuff
216 //-----------------------------------------------------------------------------
217 class wxGStreamerMediaEventHandler
: public wxEvtHandler
220 wxGStreamerMediaEventHandler(wxGStreamerMediaBackend
* be
) : m_be(be
)
222 this->Connect(wxID_ANY
, wxEVT_MEDIA_FINISHED
,
223 wxMediaEventHandler(wxGStreamerMediaEventHandler::OnMediaFinish
));
226 void OnMediaFinish(wxMediaEvent
& event
);
228 wxGStreamerMediaBackend
* m_be
;
231 //=============================================================================
233 //=============================================================================
235 IMPLEMENT_DYNAMIC_CLASS(wxGStreamerMediaBackend
, wxMediaBackend
)
237 //-----------------------------------------------------------------------------
241 //-----------------------------------------------------------------------------
243 //-----------------------------------------------------------------------------
244 // "expose_event" from m_ctrl->m_wxwindow
246 // Handle GTK expose event from our window - here we hopefully
247 // redraw the video in the case of pausing and other instances...
248 // (Returns TRUE to pass to other handlers, FALSE if not)
250 // TODO: Do a DEBUG_MAIN_THREAD/install_idle_handler here?
251 //-----------------------------------------------------------------------------
254 static gboolean
gtk_window_expose_callback(GtkWidget
*widget
,
255 GdkEventExpose
*event
,
256 wxGStreamerMediaBackend
*be
)
261 GdkWindow
*window
= widget
->window
;
263 // I've seen this reccommended somewhere...
264 // TODO: Is this needed? Maybe it is just cruft...
265 // gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(be->m_xoverlay),
266 // GDK_WINDOW_XWINDOW( window ) );
268 // If we have actual video.....
269 if(!(be
->m_videoSize
.x
==0&&be
->m_videoSize
.y
==0) &&
270 GST_STATE(be
->m_playbin
) >= GST_STATE_PAUSED
)
272 // GST Doesn't redraw automatically while paused
273 // Plus, the video sometimes doesn't redraw when it looses focus
274 // or is painted over so we just tell it to redraw...
275 gst_x_overlay_expose(be
->m_xoverlay
);
279 // draw a black background like some other backends do....
280 gdk_draw_rectangle (window
, widget
->style
->black_gc
, TRUE
, 0, 0,
281 widget
->allocation
.width
,
282 widget
->allocation
.height
);
290 //-----------------------------------------------------------------------------
291 // "realize" from m_ctrl->m_wxwindow
293 // If the window wasn't realized when Load was called, this is the
294 // callback for when it is - the purpose of which is to tell
295 // GStreamer to play the video in our control
296 //-----------------------------------------------------------------------------
299 static gint
gtk_window_realize_callback(GtkWidget
* widget
,
300 wxGStreamerMediaBackend
* be
)
304 GdkWindow
*window
= widget
->window
;
307 gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(be
->m_xoverlay
),
308 GDK_WINDOW_XWINDOW( window
)
310 g_signal_connect (be
->GetControl()->m_wxwindow
,
312 G_CALLBACK(gtk_window_expose_callback
), be
);
318 //-----------------------------------------------------------------------------
319 // "state-change" from m_playbin/GST_MESSAGE_STATE_CHANGE
321 // Called by gstreamer when the state changes - here we
322 // send the appropriate corresponding wx event.
324 // 0.8 only as HandleStateChange does this in both versions
325 //-----------------------------------------------------------------------------
326 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10
328 static void gst_state_change_callback(GstElement
*play
,
329 GstElementState oldstate
,
330 GstElementState newstate
,
331 wxGStreamerMediaBackend
* be
)
333 if(be
->m_asynclock
.TryLock() == wxMUTEX_NO_ERROR
)
335 be
->HandleStateChange(oldstate
, newstate
);
336 be
->m_asynclock
.Unlock();
342 //-----------------------------------------------------------------------------
343 // "eos" from m_playbin/GST_MESSAGE_EOS
345 // Called by gstreamer when the media is done playing ("end of stream")
346 //-----------------------------------------------------------------------------
348 static void gst_finish_callback(GstElement
*WXUNUSED(play
),
349 wxGStreamerMediaBackend
* be
)
351 wxLogTrace(wxTRACE_GStreamer
, wxT("gst_finish_callback"));
352 wxMediaEvent
event(wxEVT_MEDIA_FINISHED
);
353 be
->m_eventHandler
->AddPendingEvent(event
);
357 //-----------------------------------------------------------------------------
358 // "error" from m_playbin/GST_MESSAGE_ERROR
360 // Called by gstreamer when an error is encountered playing the media -
361 // We call wxLogTrace in addition wxLogSysError so that we can get it
362 // on the command line as well for those who want extra traces.
363 //-----------------------------------------------------------------------------
365 static void gst_error_callback(GstElement
*WXUNUSED(play
),
366 GstElement
*WXUNUSED(src
),
369 wxGStreamerMediaBackend
* WXUNUSED(be
))
372 sError
.Printf(wxT("gst_error_callback\n")
373 wxT("Error Message:%s\nDebug:%s\n"),
374 (const wxChar
*)wxConvUTF8
.cMB2WX(err
->message
),
375 (const wxChar
*)wxConvUTF8
.cMB2WX(debug
));
376 wxLogTrace(wxTRACE_GStreamer
, sError
);
377 wxLogSysError(sError
);
381 //-----------------------------------------------------------------------------
382 // "notify::caps" from the videopad inside "stream-info" of m_playbin
384 // Called by gstreamer when the video caps for the media is ready - currently
385 // we use the caps to get the natural size of the video
388 //-----------------------------------------------------------------------------
390 static void gst_notify_caps_callback(GstPad
* pad
,
391 GParamSpec
* WXUNUSED(pspec
),
392 wxGStreamerMediaBackend
* be
)
394 wxLogTrace(wxTRACE_GStreamer
, wxT("gst_notify_caps_callback"));
395 be
->QueryVideoSizeFromPad(pad
);
399 //-----------------------------------------------------------------------------
400 // "notify::stream-info" from m_playbin
402 // Run through the stuff in "stream-info" of m_playbin for a valid
403 // video pad, and then attempt to query the video size from it - if not
404 // set up an event to do so when ready.
406 // Currently unused - now we just query it directly using
407 // QueryVideoSizeFromElement.
410 //-----------------------------------------------------------------------------
411 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
413 static void gst_notify_stream_info_callback(GstElement
* WXUNUSED(element
),
414 GParamSpec
* WXUNUSED(pspec
),
415 wxGStreamerMediaBackend
* be
)
417 wxLogTrace(wxTRACE_GStreamer
, wxT("gst_notify_stream_info_callback"));
418 be
->QueryVideoSizeFromElement(be
->m_playbin
);
423 //-----------------------------------------------------------------------------
424 // "desired-size-changed" from m_xoverlay
426 // 0.8-specific this provides us with the video size when it changes -
427 // even though we get the caps as well this seems to come before the
428 // caps notification does...
430 // Note it will return 16,16 for an early-bird value or for audio
431 //-----------------------------------------------------------------------------
432 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10
434 static void gst_desired_size_changed_callback(GstElement
* play
,
435 guint width
, guint height
,
436 wxGStreamerMediaBackend
* be
)
438 if(!(width
== 16 && height
== 16))
440 be
->m_videoSize
.x
= width
;
441 be
->m_videoSize
.y
= height
;
444 be
->QueryVideoSizeFromElement(be
->m_playbin
);
449 //-----------------------------------------------------------------------------
450 // gst_bus_async_callback [static]
451 // gst_bus_sync_callback [static]
453 // Called by m_playbin for notifications such as end-of-stream in 0.10 -
454 // in previous versions g_signal notifications were used. Because everything
455 // in centered in one switch statement though it reminds one of old WinAPI
458 // gst_bus_sync_callback is that sync version that is called on the main GUI
459 // thread before the async version that we use to set the xwindow id of the
460 // XOverlay (NB: This isn't currently used - see CreateControl()).
461 //-----------------------------------------------------------------------------
462 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
464 static gboolean
gst_bus_async_callback(GstBus
* WXUNUSED(bus
),
466 wxGStreamerMediaBackend
* be
)
468 if(((GstElement
*)GST_MESSAGE_SRC(message
)) != be
->m_playbin
)
470 if(be
->m_asynclock
.TryLock() != wxMUTEX_NO_ERROR
)
473 switch(GST_MESSAGE_TYPE(message
))
475 case GST_MESSAGE_STATE_CHANGED
:
477 GstState oldstate
, newstate
, pendingstate
;
478 gst_message_parse_state_changed(message
, &oldstate
,
479 &newstate
, &pendingstate
);
480 be
->HandleStateChange(oldstate
, newstate
);
483 case GST_MESSAGE_EOS
:
485 gst_finish_callback(NULL
, be
);
488 case GST_MESSAGE_ERROR
:
492 gst_message_parse_error(message
, &error
, &debug
);
493 gst_error_callback(NULL
, NULL
, error
, debug
, be
);
500 be
->m_asynclock
.Unlock();
501 return FALSE
; // remove the message from Z queue
504 static GstBusSyncReply
gst_bus_sync_callback(GstBus
* bus
,
506 wxGStreamerMediaBackend
* be
)
508 // Pass a non-xwindowid-setting event on to the async handler where it
510 if (GST_MESSAGE_TYPE (message
) != GST_MESSAGE_ELEMENT
||
511 !gst_structure_has_name (message
->structure
, "prepare-xwindow-id"))
514 // NB: Unfortunately, the async callback can be quite
515 // buggy at times and often doesn't get called at all,
516 // so here we are processing it right here in the calling
517 // thread instead of the GUI one...
519 if(gst_bus_async_callback(bus
, message
, be
))
525 wxLogTrace(wxTRACE_GStreamer
, wxT("Got prepare-xwindow-id"));
527 return GST_BUS_DROP
; // We handled this message - drop from the queue
532 //-----------------------------------------------------------------------------
534 // Private (although not in the C++ sense) methods
536 //-----------------------------------------------------------------------------
538 //-----------------------------------------------------------------------------
539 // wxGStreamerMediaBackend::HandleStateChange
541 // Handles a state change event from our C Callback for "state-change" or
542 // the async queue in 0.10. (Mostly this is here to avoid locking the
543 // the mutex twice...)
544 //-----------------------------------------------------------------------------
545 void wxGStreamerMediaBackend::HandleStateChange(GstElementState oldstate
,
546 GstElementState newstate
)
550 case GST_STATE_PLAYING
:
551 wxLogTrace(wxTRACE_GStreamer
, wxT("Play event"));
554 case GST_STATE_PAUSED
:
555 // For some reason .10 sends a lot of oldstate == newstate
556 // messages - most likely for pending ones - also
557 // !<GST_STATE_PAUSED as we are only concerned
558 if(oldstate
< GST_STATE_PAUSED
|| oldstate
== newstate
)
560 if(wxGStreamerMediaBackend::GetPosition() != 0)
562 wxLogTrace(wxTRACE_GStreamer
, wxT("Pause event"));
567 wxLogTrace(wxTRACE_GStreamer
, wxT("Stop event"));
571 default: // GST_STATE_NULL etc.
576 //-----------------------------------------------------------------------------
577 // wxGStreamerMediaBackend::QueryVideoSizeFromElement
579 // Run through the stuff in "stream-info" of element for a valid
580 // video pad, and then attempt to query the video size from it - if not
581 // set up an event to do so when ready. Return true
582 // if we got a valid video pad.
583 //-----------------------------------------------------------------------------
584 bool wxGStreamerMediaBackend::QueryVideoSizeFromElement(GstElement
* element
)
586 const GList
*list
= NULL
;
587 g_object_get (G_OBJECT (element
), "stream-info", &list
, NULL
);
589 for ( ; list
!= NULL
; list
= list
->next
)
591 GObject
*info
= (GObject
*) list
->data
;
597 g_object_get (info
, "type", &type
, NULL
);
598 pspec
= g_object_class_find_property (
599 G_OBJECT_GET_CLASS (info
), "type");
600 val
= g_enum_get_value (G_PARAM_SPEC_ENUM (pspec
)->enum_class
, type
);
602 if (!strncasecmp(val
->value_name
, "video", 5) ||
603 !strncmp(val
->value_name
, "GST_STREAM_TYPE_VIDEO", 21))
605 // Newer gstreamer 0.8+ plugins are SUPPOSED to have "object"...
606 // but a lot of old plugins still use "pad" :)
607 pspec
= g_object_class_find_property (
608 G_OBJECT_GET_CLASS (info
), "object");
611 g_object_get (info
, "pad", &pad
, NULL
);
613 g_object_get (info
, "object", &pad
, NULL
);
615 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR <= 8
616 // Killed in 0.9, presumely because events and such
617 // should be pushed on pads regardless of whether they
618 // are currently linked
619 pad
= (GstPad
*) GST_PAD_REALIZE (pad
);
623 if(!QueryVideoSizeFromPad(pad
))
625 // wait for those caps to get ready
629 G_CALLBACK(gst_notify_caps_callback
),
634 }// end searching through info list
636 // no video (or extremely delayed stream-info)
639 m_videoSize
= wxSize(0,0);
646 //-----------------------------------------------------------------------------
647 // wxGStreamerMediaBackend::QueryVideoSizeFromPad
649 // Gets the size of our video (in wxSize) from a GstPad
650 //-----------------------------------------------------------------------------
651 bool wxGStreamerMediaBackend::QueryVideoSizeFromPad(GstPad
* pad
)
653 const GstCaps
* caps
= GST_PAD_CAPS(pad
);
656 const GstStructure
*s
= gst_caps_get_structure (caps
, 0);
659 gst_structure_get_int (s
, "width", &m_videoSize
.x
);
660 gst_structure_get_int (s
, "height", &m_videoSize
.y
);
663 par
= gst_structure_get_value (s
, "pixel-aspect-ratio");
667 wxLogTrace(wxTRACE_GStreamer
,
668 wxT("pixel-aspect-ratio found in pad"));
669 int num
= par
->data
[0].v_int
,
670 den
= par
->data
[1].v_int
;
672 // TODO: maybe better fraction normalization...
674 m_videoSize
.x
= (int) ((float) num
* m_videoSize
.x
/ den
);
676 m_videoSize
.y
= (int) ((float) den
* m_videoSize
.y
/ num
);
679 wxLogTrace(wxTRACE_GStreamer
, wxT("Adjusted video size: [%i,%i]"),
680 m_videoSize
.x
, m_videoSize
.y
);
684 return false; // not ready/massive failure
687 //-----------------------------------------------------------------------------
688 // wxGStreamerMediaBackend::SetupXOverlay
690 // Attempts to set the XWindow id of our GstXOverlay to tell it which
691 // window to play video in.
692 //-----------------------------------------------------------------------------
693 void wxGStreamerMediaBackend::SetupXOverlay()
695 // Use the xoverlay extension to tell gstreamer to play in our window
697 if(!GTK_WIDGET_REALIZED(m_ctrl
->m_wxwindow
))
699 // Not realized yet - set to connect at realization time
700 g_signal_connect (m_ctrl
->m_wxwindow
,
702 G_CALLBACK (gtk_window_realize_callback
),
709 GdkWindow
*window
= m_ctrl
->m_wxwindow
->window
;
713 gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(m_xoverlay
),
715 GDK_WINDOW_XWINDOW( window
)
722 g_signal_connect (m_ctrl
->m_wxwindow
,
723 // m_ctrl->m_wxwindow/*m_ctrl->m_widget*/,
725 G_CALLBACK(gtk_window_expose_callback
), this);
726 } // end if GtkPizza realized
730 //-----------------------------------------------------------------------------
731 // wxGStreamerMediaBackend::SyncStateChange
733 // This function is rather complex - basically the idea is that we
734 // poll the GstBus of m_playbin until it has reached desiredstate, an error
735 // is reached, or there are no more messages left in the GstBus queue.
737 // Returns true if there are no messages left in the queue or
738 // the current state reaches the disired state.
740 // PRECONDITION: Assumes m_asynclock is Lock()ed
741 //-----------------------------------------------------------------------------
742 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
743 bool wxGStreamerMediaBackend::SyncStateChange(GstElement
* element
,
744 GstElementState desiredstate
,
747 GstBus
* bus
= gst_element_get_bus(element
);
751 gint64 llTimeWaited
= 0;
756 // NB: The GStreamer gst_bus_poll is unfortunately broken and
757 // throws silly critical internal errors (for instance
758 // "message != NULL" when the whole point of it is to
759 // poll for the message in the first place!) so we implement
760 // our own "waiting mechinism"
761 if(gst_bus_have_pending(bus
) == FALSE
)
763 if(llTimeWaited
>= llTimeout
)
764 return true; // Reached timeout... assume success
765 llTimeWaited
+= 10*GST_MSECOND
;
770 message
= gst_bus_pop(bus
);
772 message
= gst_bus_poll(bus
, (GstMessageType
)
773 (GST_MESSAGE_STATE_CHANGED
|
775 GST_MESSAGE_EOS
), llTimeout
);
779 if(((GstElement
*)GST_MESSAGE_SRC(message
)) == element
)
781 switch(GST_MESSAGE_TYPE(message
))
783 case GST_MESSAGE_STATE_CHANGED
:
785 GstState oldstate
, newstate
, pendingstate
;
786 gst_message_parse_state_changed(message
, &oldstate
,
787 &newstate
, &pendingstate
);
788 if(newstate
== desiredstate
)
790 bSuccess
= bBreak
= true;
794 case GST_MESSAGE_ERROR
:
798 gst_message_parse_error(message
, &error
, &debug
);
799 gst_error_callback(NULL
, NULL
, error
, debug
, this);
803 case GST_MESSAGE_EOS
:
804 wxLogSysError(wxT("Reached end of stream prematurely"));
808 break; // not handled
812 gst_message_unref(message
);
817 #else // 0.8 implementation
818 bool wxGStreamerMediaBackend::SyncStateChange(GstElement
* element
,
819 GstElementState desiredstate
,
822 gint64 llTimeWaited
= 0;
823 while(GST_STATE(element
) != desiredstate
)
825 if(llTimeWaited
>= llTimeout
)
827 llTimeWaited
+= 10*GST_MSECOND
;
831 return llTimeWaited
!= llTimeout
;
835 //-----------------------------------------------------------------------------
836 // wxGStreamerMediaBackend::TryAudioSink
837 // wxGStreamerMediaBackend::TryVideoSink
839 // Uses various means to determine whether a passed in video/audio sink
840 // if suitable for us - if it is not we return false and unref the
841 // inappropriate sink.
842 //-----------------------------------------------------------------------------
843 bool wxGStreamerMediaBackend::TryAudioSink(GstElement
* audiosink
)
845 if( !GST_IS_ELEMENT(audiosink
) )
847 if(G_IS_OBJECT(audiosink
))
848 g_object_unref(audiosink
);
855 bool wxGStreamerMediaBackend::TryVideoSink(GstElement
* videosink
)
857 // Check if the video sink either is an xoverlay or might contain one...
858 if( !GST_IS_BIN(videosink
) && !GST_IS_X_OVERLAY(videosink
) )
860 if(G_IS_OBJECT(videosink
))
861 g_object_unref(videosink
);
865 // Make our video sink and make sure it supports the x overlay interface
866 // the x overlay enables us to put the video in our control window
867 // (i.e. we NEED it!) - also connect to the natural video size change event
868 if( GST_IS_BIN(videosink
) )
869 m_xoverlay
= (GstXOverlay
*)
870 gst_bin_get_by_interface (GST_BIN (videosink
),
873 m_xoverlay
= (GstXOverlay
*) videosink
;
875 if ( !GST_IS_X_OVERLAY(m_xoverlay
) )
877 g_object_unref(videosink
);
884 //-----------------------------------------------------------------------------
885 // wxGStreamerMediaEventHandler::OnMediaFinish
887 // Called when the media is about to stop
888 //-----------------------------------------------------------------------------
889 void wxGStreamerMediaEventHandler::OnMediaFinish(wxMediaEvent
& WXUNUSED(event
))
891 // (RN - I have no idea why I thought this was good behaviour....
892 // maybe it made sense for streaming/nonseeking data but
893 // generally it seems like a really bad idea) -
894 if(m_be
->SendStopEvent())
896 // Stop the media (we need to set it back to paused
897 // so that people can get the duration et al.
898 // and send the finish event (luckily we can "Sync" it out... LOL!)
899 // (We don't check return values here because we can't really do
901 wxMutexLocker
lock(m_be
->m_asynclock
);
903 // Set element to ready+sync it
904 gst_element_set_state (m_be
->m_playbin
, GST_STATE_READY
);
905 m_be
->SyncStateChange(m_be
->m_playbin
, GST_STATE_READY
);
907 // Now set it to paused + update pause pos to 0 and
908 // Sync that as well (note that we don't call Stop() here
909 // due to mutex issues)
910 gst_element_set_state (m_be
->m_playbin
, GST_STATE_PAUSED
);
911 m_be
->SyncStateChange(m_be
->m_playbin
, GST_STATE_PAUSED
);
912 m_be
->m_llPausedPos
= 0;
914 // Finally, queue the finish event
915 m_be
->QueueFinishEvent();
919 //-----------------------------------------------------------------------------
923 //-----------------------------------------------------------------------------
925 //-----------------------------------------------------------------------------
926 // wxGStreamerMediaBackend Constructor
928 // Sets m_playbin to NULL signifying we havn't loaded anything yet
929 //-----------------------------------------------------------------------------
930 wxGStreamerMediaBackend::wxGStreamerMediaBackend()
936 //-----------------------------------------------------------------------------
937 // wxGStreamerMediaBackend Destructor
939 // Stops/cleans up memory
941 // NB: This could trigger a critical warning but doing a SyncStateChange
942 // here is just going to slow down quitting of the app, which is bad.
943 //-----------------------------------------------------------------------------
944 wxGStreamerMediaBackend::~wxGStreamerMediaBackend()
946 // Dispose of the main player and related objects
949 wxASSERT( GST_IS_OBJECT(m_playbin
) );
950 gst_element_set_state (m_playbin
, GST_STATE_NULL
);
951 gst_object_unref (GST_OBJECT (m_playbin
));
952 delete m_eventHandler
;
956 //-----------------------------------------------------------------------------
957 // wxGStreamerMediaBackend::CreateControl
959 // Initializes GStreamer and creates the wx side of our media control
960 //-----------------------------------------------------------------------------
961 bool wxGStreamerMediaBackend::CreateControl(wxControl
* ctrl
, wxWindow
* parent
,
966 const wxValidator
& validator
,
967 const wxString
& name
)
973 //Convert arguments to unicode if enabled
976 char **argvGST
= new char*[wxTheApp
->argc
+ 1];
977 for ( i
= 0; i
< wxTheApp
->argc
; i
++ )
979 #if wxUSE_UNICODE_WCHAR
980 argvGST
[i
] = wxStrdupA(wxConvUTF8
.cWX2MB(wxTheApp
->argv
[i
]));
982 argvGST
[i
] = wxStrdupA(wxTheApp
->argv
[i
].utf8_str());
986 argvGST
[wxTheApp
->argc
] = NULL
;
988 int argcGST
= wxTheApp
->argc
;
990 #define argcGST wxTheApp->argc
991 #define argvGST wxTheApp->argv
994 //Really init gstreamer
996 GError
* error
= NULL
;
997 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
998 bInited
= gst_init_check(&argcGST
, &argvGST
, &error
);
1000 bInited
= gst_init_check(&argcGST
, &argvGST
);
1003 // Cleanup arguments for unicode case
1005 for ( i
= 0; i
< argcGST
; i
++ )
1013 if(!bInited
) //gst_init_check fail?
1017 wxLogSysError(wxT("Could not initialize GStreamer\n")
1018 wxT("Error Message:%s"),
1019 (const wxChar
*) wxConvUTF8
.cMB2WX(error
->message
)
1021 g_error_free(error
);
1024 wxLogSysError(wxT("Could not initialize GStreamer"));
1030 // wxControl creation
1032 m_ctrl
= wxStaticCast(ctrl
, wxMediaCtrl
);
1035 // We handle our own GTK expose events
1036 m_ctrl
->m_noExpose
= true;
1039 if( !m_ctrl
->wxControl::Create(parent
, id
, pos
, size
,
1040 style
, // TODO: remove borders???
1043 wxFAIL_MSG(wxT("Could not create wxControl!!!"));
1048 // Turn off double-buffering so that
1049 // so it doesn't draw over the video and cause sporadic
1050 // disappearances of the video
1051 gtk_widget_set_double_buffered(m_ctrl
->m_wxwindow
, FALSE
);
1054 // don't erase the background of our control window
1055 // so that resizing is a bit smoother
1056 m_ctrl
->SetBackgroundStyle(wxBG_STYLE_CUSTOM
);
1058 // Create our playbin object
1059 m_playbin
= gst_element_factory_make ("playbin", "play");
1060 if (!GST_IS_ELEMENT(m_playbin
))
1062 if(G_IS_OBJECT(m_playbin
))
1063 g_object_unref(m_playbin
);
1064 wxLogSysError(wxT("Got an invalid playbin"));
1068 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10
1069 // Connect the glib events/callbacks we want to our playbin
1070 g_signal_connect(m_playbin
, "eos",
1071 G_CALLBACK(gst_finish_callback
), this);
1072 g_signal_connect(m_playbin
, "error",
1073 G_CALLBACK(gst_error_callback
), this);
1074 g_signal_connect(m_playbin
, "state-change",
1075 G_CALLBACK(gst_state_change_callback
), this);
1077 // GStreamer 0.10+ uses GstBus for this now, connect to the sync
1078 // handler as well so we can set the X window id of our xoverlay
1079 gst_bus_add_watch (gst_element_get_bus(m_playbin
),
1080 (GstBusFunc
) gst_bus_async_callback
, this);
1081 gst_bus_set_sync_handler(gst_element_get_bus(m_playbin
),
1082 (GstBusSyncHandler
) gst_bus_sync_callback
, this);
1083 g_signal_connect(m_playbin
, "notify::stream-info",
1084 G_CALLBACK(gst_notify_stream_info_callback
), this);
1087 // Get the audio sink
1088 GstElement
* audiosink
= gst_gconf_get_default_audio_sink();
1089 if( !TryAudioSink(audiosink
) )
1091 // fallback to autodetection, then alsa, then oss as a stopgap
1092 audiosink
= gst_element_factory_make ("autoaudiosink", "audio-sink");
1093 if( !TryAudioSink(audiosink
) )
1095 audiosink
= gst_element_factory_make ("alsasink", "alsa-output");
1096 if( !TryAudioSink(audiosink
) )
1098 audiosink
= gst_element_factory_make ("osssink", "play_audio");
1099 if( !TryAudioSink(audiosink
) )
1101 wxLogSysError(wxT("Could not find a valid audiosink"));
1108 // Setup video sink - first try gconf, then auto, then xvimage and
1109 // then finally plain ximage
1110 GstElement
* videosink
= gst_gconf_get_default_video_sink();
1111 if( !TryVideoSink(videosink
) )
1113 videosink
= gst_element_factory_make ("autovideosink", "video-sink");
1114 if( !TryVideoSink(videosink
) )
1116 videosink
= gst_element_factory_make ("xvimagesink", "video-sink");
1117 if( !TryVideoSink(videosink
) )
1119 // finally, do a final fallback to ximagesink
1121 gst_element_factory_make ("ximagesink", "video-sink");
1122 if( !TryVideoSink(videosink
) )
1124 g_object_unref(audiosink
);
1125 wxLogSysError(wxT("Could not find a suitable video sink"));
1132 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10
1133 // Not on 0.10... called when video size changes
1134 g_signal_connect(m_xoverlay
, "desired-size-changed",
1135 G_CALLBACK(gst_desired_size_changed_callback
), this);
1137 // Tell GStreamer which window to draw to in 0.8 - 0.10
1138 // sometimes needs this too...
1141 // Now that we know (or, rather think) our video and audio sink
1142 // are valid set our playbin to use them
1143 g_object_set (G_OBJECT (m_playbin
),
1144 "video-sink", videosink
,
1145 "audio-sink", audiosink
,
1148 m_eventHandler
= new wxGStreamerMediaEventHandler(this);
1152 //-----------------------------------------------------------------------------
1153 // wxGStreamerMediaBackend::Load (File version)
1155 // Just calls DoLoad() with a prepended file scheme
1156 //-----------------------------------------------------------------------------
1157 bool wxGStreamerMediaBackend::Load(const wxString
& fileName
)
1159 return DoLoad(wxString( wxT("file://") ) + fileName
);
1162 //-----------------------------------------------------------------------------
1163 // wxGStreamerMediaBackend::Load (URI version)
1165 // In the case of a file URI passes it unencoded -
1166 // also, as of 0.10.3 and earlier GstURI (the uri parser for gstreamer)
1167 // is sort of broken and only accepts uris with at least two slashes
1168 // after the scheme (i.e. file: == not ok, file:// == ok)
1169 //-----------------------------------------------------------------------------
1170 bool wxGStreamerMediaBackend::Load(const wxURI
& location
)
1172 if(location
.GetScheme().CmpNoCase(wxT("file")) == 0)
1174 wxString uristring
= location
.BuildUnescapedURI();
1176 //Workaround GstURI leading "//" problem and make sure it leads
1178 return DoLoad(wxString(wxT("file://")) +
1179 uristring
.Right(uristring
.length() - 5)
1183 return DoLoad(location
.BuildURI());
1186 //-----------------------------------------------------------------------------
1187 // wxGStreamerMediaBackend::DoLoad
1190 // 1) Reset member variables and set playbin back to ready state
1191 // 2) Check URI for validity and then tell the playbin to load it
1192 // 3) Set the playbin to the pause state
1194 // NB: Even after this function is over with we probably don't have the
1195 // video size or duration - no amount of clever hacking is going to get
1196 // around that, unfortunately.
1197 //-----------------------------------------------------------------------------
1198 bool wxGStreamerMediaBackend::DoLoad(const wxString
& locstring
)
1200 wxMutexLocker
lock(m_asynclock
); // lock state events and async callbacks
1202 // Reset positions & rate
1205 m_videoSize
= wxSize(0,0);
1207 // Set playbin to ready to stop the current media...
1208 if( gst_element_set_state (m_playbin
,
1209 GST_STATE_READY
) == GST_STATE_FAILURE
||
1210 !SyncStateChange(m_playbin
, GST_STATE_READY
))
1212 wxLogSysError(wxT("wxGStreamerMediaBackend::Load - ")
1213 wxT("Could not set initial state to ready"));
1217 // free current media resources
1218 gst_element_set_state (m_playbin
, GST_STATE_NULL
);
1220 // Make sure the passed URI is valid and tell playbin to load it
1221 // non-file uris are encoded
1222 wxASSERT(gst_uri_protocol_is_valid("file"));
1223 wxASSERT(gst_uri_is_valid(locstring
.mb_str()));
1225 g_object_set (G_OBJECT (m_playbin
), "uri",
1226 (const char*)locstring
.mb_str(), NULL
);
1228 // Try to pause media as gstreamer won't let us query attributes
1229 // such as video size unless it is paused or playing
1230 if( gst_element_set_state (m_playbin
,
1231 GST_STATE_PAUSED
) == GST_STATE_FAILURE
||
1232 !SyncStateChange(m_playbin
, GST_STATE_PAUSED
))
1234 return false; // no real error message needed here as this is
1235 // generic failure 99% of the time (i.e. no
1236 // source etc.) and has an error message
1240 NotifyMovieLoaded(); // Notify the user - all we can do for now
1245 //-----------------------------------------------------------------------------
1246 // wxGStreamerMediaBackend::Play
1248 // Sets the stream to a playing state
1250 // THREAD-UNSAFE in 0.8, maybe in 0.10 as well
1251 //-----------------------------------------------------------------------------
1252 bool wxGStreamerMediaBackend::Play()
1254 if (gst_element_set_state (m_playbin
,
1255 GST_STATE_PLAYING
) == GST_STATE_FAILURE
)
1260 //-----------------------------------------------------------------------------
1261 // wxGStreamerMediaBackend::Pause
1263 // Marks where we paused and pauses the stream
1265 // THREAD-UNSAFE in 0.8, maybe in 0.10 as well
1266 //-----------------------------------------------------------------------------
1267 bool wxGStreamerMediaBackend::Pause()
1269 m_llPausedPos
= wxGStreamerMediaBackend::GetPosition();
1270 if (gst_element_set_state (m_playbin
,
1271 GST_STATE_PAUSED
) == GST_STATE_FAILURE
)
1276 //-----------------------------------------------------------------------------
1277 // wxGStreamerMediaBackend::Stop
1279 // Pauses the stream and sets the position to 0. Note that this is
1280 // synchronous (!) pausing.
1282 // Due to the mutex locking this is probably thread-safe actually.
1283 //-----------------------------------------------------------------------------
1284 bool wxGStreamerMediaBackend::Stop()
1286 { // begin state lock
1287 wxMutexLocker
lock(m_asynclock
);
1288 if(gst_element_set_state (m_playbin
,
1289 GST_STATE_PAUSED
) == GST_STATE_FAILURE
||
1290 !SyncStateChange(m_playbin
, GST_STATE_PAUSED
))
1292 wxLogSysError(wxT("Could not set state to paused for Stop()"));
1297 bool bSeekedOK
= wxGStreamerMediaBackend::SetPosition(0);
1301 wxLogSysError(wxT("Could not seek to initial position in Stop()"));
1305 QueueStopEvent(); // Success
1309 //-----------------------------------------------------------------------------
1310 // wxGStreamerMediaBackend::GetState
1312 // Gets the state of the media
1313 //-----------------------------------------------------------------------------
1314 wxMediaState
wxGStreamerMediaBackend::GetState()
1316 switch(GST_STATE(m_playbin
))
1318 case GST_STATE_PLAYING
:
1319 return wxMEDIASTATE_PLAYING
;
1320 case GST_STATE_PAUSED
:
1321 if (m_llPausedPos
== 0)
1322 return wxMEDIASTATE_STOPPED
;
1324 return wxMEDIASTATE_PAUSED
;
1325 default://case GST_STATE_READY:
1326 return wxMEDIASTATE_STOPPED
;
1330 //-----------------------------------------------------------------------------
1331 // wxGStreamerMediaBackend::GetPosition
1333 // If paused, returns our marked position - otherwise it queries the
1334 // GStreamer playbin for the position and returns that
1337 // NB: At least in 0.8, when you pause and seek gstreamer
1338 // NB: doesn't update the position sometimes, so we need to keep track of
1339 // NB: whether we have paused or not and keep track of the time after the
1340 // NB: pause and whenever the user seeks while paused
1343 // THREAD-UNSAFE, at least if not paused. Requires media to be at least paused.
1344 //-----------------------------------------------------------------------------
1345 wxLongLong
wxGStreamerMediaBackend::GetPosition()
1347 if(GetState() != wxMEDIASTATE_PLAYING
)
1348 return m_llPausedPos
;
1352 GstFormat fmtTime
= GST_FORMAT_TIME
;
1354 if (!wxGst_element_query_position(m_playbin
, &fmtTime
, &pos
) ||
1355 fmtTime
!= GST_FORMAT_TIME
|| pos
== -1)
1357 return pos
/ GST_MSECOND
;
1361 //-----------------------------------------------------------------------------
1362 // wxGStreamerMediaBackend::SetPosition
1364 // Sets the position of the stream
1365 // Note that GST_MSECOND is 1000000 (GStreamer uses nanoseconds - so
1366 // there is 1000000 nanoseconds in a millisecond)
1368 // If we are paused we update the cached pause position.
1370 // This is also an exceedingly ugly function due to the three implementations
1371 // (or, rather two plus one implementation without a seek function).
1373 // This is asynchronous and thread-safe on both 0.8 and 0.10.
1375 // NB: This fires both a stop and play event if the media was previously
1376 // playing... which in some ways makes sense. And yes, this makes the video
1377 // go all haywire at times - a gstreamer bug...
1378 //-----------------------------------------------------------------------------
1379 bool wxGStreamerMediaBackend::SetPosition(wxLongLong where
)
1381 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR == 8 \
1382 && GST_VERSION_MICRO == 0
1383 // 0.8.0 has no gst_element_seek according to official docs!!!
1384 wxLogSysError(wxT("GStreamer 0.8.0 does not have gst_element_seek")
1385 wxT(" according to official docs"));
1389 # if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
1390 gst_element_seek (m_playbin
, m_dRate
, GST_FORMAT_TIME
,
1391 (GstSeekFlags
)(GST_SEEK_FLAG_FLUSH
| GST_SEEK_FLAG_KEY_UNIT
),
1392 GST_SEEK_TYPE_SET
, where
.GetValue() * GST_MSECOND
,
1393 GST_SEEK_TYPE_NONE
, GST_CLOCK_TIME_NONE
);
1395 // NB: Some gstreamer versions return false basically all the time
1396 // here - even totem doesn't bother to check the return value here
1397 // so I guess we'll just assume it worked -
1398 // TODO: maybe check the gst error callback???
1399 gst_element_seek (m_playbin
, (GstSeekType
) (GST_SEEK_METHOD_SET
|
1400 GST_FORMAT_TIME
| GST_SEEK_FLAG_FLUSH
),
1401 where
.GetValue() * GST_MSECOND
);
1403 # endif // GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
1406 m_llPausedPos
= where
;
1413 //-----------------------------------------------------------------------------
1414 // wxGStreamerMediaBackend::GetDuration
1416 // Obtains the total time of our stream
1417 // THREAD-UNSAFE, requires media to be paused or playing
1418 //-----------------------------------------------------------------------------
1419 wxLongLong
wxGStreamerMediaBackend::GetDuration()
1422 GstFormat fmtTime
= GST_FORMAT_TIME
;
1424 if(!wxGst_element_query_duration(m_playbin
, &fmtTime
, &length
) ||
1425 fmtTime
!= GST_FORMAT_TIME
|| length
== -1)
1427 return length
/ GST_MSECOND
;
1430 //-----------------------------------------------------------------------------
1431 // wxGStreamerMediaBackend::Move
1433 // Called when the window is moved - GStreamer takes care of this
1434 // for us so nothing is needed
1435 //-----------------------------------------------------------------------------
1436 void wxGStreamerMediaBackend::Move(int WXUNUSED(x
),
1443 //-----------------------------------------------------------------------------
1444 // wxGStreamerMediaBackend::GetVideoSize
1446 // Returns our cached video size from Load/gst_notify_caps_callback
1447 // gst_x_overlay_get_desired_size also does this in 0.8...
1448 //-----------------------------------------------------------------------------
1449 wxSize
wxGStreamerMediaBackend::GetVideoSize() const
1454 //-----------------------------------------------------------------------------
1455 // wxGStreamerMediaBackend::GetPlaybackRate
1456 // wxGStreamerMediaBackend::SetPlaybackRate
1458 // Obtains/Sets the playback rate of the stream
1460 //TODO: PlaybackRate not currently supported via playbin directly -
1461 //TODO: Ronald S. Bultje noted on gstreamer-devel:
1463 //TODO: Like "play at twice normal speed"? Or "play at 25 fps and 44,1 kHz"? As
1464 //TODO: for the first, yes, we have elements for that, btu they"re not part of
1465 //TODO: playbin. You can create a bin (with a ghost pad) containing the actual
1466 //TODO: video/audiosink and the speed-changing element for this, and set that
1467 //TODO: element as video-sink or audio-sink property in playbin. The
1468 //TODO: audio-element is called "speed", the video-element is called "videodrop"
1469 //TODO: (although that appears to be deprecated in favour of "videorate", which
1470 //TODO: again cannot do this, so this may not work at all in the end). For
1471 //TODO: forcing frame/samplerates, see audioscale and videorate. Audioscale is
1472 //TODO: part of playbin.
1474 // In 0.10 GStreamer has new gst_element_seek API that might
1475 // support this - and I've got an attempt to do so but it is untested
1476 // but it would appear to work...
1477 //-----------------------------------------------------------------------------
1478 double wxGStreamerMediaBackend::GetPlaybackRate()
1480 return m_dRate
; // Could use GST_QUERY_RATE but the API doesn't seem
1481 // final on that yet and there may not be any actual
1482 // plugins that support it...
1485 bool wxGStreamerMediaBackend::SetPlaybackRate(double dRate
)
1487 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
1488 #if 0 // not tested enough
1489 if( gst_element_seek (m_playbin
, dRate
, GST_FORMAT_TIME
,
1490 (GstSeekFlags
)(GST_SEEK_FLAG_FLUSH
| GST_SEEK_FLAG_KEY_UNIT
),
1491 GST_SEEK_TYPE_CUR
, 0,
1492 GST_SEEK_TYPE_NONE
, GST_CLOCK_TIME_NONE
) )
1506 //-----------------------------------------------------------------------------
1507 // wxGStreamerMediaBackend::GetDownloadProgress
1509 // Not really outwardly possible - have been suggested that one could
1510 // get the information from the component that "downloads"
1511 //-----------------------------------------------------------------------------
1512 wxLongLong
wxGStreamerMediaBackend::GetDownloadProgress()
1517 //-----------------------------------------------------------------------------
1518 // wxGStreamerMediaBackend::GetDownloadTotal
1520 // TODO: Cache this?
1521 // NB: The length changes every call for some reason due to
1522 // GStreamer implementation issues
1523 // THREAD-UNSAFE, requires media to be paused or playing
1524 //-----------------------------------------------------------------------------
1525 wxLongLong
wxGStreamerMediaBackend::GetDownloadTotal()
1528 GstFormat fmtBytes
= GST_FORMAT_BYTES
;
1530 if (!wxGst_element_query_duration(m_playbin
, &fmtBytes
, &length
) ||
1531 fmtBytes
!= GST_FORMAT_BYTES
|| length
== -1)
1536 //-----------------------------------------------------------------------------
1537 // wxGStreamerMediaBackend::SetVolume
1538 // wxGStreamerMediaBackend::GetVolume
1540 // Sets/Gets the volume through the playbin object.
1541 // Note that this requires a relatively recent gst-plugins so we
1542 // check at runtime to see whether it is available or not otherwise
1543 // GST spits out an error on the command line
1544 //-----------------------------------------------------------------------------
1545 bool wxGStreamerMediaBackend::SetVolume(double dVolume
)
1547 if(g_object_class_find_property(
1548 G_OBJECT_GET_CLASS(G_OBJECT(m_playbin
)),
1551 g_object_set(G_OBJECT(m_playbin
), "volume", dVolume
, NULL
);
1556 wxLogTrace(wxTRACE_GStreamer
,
1557 wxT("SetVolume: volume prop not found - 0.8.5 of ")
1558 wxT("gst-plugins probably needed"));
1563 double wxGStreamerMediaBackend::GetVolume()
1565 double dVolume
= 1.0;
1567 if(g_object_class_find_property(
1568 G_OBJECT_GET_CLASS(G_OBJECT(m_playbin
)),
1571 g_object_get(G_OBJECT(m_playbin
), "volume", &dVolume
, NULL
);
1575 wxLogTrace(wxTRACE_GStreamer
,
1576 wxT("GetVolume: volume prop not found - 0.8.5 of ")
1577 wxT("gst-plugins probably needed"));
1583 #endif //wxUSE_GSTREAMER
1585 // Force link into main library so this backend can be loaded
1586 #include "wx/html/forcelnk.h"
1587 FORCE_LINK_ME(basewxmediabackends
)
1589 #endif //wxUSE_MEDIACTRL