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/filesys.h" // FileNameToURL()
38 #include "wx/thread.h" // wxMutex/wxMutexLocker
43 #include "wx/gtk/private/gtk2-compat.h"
46 //-----------------------------------------------------------------------------
47 // Discussion of internals
48 //-----------------------------------------------------------------------------
51 This is the GStreamer backend for unix. Currently we require 0.8 or
52 0.10. Here we use the "playbin" GstElement for ease of use.
54 Note that now we compare state change functions to GST_STATE_FAILURE
55 now rather than GST_STATE_SUCCESS as newer gstreamer versions return
56 non-success values for returns that are otherwise successful but not
59 Also this probably doesn't work with anything other than wxGTK at the
60 moment but with a tad bit of work it could theorectically work in
63 One last note is that resuming from pausing/seeking can result
64 in erratic video playback (GStreamer-based bug, happens in totem as well)
65 - this is better in 0.10, however. One thing that might make it worse
66 here is that we don't preserve the aspect ratio of the video and stretch
67 it to the whole window.
69 Note that there are some things used here that could be undocumented -
70 for reference see the media player Kiss and Totem as well as some
71 other sources. There was a backend for a kde media player as well
72 that attempted thread-safety...
74 Then there is the issue of m_asynclock. This serves several purposes:
75 1) It prevents the C callbacks from sending wx state change events
76 so that we don't get duplicate ones in 0.8
77 2) It makes the sync and async handlers in 0.10 not drop any
78 messages so that while we are polling it we get the messages in
79 SyncStateChange instead of the queue.
80 3) Keeps the pausing in Stop() synchronous
82 RN: Note that I've tried to follow the wxGTK conventions here as close
83 as possible. In the implementation the C Callbacks come first, then
84 the internal functions, then the public ones. Set your vi to 80
88 //=============================================================================
90 //=============================================================================
92 //-----------------------------------------------------------------------------
93 // GStreamer (most version compatibility) macros
94 //-----------------------------------------------------------------------------
96 // In 0.9 there was a HUGE change to GstQuery and the
97 // gst_element_query function changed dramatically and split off
98 // into two separate ones
99 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR <= 8
100 # define wxGst_element_query_duration(e, f, p) \
101 gst_element_query(e, GST_QUERY_TOTAL, f, p)
102 # define wxGst_element_query_position(e, f, p) \
103 gst_element_query(e, GST_QUERY_POSITION, f, p)
104 #elif GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR == 9
105 // However, the actual 0.9 version has a slightly different definition
106 // and instead of gst_element_query_duration it has two parameters to
107 // gst_element_query_position instead
108 # define wxGst_element_query_duration(e, f, p) \
109 gst_element_query_position(e, f, 0, p)
110 # define wxGst_element_query_position(e, f, p) \
111 gst_element_query_position(e, f, p, 0)
113 # define wxGst_element_query_duration \
114 gst_element_query_duration
115 # define wxGst_element_query_position \
116 gst_element_query_position
120 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
121 # define GST_STATE_FAILURE GST_STATE_CHANGE_FAILURE
122 # define GST_STATE_SUCCESS GST_STATE_CHANGE_SUCCESS
123 # define GstElementState GstState
124 # define gst_gconf_get_default_video_sink() \
125 gst_element_factory_make ("gconfvideosink", "video-sink");
126 # define gst_gconf_get_default_audio_sink() \
127 gst_element_factory_make ("gconfaudiosink", "audio-sink");
130 // Max wait time for element state waiting - GST_CLOCK_TIME_NONE for inf
131 #define wxGSTREAMER_TIMEOUT (100 * GST_MSECOND) // Max 100 milliseconds
133 //-----------------------------------------------------------------------------
134 // wxLogTrace mask string
135 //-----------------------------------------------------------------------------
136 #define wxTRACE_GStreamer wxT("GStreamer")
138 //-----------------------------------------------------------------------------
140 // wxGStreamerMediaBackend
142 //-----------------------------------------------------------------------------
143 class WXDLLIMPEXP_MEDIA
144 wxGStreamerMediaBackend
: public wxMediaBackendCommonBase
148 wxGStreamerMediaBackend();
149 virtual ~wxGStreamerMediaBackend();
151 virtual bool CreateControl(wxControl
* ctrl
, wxWindow
* parent
,
156 const wxValidator
& validator
,
157 const wxString
& name
);
160 virtual bool Pause();
163 virtual bool Load(const wxString
& fileName
);
164 virtual bool Load(const wxURI
& location
);
165 virtual bool Load(const wxURI
& location
,
167 { return wxMediaBackendCommonBase::Load(location
, proxy
); }
170 virtual wxMediaState
GetState();
172 virtual bool SetPosition(wxLongLong where
);
173 virtual wxLongLong
GetPosition();
174 virtual wxLongLong
GetDuration();
176 virtual void Move(int x
, int y
, int w
, int h
);
177 wxSize
GetVideoSize() const;
179 virtual double GetPlaybackRate();
180 virtual bool SetPlaybackRate(double dRate
);
182 virtual wxLongLong
GetDownloadProgress();
183 virtual wxLongLong
GetDownloadTotal();
185 virtual bool SetVolume(double dVolume
);
186 virtual double GetVolume();
188 //------------implementation from now on-----------------------------------
189 bool DoLoad(const wxString
& locstring
);
190 wxMediaCtrl
* GetControl() { return m_ctrl
; } // for C Callbacks
191 void HandleStateChange(GstElementState oldstate
, GstElementState newstate
);
192 bool QueryVideoSizeFromElement(GstElement
* element
);
193 bool QueryVideoSizeFromPad(GstPad
* caps
);
194 void SetupXOverlay();
195 bool SyncStateChange(GstElement
* element
, GstElementState state
,
196 gint64 llTimeout
= wxGSTREAMER_TIMEOUT
);
197 bool TryAudioSink(GstElement
* audiosink
);
198 bool TryVideoSink(GstElement
* videosink
);
200 GstElement
* m_playbin
; // GStreamer media element
201 wxSize m_videoSize
; // Cached actual video size
202 double m_dRate
; // Current playback rate -
203 // see GetPlaybackRate for notes
204 wxLongLong m_llPausedPos
; // Paused position - see Pause()
205 GstXOverlay
* m_xoverlay
; // X Overlay that contains the GST video
206 wxMutex m_asynclock
; // See "discussion of internals"
207 class wxGStreamerMediaEventHandler
* m_eventHandler
; // see below
209 friend class wxGStreamerMediaEventHandler
;
210 friend class wxGStreamerLoadWaitTimer
;
211 DECLARE_DYNAMIC_CLASS(wxGStreamerMediaBackend
)
214 //-----------------------------------------------------------------------------
215 // wxGStreamerMediaEventHandler
217 // OK, this will take an explanation - basically gstreamer callbacks
218 // are issued in a separate thread, and in this thread we may not set
219 // the state of the playbin, so we need to send a wx event in that
220 // callback so that we set the state of the media and other stuff
222 //-----------------------------------------------------------------------------
223 class wxGStreamerMediaEventHandler
: public wxEvtHandler
226 wxGStreamerMediaEventHandler(wxGStreamerMediaBackend
* be
) : m_be(be
)
228 this->Connect(wxID_ANY
, wxEVT_MEDIA_FINISHED
,
229 wxMediaEventHandler(wxGStreamerMediaEventHandler::OnMediaFinish
));
232 void OnMediaFinish(wxMediaEvent
& event
);
234 wxGStreamerMediaBackend
* m_be
;
237 //=============================================================================
239 //=============================================================================
241 IMPLEMENT_DYNAMIC_CLASS(wxGStreamerMediaBackend
, wxMediaBackend
)
243 //-----------------------------------------------------------------------------
247 //-----------------------------------------------------------------------------
249 //-----------------------------------------------------------------------------
250 // "expose_event" from m_ctrl->m_wxwindow
252 // Handle GTK expose event from our window - here we hopefully
253 // redraw the video in the case of pausing and other instances...
254 // (Returns TRUE to pass to other handlers, FALSE if not)
256 // TODO: Do a DEBUG_MAIN_THREAD/install_idle_handler here?
257 //-----------------------------------------------------------------------------
262 draw(GtkWidget
* widget
, cairo_t
* cr
, wxGStreamerMediaBackend
* be
)
264 expose_event(GtkWidget
* widget
, GdkEventExpose
* event
, wxGStreamerMediaBackend
* be
)
267 // I've seen this recommended somewhere...
268 // TODO: Is this needed? Maybe it is just cruft...
269 // gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(be->m_xoverlay),
270 // GDK_WINDOW_XWINDOW( window ) );
272 // If we have actual video.....
273 if(!(be
->m_videoSize
.x
==0&&be
->m_videoSize
.y
==0) &&
274 GST_STATE(be
->m_playbin
) >= GST_STATE_PAUSED
)
276 // GST Doesn't redraw automatically while paused
277 // Plus, the video sometimes doesn't redraw when it looses focus
278 // or is painted over so we just tell it to redraw...
279 gst_x_overlay_expose(be
->m_xoverlay
);
283 // draw a black background like some other backends do....
286 gtk_widget_get_allocation(widget
, &a
);
287 cairo_rectangle(cr
, 0, 0, a
.width
, a
.height
);
288 cairo_set_source_rgb(cr
, 0, 0, 0);
291 gdk_draw_rectangle (event
->window
, widget
->style
->black_gc
, TRUE
, 0, 0,
292 widget
->allocation
.width
,
293 widget
->allocation
.height
);
302 //-----------------------------------------------------------------------------
303 // "realize" from m_ctrl->m_wxwindow
305 // If the window wasn't realized when Load was called, this is the
306 // callback for when it is - the purpose of which is to tell
307 // GStreamer to play the video in our control
308 //-----------------------------------------------------------------------------
311 static gint
gtk_window_realize_callback(GtkWidget
* widget
,
312 wxGStreamerMediaBackend
* be
)
316 GdkWindow
* window
= gtk_widget_get_window(widget
);
319 gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(be
->m_xoverlay
),
320 GDK_WINDOW_XID(window
)
322 g_signal_connect (be
->GetControl()->m_wxwindow
,
324 "draw", G_CALLBACK(draw
),
326 "expose_event", G_CALLBACK(expose_event
),
334 //-----------------------------------------------------------------------------
335 // "state-change" from m_playbin/GST_MESSAGE_STATE_CHANGE
337 // Called by gstreamer when the state changes - here we
338 // send the appropriate corresponding wx event.
340 // 0.8 only as HandleStateChange does this in both versions
341 //-----------------------------------------------------------------------------
342 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10
344 static void gst_state_change_callback(GstElement
*play
,
345 GstElementState oldstate
,
346 GstElementState newstate
,
347 wxGStreamerMediaBackend
* be
)
349 if(be
->m_asynclock
.TryLock() == wxMUTEX_NO_ERROR
)
351 be
->HandleStateChange(oldstate
, newstate
);
352 be
->m_asynclock
.Unlock();
358 //-----------------------------------------------------------------------------
359 // "eos" from m_playbin/GST_MESSAGE_EOS
361 // Called by gstreamer when the media is done playing ("end of stream")
362 //-----------------------------------------------------------------------------
364 static void gst_finish_callback(GstElement
*WXUNUSED(play
),
365 wxGStreamerMediaBackend
* be
)
367 wxLogTrace(wxTRACE_GStreamer
, wxT("gst_finish_callback"));
368 wxMediaEvent
event(wxEVT_MEDIA_FINISHED
);
369 be
->m_eventHandler
->AddPendingEvent(event
);
373 //-----------------------------------------------------------------------------
374 // "error" from m_playbin/GST_MESSAGE_ERROR
376 // Called by gstreamer when an error is encountered playing the media -
377 // We call wxLogTrace in addition wxLogSysError so that we can get it
378 // on the command line as well for those who want extra traces.
379 //-----------------------------------------------------------------------------
381 static void gst_error_callback(GstElement
*WXUNUSED(play
),
382 GstElement
*WXUNUSED(src
),
385 wxGStreamerMediaBackend
* WXUNUSED(be
))
388 sError
.Printf(wxT("gst_error_callback\n")
389 wxT("Error Message:%s\nDebug:%s\n"),
390 (const wxChar
*)wxConvUTF8
.cMB2WX(err
->message
),
391 (const wxChar
*)wxConvUTF8
.cMB2WX(debug
));
392 wxLogTrace(wxTRACE_GStreamer
, sError
);
393 wxLogSysError(sError
);
397 //-----------------------------------------------------------------------------
398 // "notify::caps" from the videopad inside "stream-info" of m_playbin
400 // Called by gstreamer when the video caps for the media is ready - currently
401 // we use the caps to get the natural size of the video
404 //-----------------------------------------------------------------------------
406 static void gst_notify_caps_callback(GstPad
* pad
,
407 GParamSpec
* WXUNUSED(pspec
),
408 wxGStreamerMediaBackend
* be
)
410 wxLogTrace(wxTRACE_GStreamer
, wxT("gst_notify_caps_callback"));
411 be
->QueryVideoSizeFromPad(pad
);
415 //-----------------------------------------------------------------------------
416 // "notify::stream-info" from m_playbin
418 // Run through the stuff in "stream-info" of m_playbin for a valid
419 // video pad, and then attempt to query the video size from it - if not
420 // set up an event to do so when ready.
422 // Currently unused - now we just query it directly using
423 // QueryVideoSizeFromElement.
426 //-----------------------------------------------------------------------------
427 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
429 static void gst_notify_stream_info_callback(GstElement
* WXUNUSED(element
),
430 GParamSpec
* WXUNUSED(pspec
),
431 wxGStreamerMediaBackend
* be
)
433 wxLogTrace(wxTRACE_GStreamer
, wxT("gst_notify_stream_info_callback"));
434 be
->QueryVideoSizeFromElement(be
->m_playbin
);
439 //-----------------------------------------------------------------------------
440 // "desired-size-changed" from m_xoverlay
442 // 0.8-specific this provides us with the video size when it changes -
443 // even though we get the caps as well this seems to come before the
444 // caps notification does...
446 // Note it will return 16,16 for an early-bird value or for audio
447 //-----------------------------------------------------------------------------
448 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10
450 static void gst_desired_size_changed_callback(GstElement
* play
,
451 guint width
, guint height
,
452 wxGStreamerMediaBackend
* be
)
454 if(!(width
== 16 && height
== 16))
456 be
->m_videoSize
.x
= width
;
457 be
->m_videoSize
.y
= height
;
460 be
->QueryVideoSizeFromElement(be
->m_playbin
);
465 //-----------------------------------------------------------------------------
466 // gst_bus_async_callback [static]
467 // gst_bus_sync_callback [static]
469 // Called by m_playbin for notifications such as end-of-stream in 0.10 -
470 // in previous versions g_signal notifications were used. Because everything
471 // in centered in one switch statement though it reminds one of old WinAPI
474 // gst_bus_sync_callback is that sync version that is called on the main GUI
475 // thread before the async version that we use to set the xwindow id of the
476 // XOverlay (NB: This isn't currently used - see CreateControl()).
477 //-----------------------------------------------------------------------------
478 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
480 static gboolean
gst_bus_async_callback(GstBus
* WXUNUSED(bus
),
482 wxGStreamerMediaBackend
* be
)
484 if(((GstElement
*)GST_MESSAGE_SRC(message
)) != be
->m_playbin
)
486 if(be
->m_asynclock
.TryLock() != wxMUTEX_NO_ERROR
)
489 switch(GST_MESSAGE_TYPE(message
))
491 case GST_MESSAGE_STATE_CHANGED
:
493 GstState oldstate
, newstate
, pendingstate
;
494 gst_message_parse_state_changed(message
, &oldstate
,
495 &newstate
, &pendingstate
);
496 be
->HandleStateChange(oldstate
, newstate
);
499 case GST_MESSAGE_EOS
:
501 gst_finish_callback(NULL
, be
);
504 case GST_MESSAGE_ERROR
:
508 gst_message_parse_error(message
, &error
, &debug
);
509 gst_error_callback(NULL
, NULL
, error
, debug
, be
);
516 be
->m_asynclock
.Unlock();
517 return FALSE
; // remove the message from Z queue
520 static GstBusSyncReply
gst_bus_sync_callback(GstBus
* bus
,
522 wxGStreamerMediaBackend
* be
)
524 // Pass a non-xwindowid-setting event on to the async handler where it
526 if (GST_MESSAGE_TYPE (message
) != GST_MESSAGE_ELEMENT
||
527 !gst_structure_has_name (message
->structure
, "prepare-xwindow-id"))
530 // NB: Unfortunately, the async callback can be quite
531 // buggy at times and often doesn't get called at all,
532 // so here we are processing it right here in the calling
533 // thread instead of the GUI one...
535 if(gst_bus_async_callback(bus
, message
, be
))
541 wxLogTrace(wxTRACE_GStreamer
, wxT("Got prepare-xwindow-id"));
543 return GST_BUS_DROP
; // We handled this message - drop from the queue
548 //-----------------------------------------------------------------------------
550 // Private (although not in the C++ sense) methods
552 //-----------------------------------------------------------------------------
554 //-----------------------------------------------------------------------------
555 // wxGStreamerMediaBackend::HandleStateChange
557 // Handles a state change event from our C Callback for "state-change" or
558 // the async queue in 0.10. (Mostly this is here to avoid locking the
559 // the mutex twice...)
560 //-----------------------------------------------------------------------------
561 void wxGStreamerMediaBackend::HandleStateChange(GstElementState oldstate
,
562 GstElementState newstate
)
566 case GST_STATE_PLAYING
:
567 wxLogTrace(wxTRACE_GStreamer
, wxT("Play event"));
570 case GST_STATE_PAUSED
:
571 // For some reason .10 sends a lot of oldstate == newstate
572 // messages - most likely for pending ones - also
573 // !<GST_STATE_PAUSED as we are only concerned
574 if(oldstate
< GST_STATE_PAUSED
|| oldstate
== newstate
)
576 if(wxGStreamerMediaBackend::GetPosition() != 0)
578 wxLogTrace(wxTRACE_GStreamer
, wxT("Pause event"));
583 wxLogTrace(wxTRACE_GStreamer
, wxT("Stop event"));
587 default: // GST_STATE_NULL etc.
592 //-----------------------------------------------------------------------------
593 // wxGStreamerMediaBackend::QueryVideoSizeFromElement
595 // Run through the stuff in "stream-info" of element for a valid
596 // video pad, and then attempt to query the video size from it - if not
597 // set up an event to do so when ready. Return true
598 // if we got a valid video pad.
599 //-----------------------------------------------------------------------------
600 bool wxGStreamerMediaBackend::QueryVideoSizeFromElement(GstElement
* element
)
602 const GList
*list
= NULL
;
603 g_object_get (G_OBJECT (element
), "stream-info", &list
, NULL
);
605 for ( ; list
!= NULL
; list
= list
->next
)
607 GObject
*info
= (GObject
*) list
->data
;
613 g_object_get (info
, "type", &type
, NULL
);
614 pspec
= g_object_class_find_property (
615 G_OBJECT_GET_CLASS (info
), "type");
616 val
= g_enum_get_value (G_PARAM_SPEC_ENUM (pspec
)->enum_class
, type
);
618 if (!strncasecmp(val
->value_name
, "video", 5) ||
619 !strncmp(val
->value_name
, "GST_STREAM_TYPE_VIDEO", 21))
621 // Newer gstreamer 0.8+ plugins are SUPPOSED to have "object"...
622 // but a lot of old plugins still use "pad" :)
623 pspec
= g_object_class_find_property (
624 G_OBJECT_GET_CLASS (info
), "object");
627 g_object_get (info
, "pad", &pad
, NULL
);
629 g_object_get (info
, "object", &pad
, NULL
);
631 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR <= 8
632 // Killed in 0.9, presumely because events and such
633 // should be pushed on pads regardless of whether they
634 // are currently linked
635 pad
= (GstPad
*) GST_PAD_REALIZE (pad
);
639 if(!QueryVideoSizeFromPad(pad
))
641 // wait for those caps to get ready
645 G_CALLBACK(gst_notify_caps_callback
),
650 }// end searching through info list
652 // no video (or extremely delayed stream-info)
655 m_videoSize
= wxSize(0,0);
662 //-----------------------------------------------------------------------------
663 // wxGStreamerMediaBackend::QueryVideoSizeFromPad
665 // Gets the size of our video (in wxSize) from a GstPad
666 //-----------------------------------------------------------------------------
667 bool wxGStreamerMediaBackend::QueryVideoSizeFromPad(GstPad
* pad
)
669 const GstCaps
* caps
= GST_PAD_CAPS(pad
);
672 const GstStructure
*s
= gst_caps_get_structure (caps
, 0);
675 gst_structure_get_int (s
, "width", &m_videoSize
.x
);
676 gst_structure_get_int (s
, "height", &m_videoSize
.y
);
679 par
= gst_structure_get_value (s
, "pixel-aspect-ratio");
683 wxLogTrace(wxTRACE_GStreamer
,
684 wxT("pixel-aspect-ratio found in pad"));
685 int num
= par
->data
[0].v_int
,
686 den
= par
->data
[1].v_int
;
688 // TODO: maybe better fraction normalization...
690 m_videoSize
.x
= (int) ((float) num
* m_videoSize
.x
/ den
);
692 m_videoSize
.y
= (int) ((float) den
* m_videoSize
.y
/ num
);
695 wxLogTrace(wxTRACE_GStreamer
, wxT("Adjusted video size: [%i,%i]"),
696 m_videoSize
.x
, m_videoSize
.y
);
700 return false; // not ready/massive failure
703 //-----------------------------------------------------------------------------
704 // wxGStreamerMediaBackend::SetupXOverlay
706 // Attempts to set the XWindow id of our GstXOverlay to tell it which
707 // window to play video in.
708 //-----------------------------------------------------------------------------
709 void wxGStreamerMediaBackend::SetupXOverlay()
711 // Use the xoverlay extension to tell gstreamer to play in our window
713 if (!gtk_widget_get_realized(m_ctrl
->m_wxwindow
))
715 // Not realized yet - set to connect at realization time
716 g_signal_connect (m_ctrl
->m_wxwindow
,
718 G_CALLBACK (gtk_window_realize_callback
),
725 GdkWindow
* window
= gtk_widget_get_window(m_ctrl
->m_wxwindow
);
728 gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(m_xoverlay
),
730 GDK_WINDOW_XID(window
)
736 g_signal_connect(m_ctrl
->m_wxwindow
,
738 "draw", G_CALLBACK(draw
),
740 "expose_event", G_CALLBACK(expose_event
),
743 } // end if GtkPizza realized
747 //-----------------------------------------------------------------------------
748 // wxGStreamerMediaBackend::SyncStateChange
750 // This function is rather complex - basically the idea is that we
751 // poll the GstBus of m_playbin until it has reached desiredstate, an error
752 // is reached, or there are no more messages left in the GstBus queue.
754 // Returns true if there are no messages left in the queue or
755 // the current state reaches the disired state.
757 // PRECONDITION: Assumes m_asynclock is Lock()ed
758 //-----------------------------------------------------------------------------
759 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
760 bool wxGStreamerMediaBackend::SyncStateChange(GstElement
* element
,
761 GstElementState desiredstate
,
764 GstBus
* bus
= gst_element_get_bus(element
);
768 gint64 llTimeWaited
= 0;
773 // NB: The GStreamer gst_bus_poll is unfortunately broken and
774 // throws silly critical internal errors (for instance
775 // "message != NULL" when the whole point of it is to
776 // poll for the message in the first place!) so we implement
777 // our own "waiting mechinism"
778 if(gst_bus_have_pending(bus
) == FALSE
)
780 if(llTimeWaited
>= llTimeout
)
781 return true; // Reached timeout... assume success
782 llTimeWaited
+= 10*GST_MSECOND
;
787 message
= gst_bus_pop(bus
);
789 message
= gst_bus_poll(bus
, (GstMessageType
)
790 (GST_MESSAGE_STATE_CHANGED
|
792 GST_MESSAGE_EOS
), llTimeout
);
796 if(((GstElement
*)GST_MESSAGE_SRC(message
)) == element
)
798 switch(GST_MESSAGE_TYPE(message
))
800 case GST_MESSAGE_STATE_CHANGED
:
802 GstState oldstate
, newstate
, pendingstate
;
803 gst_message_parse_state_changed(message
, &oldstate
,
804 &newstate
, &pendingstate
);
805 if(newstate
== desiredstate
)
807 bSuccess
= bBreak
= true;
811 case GST_MESSAGE_ERROR
:
815 gst_message_parse_error(message
, &error
, &debug
);
816 gst_error_callback(NULL
, NULL
, error
, debug
, this);
820 case GST_MESSAGE_EOS
:
821 wxLogSysError(wxT("Reached end of stream prematurely"));
825 break; // not handled
829 gst_message_unref(message
);
834 #else // 0.8 implementation
835 bool wxGStreamerMediaBackend::SyncStateChange(GstElement
* element
,
836 GstElementState desiredstate
,
839 gint64 llTimeWaited
= 0;
840 while(GST_STATE(element
) != desiredstate
)
842 if(llTimeWaited
>= llTimeout
)
844 llTimeWaited
+= 10*GST_MSECOND
;
848 return llTimeWaited
!= llTimeout
;
852 //-----------------------------------------------------------------------------
853 // wxGStreamerMediaBackend::TryAudioSink
854 // wxGStreamerMediaBackend::TryVideoSink
856 // Uses various means to determine whether a passed in video/audio sink
857 // if suitable for us - if it is not we return false and unref the
858 // inappropriate sink.
859 //-----------------------------------------------------------------------------
860 bool wxGStreamerMediaBackend::TryAudioSink(GstElement
* audiosink
)
862 if( !GST_IS_ELEMENT(audiosink
) )
864 if(G_IS_OBJECT(audiosink
))
865 g_object_unref(audiosink
);
872 bool wxGStreamerMediaBackend::TryVideoSink(GstElement
* videosink
)
874 // Check if the video sink either is an xoverlay or might contain one...
875 if( !GST_IS_BIN(videosink
) && !GST_IS_X_OVERLAY(videosink
) )
877 if(G_IS_OBJECT(videosink
))
878 g_object_unref(videosink
);
882 // Make our video sink and make sure it supports the x overlay interface
883 // the x overlay enables us to put the video in our control window
884 // (i.e. we NEED it!) - also connect to the natural video size change event
885 if( GST_IS_BIN(videosink
) )
886 m_xoverlay
= (GstXOverlay
*)
887 gst_bin_get_by_interface (GST_BIN (videosink
),
890 m_xoverlay
= (GstXOverlay
*) videosink
;
892 if ( !GST_IS_X_OVERLAY(m_xoverlay
) )
894 g_object_unref(videosink
);
901 //-----------------------------------------------------------------------------
902 // wxGStreamerMediaEventHandler::OnMediaFinish
904 // Called when the media is about to stop
905 //-----------------------------------------------------------------------------
906 void wxGStreamerMediaEventHandler::OnMediaFinish(wxMediaEvent
& WXUNUSED(event
))
908 // (RN - I have no idea why I thought this was good behaviour....
909 // maybe it made sense for streaming/nonseeking data but
910 // generally it seems like a really bad idea) -
911 if(m_be
->SendStopEvent())
913 // Stop the media (we need to set it back to paused
914 // so that people can get the duration et al.
915 // and send the finish event (luckily we can "Sync" it out... LOL!)
916 // (We don't check return values here because we can't really do
918 wxMutexLocker
lock(m_be
->m_asynclock
);
920 // Set element to ready+sync it
921 gst_element_set_state (m_be
->m_playbin
, GST_STATE_READY
);
922 m_be
->SyncStateChange(m_be
->m_playbin
, GST_STATE_READY
);
924 // Now set it to paused + update pause pos to 0 and
925 // Sync that as well (note that we don't call Stop() here
926 // due to mutex issues)
927 gst_element_set_state (m_be
->m_playbin
, GST_STATE_PAUSED
);
928 m_be
->SyncStateChange(m_be
->m_playbin
, GST_STATE_PAUSED
);
929 m_be
->m_llPausedPos
= 0;
931 // Finally, queue the finish event
932 m_be
->QueueFinishEvent();
936 //-----------------------------------------------------------------------------
940 //-----------------------------------------------------------------------------
942 //-----------------------------------------------------------------------------
943 // wxGStreamerMediaBackend Constructor
945 // Sets m_playbin to NULL signifying we havn't loaded anything yet
946 //-----------------------------------------------------------------------------
947 wxGStreamerMediaBackend::wxGStreamerMediaBackend()
953 //-----------------------------------------------------------------------------
954 // wxGStreamerMediaBackend Destructor
956 // Stops/cleans up memory
958 // NB: This could trigger a critical warning but doing a SyncStateChange
959 // here is just going to slow down quitting of the app, which is bad.
960 //-----------------------------------------------------------------------------
961 wxGStreamerMediaBackend::~wxGStreamerMediaBackend()
963 // Dispose of the main player and related objects
966 wxASSERT( GST_IS_OBJECT(m_playbin
) );
967 gst_element_set_state (m_playbin
, GST_STATE_NULL
);
968 gst_object_unref (GST_OBJECT (m_playbin
));
969 delete m_eventHandler
;
973 //-----------------------------------------------------------------------------
974 // wxGStreamerMediaBackend::CreateControl
976 // Initializes GStreamer and creates the wx side of our media control
977 //-----------------------------------------------------------------------------
978 bool wxGStreamerMediaBackend::CreateControl(wxControl
* ctrl
, wxWindow
* parent
,
983 const wxValidator
& validator
,
984 const wxString
& name
)
990 //Convert arguments to unicode if enabled
993 char **argvGST
= new char*[wxTheApp
->argc
+ 1];
994 for ( i
= 0; i
< wxTheApp
->argc
; i
++ )
996 argvGST
[i
] = wxStrdupA(wxTheApp
->argv
[i
].utf8_str());
999 argvGST
[wxTheApp
->argc
] = NULL
;
1001 int argcGST
= wxTheApp
->argc
;
1003 #define argcGST wxTheApp->argc
1004 #define argvGST wxTheApp->argv
1007 //Really init gstreamer
1009 GError
* error
= NULL
;
1010 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
1011 bInited
= gst_init_check(&argcGST
, &argvGST
, &error
);
1013 bInited
= gst_init_check(&argcGST
, &argvGST
);
1016 // Cleanup arguments for unicode case
1018 for ( i
= 0; i
< argcGST
; i
++ )
1026 if(!bInited
) //gst_init_check fail?
1030 wxLogSysError(wxT("Could not initialize GStreamer\n")
1031 wxT("Error Message:%s"),
1032 (const wxChar
*) wxConvUTF8
.cMB2WX(error
->message
)
1034 g_error_free(error
);
1037 wxLogSysError(wxT("Could not initialize GStreamer"));
1043 // wxControl creation
1045 m_ctrl
= wxStaticCast(ctrl
, wxMediaCtrl
);
1048 // We handle our own GTK expose events
1049 m_ctrl
->m_noExpose
= true;
1052 if( !m_ctrl
->wxControl::Create(parent
, id
, pos
, size
,
1053 style
, // TODO: remove borders???
1056 wxFAIL_MSG(wxT("Could not create wxControl!!!"));
1061 // Turn off double-buffering so that
1062 // so it doesn't draw over the video and cause sporadic
1063 // disappearances of the video
1064 gtk_widget_set_double_buffered(m_ctrl
->m_wxwindow
, FALSE
);
1067 // don't erase the background of our control window
1068 // so that resizing is a bit smoother
1069 m_ctrl
->SetBackgroundStyle(wxBG_STYLE_CUSTOM
);
1071 // Create our playbin object
1072 m_playbin
= gst_element_factory_make ("playbin", "play");
1073 if (!GST_IS_ELEMENT(m_playbin
))
1075 if(G_IS_OBJECT(m_playbin
))
1076 g_object_unref(m_playbin
);
1077 wxLogSysError(wxT("Got an invalid playbin"));
1081 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10
1082 // Connect the glib events/callbacks we want to our playbin
1083 g_signal_connect(m_playbin
, "eos",
1084 G_CALLBACK(gst_finish_callback
), this);
1085 g_signal_connect(m_playbin
, "error",
1086 G_CALLBACK(gst_error_callback
), this);
1087 g_signal_connect(m_playbin
, "state-change",
1088 G_CALLBACK(gst_state_change_callback
), this);
1090 // GStreamer 0.10+ uses GstBus for this now, connect to the sync
1091 // handler as well so we can set the X window id of our xoverlay
1092 gst_bus_add_watch (gst_element_get_bus(m_playbin
),
1093 (GstBusFunc
) gst_bus_async_callback
, this);
1094 gst_bus_set_sync_handler(gst_element_get_bus(m_playbin
),
1095 (GstBusSyncHandler
) gst_bus_sync_callback
, this);
1096 g_signal_connect(m_playbin
, "notify::stream-info",
1097 G_CALLBACK(gst_notify_stream_info_callback
), this);
1100 // Get the audio sink
1101 GstElement
* audiosink
= gst_gconf_get_default_audio_sink();
1102 if( !TryAudioSink(audiosink
) )
1104 // fallback to autodetection, then alsa, then oss as a stopgap
1105 audiosink
= gst_element_factory_make ("autoaudiosink", "audio-sink");
1106 if( !TryAudioSink(audiosink
) )
1108 audiosink
= gst_element_factory_make ("alsasink", "alsa-output");
1109 if( !TryAudioSink(audiosink
) )
1111 audiosink
= gst_element_factory_make ("osssink", "play_audio");
1112 if( !TryAudioSink(audiosink
) )
1114 wxLogSysError(wxT("Could not find a valid audiosink"));
1121 // Setup video sink - first try gconf, then auto, then xvimage and
1122 // then finally plain ximage
1123 GstElement
* videosink
= gst_gconf_get_default_video_sink();
1124 if( !TryVideoSink(videosink
) )
1126 videosink
= gst_element_factory_make ("autovideosink", "video-sink");
1127 if( !TryVideoSink(videosink
) )
1129 videosink
= gst_element_factory_make ("xvimagesink", "video-sink");
1130 if( !TryVideoSink(videosink
) )
1132 // finally, do a final fallback to ximagesink
1134 gst_element_factory_make ("ximagesink", "video-sink");
1135 if( !TryVideoSink(videosink
) )
1137 g_object_unref(audiosink
);
1138 wxLogSysError(wxT("Could not find a suitable video sink"));
1145 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10
1146 // Not on 0.10... called when video size changes
1147 g_signal_connect(m_xoverlay
, "desired-size-changed",
1148 G_CALLBACK(gst_desired_size_changed_callback
), this);
1150 // Tell GStreamer which window to draw to in 0.8 - 0.10
1151 // sometimes needs this too...
1154 // Now that we know (or, rather think) our video and audio sink
1155 // are valid set our playbin to use them
1156 g_object_set (G_OBJECT (m_playbin
),
1157 "video-sink", videosink
,
1158 "audio-sink", audiosink
,
1161 m_eventHandler
= new wxGStreamerMediaEventHandler(this);
1165 //-----------------------------------------------------------------------------
1166 // wxGStreamerMediaBackend::Load (File version)
1168 // Just calls DoLoad() with a prepended file scheme
1169 //-----------------------------------------------------------------------------
1170 bool wxGStreamerMediaBackend::Load(const wxString
& fileName
)
1172 return DoLoad(wxFileSystem::FileNameToURL(fileName
));
1175 //-----------------------------------------------------------------------------
1176 // wxGStreamerMediaBackend::Load (URI version)
1178 // In the case of a file URI passes it unencoded -
1179 // also, as of 0.10.3 and earlier GstURI (the uri parser for gstreamer)
1180 // is sort of broken and only accepts uris with at least two slashes
1181 // after the scheme (i.e. file: == not ok, file:// == ok)
1182 //-----------------------------------------------------------------------------
1183 bool wxGStreamerMediaBackend::Load(const wxURI
& location
)
1185 if(location
.GetScheme().CmpNoCase(wxT("file")) == 0)
1187 wxString uristring
= location
.BuildUnescapedURI();
1189 //Workaround GstURI leading "//" problem and make sure it leads
1191 return DoLoad(wxString(wxT("file://")) +
1192 uristring
.Right(uristring
.length() - 5)
1196 return DoLoad(location
.BuildURI());
1199 //-----------------------------------------------------------------------------
1200 // wxGStreamerMediaBackend::DoLoad
1203 // 1) Reset member variables and set playbin back to ready state
1204 // 2) Check URI for validity and then tell the playbin to load it
1205 // 3) Set the playbin to the pause state
1207 // NB: Even after this function is over with we probably don't have the
1208 // video size or duration - no amount of clever hacking is going to get
1209 // around that, unfortunately.
1210 //-----------------------------------------------------------------------------
1211 bool wxGStreamerMediaBackend::DoLoad(const wxString
& locstring
)
1213 wxMutexLocker
lock(m_asynclock
); // lock state events and async callbacks
1215 // Reset positions & rate
1218 m_videoSize
= wxSize(0,0);
1220 // Set playbin to ready to stop the current media...
1221 if( gst_element_set_state (m_playbin
,
1222 GST_STATE_READY
) == GST_STATE_FAILURE
||
1223 !SyncStateChange(m_playbin
, GST_STATE_READY
))
1225 wxLogSysError(wxT("wxGStreamerMediaBackend::Load - ")
1226 wxT("Could not set initial state to ready"));
1230 // free current media resources
1231 gst_element_set_state (m_playbin
, GST_STATE_NULL
);
1233 // Make sure the passed URI is valid and tell playbin to load it
1234 // non-file uris are encoded
1235 wxASSERT(gst_uri_protocol_is_valid("file"));
1236 wxASSERT(gst_uri_is_valid(locstring
.mb_str()));
1238 g_object_set (G_OBJECT (m_playbin
), "uri",
1239 (const char*)locstring
.mb_str(), NULL
);
1241 // Try to pause media as gstreamer won't let us query attributes
1242 // such as video size unless it is paused or playing
1243 if( gst_element_set_state (m_playbin
,
1244 GST_STATE_PAUSED
) == GST_STATE_FAILURE
||
1245 !SyncStateChange(m_playbin
, GST_STATE_PAUSED
))
1247 return false; // no real error message needed here as this is
1248 // generic failure 99% of the time (i.e. no
1249 // source etc.) and has an error message
1253 NotifyMovieLoaded(); // Notify the user - all we can do for now
1258 //-----------------------------------------------------------------------------
1259 // wxGStreamerMediaBackend::Play
1261 // Sets the stream to a playing state
1263 // THREAD-UNSAFE in 0.8, maybe in 0.10 as well
1264 //-----------------------------------------------------------------------------
1265 bool wxGStreamerMediaBackend::Play()
1267 if (gst_element_set_state (m_playbin
,
1268 GST_STATE_PLAYING
) == GST_STATE_FAILURE
)
1273 //-----------------------------------------------------------------------------
1274 // wxGStreamerMediaBackend::Pause
1276 // Marks where we paused and pauses the stream
1278 // THREAD-UNSAFE in 0.8, maybe in 0.10 as well
1279 //-----------------------------------------------------------------------------
1280 bool wxGStreamerMediaBackend::Pause()
1282 m_llPausedPos
= wxGStreamerMediaBackend::GetPosition();
1283 if (gst_element_set_state (m_playbin
,
1284 GST_STATE_PAUSED
) == GST_STATE_FAILURE
)
1289 //-----------------------------------------------------------------------------
1290 // wxGStreamerMediaBackend::Stop
1292 // Pauses the stream and sets the position to 0. Note that this is
1293 // synchronous (!) pausing.
1295 // Due to the mutex locking this is probably thread-safe actually.
1296 //-----------------------------------------------------------------------------
1297 bool wxGStreamerMediaBackend::Stop()
1299 { // begin state lock
1300 wxMutexLocker
lock(m_asynclock
);
1301 if(gst_element_set_state (m_playbin
,
1302 GST_STATE_PAUSED
) == GST_STATE_FAILURE
||
1303 !SyncStateChange(m_playbin
, GST_STATE_PAUSED
))
1305 wxLogSysError(wxT("Could not set state to paused for Stop()"));
1310 bool bSeekedOK
= wxGStreamerMediaBackend::SetPosition(0);
1314 wxLogSysError(wxT("Could not seek to initial position in Stop()"));
1318 QueueStopEvent(); // Success
1322 //-----------------------------------------------------------------------------
1323 // wxGStreamerMediaBackend::GetState
1325 // Gets the state of the media
1326 //-----------------------------------------------------------------------------
1327 wxMediaState
wxGStreamerMediaBackend::GetState()
1329 switch(GST_STATE(m_playbin
))
1331 case GST_STATE_PLAYING
:
1332 return wxMEDIASTATE_PLAYING
;
1333 case GST_STATE_PAUSED
:
1334 if (m_llPausedPos
== 0)
1335 return wxMEDIASTATE_STOPPED
;
1337 return wxMEDIASTATE_PAUSED
;
1338 default://case GST_STATE_READY:
1339 return wxMEDIASTATE_STOPPED
;
1343 //-----------------------------------------------------------------------------
1344 // wxGStreamerMediaBackend::GetPosition
1346 // If paused, returns our marked position - otherwise it queries the
1347 // GStreamer playbin for the position and returns that
1350 // NB: At least in 0.8, when you pause and seek gstreamer
1351 // NB: doesn't update the position sometimes, so we need to keep track of
1352 // NB: whether we have paused or not and keep track of the time after the
1353 // NB: pause and whenever the user seeks while paused
1356 // THREAD-UNSAFE, at least if not paused. Requires media to be at least paused.
1357 //-----------------------------------------------------------------------------
1358 wxLongLong
wxGStreamerMediaBackend::GetPosition()
1360 if(GetState() != wxMEDIASTATE_PLAYING
)
1361 return m_llPausedPos
;
1365 GstFormat fmtTime
= GST_FORMAT_TIME
;
1367 if (!wxGst_element_query_position(m_playbin
, &fmtTime
, &pos
) ||
1368 fmtTime
!= GST_FORMAT_TIME
|| pos
== -1)
1370 return pos
/ GST_MSECOND
;
1374 //-----------------------------------------------------------------------------
1375 // wxGStreamerMediaBackend::SetPosition
1377 // Sets the position of the stream
1378 // Note that GST_MSECOND is 1000000 (GStreamer uses nanoseconds - so
1379 // there is 1000000 nanoseconds in a millisecond)
1381 // If we are paused we update the cached pause position.
1383 // This is also an exceedingly ugly function due to the three implementations
1384 // (or, rather two plus one implementation without a seek function).
1386 // This is asynchronous and thread-safe on both 0.8 and 0.10.
1388 // NB: This fires both a stop and play event if the media was previously
1389 // playing... which in some ways makes sense. And yes, this makes the video
1390 // go all haywire at times - a gstreamer bug...
1391 //-----------------------------------------------------------------------------
1392 bool wxGStreamerMediaBackend::SetPosition(wxLongLong where
)
1394 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR == 8 \
1395 && GST_VERSION_MICRO == 0
1396 // 0.8.0 has no gst_element_seek according to official docs!!!
1397 wxLogSysError(wxT("GStreamer 0.8.0 does not have gst_element_seek")
1398 wxT(" according to official docs"));
1402 # if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
1403 gst_element_seek (m_playbin
, m_dRate
, GST_FORMAT_TIME
,
1404 (GstSeekFlags
)(GST_SEEK_FLAG_FLUSH
| GST_SEEK_FLAG_KEY_UNIT
),
1405 GST_SEEK_TYPE_SET
, where
.GetValue() * GST_MSECOND
,
1406 GST_SEEK_TYPE_NONE
, GST_CLOCK_TIME_NONE
);
1408 // NB: Some gstreamer versions return false basically all the time
1409 // here - even totem doesn't bother to check the return value here
1410 // so I guess we'll just assume it worked -
1411 // TODO: maybe check the gst error callback???
1412 gst_element_seek (m_playbin
, (GstSeekType
) (GST_SEEK_METHOD_SET
|
1413 GST_FORMAT_TIME
| GST_SEEK_FLAG_FLUSH
),
1414 where
.GetValue() * GST_MSECOND
);
1416 # endif // GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
1419 m_llPausedPos
= where
;
1426 //-----------------------------------------------------------------------------
1427 // wxGStreamerMediaBackend::GetDuration
1429 // Obtains the total time of our stream
1430 // THREAD-UNSAFE, requires media to be paused or playing
1431 //-----------------------------------------------------------------------------
1432 wxLongLong
wxGStreamerMediaBackend::GetDuration()
1435 GstFormat fmtTime
= GST_FORMAT_TIME
;
1437 if(!wxGst_element_query_duration(m_playbin
, &fmtTime
, &length
) ||
1438 fmtTime
!= GST_FORMAT_TIME
|| length
== -1)
1440 return length
/ GST_MSECOND
;
1443 //-----------------------------------------------------------------------------
1444 // wxGStreamerMediaBackend::Move
1446 // Called when the window is moved - GStreamer takes care of this
1447 // for us so nothing is needed
1448 //-----------------------------------------------------------------------------
1449 void wxGStreamerMediaBackend::Move(int WXUNUSED(x
),
1456 //-----------------------------------------------------------------------------
1457 // wxGStreamerMediaBackend::GetVideoSize
1459 // Returns our cached video size from Load/gst_notify_caps_callback
1460 // gst_x_overlay_get_desired_size also does this in 0.8...
1461 //-----------------------------------------------------------------------------
1462 wxSize
wxGStreamerMediaBackend::GetVideoSize() const
1467 //-----------------------------------------------------------------------------
1468 // wxGStreamerMediaBackend::GetPlaybackRate
1469 // wxGStreamerMediaBackend::SetPlaybackRate
1471 // Obtains/Sets the playback rate of the stream
1473 //TODO: PlaybackRate not currently supported via playbin directly -
1474 //TODO: Ronald S. Bultje noted on gstreamer-devel:
1476 //TODO: Like "play at twice normal speed"? Or "play at 25 fps and 44,1 kHz"? As
1477 //TODO: for the first, yes, we have elements for that, btu they"re not part of
1478 //TODO: playbin. You can create a bin (with a ghost pad) containing the actual
1479 //TODO: video/audiosink and the speed-changing element for this, and set that
1480 //TODO: element as video-sink or audio-sink property in playbin. The
1481 //TODO: audio-element is called "speed", the video-element is called "videodrop"
1482 //TODO: (although that appears to be deprecated in favour of "videorate", which
1483 //TODO: again cannot do this, so this may not work at all in the end). For
1484 //TODO: forcing frame/samplerates, see audioscale and videorate. Audioscale is
1485 //TODO: part of playbin.
1487 // In 0.10 GStreamer has new gst_element_seek API that might
1488 // support this - and I've got an attempt to do so but it is untested
1489 // but it would appear to work...
1490 //-----------------------------------------------------------------------------
1491 double wxGStreamerMediaBackend::GetPlaybackRate()
1493 return m_dRate
; // Could use GST_QUERY_RATE but the API doesn't seem
1494 // final on that yet and there may not be any actual
1495 // plugins that support it...
1498 bool wxGStreamerMediaBackend::SetPlaybackRate(double dRate
)
1500 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
1501 #if 0 // not tested enough
1502 if( gst_element_seek (m_playbin
, dRate
, GST_FORMAT_TIME
,
1503 (GstSeekFlags
)(GST_SEEK_FLAG_FLUSH
| GST_SEEK_FLAG_KEY_UNIT
),
1504 GST_SEEK_TYPE_CUR
, 0,
1505 GST_SEEK_TYPE_NONE
, GST_CLOCK_TIME_NONE
) )
1519 //-----------------------------------------------------------------------------
1520 // wxGStreamerMediaBackend::GetDownloadProgress
1522 // Not really outwardly possible - have been suggested that one could
1523 // get the information from the component that "downloads"
1524 //-----------------------------------------------------------------------------
1525 wxLongLong
wxGStreamerMediaBackend::GetDownloadProgress()
1530 //-----------------------------------------------------------------------------
1531 // wxGStreamerMediaBackend::GetDownloadTotal
1533 // TODO: Cache this?
1534 // NB: The length changes every call for some reason due to
1535 // GStreamer implementation issues
1536 // THREAD-UNSAFE, requires media to be paused or playing
1537 //-----------------------------------------------------------------------------
1538 wxLongLong
wxGStreamerMediaBackend::GetDownloadTotal()
1541 GstFormat fmtBytes
= GST_FORMAT_BYTES
;
1543 if (!wxGst_element_query_duration(m_playbin
, &fmtBytes
, &length
) ||
1544 fmtBytes
!= GST_FORMAT_BYTES
|| length
== -1)
1549 //-----------------------------------------------------------------------------
1550 // wxGStreamerMediaBackend::SetVolume
1551 // wxGStreamerMediaBackend::GetVolume
1553 // Sets/Gets the volume through the playbin object.
1554 // Note that this requires a relatively recent gst-plugins so we
1555 // check at runtime to see whether it is available or not otherwise
1556 // GST spits out an error on the command line
1557 //-----------------------------------------------------------------------------
1558 bool wxGStreamerMediaBackend::SetVolume(double dVolume
)
1560 if(g_object_class_find_property(
1561 G_OBJECT_GET_CLASS(G_OBJECT(m_playbin
)),
1564 g_object_set(G_OBJECT(m_playbin
), "volume", dVolume
, NULL
);
1569 wxLogTrace(wxTRACE_GStreamer
,
1570 wxT("SetVolume: volume prop not found - 0.8.5 of ")
1571 wxT("gst-plugins probably needed"));
1576 double wxGStreamerMediaBackend::GetVolume()
1578 double dVolume
= 1.0;
1580 if(g_object_class_find_property(
1581 G_OBJECT_GET_CLASS(G_OBJECT(m_playbin
)),
1584 g_object_get(G_OBJECT(m_playbin
), "volume", &dVolume
, NULL
);
1588 wxLogTrace(wxTRACE_GStreamer
,
1589 wxT("GetVolume: volume prop not found - 0.8.5 of ")
1590 wxT("gst-plugins probably needed"));
1596 #endif //wxUSE_GSTREAMER
1598 // Force link into main library so this backend can be loaded
1599 #include "wx/html/forcelnk.h"
1600 FORCE_LINK_ME(basewxmediabackends
)
1602 #endif //wxUSE_MEDIACTRL