1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/unix/mediactrl.cpp
3 // Purpose: GStreamer backend for Unix
4 // Author: Ryan Norton <wxprojects@comcast.net>
7 // Copyright: (c) 2004-2005 Ryan Norton
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
16 #include "wx/mediactrl.h"
20 #include <gst/gst.h> // main gstreamer header
22 // xoverlay/video stuff, gst-gconf for 0.8
23 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
24 # include <gst/interfaces/xoverlay.h>
26 # include <gst/xoverlay/xoverlay.h>
27 # include <gst/gconf/gconf.h> // gstreamer glib configuration
31 #include "wx/log.h" // wxLogDebug/wxLogSysError/wxLogTrace
32 #include "wx/app.h" // wxTheApp->argc, wxTheApp->argv
33 #include "wx/timer.h" // wxTimer
36 #include "wx/filesys.h" // FileNameToURL()
37 #include "wx/thread.h" // wxMutex/wxMutexLocker
38 #include "wx/vector.h" // wxVector<wxString>
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 CheckForErrors();
190 bool DoLoad(const wxString
& locstring
);
191 wxMediaCtrl
* GetControl() { return m_ctrl
; } // for C Callbacks
192 void HandleStateChange(GstElementState oldstate
, GstElementState newstate
);
193 bool QueryVideoSizeFromElement(GstElement
* element
);
194 bool QueryVideoSizeFromPad(GstPad
* caps
);
195 void SetupXOverlay();
196 bool SyncStateChange(GstElement
* element
, GstElementState state
,
197 gint64 llTimeout
= wxGSTREAMER_TIMEOUT
);
198 bool TryAudioSink(GstElement
* audiosink
);
199 bool TryVideoSink(GstElement
* videosink
);
201 GstElement
* m_playbin
; // GStreamer media element
202 wxSize m_videoSize
; // Cached actual video size
203 double m_dRate
; // Current playback rate -
204 // see GetPlaybackRate for notes
205 wxLongLong m_llPausedPos
; // Paused position - see Pause()
206 GstXOverlay
* m_xoverlay
; // X Overlay that contains the GST video
207 wxMutex m_asynclock
; // See "discussion of internals"
208 class wxGStreamerMediaEventHandler
* m_eventHandler
; // see below
210 // Mutex protecting just the variables below which are set from
211 // gst_error_callback() called from a different thread.
215 Error(const gchar
* message
, const gchar
* debug
)
216 : m_message(message
, wxConvUTF8
),
217 m_debug(debug
, wxConvUTF8
)
225 wxVector
<Error
> m_errors
;
227 friend class wxGStreamerMediaEventHandler
;
228 friend class wxGStreamerLoadWaitTimer
;
229 DECLARE_DYNAMIC_CLASS(wxGStreamerMediaBackend
)
232 //-----------------------------------------------------------------------------
233 // wxGStreamerMediaEventHandler
235 // OK, this will take an explanation - basically gstreamer callbacks
236 // are issued in a separate thread, and in this thread we may not set
237 // the state of the playbin, so we need to send a wx event in that
238 // callback so that we set the state of the media and other stuff
240 //-----------------------------------------------------------------------------
241 class wxGStreamerMediaEventHandler
: public wxEvtHandler
244 wxGStreamerMediaEventHandler(wxGStreamerMediaBackend
* be
) : m_be(be
)
246 this->Connect(wxID_ANY
, wxEVT_MEDIA_FINISHED
,
247 wxMediaEventHandler(wxGStreamerMediaEventHandler::OnMediaFinish
));
250 void OnMediaFinish(wxMediaEvent
& event
);
252 wxGStreamerMediaBackend
* m_be
;
255 //=============================================================================
257 //=============================================================================
259 IMPLEMENT_DYNAMIC_CLASS(wxGStreamerMediaBackend
, wxMediaBackend
)
261 //-----------------------------------------------------------------------------
265 //-----------------------------------------------------------------------------
267 //-----------------------------------------------------------------------------
268 // "expose_event" from m_ctrl->m_wxwindow
270 // Handle GTK expose event from our window - here we hopefully
271 // redraw the video in the case of pausing and other instances...
272 // (Returns TRUE to pass to other handlers, FALSE if not)
274 // TODO: Do a DEBUG_MAIN_THREAD/install_idle_handler here?
275 //-----------------------------------------------------------------------------
280 draw(GtkWidget
* widget
, cairo_t
* cr
, wxGStreamerMediaBackend
* be
)
282 expose_event(GtkWidget
* widget
, GdkEventExpose
* event
, wxGStreamerMediaBackend
* be
)
285 // I've seen this recommended 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....
304 gtk_widget_get_allocation(widget
, &a
);
305 cairo_rectangle(cr
, 0, 0, a
.width
, a
.height
);
306 cairo_set_source_rgb(cr
, 0, 0, 0);
309 gdk_draw_rectangle (event
->window
, widget
->style
->black_gc
, TRUE
, 0, 0,
310 widget
->allocation
.width
,
311 widget
->allocation
.height
);
320 //-----------------------------------------------------------------------------
321 // "realize" from m_ctrl->m_wxwindow
323 // If the window wasn't realized when Load was called, this is the
324 // callback for when it is - the purpose of which is to tell
325 // GStreamer to play the video in our control
326 //-----------------------------------------------------------------------------
329 static gint
gtk_window_realize_callback(GtkWidget
* widget
,
330 wxGStreamerMediaBackend
* be
)
334 GdkWindow
* window
= gtk_widget_get_window(widget
);
337 gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(be
->m_xoverlay
),
338 GDK_WINDOW_XID(window
)
340 g_signal_connect (be
->GetControl()->m_wxwindow
,
342 "draw", G_CALLBACK(draw
),
344 "expose_event", G_CALLBACK(expose_event
),
352 //-----------------------------------------------------------------------------
353 // "state-change" from m_playbin/GST_MESSAGE_STATE_CHANGE
355 // Called by gstreamer when the state changes - here we
356 // send the appropriate corresponding wx event.
358 // 0.8 only as HandleStateChange does this in both versions
359 //-----------------------------------------------------------------------------
360 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10
362 static void gst_state_change_callback(GstElement
*play
,
363 GstElementState oldstate
,
364 GstElementState newstate
,
365 wxGStreamerMediaBackend
* be
)
367 if(be
->m_asynclock
.TryLock() == wxMUTEX_NO_ERROR
)
369 be
->HandleStateChange(oldstate
, newstate
);
370 be
->m_asynclock
.Unlock();
376 //-----------------------------------------------------------------------------
377 // "eos" from m_playbin/GST_MESSAGE_EOS
379 // Called by gstreamer when the media is done playing ("end of stream")
380 //-----------------------------------------------------------------------------
382 static void gst_finish_callback(GstElement
*WXUNUSED(play
),
383 wxGStreamerMediaBackend
* be
)
385 wxLogTrace(wxTRACE_GStreamer
, wxT("gst_finish_callback"));
386 wxMediaEvent
event(wxEVT_MEDIA_FINISHED
);
387 be
->m_eventHandler
->AddPendingEvent(event
);
391 //-----------------------------------------------------------------------------
392 // "error" from m_playbin/GST_MESSAGE_ERROR
394 // Called by gstreamer when an error is encountered playing the media -
395 // We call wxLogTrace in addition wxLogSysError so that we can get it
396 // on the command line as well for those who want extra traces.
397 //-----------------------------------------------------------------------------
399 static void gst_error_callback(GstElement
*WXUNUSED(play
),
400 GstElement
*WXUNUSED(src
),
403 wxGStreamerMediaBackend
* be
)
405 wxMutexLocker
lock(be
->m_mutexErr
);
406 be
->m_errors
.push_back(wxGStreamerMediaBackend::Error(err
->message
, debug
));
410 //-----------------------------------------------------------------------------
411 // "notify::caps" from the videopad inside "stream-info" of m_playbin
413 // Called by gstreamer when the video caps for the media is ready - currently
414 // we use the caps to get the natural size of the video
417 //-----------------------------------------------------------------------------
419 static void gst_notify_caps_callback(GstPad
* pad
,
420 GParamSpec
* WXUNUSED(pspec
),
421 wxGStreamerMediaBackend
* be
)
423 wxLogTrace(wxTRACE_GStreamer
, wxT("gst_notify_caps_callback"));
424 be
->QueryVideoSizeFromPad(pad
);
428 //-----------------------------------------------------------------------------
429 // "notify::stream-info" from m_playbin
431 // Run through the stuff in "stream-info" of m_playbin for a valid
432 // video pad, and then attempt to query the video size from it - if not
433 // set up an event to do so when ready.
435 // Currently unused - now we just query it directly using
436 // QueryVideoSizeFromElement.
439 //-----------------------------------------------------------------------------
440 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
442 static void gst_notify_stream_info_callback(GstElement
* WXUNUSED(element
),
443 GParamSpec
* WXUNUSED(pspec
),
444 wxGStreamerMediaBackend
* be
)
446 wxLogTrace(wxTRACE_GStreamer
, wxT("gst_notify_stream_info_callback"));
447 be
->QueryVideoSizeFromElement(be
->m_playbin
);
452 //-----------------------------------------------------------------------------
453 // "desired-size-changed" from m_xoverlay
455 // 0.8-specific this provides us with the video size when it changes -
456 // even though we get the caps as well this seems to come before the
457 // caps notification does...
459 // Note it will return 16,16 for an early-bird value or for audio
460 //-----------------------------------------------------------------------------
461 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10
463 static void gst_desired_size_changed_callback(GstElement
* play
,
464 guint width
, guint height
,
465 wxGStreamerMediaBackend
* be
)
467 if(!(width
== 16 && height
== 16))
469 be
->m_videoSize
.x
= width
;
470 be
->m_videoSize
.y
= height
;
473 be
->QueryVideoSizeFromElement(be
->m_playbin
);
478 //-----------------------------------------------------------------------------
479 // gst_bus_async_callback [static]
480 // gst_bus_sync_callback [static]
482 // Called by m_playbin for notifications such as end-of-stream in 0.10 -
483 // in previous versions g_signal notifications were used. Because everything
484 // in centered in one switch statement though it reminds one of old WinAPI
487 // gst_bus_sync_callback is that sync version that is called on the main GUI
488 // thread before the async version that we use to set the xwindow id of the
489 // XOverlay (NB: This isn't currently used - see CreateControl()).
490 //-----------------------------------------------------------------------------
491 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
493 static gboolean
gst_bus_async_callback(GstBus
* WXUNUSED(bus
),
495 wxGStreamerMediaBackend
* be
)
497 if ( GST_MESSAGE_TYPE(message
) == GST_MESSAGE_ERROR
)
501 gst_message_parse_error(message
, &error
, &debug
);
502 gst_error_callback(NULL
, NULL
, error
, debug
, be
);
506 if(((GstElement
*)GST_MESSAGE_SRC(message
)) != be
->m_playbin
)
508 if(be
->m_asynclock
.TryLock() != wxMUTEX_NO_ERROR
)
511 switch(GST_MESSAGE_TYPE(message
))
513 case GST_MESSAGE_STATE_CHANGED
:
515 GstState oldstate
, newstate
, pendingstate
;
516 gst_message_parse_state_changed(message
, &oldstate
,
517 &newstate
, &pendingstate
);
518 be
->HandleStateChange(oldstate
, newstate
);
521 case GST_MESSAGE_EOS
:
523 gst_finish_callback(NULL
, be
);
531 be
->m_asynclock
.Unlock();
532 return FALSE
; // remove the message from Z queue
535 static GstBusSyncReply
gst_bus_sync_callback(GstBus
* bus
,
537 wxGStreamerMediaBackend
* be
)
539 // Pass a non-xwindowid-setting event on to the async handler where it
541 if (GST_MESSAGE_TYPE (message
) != GST_MESSAGE_ELEMENT
||
542 !gst_structure_has_name (message
->structure
, "prepare-xwindow-id"))
545 // NB: Unfortunately, the async callback can be quite
546 // buggy at times and often doesn't get called at all,
547 // so here we are processing it right here in the calling
548 // thread instead of the GUI one...
550 if(gst_bus_async_callback(bus
, message
, be
))
556 wxLogTrace(wxTRACE_GStreamer
, wxT("Got prepare-xwindow-id"));
558 return GST_BUS_DROP
; // We handled this message - drop from the queue
563 //-----------------------------------------------------------------------------
565 // Private (although not in the C++ sense) methods
567 //-----------------------------------------------------------------------------
569 //-----------------------------------------------------------------------------
570 // wxGStreamerMediaBackend::HandleStateChange
572 // Handles a state change event from our C Callback for "state-change" or
573 // the async queue in 0.10. (Mostly this is here to avoid locking the
574 // the mutex twice...)
575 //-----------------------------------------------------------------------------
576 void wxGStreamerMediaBackend::HandleStateChange(GstElementState oldstate
,
577 GstElementState newstate
)
581 case GST_STATE_PLAYING
:
582 wxLogTrace(wxTRACE_GStreamer
, wxT("Play event"));
585 case GST_STATE_PAUSED
:
586 // For some reason .10 sends a lot of oldstate == newstate
587 // messages - most likely for pending ones - also
588 // !<GST_STATE_PAUSED as we are only concerned
589 if(oldstate
< GST_STATE_PAUSED
|| oldstate
== newstate
)
591 if(wxGStreamerMediaBackend::GetPosition() != 0)
593 wxLogTrace(wxTRACE_GStreamer
, wxT("Pause event"));
598 wxLogTrace(wxTRACE_GStreamer
, wxT("Stop event"));
602 default: // GST_STATE_NULL etc.
607 //-----------------------------------------------------------------------------
608 // wxGStreamerMediaBackend::QueryVideoSizeFromElement
610 // Run through the stuff in "stream-info" of element for a valid
611 // video pad, and then attempt to query the video size from it - if not
612 // set up an event to do so when ready. Return true
613 // if we got a valid video pad.
614 //-----------------------------------------------------------------------------
615 bool wxGStreamerMediaBackend::QueryVideoSizeFromElement(GstElement
* element
)
617 const GList
*list
= NULL
;
618 g_object_get (G_OBJECT (element
), "stream-info", &list
, NULL
);
620 for ( ; list
!= NULL
; list
= list
->next
)
622 GObject
*info
= (GObject
*) list
->data
;
628 g_object_get (info
, "type", &type
, NULL
);
629 pspec
= g_object_class_find_property (
630 G_OBJECT_GET_CLASS (info
), "type");
631 val
= g_enum_get_value (G_PARAM_SPEC_ENUM (pspec
)->enum_class
, type
);
633 if (!strncasecmp(val
->value_name
, "video", 5) ||
634 !strncmp(val
->value_name
, "GST_STREAM_TYPE_VIDEO", 21))
636 // Newer gstreamer 0.8+ plugins are SUPPOSED to have "object"...
637 // but a lot of old plugins still use "pad" :)
638 pspec
= g_object_class_find_property (
639 G_OBJECT_GET_CLASS (info
), "object");
642 g_object_get (info
, "pad", &pad
, NULL
);
644 g_object_get (info
, "object", &pad
, NULL
);
646 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR <= 8
647 // Killed in 0.9, presumely because events and such
648 // should be pushed on pads regardless of whether they
649 // are currently linked
650 pad
= (GstPad
*) GST_PAD_REALIZE (pad
);
654 if(!QueryVideoSizeFromPad(pad
))
656 // wait for those caps to get ready
660 G_CALLBACK(gst_notify_caps_callback
),
665 }// end searching through info list
667 // no video (or extremely delayed stream-info)
670 m_videoSize
= wxSize(0,0);
677 //-----------------------------------------------------------------------------
678 // wxGStreamerMediaBackend::QueryVideoSizeFromPad
680 // Gets the size of our video (in wxSize) from a GstPad
681 //-----------------------------------------------------------------------------
682 bool wxGStreamerMediaBackend::QueryVideoSizeFromPad(GstPad
* pad
)
684 const GstCaps
* caps
= GST_PAD_CAPS(pad
);
687 const GstStructure
*s
= gst_caps_get_structure (caps
, 0);
690 gst_structure_get_int (s
, "width", &m_videoSize
.x
);
691 gst_structure_get_int (s
, "height", &m_videoSize
.y
);
694 par
= gst_structure_get_value (s
, "pixel-aspect-ratio");
698 wxLogTrace(wxTRACE_GStreamer
,
699 wxT("pixel-aspect-ratio found in pad"));
700 int num
= par
->data
[0].v_int
,
701 den
= par
->data
[1].v_int
;
703 // TODO: maybe better fraction normalization...
705 m_videoSize
.x
= (int) ((float) num
* m_videoSize
.x
/ den
);
707 m_videoSize
.y
= (int) ((float) den
* m_videoSize
.y
/ num
);
710 wxLogTrace(wxTRACE_GStreamer
, wxT("Adjusted video size: [%i,%i]"),
711 m_videoSize
.x
, m_videoSize
.y
);
715 return false; // not ready/massive failure
718 //-----------------------------------------------------------------------------
719 // wxGStreamerMediaBackend::SetupXOverlay
721 // Attempts to set the XWindow id of our GstXOverlay to tell it which
722 // window to play video in.
723 //-----------------------------------------------------------------------------
724 void wxGStreamerMediaBackend::SetupXOverlay()
726 // Use the xoverlay extension to tell gstreamer to play in our window
728 if (!gtk_widget_get_realized(m_ctrl
->m_wxwindow
))
730 // Not realized yet - set to connect at realization time
731 g_signal_connect (m_ctrl
->m_wxwindow
,
733 G_CALLBACK (gtk_window_realize_callback
),
740 GdkWindow
* window
= gtk_widget_get_window(m_ctrl
->m_wxwindow
);
743 gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(m_xoverlay
),
745 GDK_WINDOW_XID(window
)
751 g_signal_connect(m_ctrl
->m_wxwindow
,
753 "draw", G_CALLBACK(draw
),
755 "expose_event", G_CALLBACK(expose_event
),
758 } // end if GtkPizza realized
762 //-----------------------------------------------------------------------------
763 // wxGStreamerMediaBackend::SyncStateChange
765 // This function is rather complex - basically the idea is that we
766 // poll the GstBus of m_playbin until it has reached desiredstate, an error
767 // is reached, or there are no more messages left in the GstBus queue.
769 // Returns true if there are no messages left in the queue or
770 // the current state reaches the disired state.
772 // PRECONDITION: Assumes m_asynclock is Lock()ed
773 //-----------------------------------------------------------------------------
774 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
775 bool wxGStreamerMediaBackend::SyncStateChange(GstElement
* element
,
776 GstElementState desiredstate
,
779 GstBus
* bus
= gst_element_get_bus(element
);
783 gint64 llTimeWaited
= 0;
788 // NB: The GStreamer gst_bus_poll is unfortunately broken and
789 // throws silly critical internal errors (for instance
790 // "message != NULL" when the whole point of it is to
791 // poll for the message in the first place!) so we implement
792 // our own "waiting mechinism"
793 if(gst_bus_have_pending(bus
) == FALSE
)
795 if(llTimeWaited
>= llTimeout
)
796 return true; // Reached timeout... assume success
797 llTimeWaited
+= 10*GST_MSECOND
;
802 message
= gst_bus_pop(bus
);
804 message
= gst_bus_poll(bus
, (GstMessageType
)
805 (GST_MESSAGE_STATE_CHANGED
|
807 GST_MESSAGE_EOS
), llTimeout
);
811 if(((GstElement
*)GST_MESSAGE_SRC(message
)) == element
)
813 switch(GST_MESSAGE_TYPE(message
))
815 case GST_MESSAGE_STATE_CHANGED
:
817 GstState oldstate
, newstate
, pendingstate
;
818 gst_message_parse_state_changed(message
, &oldstate
,
819 &newstate
, &pendingstate
);
820 if(newstate
== desiredstate
)
822 bSuccess
= bBreak
= true;
826 case GST_MESSAGE_ERROR
:
830 gst_message_parse_error(message
, &error
, &debug
);
831 gst_error_callback(NULL
, NULL
, error
, debug
, this);
835 case GST_MESSAGE_EOS
:
836 wxLogSysError(wxT("Reached end of stream prematurely"));
840 break; // not handled
844 gst_message_unref(message
);
849 #else // 0.8 implementation
850 bool wxGStreamerMediaBackend::SyncStateChange(GstElement
* element
,
851 GstElementState desiredstate
,
854 gint64 llTimeWaited
= 0;
855 while(GST_STATE(element
) != desiredstate
)
857 if(llTimeWaited
>= llTimeout
)
859 llTimeWaited
+= 10*GST_MSECOND
;
863 return llTimeWaited
!= llTimeout
;
867 //-----------------------------------------------------------------------------
868 // wxGStreamerMediaBackend::TryAudioSink
869 // wxGStreamerMediaBackend::TryVideoSink
871 // Uses various means to determine whether a passed in video/audio sink
872 // if suitable for us - if it is not we return false and unref the
873 // inappropriate sink.
874 //-----------------------------------------------------------------------------
875 bool wxGStreamerMediaBackend::TryAudioSink(GstElement
* audiosink
)
877 if( !GST_IS_ELEMENT(audiosink
) )
879 if(G_IS_OBJECT(audiosink
))
880 g_object_unref(audiosink
);
887 bool wxGStreamerMediaBackend::TryVideoSink(GstElement
* videosink
)
889 // Check if the video sink either is an xoverlay or might contain one...
890 if( !GST_IS_BIN(videosink
) && !GST_IS_X_OVERLAY(videosink
) )
892 if(G_IS_OBJECT(videosink
))
893 g_object_unref(videosink
);
897 // Make our video sink and make sure it supports the x overlay interface
898 // the x overlay enables us to put the video in our control window
899 // (i.e. we NEED it!) - also connect to the natural video size change event
900 if( GST_IS_BIN(videosink
) )
901 m_xoverlay
= (GstXOverlay
*)
902 gst_bin_get_by_interface (GST_BIN (videosink
),
905 m_xoverlay
= (GstXOverlay
*) videosink
;
907 if ( !GST_IS_X_OVERLAY(m_xoverlay
) )
909 g_object_unref(videosink
);
916 //-----------------------------------------------------------------------------
917 // wxGStreamerMediaEventHandler::OnMediaFinish
919 // Called when the media is about to stop
920 //-----------------------------------------------------------------------------
921 void wxGStreamerMediaEventHandler::OnMediaFinish(wxMediaEvent
& WXUNUSED(event
))
923 // (RN - I have no idea why I thought this was good behaviour....
924 // maybe it made sense for streaming/nonseeking data but
925 // generally it seems like a really bad idea) -
926 if(m_be
->SendStopEvent())
928 // Stop the media (we need to set it back to paused
929 // so that people can get the duration et al.
930 // and send the finish event (luckily we can "Sync" it out... LOL!)
931 // (We don't check return values here because we can't really do
933 wxMutexLocker
lock(m_be
->m_asynclock
);
935 // Set element to ready+sync it
936 gst_element_set_state (m_be
->m_playbin
, GST_STATE_READY
);
937 m_be
->SyncStateChange(m_be
->m_playbin
, GST_STATE_READY
);
939 // Now set it to paused + update pause pos to 0 and
940 // Sync that as well (note that we don't call Stop() here
941 // due to mutex issues)
942 gst_element_set_state (m_be
->m_playbin
, GST_STATE_PAUSED
);
943 m_be
->SyncStateChange(m_be
->m_playbin
, GST_STATE_PAUSED
);
944 m_be
->m_llPausedPos
= 0;
946 // Finally, queue the finish event
947 m_be
->QueueFinishEvent();
951 //-----------------------------------------------------------------------------
955 //-----------------------------------------------------------------------------
957 //-----------------------------------------------------------------------------
958 // wxGStreamerMediaBackend Constructor
960 // Sets m_playbin to NULL signifying we havn't loaded anything yet
961 //-----------------------------------------------------------------------------
962 wxGStreamerMediaBackend::wxGStreamerMediaBackend()
968 //-----------------------------------------------------------------------------
969 // wxGStreamerMediaBackend Destructor
971 // Stops/cleans up memory
973 // NB: This could trigger a critical warning but doing a SyncStateChange
974 // here is just going to slow down quitting of the app, which is bad.
975 //-----------------------------------------------------------------------------
976 wxGStreamerMediaBackend::~wxGStreamerMediaBackend()
978 // Dispose of the main player and related objects
981 wxASSERT( GST_IS_OBJECT(m_playbin
) );
982 gst_element_set_state (m_playbin
, GST_STATE_NULL
);
983 gst_object_unref (GST_OBJECT (m_playbin
));
984 delete m_eventHandler
;
988 //-----------------------------------------------------------------------------
989 // wxGStreamerMediaBackend::CheckForErrors
991 // Reports any errors received from gstreamer. Should be called after any
993 //-----------------------------------------------------------------------------
994 bool wxGStreamerMediaBackend::CheckForErrors()
996 wxMutexLocker
lock(m_mutexErr
);
997 if ( m_errors
.empty() )
1000 for ( unsigned n
= 0; n
< m_errors
.size(); n
++ )
1002 const Error
& err
= m_errors
[n
];
1004 wxLogTrace(wxTRACE_GStreamer
,
1005 "gst_error_callback: %s", err
.m_debug
);
1006 wxLogError(_("Media playback error: %s"), err
.m_message
);
1014 //-----------------------------------------------------------------------------
1015 // wxGStreamerMediaBackend::CreateControl
1017 // Initializes GStreamer and creates the wx side of our media control
1018 //-----------------------------------------------------------------------------
1019 bool wxGStreamerMediaBackend::CreateControl(wxControl
* ctrl
, wxWindow
* parent
,
1024 const wxValidator
& validator
,
1025 const wxString
& name
)
1031 //Convert arguments to unicode if enabled
1034 char **argvGST
= new char*[wxTheApp
->argc
+ 1];
1035 for ( i
= 0; i
< wxTheApp
->argc
; i
++ )
1037 argvGST
[i
] = wxStrdupA(wxTheApp
->argv
[i
].utf8_str());
1040 argvGST
[wxTheApp
->argc
] = NULL
;
1042 int argcGST
= wxTheApp
->argc
;
1044 #define argcGST wxTheApp->argc
1045 #define argvGST wxTheApp->argv
1048 //Really init gstreamer
1050 GError
* error
= NULL
;
1051 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
1052 bInited
= gst_init_check(&argcGST
, &argvGST
, &error
);
1054 bInited
= gst_init_check(&argcGST
, &argvGST
);
1057 // Cleanup arguments for unicode case
1059 for ( i
= 0; i
< argcGST
; i
++ )
1067 if(!bInited
) //gst_init_check fail?
1071 wxLogSysError(wxT("Could not initialize GStreamer\n")
1072 wxT("Error Message:%s"),
1073 (const wxChar
*) wxConvUTF8
.cMB2WX(error
->message
)
1075 g_error_free(error
);
1078 wxLogSysError(wxT("Could not initialize GStreamer"));
1084 // wxControl creation
1086 m_ctrl
= wxStaticCast(ctrl
, wxMediaCtrl
);
1089 // We handle our own GTK expose events
1090 m_ctrl
->m_noExpose
= true;
1093 if( !m_ctrl
->wxControl::Create(parent
, id
, pos
, size
,
1094 style
, // TODO: remove borders???
1097 wxFAIL_MSG(wxT("Could not create wxControl!!!"));
1102 // Turn off double-buffering so that
1103 // so it doesn't draw over the video and cause sporadic
1104 // disappearances of the video
1105 gtk_widget_set_double_buffered(m_ctrl
->m_wxwindow
, FALSE
);
1108 // don't erase the background of our control window
1109 // so that resizing is a bit smoother
1110 m_ctrl
->SetBackgroundStyle(wxBG_STYLE_CUSTOM
);
1112 // Create our playbin object
1113 m_playbin
= gst_element_factory_make ("playbin", "play");
1114 if (!GST_IS_ELEMENT(m_playbin
))
1116 if(G_IS_OBJECT(m_playbin
))
1117 g_object_unref(m_playbin
);
1118 wxLogSysError(wxT("Got an invalid playbin"));
1122 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10
1123 // Connect the glib events/callbacks we want to our playbin
1124 g_signal_connect(m_playbin
, "eos",
1125 G_CALLBACK(gst_finish_callback
), this);
1126 g_signal_connect(m_playbin
, "error",
1127 G_CALLBACK(gst_error_callback
), this);
1128 g_signal_connect(m_playbin
, "state-change",
1129 G_CALLBACK(gst_state_change_callback
), this);
1131 // GStreamer 0.10+ uses GstBus for this now, connect to the sync
1132 // handler as well so we can set the X window id of our xoverlay
1133 gst_bus_add_watch (gst_element_get_bus(m_playbin
),
1134 (GstBusFunc
) gst_bus_async_callback
, this);
1135 gst_bus_set_sync_handler(gst_element_get_bus(m_playbin
),
1136 (GstBusSyncHandler
) gst_bus_sync_callback
, this);
1137 g_signal_connect(m_playbin
, "notify::stream-info",
1138 G_CALLBACK(gst_notify_stream_info_callback
), this);
1141 // Get the audio sink
1142 GstElement
* audiosink
= gst_gconf_get_default_audio_sink();
1143 if( !TryAudioSink(audiosink
) )
1145 // fallback to autodetection, then alsa, then oss as a stopgap
1146 audiosink
= gst_element_factory_make ("autoaudiosink", "audio-sink");
1147 if( !TryAudioSink(audiosink
) )
1149 audiosink
= gst_element_factory_make ("alsasink", "alsa-output");
1150 if( !TryAudioSink(audiosink
) )
1152 audiosink
= gst_element_factory_make ("osssink", "play_audio");
1153 if( !TryAudioSink(audiosink
) )
1155 wxLogSysError(wxT("Could not find a valid audiosink"));
1162 // Setup video sink - first try gconf, then auto, then xvimage and
1163 // then finally plain ximage
1164 GstElement
* videosink
= gst_gconf_get_default_video_sink();
1165 if( !TryVideoSink(videosink
) )
1167 videosink
= gst_element_factory_make ("autovideosink", "video-sink");
1168 if( !TryVideoSink(videosink
) )
1170 videosink
= gst_element_factory_make ("xvimagesink", "video-sink");
1171 if( !TryVideoSink(videosink
) )
1173 // finally, do a final fallback to ximagesink
1175 gst_element_factory_make ("ximagesink", "video-sink");
1176 if( !TryVideoSink(videosink
) )
1178 g_object_unref(audiosink
);
1179 wxLogSysError(wxT("Could not find a suitable video sink"));
1186 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10
1187 // Not on 0.10... called when video size changes
1188 g_signal_connect(m_xoverlay
, "desired-size-changed",
1189 G_CALLBACK(gst_desired_size_changed_callback
), this);
1191 // Tell GStreamer which window to draw to in 0.8 - 0.10
1192 // sometimes needs this too...
1195 // Now that we know (or, rather think) our video and audio sink
1196 // are valid set our playbin to use them
1197 g_object_set (G_OBJECT (m_playbin
),
1198 "video-sink", videosink
,
1199 "audio-sink", audiosink
,
1202 m_eventHandler
= new wxGStreamerMediaEventHandler(this);
1206 //-----------------------------------------------------------------------------
1207 // wxGStreamerMediaBackend::Load (File version)
1209 // Just calls DoLoad() with a prepended file scheme
1210 //-----------------------------------------------------------------------------
1211 bool wxGStreamerMediaBackend::Load(const wxString
& fileName
)
1213 return DoLoad(wxFileSystem::FileNameToURL(fileName
));
1216 //-----------------------------------------------------------------------------
1217 // wxGStreamerMediaBackend::Load (URI version)
1219 // In the case of a file URI passes it unencoded -
1220 // also, as of 0.10.3 and earlier GstURI (the uri parser for gstreamer)
1221 // is sort of broken and only accepts uris with at least two slashes
1222 // after the scheme (i.e. file: == not ok, file:// == ok)
1223 //-----------------------------------------------------------------------------
1224 bool wxGStreamerMediaBackend::Load(const wxURI
& location
)
1226 if(location
.GetScheme().CmpNoCase(wxT("file")) == 0)
1228 wxString uristring
= location
.BuildUnescapedURI();
1230 //Workaround GstURI leading "//" problem and make sure it leads
1232 return DoLoad(wxString(wxT("file://")) +
1233 uristring
.Right(uristring
.length() - 5)
1237 return DoLoad(location
.BuildURI());
1240 //-----------------------------------------------------------------------------
1241 // wxGStreamerMediaBackend::DoLoad
1244 // 1) Reset member variables and set playbin back to ready state
1245 // 2) Check URI for validity and then tell the playbin to load it
1246 // 3) Set the playbin to the pause state
1248 // NB: Even after this function is over with we probably don't have the
1249 // video size or duration - no amount of clever hacking is going to get
1250 // around that, unfortunately.
1251 //-----------------------------------------------------------------------------
1252 bool wxGStreamerMediaBackend::DoLoad(const wxString
& locstring
)
1254 wxMutexLocker
lock(m_asynclock
); // lock state events and async callbacks
1256 // Reset positions & rate
1259 m_videoSize
= wxSize(0,0);
1261 // Set playbin to ready to stop the current media...
1262 if( gst_element_set_state (m_playbin
,
1263 GST_STATE_READY
) == GST_STATE_FAILURE
||
1264 !SyncStateChange(m_playbin
, GST_STATE_READY
))
1268 wxLogError(_("Failed to prepare playing \"%s\"."), locstring
);
1272 // free current media resources
1273 gst_element_set_state (m_playbin
, GST_STATE_NULL
);
1275 // Make sure the passed URI is valid and tell playbin to load it
1276 // non-file uris are encoded
1277 wxASSERT(gst_uri_protocol_is_valid("file"));
1278 wxASSERT(gst_uri_is_valid(locstring
.mb_str()));
1280 g_object_set (G_OBJECT (m_playbin
), "uri",
1281 (const char*)locstring
.mb_str(), NULL
);
1283 // Try to pause media as gstreamer won't let us query attributes
1284 // such as video size unless it is paused or playing
1285 if( gst_element_set_state (m_playbin
,
1286 GST_STATE_PAUSED
) == GST_STATE_FAILURE
||
1287 !SyncStateChange(m_playbin
, GST_STATE_PAUSED
))
1290 return false; // no real error message needed here as this is
1291 // generic failure 99% of the time (i.e. no
1292 // source etc.) and has an error message
1295 // It may happen that both calls above succeed but we actually had some
1296 // errors during the pipeline setup and it doesn't play. E.g. this happens
1297 // if XVideo extension is unavailable but xvimagesink is still used.
1298 if ( CheckForErrors() )
1302 NotifyMovieLoaded(); // Notify the user - all we can do for now
1307 //-----------------------------------------------------------------------------
1308 // wxGStreamerMediaBackend::Play
1310 // Sets the stream to a playing state
1312 // THREAD-UNSAFE in 0.8, maybe in 0.10 as well
1313 //-----------------------------------------------------------------------------
1314 bool wxGStreamerMediaBackend::Play()
1316 if (gst_element_set_state (m_playbin
,
1317 GST_STATE_PLAYING
) == GST_STATE_FAILURE
)
1326 //-----------------------------------------------------------------------------
1327 // wxGStreamerMediaBackend::Pause
1329 // Marks where we paused and pauses the stream
1331 // THREAD-UNSAFE in 0.8, maybe in 0.10 as well
1332 //-----------------------------------------------------------------------------
1333 bool wxGStreamerMediaBackend::Pause()
1335 m_llPausedPos
= wxGStreamerMediaBackend::GetPosition();
1336 if (gst_element_set_state (m_playbin
,
1337 GST_STATE_PAUSED
) == GST_STATE_FAILURE
)
1345 //-----------------------------------------------------------------------------
1346 // wxGStreamerMediaBackend::Stop
1348 // Pauses the stream and sets the position to 0. Note that this is
1349 // synchronous (!) pausing.
1351 // Due to the mutex locking this is probably thread-safe actually.
1352 //-----------------------------------------------------------------------------
1353 bool wxGStreamerMediaBackend::Stop()
1355 { // begin state lock
1356 wxMutexLocker
lock(m_asynclock
);
1357 if(gst_element_set_state (m_playbin
,
1358 GST_STATE_PAUSED
) == GST_STATE_FAILURE
||
1359 !SyncStateChange(m_playbin
, GST_STATE_PAUSED
))
1362 wxLogSysError(wxT("Could not set state to paused for Stop()"));
1367 bool bSeekedOK
= wxGStreamerMediaBackend::SetPosition(0);
1371 wxLogSysError(wxT("Could not seek to initial position in Stop()"));
1375 QueueStopEvent(); // Success
1379 //-----------------------------------------------------------------------------
1380 // wxGStreamerMediaBackend::GetState
1382 // Gets the state of the media
1383 //-----------------------------------------------------------------------------
1384 wxMediaState
wxGStreamerMediaBackend::GetState()
1386 switch(GST_STATE(m_playbin
))
1388 case GST_STATE_PLAYING
:
1389 return wxMEDIASTATE_PLAYING
;
1390 case GST_STATE_PAUSED
:
1391 if (m_llPausedPos
== 0)
1392 return wxMEDIASTATE_STOPPED
;
1394 return wxMEDIASTATE_PAUSED
;
1395 default://case GST_STATE_READY:
1396 return wxMEDIASTATE_STOPPED
;
1400 //-----------------------------------------------------------------------------
1401 // wxGStreamerMediaBackend::GetPosition
1403 // If paused, returns our marked position - otherwise it queries the
1404 // GStreamer playbin for the position and returns that
1407 // NB: At least in 0.8, when you pause and seek gstreamer
1408 // NB: doesn't update the position sometimes, so we need to keep track of
1409 // NB: whether we have paused or not and keep track of the time after the
1410 // NB: pause and whenever the user seeks while paused
1413 // THREAD-UNSAFE, at least if not paused. Requires media to be at least paused.
1414 //-----------------------------------------------------------------------------
1415 wxLongLong
wxGStreamerMediaBackend::GetPosition()
1417 if(GetState() != wxMEDIASTATE_PLAYING
)
1418 return m_llPausedPos
;
1422 GstFormat fmtTime
= GST_FORMAT_TIME
;
1424 if (!wxGst_element_query_position(m_playbin
, &fmtTime
, &pos
) ||
1425 fmtTime
!= GST_FORMAT_TIME
|| pos
== -1)
1427 return pos
/ GST_MSECOND
;
1431 //-----------------------------------------------------------------------------
1432 // wxGStreamerMediaBackend::SetPosition
1434 // Sets the position of the stream
1435 // Note that GST_MSECOND is 1000000 (GStreamer uses nanoseconds - so
1436 // there is 1000000 nanoseconds in a millisecond)
1438 // If we are paused we update the cached pause position.
1440 // This is also an exceedingly ugly function due to the three implementations
1441 // (or, rather two plus one implementation without a seek function).
1443 // This is asynchronous and thread-safe on both 0.8 and 0.10.
1445 // NB: This fires both a stop and play event if the media was previously
1446 // playing... which in some ways makes sense. And yes, this makes the video
1447 // go all haywire at times - a gstreamer bug...
1448 //-----------------------------------------------------------------------------
1449 bool wxGStreamerMediaBackend::SetPosition(wxLongLong where
)
1451 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR == 8 \
1452 && GST_VERSION_MICRO == 0
1453 // 0.8.0 has no gst_element_seek according to official docs!!!
1454 wxLogSysError(wxT("GStreamer 0.8.0 does not have gst_element_seek")
1455 wxT(" according to official docs"));
1459 # if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
1460 gst_element_seek (m_playbin
, m_dRate
, GST_FORMAT_TIME
,
1461 (GstSeekFlags
)(GST_SEEK_FLAG_FLUSH
| GST_SEEK_FLAG_KEY_UNIT
),
1462 GST_SEEK_TYPE_SET
, where
.GetValue() * GST_MSECOND
,
1463 GST_SEEK_TYPE_NONE
, GST_CLOCK_TIME_NONE
);
1465 // NB: Some gstreamer versions return false basically all the time
1466 // here - even totem doesn't bother to check the return value here
1467 // so I guess we'll just assume it worked -
1468 // TODO: maybe check the gst error callback???
1469 gst_element_seek (m_playbin
, (GstSeekType
) (GST_SEEK_METHOD_SET
|
1470 GST_FORMAT_TIME
| GST_SEEK_FLAG_FLUSH
),
1471 where
.GetValue() * GST_MSECOND
);
1473 # endif // GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
1476 m_llPausedPos
= where
;
1483 //-----------------------------------------------------------------------------
1484 // wxGStreamerMediaBackend::GetDuration
1486 // Obtains the total time of our stream
1487 // THREAD-UNSAFE, requires media to be paused or playing
1488 //-----------------------------------------------------------------------------
1489 wxLongLong
wxGStreamerMediaBackend::GetDuration()
1492 GstFormat fmtTime
= GST_FORMAT_TIME
;
1494 if(!wxGst_element_query_duration(m_playbin
, &fmtTime
, &length
) ||
1495 fmtTime
!= GST_FORMAT_TIME
|| length
== -1)
1497 return length
/ GST_MSECOND
;
1500 //-----------------------------------------------------------------------------
1501 // wxGStreamerMediaBackend::Move
1503 // Called when the window is moved - GStreamer takes care of this
1504 // for us so nothing is needed
1505 //-----------------------------------------------------------------------------
1506 void wxGStreamerMediaBackend::Move(int WXUNUSED(x
),
1513 //-----------------------------------------------------------------------------
1514 // wxGStreamerMediaBackend::GetVideoSize
1516 // Returns our cached video size from Load/gst_notify_caps_callback
1517 // gst_x_overlay_get_desired_size also does this in 0.8...
1518 //-----------------------------------------------------------------------------
1519 wxSize
wxGStreamerMediaBackend::GetVideoSize() const
1524 //-----------------------------------------------------------------------------
1525 // wxGStreamerMediaBackend::GetPlaybackRate
1526 // wxGStreamerMediaBackend::SetPlaybackRate
1528 // Obtains/Sets the playback rate of the stream
1530 //TODO: PlaybackRate not currently supported via playbin directly -
1531 //TODO: Ronald S. Bultje noted on gstreamer-devel:
1533 //TODO: Like "play at twice normal speed"? Or "play at 25 fps and 44,1 kHz"? As
1534 //TODO: for the first, yes, we have elements for that, btu they"re not part of
1535 //TODO: playbin. You can create a bin (with a ghost pad) containing the actual
1536 //TODO: video/audiosink and the speed-changing element for this, and set that
1537 //TODO: element as video-sink or audio-sink property in playbin. The
1538 //TODO: audio-element is called "speed", the video-element is called "videodrop"
1539 //TODO: (although that appears to be deprecated in favour of "videorate", which
1540 //TODO: again cannot do this, so this may not work at all in the end). For
1541 //TODO: forcing frame/samplerates, see audioscale and videorate. Audioscale is
1542 //TODO: part of playbin.
1544 // In 0.10 GStreamer has new gst_element_seek API that might
1545 // support this - and I've got an attempt to do so but it is untested
1546 // but it would appear to work...
1547 //-----------------------------------------------------------------------------
1548 double wxGStreamerMediaBackend::GetPlaybackRate()
1550 return m_dRate
; // Could use GST_QUERY_RATE but the API doesn't seem
1551 // final on that yet and there may not be any actual
1552 // plugins that support it...
1555 bool wxGStreamerMediaBackend::SetPlaybackRate(double dRate
)
1557 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
1558 #if 0 // not tested enough
1559 if( gst_element_seek (m_playbin
, dRate
, GST_FORMAT_TIME
,
1560 (GstSeekFlags
)(GST_SEEK_FLAG_FLUSH
| GST_SEEK_FLAG_KEY_UNIT
),
1561 GST_SEEK_TYPE_CUR
, 0,
1562 GST_SEEK_TYPE_NONE
, GST_CLOCK_TIME_NONE
) )
1576 //-----------------------------------------------------------------------------
1577 // wxGStreamerMediaBackend::GetDownloadProgress
1579 // Not really outwardly possible - have been suggested that one could
1580 // get the information from the component that "downloads"
1581 //-----------------------------------------------------------------------------
1582 wxLongLong
wxGStreamerMediaBackend::GetDownloadProgress()
1587 //-----------------------------------------------------------------------------
1588 // wxGStreamerMediaBackend::GetDownloadTotal
1590 // TODO: Cache this?
1591 // NB: The length changes every call for some reason due to
1592 // GStreamer implementation issues
1593 // THREAD-UNSAFE, requires media to be paused or playing
1594 //-----------------------------------------------------------------------------
1595 wxLongLong
wxGStreamerMediaBackend::GetDownloadTotal()
1598 GstFormat fmtBytes
= GST_FORMAT_BYTES
;
1600 if (!wxGst_element_query_duration(m_playbin
, &fmtBytes
, &length
) ||
1601 fmtBytes
!= GST_FORMAT_BYTES
|| length
== -1)
1606 //-----------------------------------------------------------------------------
1607 // wxGStreamerMediaBackend::SetVolume
1608 // wxGStreamerMediaBackend::GetVolume
1610 // Sets/Gets the volume through the playbin object.
1611 // Note that this requires a relatively recent gst-plugins so we
1612 // check at runtime to see whether it is available or not otherwise
1613 // GST spits out an error on the command line
1614 //-----------------------------------------------------------------------------
1615 bool wxGStreamerMediaBackend::SetVolume(double dVolume
)
1617 if(g_object_class_find_property(
1618 G_OBJECT_GET_CLASS(G_OBJECT(m_playbin
)),
1621 g_object_set(G_OBJECT(m_playbin
), "volume", dVolume
, NULL
);
1626 wxLogTrace(wxTRACE_GStreamer
,
1627 wxT("SetVolume: volume prop not found - 0.8.5 of ")
1628 wxT("gst-plugins probably needed"));
1633 double wxGStreamerMediaBackend::GetVolume()
1635 double dVolume
= 1.0;
1637 if(g_object_class_find_property(
1638 G_OBJECT_GET_CLASS(G_OBJECT(m_playbin
)),
1641 g_object_get(G_OBJECT(m_playbin
), "volume", &dVolume
, NULL
);
1645 wxLogTrace(wxTRACE_GStreamer
,
1646 wxT("GetVolume: volume prop not found - 0.8.5 of ")
1647 wxT("gst-plugins probably needed"));
1653 #endif //wxUSE_GSTREAMER
1655 // Force link into main library so this backend can be loaded
1656 #include "wx/html/forcelnk.h"
1657 FORCE_LINK_ME(basewxmediabackends
)
1659 #endif //wxUSE_MEDIACTRL