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
40 # include "wx/gtk/win_gtk.h"
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 // wxGTK Debugging and idle stuff
133 //-----------------------------------------------------------------------------
138 # define DEBUG_MAIN_THREAD \
139 if (wxThread::IsMain() && g_mainThreadLocked) \
140 wxPrintf(wxT("gui reentrance"));
142 # define DEBUG_MAIN_THREAD
145 # define DEBUG_MAIN_THREAD
148 extern void wxapp_install_idle_handler();
149 extern bool g_isIdle
;
150 extern bool g_mainThreadLocked
;
153 //-----------------------------------------------------------------------------
154 // wxLogTrace mask string
155 //-----------------------------------------------------------------------------
156 #define wxTRACE_GStreamer wxT("GStreamer")
158 //-----------------------------------------------------------------------------
160 // wxGStreamerMediaBackend
162 //-----------------------------------------------------------------------------
163 class WXDLLIMPEXP_MEDIA
164 wxGStreamerMediaBackend
: public wxMediaBackendCommonBase
168 wxGStreamerMediaBackend();
169 ~wxGStreamerMediaBackend();
171 virtual bool CreateControl(wxControl
* ctrl
, wxWindow
* parent
,
176 const wxValidator
& validator
,
177 const wxString
& name
);
180 virtual bool Pause();
183 virtual bool Load(const wxString
& fileName
);
184 virtual bool Load(const wxURI
& location
);
186 virtual wxMediaState
GetState();
188 virtual bool SetPosition(wxLongLong where
);
189 virtual wxLongLong
GetPosition();
190 virtual wxLongLong
GetDuration();
192 virtual void Move(int x
, int y
, int w
, int h
);
193 wxSize
GetVideoSize() const;
195 virtual double GetPlaybackRate();
196 virtual bool SetPlaybackRate(double dRate
);
198 virtual wxLongLong
GetDownloadProgress();
199 virtual wxLongLong
GetDownloadTotal();
201 virtual bool SetVolume(double dVolume
);
202 virtual double GetVolume();
204 //------------implementation from now on-----------------------------------
205 bool DoLoad(const wxString
& locstring
);
206 wxMediaCtrl
* GetControl() { return m_ctrl
; } // for C Callbacks
207 void HandleStateChange(GstElementState oldstate
, GstElementState newstate
);
208 bool QueryVideoSizeFromElement(GstElement
* element
);
209 bool QueryVideoSizeFromPad(GstPad
* caps
);
210 void SetupXOverlay();
211 bool SyncStateChange(GstElement
* element
, GstElementState state
,
212 gint64 llTimeout
= wxGSTREAMER_TIMEOUT
);
213 bool TryAudioSink(GstElement
* audiosink
);
214 bool TryVideoSink(GstElement
* videosink
);
216 GstElement
* m_playbin
; // GStreamer media element
217 wxSize m_videoSize
; // Cached actual video size
218 double m_dRate
; // Current playback rate -
219 // see GetPlaybackRate for notes
220 wxLongLong m_llPausedPos
; // Paused position - see Pause()
221 GstXOverlay
* m_xoverlay
; // X Overlay that contains the GST video
222 wxMutex m_asynclock
; // See "discussion of internals"
223 class wxGStreamerMediaEventHandler
* m_eventHandler
; // see below
225 friend class wxGStreamerMediaEventHandler
;
226 friend class wxGStreamerLoadWaitTimer
;
227 DECLARE_DYNAMIC_CLASS(wxGStreamerMediaBackend
);
230 //-----------------------------------------------------------------------------
231 // wxGStreamerMediaEventHandler
233 // OK, this will take an explanation - basically gstreamer callbacks
234 // are issued in a seperate thread, and in this thread we may not set
235 // the state of the playbin, so we need to send a wx event in that
236 // callback so that we set the state of the media and other stuff
238 //-----------------------------------------------------------------------------
239 class wxGStreamerMediaEventHandler
: public wxEvtHandler
242 wxGStreamerMediaEventHandler(wxGStreamerMediaBackend
* be
) : m_be(be
)
244 this->Connect(wxID_ANY
, wxEVT_MEDIA_FINISHED
,
245 wxMediaEventHandler(wxGStreamerMediaEventHandler::OnMediaFinish
));
248 void OnMediaFinish(wxMediaEvent
& event
);
250 wxGStreamerMediaBackend
* m_be
;
253 //=============================================================================
255 //=============================================================================
257 IMPLEMENT_DYNAMIC_CLASS(wxGStreamerMediaBackend
, wxMediaBackend
)
259 //-----------------------------------------------------------------------------
263 //-----------------------------------------------------------------------------
265 //-----------------------------------------------------------------------------
266 // "expose_event" from m_ctrl->m_wxwindow
268 // Handle GTK expose event from our window - here we hopefully
269 // redraw the video in the case of pausing and other instances...
270 // (Returns TRUE to pass to other handlers, FALSE if not)
272 // TODO: Do a DEBUG_MAIN_THREAD/install_idle_handler here?
273 //-----------------------------------------------------------------------------
276 static gboolean
gtk_window_expose_callback(GtkWidget
*widget
,
277 GdkEventExpose
*event
,
278 wxGStreamerMediaBackend
*be
)
283 GdkWindow
*window
= GTK_PIZZA(be
->GetControl()->m_wxwindow
)->bin_window
;
285 // I've seen this reccommended somewhere...
286 // TODO: Is this needed? Maybe it is just cruft...
287 // gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(be->m_xoverlay),
288 // GDK_WINDOW_XWINDOW( window ) );
290 // If we have actual video.....
291 if(!(be
->m_videoSize
.x
==0&&be
->m_videoSize
.y
==0) &&
292 GST_STATE(be
->m_playbin
) >= GST_STATE_PAUSED
)
294 // GST Doesn't redraw automatically while paused
295 // Plus, the video sometimes doesn't redraw when it looses focus
296 // or is painted over so we just tell it to redraw...
297 gst_x_overlay_expose(be
->m_xoverlay
);
301 // draw a black background like some other backends do....
302 gdk_draw_rectangle (window
, widget
->style
->black_gc
, TRUE
, 0, 0,
303 widget
->allocation
.width
,
304 widget
->allocation
.height
);
312 //-----------------------------------------------------------------------------
313 // "realize" from m_ctrl->m_wxwindow
315 // If the window wasn't realized when Load was called, this is the
316 // callback for when it is - the purpose of which is to tell
317 // GStreamer to play the video in our control
318 //-----------------------------------------------------------------------------
321 static gint
gtk_window_realize_callback(GtkWidget
* theWidget
,
322 wxGStreamerMediaBackend
* be
)
324 DEBUG_MAIN_THREAD
// TODO: Is this neccessary?
326 if (g_isIdle
) // FIXME: Why is needed? For wxYield? ??
327 wxapp_install_idle_handler();
329 wxYield(); // FIXME: RN: X Server gets an error/crash if I don't do
330 // this or a messagebox beforehand?!?!??
332 GdkWindow
*window
= GTK_PIZZA(theWidget
)->bin_window
;
335 gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(be
->m_xoverlay
),
336 GDK_WINDOW_XWINDOW( window
)
338 g_signal_connect (be
->GetControl()->m_wxwindow
,
340 G_CALLBACK(gtk_window_expose_callback
), be
);
346 //-----------------------------------------------------------------------------
347 // "state-change" from m_playbin/GST_MESSAGE_STATE_CHANGE
349 // Called by gstreamer when the state changes - here we
350 // send the appropriate corresponding wx event.
352 // 0.8 only as HandleStateChange does this in both versions
353 //-----------------------------------------------------------------------------
354 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10
356 static void gst_state_change_callback(GstElement
*play
,
357 GstElementState oldstate
,
358 GstElementState newstate
,
359 wxGStreamerMediaBackend
* be
)
361 if(be
->m_asynclock
.TryLock() == wxMUTEX_NO_ERROR
)
363 be
->HandleStateChange(oldstate
, newstate
);
364 be
->m_asynclock
.Unlock();
370 //-----------------------------------------------------------------------------
371 // "eos" from m_playbin/GST_MESSAGE_EOS
373 // Called by gstreamer when the media is done playing ("end of stream")
374 //-----------------------------------------------------------------------------
376 static void gst_finish_callback(GstElement
*play
,
377 wxGStreamerMediaBackend
* be
)
379 wxLogTrace(wxTRACE_GStreamer
, wxT("gst_finish_callback"));
380 wxMediaEvent
event(wxEVT_MEDIA_FINISHED
);
381 be
->m_eventHandler
->AddPendingEvent(event
);
385 //-----------------------------------------------------------------------------
386 // "error" from m_playbin/GST_MESSAGE_ERROR
388 // Called by gstreamer when an error is encountered playing the media -
389 // We call wxLogTrace in addition wxLogSysError so that we can get it
390 // on the command line as well for those who want extra traces.
391 //-----------------------------------------------------------------------------
393 static void gst_error_callback(GstElement
*play
,
397 wxGStreamerMediaBackend
* be
)
400 sError
.Printf(wxT("gst_error_callback\n")
401 wxT("Error Message:%s\nDebug:%s\n"),
402 (const wxChar
*)wxConvUTF8
.cMB2WX(err
->message
),
403 (const wxChar
*)wxConvUTF8
.cMB2WX(debug
));
404 wxLogTrace(wxTRACE_GStreamer
, sError
);
405 wxLogSysError(sError
);
409 //-----------------------------------------------------------------------------
410 // "notify::caps" from the videopad inside "stream-info" of m_playbin
412 // Called by gstreamer when the video caps for the media is ready - currently
413 // we use the caps to get the natural size of the video
416 //-----------------------------------------------------------------------------
418 static void gst_notify_caps_callback(GstPad
* pad
,
420 wxGStreamerMediaBackend
* be
)
422 wxLogTrace(wxTRACE_GStreamer
, wxT("gst_notify_caps_callback"));
423 be
->QueryVideoSizeFromPad(pad
);
427 //-----------------------------------------------------------------------------
428 // "notify::stream-info" from m_playbin
430 // Run through the stuff in "stream-info" of m_playbin for a valid
431 // video pad, and then attempt to query the video size from it - if not
432 // set up an event to do so when ready.
434 // Currently unused - now we just query it directly using
435 // QueryVideoSizeFromElement.
438 //-----------------------------------------------------------------------------
439 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
441 static void gst_notify_stream_info_callback(GstElement
* element
,
443 wxGStreamerMediaBackend
* be
)
445 wxLogTrace(wxTRACE_GStreamer
, wxT("gst_notify_stream_info_callback"));
446 be
->QueryVideoSizeFromElement(be
->m_playbin
);
451 //-----------------------------------------------------------------------------
452 // "desired-size-changed" from m_xoverlay
454 // 0.8-specific this provides us with the video size when it changes -
455 // even though we get the caps as well this seems to come before the
456 // caps notification does...
458 // Note it will return 16,16 for an early-bird value or for audio
459 //-----------------------------------------------------------------------------
460 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10
462 static void gst_desired_size_changed_callback(GstElement
* play
,
463 guint width
, guint height
,
464 wxGStreamerMediaBackend
* be
)
466 if(!(width
== 16 && height
== 16))
468 be
->m_videoSize
.x
= width
;
469 be
->m_videoSize
.y
= height
;
472 be
->QueryVideoSizeFromElement(be
->m_playbin
);
477 //-----------------------------------------------------------------------------
478 // gst_bus_async_callback [static]
479 // gst_bus_sync_callback [static]
481 // Called by m_playbin for notifications such as end-of-stream in 0.10 -
482 // in previous versions g_signal notifications were used. Because everything
483 // in centered in one switch statement though it reminds one of old WinAPI
486 // gst_bus_sync_callback is that sync version that is called on the main GUI
487 // thread before the async version that we use to set the xwindow id of the
488 // XOverlay (NB: This isn't currently used - see CreateControl()).
489 //-----------------------------------------------------------------------------
490 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
492 static gboolean
gst_bus_async_callback(GstBus
* bus
,
494 wxGStreamerMediaBackend
* be
)
496 if(((GstElement
*)GST_MESSAGE_SRC(message
)) != be
->m_playbin
)
498 if(be
->m_asynclock
.TryLock() != wxMUTEX_NO_ERROR
)
501 switch(GST_MESSAGE_TYPE(message
))
503 case GST_MESSAGE_STATE_CHANGED
:
505 GstState oldstate
, newstate
, pendingstate
;
506 gst_message_parse_state_changed(message
, &oldstate
,
507 &newstate
, &pendingstate
);
508 be
->HandleStateChange(oldstate
, newstate
);
511 case GST_MESSAGE_EOS
:
513 gst_finish_callback(NULL
, be
);
516 case GST_MESSAGE_ERROR
:
520 gst_message_parse_error(message
, &error
, &debug
);
521 gst_error_callback(NULL
, NULL
, error
, debug
, be
);
528 be
->m_asynclock
.Unlock();
529 return FALSE
; // remove the message from Z queue
532 static GstBusSyncReply
gst_bus_sync_callback(GstBus
* bus
,
534 wxGStreamerMediaBackend
* be
)
536 // Pass a non-xwindowid-setting event on to the async handler where it
538 if (GST_MESSAGE_TYPE (message
) != GST_MESSAGE_ELEMENT
||
539 !gst_structure_has_name (message
->structure
, "prepare-xwindow-id"))
542 // NB: Unfortunately, the async callback can be quite
543 // buggy at times and often doesn't get called at all,
544 // so here we are processing it right here in the calling
545 // thread instead of the GUI one...
547 if(gst_bus_async_callback(bus
, message
, be
))
553 wxLogTrace(wxTRACE_GStreamer
, wxT("Got prepare-xwindow-id"));
555 return GST_BUS_DROP
; // We handled this message - drop from the queue
560 //-----------------------------------------------------------------------------
562 // Private (although not in the C++ sense) methods
564 //-----------------------------------------------------------------------------
566 //-----------------------------------------------------------------------------
567 // wxGStreamerMediaBackend::HandleStateChange
569 // Handles a state change event from our C Callback for "state-change" or
570 // the async queue in 0.10. (Mostly this is here to avoid locking the
571 // the mutex twice...)
572 //-----------------------------------------------------------------------------
573 void wxGStreamerMediaBackend::HandleStateChange(GstElementState oldstate
,
574 GstElementState newstate
)
578 case GST_STATE_PLAYING
:
579 wxLogTrace(wxTRACE_GStreamer
, wxT("Play event"));
582 case GST_STATE_PAUSED
:
583 // For some reason .10 sends a lot of oldstate == newstate
584 // messages - most likely for pending ones - also
585 // !<GST_STATE_PAUSED as we are only concerned
586 if(oldstate
< GST_STATE_PAUSED
|| oldstate
== newstate
)
588 if(wxGStreamerMediaBackend::GetPosition() != 0)
590 wxLogTrace(wxTRACE_GStreamer
, wxT("Pause event"));
595 wxLogTrace(wxTRACE_GStreamer
, wxT("Stop event"));
599 default: // GST_STATE_NULL etc.
604 //-----------------------------------------------------------------------------
605 // wxGStreamerMediaBackend::QueryVideoSizeFromElement
607 // Run through the stuff in "stream-info" of element for a valid
608 // video pad, and then attempt to query the video size from it - if not
609 // set up an event to do so when ready. Return true
610 // if we got a valid video pad.
611 //-----------------------------------------------------------------------------
612 bool wxGStreamerMediaBackend::QueryVideoSizeFromElement(GstElement
* element
)
614 const GList
*list
= NULL
;
615 g_object_get (G_OBJECT (element
), "stream-info", &list
, NULL
);
617 for ( ; list
!= NULL
; list
= list
->next
)
619 GObject
*info
= (GObject
*) list
->data
;
625 g_object_get (info
, "type", &type
, NULL
);
626 pspec
= g_object_class_find_property (
627 G_OBJECT_GET_CLASS (info
), "type");
628 val
= g_enum_get_value (G_PARAM_SPEC_ENUM (pspec
)->enum_class
, type
);
630 if (!strncasecmp(val
->value_name
, "video", 5) ||
631 !strncmp(val
->value_name
, "GST_STREAM_TYPE_VIDEO", 21))
633 // Newer gstreamer 0.8+ plugins are SUPPOSED to have "object"...
634 // but a lot of old plugins still use "pad" :)
635 pspec
= g_object_class_find_property (
636 G_OBJECT_GET_CLASS (info
), "object");
639 g_object_get (info
, "pad", &pad
, NULL
);
641 g_object_get (info
, "object", &pad
, NULL
);
643 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR <= 8
644 // Killed in 0.9, presumely because events and such
645 // should be pushed on pads regardless of whether they
646 // are currently linked
647 pad
= (GstPad
*) GST_PAD_REALIZE (pad
);
651 if(!QueryVideoSizeFromPad(pad
))
653 // wait for those caps to get ready
657 G_CALLBACK(gst_notify_caps_callback
),
662 }// end searching through info list
664 // no video (or extremely delayed stream-info)
667 m_videoSize
= wxSize(0,0);
674 //-----------------------------------------------------------------------------
675 // wxGStreamerMediaBackend::QueryVideoSizeFromPad
677 // Gets the size of our video (in wxSize) from a GstPad
678 //-----------------------------------------------------------------------------
679 bool wxGStreamerMediaBackend::QueryVideoSizeFromPad(GstPad
* pad
)
681 const GstCaps
* caps
= GST_PAD_CAPS(pad
);
684 const GstStructure
*s
= gst_caps_get_structure (caps
, 0);
687 gst_structure_get_int (s
, "width", &m_videoSize
.x
);
688 gst_structure_get_int (s
, "height", &m_videoSize
.y
);
691 par
= gst_structure_get_value (s
, "pixel-aspect-ratio");
695 wxLogTrace(wxTRACE_GStreamer
,
696 wxT("pixel-aspect-ratio found in pad"));
697 int num
= par
->data
[0].v_int
,
698 den
= par
->data
[1].v_int
;
700 // TODO: maybe better fraction normalization...
702 m_videoSize
.x
= (int) ((float) num
* m_videoSize
.x
/ den
);
704 m_videoSize
.y
= (int) ((float) den
* m_videoSize
.y
/ num
);
707 wxLogTrace(wxTRACE_GStreamer
, wxT("Adjusted video size: [%i,%i]"),
708 m_videoSize
.x
, m_videoSize
.y
);
712 return false; // not ready/massive failure
715 //-----------------------------------------------------------------------------
716 // wxGStreamerMediaBackend::SetupXOverlay
718 // Attempts to set the XWindow id of our GstXOverlay to tell it which
719 // window to play video in.
720 //-----------------------------------------------------------------------------
721 void wxGStreamerMediaBackend::SetupXOverlay()
723 // Use the xoverlay extension to tell gstreamer to play in our window
725 if(!GTK_WIDGET_REALIZED(m_ctrl
->m_wxwindow
))
727 // Not realized yet - set to connect at realization time
728 g_signal_connect (m_ctrl
->m_wxwindow
,
730 G_CALLBACK (gtk_window_realize_callback
),
735 wxYield(); // see realize callback...
736 GdkWindow
*window
= GTK_PIZZA(m_ctrl
->m_wxwindow
)->bin_window
;
740 gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(m_xoverlay
),
742 GDK_WINDOW_XWINDOW( window
)
749 g_signal_connect (m_ctrl
->m_wxwindow
,
750 // m_ctrl->m_wxwindow/*m_ctrl->m_widget*/,
752 G_CALLBACK(gtk_window_expose_callback
), this);
753 } // end if GtkPizza realized
757 //-----------------------------------------------------------------------------
758 // wxGStreamerMediaBackend::SyncStateChange
760 // This function is rather complex - basically the idea is that we
761 // poll the GstBus of m_playbin until it has reached desiredstate, an error
762 // is reached, or there are no more messages left in the GstBus queue.
764 // Returns true if there are no messages left in the queue or
765 // the current state reaches the disired state.
767 // PRECONDITION: Assumes m_asynclock is Lock()ed
768 //-----------------------------------------------------------------------------
769 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
770 bool wxGStreamerMediaBackend::SyncStateChange(GstElement
* element
,
771 GstElementState desiredstate
,
774 GstBus
* bus
= gst_element_get_bus(element
);
778 gint64 llTimeWaited
= 0;
783 // NB: The GStreamer gst_bus_poll is unfortunately broken and
784 // throws silly critical internal errors (for instance
785 // "message != NULL" when the whole point of it is to
786 // poll for the message in the first place!) so we implement
787 // our own "waiting mechinism"
788 if(gst_bus_have_pending(bus
) == FALSE
)
790 if(llTimeWaited
>= llTimeout
)
791 return true; // Reached timeout... assume success
792 llTimeWaited
+= 10*GST_MSECOND
;
797 message
= gst_bus_pop(bus
);
799 message
= gst_bus_poll(bus
, (GstMessageType
)
800 (GST_MESSAGE_STATE_CHANGED
|
802 GST_MESSAGE_EOS
), llTimeout
);
806 if(((GstElement
*)GST_MESSAGE_SRC(message
)) == element
)
808 switch(GST_MESSAGE_TYPE(message
))
810 case GST_MESSAGE_STATE_CHANGED
:
812 GstState oldstate
, newstate
, pendingstate
;
813 gst_message_parse_state_changed(message
, &oldstate
,
814 &newstate
, &pendingstate
);
815 if(newstate
== desiredstate
)
817 bSuccess
= bBreak
= true;
821 case GST_MESSAGE_ERROR
:
825 gst_message_parse_error(message
, &error
, &debug
);
826 gst_error_callback(NULL
, NULL
, error
, debug
, this);
830 case GST_MESSAGE_EOS
:
831 wxLogSysError(wxT("Reached end of stream prematurely"));
835 break; // not handled
839 gst_message_unref(message
);
844 #else // 0.8 implementation
845 bool wxGStreamerMediaBackend::SyncStateChange(GstElement
* element
,
846 GstElementState desiredstate
,
849 gint64 llTimeWaited
= 0;
850 while(GST_STATE(element
) != desiredstate
)
852 if(llTimeWaited
>= llTimeout
)
854 llTimeWaited
+= 10*GST_MSECOND
;
858 return llTimeWaited
!= llTimeout
;
862 //-----------------------------------------------------------------------------
863 // wxGStreamerMediaBackend::TryAudioSink
864 // wxGStreamerMediaBackend::TryVideoSink
866 // Uses various means to determine whether a passed in video/audio sink
867 // if suitable for us - if it is not we return false and unref the
868 // inappropriate sink.
869 //-----------------------------------------------------------------------------
870 bool wxGStreamerMediaBackend::TryAudioSink(GstElement
* audiosink
)
872 if( !GST_IS_ELEMENT(audiosink
) )
874 if(G_IS_OBJECT(audiosink
))
875 g_object_unref(audiosink
);
882 bool wxGStreamerMediaBackend::TryVideoSink(GstElement
* videosink
)
884 // Check if the video sink either is an xoverlay or might contain one...
885 if( !GST_IS_BIN(videosink
) && !GST_IS_X_OVERLAY(videosink
) )
887 if(G_IS_OBJECT(videosink
))
888 g_object_unref(videosink
);
892 // Make our video sink and make sure it supports the x overlay interface
893 // the x overlay enables us to put the video in our control window
894 // (i.e. we NEED it!) - also connect to the natural video size change event
895 if( GST_IS_BIN(videosink
) )
896 m_xoverlay
= (GstXOverlay
*)
897 gst_bin_get_by_interface (GST_BIN (videosink
),
900 m_xoverlay
= (GstXOverlay
*) videosink
;
902 if ( !GST_IS_X_OVERLAY(m_xoverlay
) )
904 g_object_unref(videosink
);
911 //-----------------------------------------------------------------------------
912 // wxGStreamerMediaEventHandler::OnMediaFinish
914 // Called when the media is about to stop
915 //-----------------------------------------------------------------------------
916 void wxGStreamerMediaEventHandler::OnMediaFinish(wxMediaEvent
& WXUNUSED(event
))
918 // (RN - I have no idea why I thought this was good behaviour....
919 // maybe it made sense for streaming/nonseeking data but
920 // generally it seems like a really bad idea) -
921 if(m_be
->SendStopEvent())
923 // Stop the media (we need to set it back to paused
924 // so that people can get the duration et al.
925 // and send the finish event (luckily we can "Sync" it out... LOL!)
926 // (We don't check return values here because we can't really do
928 wxMutexLocker
lock(m_be
->m_asynclock
);
930 // Set element to ready+sync it
931 gst_element_set_state (m_be
->m_playbin
, GST_STATE_READY
);
932 m_be
->SyncStateChange(m_be
->m_playbin
, GST_STATE_READY
);
934 // Now set it to paused + update pause pos to 0 and
935 // Sync that as well (note that we don't call Stop() here
936 // due to mutex issues)
937 gst_element_set_state (m_be
->m_playbin
, GST_STATE_PAUSED
);
938 m_be
->SyncStateChange(m_be
->m_playbin
, GST_STATE_PAUSED
);
939 m_be
->m_llPausedPos
= 0;
941 // Finally, queue the finish event
942 m_be
->QueueFinishEvent();
946 //-----------------------------------------------------------------------------
950 //-----------------------------------------------------------------------------
952 //-----------------------------------------------------------------------------
953 // wxGStreamerMediaBackend Constructor
955 // Sets m_playbin to NULL signifying we havn't loaded anything yet
956 //-----------------------------------------------------------------------------
957 wxGStreamerMediaBackend::wxGStreamerMediaBackend()
962 //-----------------------------------------------------------------------------
963 // wxGStreamerMediaBackend Destructor
965 // Stops/cleans up memory
967 // NB: This could trigger a critical warning but doing a SyncStateChange
968 // here is just going to slow down quitting of the app, which is bad.
969 //-----------------------------------------------------------------------------
970 wxGStreamerMediaBackend::~wxGStreamerMediaBackend()
972 // Dispose of the main player and related objects
975 wxASSERT( GST_IS_OBJECT(m_playbin
) );
976 gst_element_set_state (m_playbin
, GST_STATE_NULL
);
977 gst_object_unref (GST_OBJECT (m_playbin
));
978 delete m_eventHandler
;
982 //-----------------------------------------------------------------------------
983 // wxGStreamerMediaBackend::CreateControl
985 // Initializes GStreamer and creates the wx side of our media control
986 //-----------------------------------------------------------------------------
987 bool wxGStreamerMediaBackend::CreateControl(wxControl
* ctrl
, wxWindow
* parent
,
992 const wxValidator
& validator
,
993 const wxString
& name
)
999 //Convert arguments to unicode if enabled
1002 char **argvGST
= new char*[wxTheApp
->argc
+ 1];
1003 for ( i
= 0; i
< wxTheApp
->argc
; i
++ )
1005 argvGST
[i
] = wxStrdupA(wxConvUTF8
.cWX2MB(wxTheApp
->argv
[i
]));
1008 argvGST
[wxTheApp
->argc
] = NULL
;
1010 int argcGST
= wxTheApp
->argc
;
1012 #define argcGST wxTheApp->argc
1013 #define argvGST wxTheApp->argv
1016 //Really init gstreamer
1018 GError
* error
= NULL
;
1019 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
1020 bInited
= gst_init_check(&argcGST
, &argvGST
, &error
);
1022 bInited
= gst_init_check(&argcGST
, &argvGST
);
1025 // Cleanup arguments for unicode case
1027 for ( i
= 0; i
< argcGST
; i
++ )
1035 if(!bInited
) //gst_init_check fail?
1039 wxLogSysError(wxT("Could not initialize GStreamer\n")
1040 wxT("Error Message:%s"),
1041 (const wxChar
*) wxConvUTF8
.cMB2WX(error
->message
)
1043 g_error_free(error
);
1046 wxLogSysError(wxT("Could not initialize GStreamer"));
1052 // wxControl creation
1054 m_ctrl
= wxStaticCast(ctrl
, wxMediaCtrl
);
1057 // We handle our own GTK expose events
1058 m_ctrl
->m_noExpose
= true;
1061 if( !m_ctrl
->wxControl::Create(parent
, id
, pos
, size
,
1062 style
, // TODO: remove borders???
1065 wxFAIL_MSG(wxT("Could not create wxControl!!!"));
1070 // Turn off double-buffering so that
1071 // so it doesn't draw over the video and cause sporadic
1072 // disappearances of the video
1073 gtk_widget_set_double_buffered(m_ctrl
->m_wxwindow
, FALSE
);
1076 // don't erase the background of our control window
1077 // so that resizing is a bit smoother
1078 m_ctrl
->SetBackgroundStyle(wxBG_STYLE_CUSTOM
);
1080 // Create our playbin object
1081 m_playbin
= gst_element_factory_make ("playbin", "play");
1082 if (!GST_IS_ELEMENT(m_playbin
))
1084 if(G_IS_OBJECT(m_playbin
))
1085 g_object_unref(m_playbin
);
1086 wxLogSysError(wxT("Got an invalid playbin"));
1090 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10
1091 // Connect the glib events/callbacks we want to our playbin
1092 g_signal_connect(m_playbin
, "eos",
1093 G_CALLBACK(gst_finish_callback
), this);
1094 g_signal_connect(m_playbin
, "error",
1095 G_CALLBACK(gst_error_callback
), this);
1096 g_signal_connect(m_playbin
, "state-change",
1097 G_CALLBACK(gst_state_change_callback
), this);
1099 // GStreamer 0.10+ uses GstBus for this now, connect to the sync
1100 // handler as well so we can set the X window id of our xoverlay
1101 gst_bus_add_watch (gst_element_get_bus(m_playbin
),
1102 (GstBusFunc
) gst_bus_async_callback
, this);
1103 gst_bus_set_sync_handler(gst_element_get_bus(m_playbin
),
1104 (GstBusSyncHandler
) gst_bus_sync_callback
, this);
1105 g_signal_connect(m_playbin
, "notify::stream-info",
1106 G_CALLBACK(gst_notify_stream_info_callback
), this);
1109 // Get the audio sink
1110 GstElement
* audiosink
= gst_gconf_get_default_audio_sink();
1111 if( !TryAudioSink(audiosink
) )
1113 // fallback to autodetection, then alsa, then oss as a stopgap
1114 audiosink
= gst_element_factory_make ("autoaudiosink", "audio-sink");
1115 if( !TryAudioSink(audiosink
) )
1117 audiosink
= gst_element_factory_make ("alsasink", "alsa-output");
1118 if( !TryAudioSink(audiosink
) )
1120 audiosink
= gst_element_factory_make ("osssink", "play_audio");
1121 if( !TryAudioSink(audiosink
) )
1123 wxLogSysError(wxT("Could not find a valid audiosink"));
1130 // Setup video sink - first try gconf, then auto, then xvimage and
1131 // then finally plain ximage
1132 GstElement
* videosink
= gst_gconf_get_default_video_sink();
1133 if( !TryVideoSink(videosink
) )
1135 videosink
= gst_element_factory_make ("autovideosink", "video-sink");
1136 if( !TryVideoSink(videosink
) )
1138 videosink
= gst_element_factory_make ("xvimagesink", "video-sink");
1139 if( !TryVideoSink(videosink
) )
1141 // finally, do a final fallback to ximagesink
1143 gst_element_factory_make ("ximagesink", "video-sink");
1144 if( !TryVideoSink(videosink
) )
1146 g_object_unref(audiosink
);
1147 wxLogSysError(wxT("Could not find a suitable video sink"));
1154 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10
1155 // Not on 0.10... called when video size changes
1156 g_signal_connect(m_xoverlay
, "desired-size-changed",
1157 G_CALLBACK(gst_desired_size_changed_callback
), this);
1159 // Tell GStreamer which window to draw to in 0.8 - 0.10
1160 // sometimes needs this too...
1163 // Now that we know (or, rather think) our video and audio sink
1164 // are valid set our playbin to use them
1165 g_object_set (G_OBJECT (m_playbin
),
1166 "video-sink", videosink
,
1167 "audio-sink", audiosink
,
1170 m_eventHandler
= new wxGStreamerMediaEventHandler(this);
1174 //-----------------------------------------------------------------------------
1175 // wxGStreamerMediaBackend::Load (File version)
1177 // Just calls DoLoad() with a prepended file scheme
1178 //-----------------------------------------------------------------------------
1179 bool wxGStreamerMediaBackend::Load(const wxString
& fileName
)
1181 return DoLoad(wxString( wxT("file://") ) + fileName
);
1184 //-----------------------------------------------------------------------------
1185 // wxGStreamerMediaBackend::Load (URI version)
1187 // In the case of a file URI passes it unencoded -
1188 // also, as of 0.10.3 and earlier GstURI (the uri parser for gstreamer)
1189 // is sort of broken and only accepts uris with at least two slashes
1190 // after the scheme (i.e. file: == not ok, file:// == ok)
1191 //-----------------------------------------------------------------------------
1192 bool wxGStreamerMediaBackend::Load(const wxURI
& location
)
1194 if(location
.GetScheme().CmpNoCase(wxT("file")) == 0)
1196 wxString uristring
= location
.BuildUnescapedURI();
1198 //Workaround GstURI leading "//" problem and make sure it leads
1200 return DoLoad(wxString(wxT("file://")) +
1201 uristring
.Right(uristring
.length() - 5)
1205 return DoLoad(location
.BuildURI());
1208 //-----------------------------------------------------------------------------
1209 // wxGStreamerMediaBackend::DoLoad
1212 // 1) Reset member variables and set playbin back to ready state
1213 // 2) Check URI for validity and then tell the playbin to load it
1214 // 3) Set the playbin to the pause state
1216 // NB: Even after this function is over with we probably don't have the
1217 // video size or duration - no amount of clever hacking is going to get
1218 // around that, unfortunately.
1219 //-----------------------------------------------------------------------------
1220 bool wxGStreamerMediaBackend::DoLoad(const wxString
& locstring
)
1222 wxMutexLocker
lock(m_asynclock
); // lock state events and async callbacks
1224 // Reset positions & rate
1227 m_videoSize
= wxSize(0,0);
1229 // Set playbin to ready to stop the current media...
1230 if( gst_element_set_state (m_playbin
,
1231 GST_STATE_READY
) == GST_STATE_FAILURE
||
1232 !SyncStateChange(m_playbin
, GST_STATE_READY
))
1234 wxLogSysError(wxT("wxGStreamerMediaBackend::Load - ")
1235 wxT("Could not set initial state to ready"));
1239 // Make sure the passed URI is valid and tell playbin to load it
1240 // non-file uris are encoded
1241 wxASSERT(gst_uri_protocol_is_valid("file"));
1242 wxASSERT(gst_uri_is_valid(locstring
.mb_str()));
1244 g_object_set (G_OBJECT (m_playbin
), "uri",
1245 (const char*)locstring
.mb_str(), NULL
);
1247 // Try to pause media as gstreamer won't let us query attributes
1248 // such as video size unless it is paused or playing
1249 if( gst_element_set_state (m_playbin
,
1250 GST_STATE_PAUSED
) == GST_STATE_FAILURE
||
1251 !SyncStateChange(m_playbin
, GST_STATE_PAUSED
))
1253 return false; // no real error message needed here as this is
1254 // generic failure 99% of the time (i.e. no
1255 // source etc.) and has an error message
1259 NotifyMovieLoaded(); // Notify the user - all we can do for now
1264 //-----------------------------------------------------------------------------
1265 // wxGStreamerMediaBackend::Play
1267 // Sets the stream to a playing state
1269 // THREAD-UNSAFE in 0.8, maybe in 0.10 as well
1270 //-----------------------------------------------------------------------------
1271 bool wxGStreamerMediaBackend::Play()
1273 if (gst_element_set_state (m_playbin
,
1274 GST_STATE_PLAYING
) == GST_STATE_FAILURE
)
1279 //-----------------------------------------------------------------------------
1280 // wxGStreamerMediaBackend::Pause
1282 // Marks where we paused and pauses the stream
1284 // THREAD-UNSAFE in 0.8, maybe in 0.10 as well
1285 //-----------------------------------------------------------------------------
1286 bool wxGStreamerMediaBackend::Pause()
1288 m_llPausedPos
= wxGStreamerMediaBackend::GetPosition();
1289 if (gst_element_set_state (m_playbin
,
1290 GST_STATE_PAUSED
) == GST_STATE_FAILURE
)
1295 //-----------------------------------------------------------------------------
1296 // wxGStreamerMediaBackend::Stop
1298 // Pauses the stream and sets the position to 0. Note that this is
1299 // synchronous (!) pausing.
1301 // Due to the mutex locking this is probably thread-safe actually.
1302 //-----------------------------------------------------------------------------
1303 bool wxGStreamerMediaBackend::Stop()
1305 { // begin state lock
1306 wxMutexLocker
lock(m_asynclock
);
1307 if(gst_element_set_state (m_playbin
,
1308 GST_STATE_PAUSED
) == GST_STATE_FAILURE
||
1309 !SyncStateChange(m_playbin
, GST_STATE_PAUSED
))
1311 wxLogSysError(wxT("Could not set state to paused for Stop()"));
1316 bool bSeekedOK
= wxGStreamerMediaBackend::SetPosition(0);
1320 wxLogSysError(wxT("Could not seek to initial position in Stop()"));
1324 QueueStopEvent(); // Success
1328 //-----------------------------------------------------------------------------
1329 // wxGStreamerMediaBackend::GetState
1331 // Gets the state of the media
1332 //-----------------------------------------------------------------------------
1333 wxMediaState
wxGStreamerMediaBackend::GetState()
1335 switch(GST_STATE(m_playbin
))
1337 case GST_STATE_PLAYING
:
1338 return wxMEDIASTATE_PLAYING
;
1339 case GST_STATE_PAUSED
:
1340 if (m_llPausedPos
== 0)
1341 return wxMEDIASTATE_STOPPED
;
1343 return wxMEDIASTATE_PAUSED
;
1344 default://case GST_STATE_READY:
1345 return wxMEDIASTATE_STOPPED
;
1349 //-----------------------------------------------------------------------------
1350 // wxGStreamerMediaBackend::GetPosition
1352 // If paused, returns our marked position - otherwise it queries the
1353 // GStreamer playbin for the position and returns that
1356 // NB: At least in 0.8, when you pause and seek gstreamer
1357 // NB: doesn't update the position sometimes, so we need to keep track of
1358 // NB: whether we have paused or not and keep track of the time after the
1359 // NB: pause and whenever the user seeks while paused
1362 // THREAD-UNSAFE, at least if not paused. Requires media to be at least paused.
1363 //-----------------------------------------------------------------------------
1364 wxLongLong
wxGStreamerMediaBackend::GetPosition()
1366 if(GetState() != wxMEDIASTATE_PLAYING
)
1367 return m_llPausedPos
;
1371 GstFormat fmtTime
= GST_FORMAT_TIME
;
1373 if (!wxGst_element_query_position(m_playbin
, &fmtTime
, &pos
) ||
1374 fmtTime
!= GST_FORMAT_TIME
|| pos
== -1)
1376 return pos
/ GST_MSECOND
;
1380 //-----------------------------------------------------------------------------
1381 // wxGStreamerMediaBackend::SetPosition
1383 // Sets the position of the stream
1384 // Note that GST_MSECOND is 1000000 (GStreamer uses nanoseconds - so
1385 // there is 1000000 nanoseconds in a millisecond)
1387 // If we are paused we update the cached pause position.
1389 // This is also an exceedingly ugly function due to the three implementations
1390 // (or, rather two plus one implementation without a seek function).
1392 // This is asynchronous and thread-safe on both 0.8 and 0.10.
1394 // NB: This fires both a stop and play event if the media was previously
1395 // playing... which in some ways makes sense. And yes, this makes the video
1396 // go all haywire at times - a gstreamer bug...
1397 //-----------------------------------------------------------------------------
1398 bool wxGStreamerMediaBackend::SetPosition(wxLongLong where
)
1400 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR == 8 \
1401 && GST_VERSION_MICRO == 0
1402 // 0.8.0 has no gst_element_seek according to official docs!!!
1403 wxLogSysError(wxT("GStreamer 0.8.0 does not have gst_element_seek")
1404 wxT(" according to official docs"));
1408 # if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
1409 gst_element_seek (m_playbin
, m_dRate
, GST_FORMAT_TIME
,
1410 (GstSeekFlags
)(GST_SEEK_FLAG_FLUSH
| GST_SEEK_FLAG_KEY_UNIT
),
1411 GST_SEEK_TYPE_SET
, where
.GetValue() * GST_MSECOND
,
1412 GST_SEEK_TYPE_NONE
, GST_CLOCK_TIME_NONE
);
1414 // NB: Some gstreamer versions return false basically all the time
1415 // here - even totem doesn't bother to check the return value here
1416 // so I guess we'll just assume it worked -
1417 // TODO: maybe check the gst error callback???
1418 gst_element_seek (m_playbin
, (GstSeekType
) (GST_SEEK_METHOD_SET
|
1419 GST_FORMAT_TIME
| GST_SEEK_FLAG_FLUSH
),
1420 where
.GetValue() * GST_MSECOND
);
1422 # endif // GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
1425 m_llPausedPos
= where
;
1432 //-----------------------------------------------------------------------------
1433 // wxGStreamerMediaBackend::GetDuration
1435 // Obtains the total time of our stream
1436 // THREAD-UNSAFE, requires media to be paused or playing
1437 //-----------------------------------------------------------------------------
1438 wxLongLong
wxGStreamerMediaBackend::GetDuration()
1441 GstFormat fmtTime
= GST_FORMAT_TIME
;
1443 if(!wxGst_element_query_duration(m_playbin
, &fmtTime
, &length
) ||
1444 fmtTime
!= GST_FORMAT_TIME
|| length
== -1)
1446 return length
/ GST_MSECOND
;
1449 //-----------------------------------------------------------------------------
1450 // wxGStreamerMediaBackend::Move
1452 // Called when the window is moved - GStreamer takes care of this
1453 // for us so nothing is needed
1454 //-----------------------------------------------------------------------------
1455 void wxGStreamerMediaBackend::Move(int x
, int y
, int w
, int h
)
1459 //-----------------------------------------------------------------------------
1460 // wxGStreamerMediaBackend::GetVideoSize
1462 // Returns our cached video size from Load/gst_notify_caps_callback
1463 // gst_x_overlay_get_desired_size also does this in 0.8...
1464 //-----------------------------------------------------------------------------
1465 wxSize
wxGStreamerMediaBackend::GetVideoSize() const
1470 //-----------------------------------------------------------------------------
1471 // wxGStreamerMediaBackend::GetPlaybackRate
1472 // wxGStreamerMediaBackend::SetPlaybackRate
1474 // Obtains/Sets the playback rate of the stream
1476 //TODO: PlaybackRate not currently supported via playbin directly -
1477 //TODO: Ronald S. Bultje noted on gstreamer-devel:
1479 //TODO: Like "play at twice normal speed"? Or "play at 25 fps and 44,1 kHz"? As
1480 //TODO: for the first, yes, we have elements for that, btu they"re not part of
1481 //TODO: playbin. You can create a bin (with a ghost pad) containing the actual
1482 //TODO: video/audiosink and the speed-changing element for this, and set that
1483 //TODO: element as video-sink or audio-sink property in playbin. The
1484 //TODO: audio-element is called "speed", the video-element is called "videodrop"
1485 //TODO: (although that appears to be deprecated in favour of "videorate", which
1486 //TODO: again cannot do this, so this may not work at all in the end). For
1487 //TODO: forcing frame/samplerates, see audioscale and videorate. Audioscale is
1488 //TODO: part of playbin.
1490 // In 0.10 GStreamer has new gst_element_seek API that might
1491 // support this - and I've got an attempt to do so but it is untested
1492 // but it would appear to work...
1493 //-----------------------------------------------------------------------------
1494 double wxGStreamerMediaBackend::GetPlaybackRate()
1496 return m_dRate
; // Could use GST_QUERY_RATE but the API doesn't seem
1497 // final on that yet and there may not be any actual
1498 // plugins that support it...
1501 bool wxGStreamerMediaBackend::SetPlaybackRate(double dRate
)
1503 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
1504 #if 0 // not tested enough
1505 if( gst_element_seek (m_playbin
, dRate
, GST_FORMAT_TIME
,
1506 (GstSeekFlags
)(GST_SEEK_FLAG_FLUSH
| GST_SEEK_FLAG_KEY_UNIT
),
1507 GST_SEEK_TYPE_CUR
, 0,
1508 GST_SEEK_TYPE_NONE
, GST_CLOCK_TIME_NONE
) )
1520 //-----------------------------------------------------------------------------
1521 // wxGStreamerMediaBackend::GetDownloadProgress
1523 // Not really outwardly possible - have been suggested that one could
1524 // get the information from the component that "downloads"
1525 //-----------------------------------------------------------------------------
1526 wxLongLong
wxGStreamerMediaBackend::GetDownloadProgress()
1531 //-----------------------------------------------------------------------------
1532 // wxGStreamerMediaBackend::GetDownloadTotal
1534 // TODO: Cache this?
1535 // NB: The length changes every call for some reason due to
1536 // GStreamer implementation issues
1537 // THREAD-UNSAFE, requires media to be paused or playing
1538 //-----------------------------------------------------------------------------
1539 wxLongLong
wxGStreamerMediaBackend::GetDownloadTotal()
1542 GstFormat fmtBytes
= GST_FORMAT_BYTES
;
1544 if (!wxGst_element_query_duration(m_playbin
, &fmtBytes
, &length
) ||
1545 fmtBytes
!= GST_FORMAT_BYTES
|| length
== -1)
1550 //-----------------------------------------------------------------------------
1551 // wxGStreamerMediaBackend::SetVolume
1552 // wxGStreamerMediaBackend::GetVolume
1554 // Sets/Gets the volume through the playbin object.
1555 // Note that this requires a relatively recent gst-plugins so we
1556 // check at runtime to see whether it is available or not otherwise
1557 // GST spits out an error on the command line
1558 //-----------------------------------------------------------------------------
1559 bool wxGStreamerMediaBackend::SetVolume(double dVolume
)
1561 if(g_object_class_find_property(
1562 G_OBJECT_GET_CLASS(G_OBJECT(m_playbin
)),
1565 g_object_set(G_OBJECT(m_playbin
), "volume", dVolume
, NULL
);
1570 wxLogTrace(wxTRACE_GStreamer
,
1571 wxT("SetVolume: volume prop not found - 0.8.5 of ")
1572 wxT("gst-plugins probably needed"));
1577 double wxGStreamerMediaBackend::GetVolume()
1579 double dVolume
= 1.0;
1581 if(g_object_class_find_property(
1582 G_OBJECT_GET_CLASS(G_OBJECT(m_playbin
)),
1585 g_object_get(G_OBJECT(m_playbin
), "volume", &dVolume
, NULL
);
1589 wxLogTrace(wxTRACE_GStreamer
,
1590 wxT("GetVolume: volume prop not found - 0.8.5 of ")
1591 wxT("gst-plugins probably needed"));
1597 #endif //wxUSE_GSTREAMER
1599 // Force link into main library so this backend can be loaded
1600 #include "wx/html/forcelnk.h"
1601 FORCE_LINK_ME(basewxmediabackends
)
1603 #endif //wxUSE_MEDIACTRL